ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

树莓派+虚拟机:ROS与OpenCV实现CamShift目标跟踪小车

树莓派+虚拟机:ROS与OpenCV实现CamShift目标跟踪小车 简介面向“停车场管理系统”毕业设计课题的基于机器人操作系统与开源计算机视觉库的视觉跟踪智能小车项目包围绕树莓派3B主机与Ubuntu虚拟机的联调配置基于USB摄像头采集视频流采用CamShift算法完成实时图像处理与目标跟踪适合机器人、嵌入式及计算机视觉方向的本科生作为课程设计或毕业设计参考。压缩包共八个文件以Python脚本、ROS配置、文本说明、Word与Markdown文档为主整体约四十三KB内含工作空间源码模块与配套说明文档便于对照学习。目前已有三十三人浏览学习项目完整覆盖主从机配置、视频流采集、目标跟踪算法与停车场管理应用场景能帮助读者快速搭建同类视觉小车原型。此外附赠的Word版参考文档可从中获取树莓派环境配置思路、ROS节点组织方式以及算法实现细节适合作为毕业设计开题或中期实现的辅助素材。 最近在整理一套基于ROS和OpenCV的视觉跟踪智能小车项目正好把树莓派3B、Ubuntu虚拟机和CamShift这些关键词串到了一起。项目结构很典型树莓派3B作为主机负责USB摄像头采集和ROS主控Ubuntu虚拟机作为从机跑OpenCV的CamShift实时目标跟踪算法。标题里那份R.zip其实是我打包好的ROS工作空间和OpenCV测试代码解压后改改IP就能跑。对于想学ROS多机通信、OpenCV目标跟踪、或者树莓派视觉小车的人来说这套配置能直接照抄。我先把整体设计思路写清楚再一步步拆解环境、算法、通信和排坑。1. 项目整体设计树莓派当主机虚拟机当从机1.1 为什么这么分树莓派3B的性能摆在那里四核1.2GHz内存只有1GB跑桌面环境和编译任务都费劲要是再让它同时处理USB摄像头采集、CamShift直方图计算和反向投影帧率会掉得很难看。所以在动手之前我先把这个项目拆成了两层树莓派只负责把摄像头的图像数据拿到包装成ROS话题发到网络上顺便把roscore跑起来当主机虚拟机跑在宿主机里CPU和内存都更宽裕专门处理OpenCV的视觉跟踪再把处理结果发回给控制端。这种主从架构在实际机器人项目里非常常见本质就是“嵌入式端做数据采集上位机做算法”。虚拟机的另一个优势是方便做快照。CamShift的调参、直方图区间选择、跟踪窗口初始化这些步骤一不小心就会把代码改乱虚拟机里随时可以回滚到之前的干净环境。树莓派上则尽量少动系统保持网络配置稳定。这样分工调试起来思路清晰得多。1.2 系统架构与数据流整个系统的数据链路是这样的USB摄像头插入树莓派通过V4L2驱动读出原始图像树莓派上的摄像头节点用cv_bridge把OpenCV的Mat转换成ROS的sensor_msgs/Image发布到话题 /camera/image_raw。虚拟机上运行的跟踪节点订阅这个图像话题拿到每一帧后先做色彩空间转换再用CamShift算法计算出目标所在的搜索窗口最后把结果转换成可视化图像 /track/image 和位置数据话题 /target/position。如果需要驱动小车控制节点可以订阅 /target/position把像素偏差换算成 /cmd_vel 速度指令。这样设计的好处是模块之间完全解耦。摄像头坏了只查树莓派算法跑飞了只查虚拟机不会相互牵连。对于学习ROS的人来说这种节点划分也正好对应发布者、订阅者两种通信模型可以在实战里把概念吃透。1.3 R.zip里面有什么标题里提到的R.zip很多人会以为是个专门的软件包其实它是整个项目的压缩包里面主要包含一个catkin工作空间常见结构是catkin_ws/srcsrc下分几个功能包。比如usb_cam或自定义的camera_node负责摄像头采集track_node负责CamShift跟踪还有一份README记录了IP修改和启动顺序。解压之后不要整个目录直接复制到树莓派我习惯只拷贝src目录在自己机器上重新catkin_make这样可以避免编译缓存和绝对路径问题。如果你下到的R.zip里没有launch文件我建议自己写一个combine.launch把摄像头节点和跟踪节点都丢进去这样每次只需要roslaunch一个命令不需要开好几个终端。后面我会把launch的写法也放在文章里。2. 环境搭建与依赖安装2.1 树莓派端系统选型和基础安装树莓派3B建议直接装Ubuntu MATE 16.04或者18.04分别对应ROS Kinetic和Melodic。也有人用树莓派官方系统Raspbian但ROS的软件源支持不如Ubuntu方便后面装包会遇到各种依赖问题。系统确定之后ROS的安装我不展开重复官方教程直接推荐新手用鱼香ROS的一键安装命令它可以自动配置源和安装ROS省掉很多手打命令的麻烦。如果想手动装就把key、source、apt install ros-xxx-desktop全部走一遍效果一样。OpenCV这边树莓派上不需要源码编译直接apt安装python3-opencv就能满足项目要求。注意apt源里的OpenCV通常是4.x版本配ROS Melodic/Noetic的cv_bridge时可能会出现Python绑定问题最好先用python3 -c import cv2确认能否正常导入。摄像头方面插上USB摄像头后先lsusb确认设备再看ls -l /dev/video0很多问题其实在第一步就能发现。2.2 虚拟机端Ubuntu和ROS版本搭配虚拟机建议用VMware或者VirtualBoxUbuntu版本选18.04或20.04对应ROS Melodic或Noetic。安装完系统以后先把VMware Tools或增强功能装上否则虚拟机网络和剪贴板都不好使。这里有一个关键点虚拟机的网络模式必须选桥接让虚拟机和树莓派处于同一个局域网这样两边的ROS才能发现对方。很多人在NAT模式下折腾半天都连不上其实问题就出在这里。OpenCV同样用apt安装就好python3-opencv足够跑CamShift。至于热词里的cuda opencv在这个项目里没必要碰除非你宿主机有独立NVIDIA显卡并且虚拟机配置了GPU直通否则CUDA加速在虚拟机里只是摆设。真的要想跑深度学习视觉不如把整个算法放到宿主机树莓派只发图这是后话。树莓派和虚拟机之间最好配置SSH方便互相拷贝代码和配置文件。2.3 ROS主从机通信配置ROS多机通信的核心就两个环境变量ROS_MASTER_URI和ROS_IP。树莓派的IP假设是192.168.1.100虚拟机是192.168.1.101则树莓派上执行export ROS_MASTER_URIhttp://192.168.1.100:11311 export ROS_IP192.168.1.100虚拟机上执行export ROS_MASTER_URIhttp://192.168.1.100:11311 export ROS_IP192.168.1.101关键点有两个。第一ROS_MASTER_URI要指向跑roscore的那台机器这里就是树莓派第二ROS_IP必须填自己本机的IP不能填对方IP。建议把这两行追加到~/.bashrc但要注意如果IP会变开机后还是手动检查一遍。再进一步可以在两边的/etc/hosts里互相加入对方的主机名和IP映射这样节点里的hostname就能解析避免有些包写死localhost。配置完以后先在树莓派上启动roscore然后在虚拟机里rostopic list如果能列出节点列表就说明主从通信成功了。还有一个小技巧rqt_graph能看到完整的节点和话题连接图排错非常直观。2.4 摄像头节点的启动方式摄像头节点最简单的方案是直接用usb_cam包参数在launch文件里配置。下面是一个最小示例launch node nameusb_cam pkgusb_cam typeusb_cam_node outputscreen param namevideo_device value/dev/video0 / param nameimage_width value640 / param nameimage_height value480 / param namepixel_format valueyuyv / param namecamera_frame_id valuecamera / param nameframerate value15 / /node /launch树莓派3B的USB带宽有限640x48015fps是一个比较稳的起步配置。如果发现画面卡顿优先降低framerate到10而不是提高分辨率。usb_cam发布的话题默认是/usb_cam/image_raw后续跟踪节点订阅时改成自己实际的话题名就行。3. CamShift算法原理与代码实现3.1 从Meanshift到CamShiftCamShift的全称是Continuously Adaptive Meanshift直译过来是“连续自适应均值漂移”。它从Meanshift演化而来。Meanshift的思路是先在图像上根据目标颜色直方图生成一个概率分布图然后从初始窗口位置开始不断把窗口中心移动到窗口内像素概率密度的质心直到收敛。这种方法的优点是计算量小、不需要训练缺点是窗口大小固定目标走近时窗口不会变大目标走远时窗口也不会缩小很容易跟丢。CamShift在Meanshift的基础上通过计算窗口内像素的二阶矩得到目标的尺寸和方向然后自适应调整下一帧的搜索窗口大小。所以在目标距离变化、有轻微旋转的场景下CamShift比Meanshift稳定很多。OpenCV里已经封装好了cv2.CamShift函数我们只需要自己准备反向投影图和初始窗口它返回一个旋转矩形可以直接画目标框。虽然深度学习跟踪方法已经很成熟但CamShift作为轻量级跟踪器在树莓派加虚拟机这种架构上依然有学习价值。3.2 实时图像处理流程整个CamShift跟踪流程可以分成六个步骤。第一步从ROS图像消息拿到当前帧用cv_bridge转成OpenCV的BGR格式。第二步把BGR转成HSV色彩空间因为HSV中的H分量对光照变化相对不敏感CamShift通常只用H通道做颜色直方图效果就足够好。第三步在初始帧里用鼠标框选目标cv2.selectROI计算目标区域的H通道直方图再做归一化。第四步对后续每一帧调用cv2.calcBackProject得到当前帧中每个像素属于目标颜色的概率分布图。第五步把上一步的反向投影图和当前搜索窗口传给cv2.CamShift它会返回更新后的窗口和旋转矩形。第六步在原始帧上绘制跟踪结果并发布出去。颜色直方图的bin数量很关键。bin太粗比如8区分度太低画面里很多非目标区域都会被当成目标bin太细比如256又容易受到噪声影响。我实测下来H通道用32到64个bin是比较合适的既能保住颜色区分度又不会太敏感。3.3 关键代码片段解析下面给出跟踪节点的核心Python代码展示最关键的几个函数调用import cv2 import numpy as np # 初始化框选目标计算直方图 ret, frame cap.read() roi cv2.selectROI(select, frame, False) x, y, w, h roi track_window (x, y, w, h) hsv cv2.cvtColor(frame, cv2.COLOR_BGR2HSV) roi_hsv hsv[y:yh, x:xw] roi_hist cv2.calcHist([roi_hsv], [0], None, [32], [0, 180]) cv2.normalize(roi_hist, roi_hist, 0, 255, cv2.NORM_MINMAX) # 跟踪循环 term_crit (cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 1) while True: ret, frame cap.read() if not ret: break hsv cv2.cvtColor(frame, cv2.COLOR_BGR2HSV) back_proj cv2.calcBackProject([hsv], [0], roi_hist, [0, 180], 1) ret, track_window cv2.CamShift(back_proj, track_window, term_crit) pts cv2.boxPoints(ret) pts np.int0(pts) cv2.polylines(frame, [pts], True, (0, 255, 0), 2) cv2.imshow(track, frame) if cv2.waitKey(1) 0xFF 27: breaktrack_window是上一帧返回的窗口必须作为下一次调用的输入传进去这样CamShift才能持续自适应。cv2.boxPoints把返回的RotatedRect转成四个顶点用polylines画出来就是带角度的目标框。如果你不需要方向只需要目标中心可以直接取ret[0]作为质心坐标。4. ROS节点通信与小车控制4.1 节点划分这个项目的核心节点有三个。camera_node负责图像采集一直发布sensor_msgs/Image到/camera/image_raw它在树莓派上运行。track_node订阅这个图像话题执行CamShift算法然后发布结果到/track/image同时把目标中心坐标发布到/target/position它在虚拟机上运行。control_node是可选节点订阅/target/position根据目标中心和画面中心的偏差计算线速度和角速度发布到/cmd_vel它可以在树莓派上跑也可以跑在虚拟机里。划分节点的原则是“采集端小而稳定计算端灵活可变”。树莓派上尽量不要跑图形界面和OpenCV窗口显示否则会抢占CPU。跟踪节点里的cv2.imshow只在调试时开启正式运行时最好注释掉用rqt_image_view远程查看就可以了。4.2 图像消息与cv_bridge转换ROS里的图像消息是sensor_msgs/ImageOpenCV里则是cv::Mat或者NumPy数组两者不能直接互用必须用cv_bridge转换。在跟踪节点的回调函数里先调用cv_bridge的imgmsg_to_cv2注意编码参数要传bgr8from cv_bridge import CvBridge import rospy from sensor_msgs.msg import Image bridge CvBridge() def image_callback(msg): frame bridge.imgmsg_to_cv2(msg, bgr8) # 这里执行CamShift处理 result_msg bridge.cv2_to_imgmsg(frame, bgr8) track_pub.publish(result_msg)如果图像是灰度图编码要改成mono8。还有一点cv_bridge在ROS2和ROS1之间的API略有不同本文基于ROS1。摄像头话题帧率如果很高而跟踪节点处理速度跟不上回调会阻塞可以在订阅时设置队列长度或者用rospy.Subscriber的buff_size参数但不要盲目增大否则内存会飙升。4.3 在从机运行跟踪算法实际运行时整个启动流程是这样的。先在树莓派上打开终端启动roscore和摄像头节点确认话题有输出。然后在虚拟机里source工作空间运行track_node。注意顺序不能乱如果虚拟机先启动跟踪节点它会因为找不到master一直重试不过ROS会在master出现后自动恢复连接所以问题不大。为了减少手动开关终端的次数可以写一个launch文件放在虚拟机的工作空间里包含track_node再写一个包含camera_node的launch放在树莓派上。不要在同一个launch里同时启动两端节点因为两边的代码和依赖环境不完全一样强行跨机启动会引入不少麻烦。用rqt_image_view订阅/track/image就能实时看到跟踪效果。5. 常见问题与排查技巧5.1 图像延迟高跟踪卡顿树莓派3B的USB带宽有限640x48030fps在usb_cam下容易出现画面延迟和丢帧。我的经验是优先降到15fps再不行降到10fps不要一上来就换摄像头。OpenCV的CamShift本身很快瓶颈通常出在图像传输和显示上可以检查一下摄像头节点是否频繁打印错误虚拟机是不是同时开了多个rviz窗口树莓派是否在无线网络环境下传输图像如果是建议改成有线连接带宽更稳定。图像话题传输也受限于网络。如果树莓派走WiFi路由器又是2.4GHz的老设备即使算法再快图像帧率也上不去。可以把图像分辨率降到320x240做一次对比测试如果帧率明显提升说明网络带宽是瓶颈。5.2 CamShift跟丢目标CamShift最怕的是目标和背景颜色太接近。比如红色目标走在红色地板上直方图匹配时整片地板都会被当成目标窗口就会扩展得很大。遇到这种情况单纯调bin数量没用可以尝试在HSV空间里加上S和V通道的阈值过滤先把低饱和度、低亮度的背景去掉再做反向投影。另外如果目标本身是纯色的直方图的区分度也有限可以考虑加入纹理信息但那已经超出CamShift的范畴了。跟踪丢失的判断也很重要。CamShift返回的窗口面积如果突然变得特别大或者特别小都可以视为跟踪失败。我习惯设置两个阈值比如窗口面积小于初始面积的20%就认为目标丢失超过初始面积的4倍也认为丢失。丢失后可以让节点暂停发布位置或者重新在画面中心搜索相同颜色直方图的区域实现简单的重捕获。5.3 主从机ROS通信不上这类问题几乎每个做多机ROS的人都会遇到。先ping树莓派和虚拟机的IP确认网络通不通再确认虚拟机网络模式是桥接而不是NAT然后检查两边的ROS_MASTER_URI和ROS_IP是否设置正确尤其是ROS_IP不能用localhost。如果这些都正常还有可能是防火墙拦了11311端口Ubuntu上可以用sudo ufw disable临时关掉验证是防火墙问题后再放行。还有一个很隐蔽的坑树莓派跑roscore的终端如果ROS_IP没有配置好ROS master会默认绑定到主机名的回环地址上导致虚拟机能看到master却收不到树莓派节点的话题。解决办法就是在跑roscore的终端里也把export ROS_IP192.168.1.100写上确保master监听在局域网网卡上。5.4 USB摄像头在树莓派上不识别插上摄像头后如果lsusb看不到任何摄像头设备先换一个USB口试。树莓派3B的USB口供电能力有限带电机驱动和摄像头同时供电时容易掉设备最好用带外部供电的USB HUB。如果lsusb能识别但/dev/video0不存在检查v4l2驱动是否加载可能需要在系统里安装linux-modules-extra。推荐用罗技C270、C920这类UVC免驱摄像头兼容性好折腾成本低。画面颜色不对或者偏色可以用v4l2-ctl调整白平衡、亮度、对比度。比如v4l2-ctl -d /dev/video0 -c white_balance_temperature_auto1 v4l2-ctl -d /dev/video0 -c brightness128有些摄像头的自动曝光在逆光环境下会让目标颜色失真影响CamShift效果建议把自动曝光关掉手动设一个固定曝光值这样颜色直方图更稳定。我在实际调这个项目时最大的体会是不要一上来就想把整个系统跑通而是先把数据链路拆开验证树莓派能不能出图虚拟机能不能订阅到图像OpenCV能不能框住目标每一步都单独确认后再合到一起。这样即使出了问题也能快速知道是通信、算法还是摄像头的问题。另一个小技巧是项目刚开始调跟踪参数时可以先直接用树莓派跑OpenCV读取摄像头文件把算法逻辑调好再套上ROS话题避免ROS通信问题干扰算法验证。这套配置跑起来以后摄像头画面基本稳定在20fps左右作为学习ROS多机协作和OpenCV跟踪的入门项目完全够用。如果后续想增强可以把摄像头上到720p或者用深度学习检测器替换CamShift但树莓派加虚拟机的主从架构依然可以继续用。本文还有配套的精品资源点击获取
返回列表