上周调一台巡检机器人,我把Astra Pro深度相机接到了ROS 2 Humble上做近距离避障。机器人在楼道里来回跑了半天,我坐在电脑前盯着RViz2里的深度图,一边看数值一边头疼——距离测量的抖动比我预想的大得多。群里正好有人问“Astra Pro在ROS 2里能不能出图”,我就知道,这个话题值得系统写一篇。
这篇文章里面所有操作都基于我实际跑过的环境:Ubuntu 22.04 + ROS 2 Humble + Astra Pro(我手上有S10386这个型号),内容包括从装驱动、看到深度图、写距离检测节点,再到局域网远程可视化,最后把实战中踩过的坑全部摊开来讲。如果你也打算把这台十几年前的经典结构光相机重新用在ROS 2项目里,这篇文章能帮你少走至少一个下午的弯路。
1. 为什么是Astra Pro + ROS 2 Humble:这个组合解决了什么问题
1.1 从一次现场调试说起
那天我调试的是一台底盘巡检机器人,轮子上已经带了超声波和激光雷达,但激光雷达装的位置偏低,遇到桌面高度的小障碍物,比如椅子腿、快递盒、电线拖板,经常扫不到。需要补一个视觉传感器,最直接的方案就是加深度相机。
我手上正好有一台Astra Pro。这相机当年是跟着Orbbec第一代开发套件买的,一直闲置在柜子里。现在翻出来,发现问题是:网上大部分教程还在教ROS 1时代的astra_camera、rgbd_launch、OpenNI2,而我的开发环境已经全部迁到ROS 2 Humble + Ubuntu 22.04了。旧驱动在新系统上编译不过,ROS 1的包也没法直接装。所以我只能硬着头皮走ROS 2原生的驱动路线,结果发现官方生态其实比我想象中好——只要找对仓库和分支。
1.2 Astra Pro的硬件底子决定了它的适用边界
Astra Pro采用的是结构光方案,通过投射不可见的红外光斑图案,再由红外摄像头读取图案变形来解算深度。从硬件指标来看:
- 深度分辨率最高1280x1024,帧率最高30fps
- 测量范围标称0.6米到8米,近距离0.35米附近勉强可用但精度明显下降
- 视场角大约水平58度、垂直45度左右
- 带一颗RGB摄像头,分辨率1280x960,可以用来做人脸检测、颜色识别
- 供电和数据传输走USB 3.0,线材对信号稳定性影响非常大
这套参数放到今天当然不算惊艳,但做室内机器人的近距离避障、桌面物体检测、人员跟随,完全够用。我实际测试下来,1米到2.5米范围内的深度数据表现最稳定,这个区间恰好是移动机器人最常见的感知范围。你要是拿它去做户外远距离测绘,那确实强人所难。
1.3 生态现状:ROS 2时代为什么值得重新用它
很多人以为Astra Pro是“老掉牙的设备”,在ROS 2里肯定没人维护。实际上Orbbec官方在GitHub上有维护orbbec_ros和orbbec_camera两个仓库,其中orbbec_camera就是专门对接ROS 2的,支持Foxy、Galactic、Humble等多个发行版。如果你的项目系统是Ubuntu 22.04,直接选Humble分支就行。
选Astra Pro还有一个务实原因:市面上的二手价格非常便宜,而且货源充足。对学生党、个人开发者、搞毕设的人来说,用几十块钱的深度相机完成避障、抓取、跟随等视觉功能的前期验证,性价比确实高。在和激光雷达配合使用的时候,它补盲区的价值很明显。
2. 从装驱动到相机出图:前半小时做的事
2.1 先搞清楚该装哪套驱动,别一上来就编译
在ROS 2里让Astra Pro工作,你大概率会碰到两个名字很像的包。
astra_camera:这是比较老的驱动,最早面向ROS 1,后来有人维护了ROS 2版本,但接口设计和话题名都比较旧。orbbec_camera:Orbbec官方专门为ROS 2维护的新驱动,支持UVC协议的Orbbec相机(包括Astra Pro、Astra Plus、Dabai系列等)。
直接选orbbec_camera,不要纠结。astra_camera的文档不完整,而且有些分支的API跟新版本不兼容,你就算编译过了也容易在运行时出各种莫名其妙的段错误。
2.2 编译安装orbbec_camera的完整步骤
我用的环境是Ubuntu 22.04 + ROS 2 Humble + Python 3.10,如果你是别的主流发行版,流程基本一样,只是有几个依赖包的版本号可能略不同。
先安装编译工具链和依赖:
sudo apt install -y ros-humble-camera-info-manager ros-humble-cv-bridge ros-humble-image-transport sudo apt install -y libgflags-dev libgoogle-glog-dev libusb-1.0-0-dev然后创建工作空间并克隆代码:
mkdir -p ~/ros2_ws/src cd ~/ros2_ws/src git clone -b ros2 https://github.com/orbbec/OrbbecSDK_ROS2.git注意这里的分支名,我之前按默认分支克隆下来,编译的时候依赖的SDK版本和ROS 2接口不匹配,报了一堆错。后来换成ros2分支才顺利通过。
编译:
cd ~/ros2_ws colcon build --symlink-install source install/setup.bash编译过程大概几分钟,取决于机器性能。如果报错缺少某个包,一般是依赖没装全,用rosdep install --from-paths src --ignore-src -r -y可以自动补依赖。
2.3 udev规则:这一步不做,后面全程白忙
把相机通过USB 3.0接到电脑上,先看系统有没有识别到设备:
lsusb正常情况下会看到一个2bc5开头的Vendor ID(Orbbec的USB Vendor ID),比如2bc5:0403或者类似。某些相机型号在旧固件下显示为8087的Intel ID,如果你拿到的机器是这种情况,需要先升级固件。
接下来配置udev规则,否则非root用户启动节点时会报权限错误。orbbec_camera仓库自带了脚本,直接运行:
cd ~/ros2_ws/src/OrbbecSDK_ROS2/scripts bash install_udev_rules.sh sudo udevadm control --reload sudo udevadm trigger插拔一次USB线,让权限规则生效。
2.4 启动节点,验证相机出图
启动相机节点:
ros2 launch orbbec_camera orbbec_camera.launch.py启动之后如果一切正常,终端里会打印相机型号、固件版本、序列号这些信息。这时候开另一个终端,查看话题列表:
ros2 topic list你会看到类似这样的话题:
- /camera/color/image_raw
- /camera/depth/image_raw
- /camera/depth/points
- /camera/extrinsic/depth_to_color
- /camera/color/camera_info
到这里相机已经跑起来了。整个过程顺利的话确实五六分钟能搞定。但别高兴太早,后边的可视化、距离检测、远程传输才是真正的重头戏。
3. RViz2里看到深度图:图像话题与点云显示
3.1 用RViz2添加深度图像
相机节点跑通以后,并不能立刻在屏幕上看到画面,你需要用RViz2来订阅话题。
ros2 run rviz2 rviz2打开RViz2后:
- 在左侧Displays面板点击Add
- 选择By topic,找到
/camera/depth/image_raw话题 - 添加Image display
默认情况下,深度图会显示成灰度图——因为深度值是毫米量级的整数,直接映射到图像灰度后,近距离物体会显得很亮,远距离物体几乎黑成一片,看起来就像一张“花屏”图。这不是设备坏了,而是显示方式不对。
正确的做法是在Image display的Color Scheme下拉菜单里,把Overlay改成Black to Red映射或者Turbo(如果版本支持)。改完之后,近距离是红色,远距离逐渐变成蓝色,色彩层次立刻就出来了,画面看起来才真正“像一张深度图”。
3.2 点云显示:Fixed Frame的坑
除了看图像,深度相机最大的价值在于能直接输出三维点云。在RViz2里添加PointCloud2 display,选择/camera/depth/points话题,然后你会遇到一个经典问题——画面里什么都没有,或者点云位置飘到不知道哪里去了。
原因几乎都是Fixed Frame设置不对。RViz2里所有点云数据的坐标都是相对于某个坐标系来渲染的,你需要在左上角Fixed Frame下拉框里选择camera_depth_frame或者camera_link。选错坐标系,比如保持默认的map,而你的机器人又没有发布从map到相机坐标系的TF变换,点云自然显示不出来。
我自己喜欢用camera_depth_frame作为Fixed Frame,理由是它对调试最直观——你看到的点云就是相机自己视角下的三维信息,不会因为机器人的TF树没建全而出现奇怪偏移。
3.3 RGB和深度图的对齐问题
有一类很常见的需求:把RGB图像和深度图像逐像素对齐,这样点击画面中某个物体,就能同时知道它的颜色和距离。Astra Pro的深度图是红外相机拍摄的,它和RGB相机的视野范围不完全重叠,两个相机在硬件上也有物理间距,所以默认情况下颜色图和深度图是不对齐的。
如果你需要做颜色+距离的融合,可以在orbbec_camera的launch参数里打开对齐开关:
ros2 launch orbbec_camera orbbec_camera.launch.py depth_registration:=true打开之后,系统会用内部的标定参数把深度图对齐到RGB坐标系,输出一个/camera/depth/image_raw直接对应RGB像素位置的深度流。这个功能开启后会额外消耗CPU,如果只是做距离检测,可以不开。
4. 距离检测:把深度值从像素变成厘米
4.1 深度图的编码方式:16UC1与毫米单位
深度图每个像素存储的并不是“距离值”,而是一个表示深度的整数值,单位是毫米,编码格式为16UC1(16位无符号单通道)。所以你在图像里看到的某个像素值为1234,意味着该点在相机前方1.234米处。
这个编码方式很多人第一次接触时会搞错,我见过有人直接用numpy把深度图除以255当灰度图处理,导致所有距离都变成几十厘米。正确的是这样:
depth_image = cv2.imread("depth.png", cv2.IMREAD_UNCHANGED) distance_mm = depth_image[y, x] distance_m = distance_mm / 1000.0在ROS 2里订阅深度话题时,拿到的数据是以nav_msgs/msg/OccupancyGrid的变体?不对,是sensor_msgs/msg/Image里的data字段,需要先用cv_bridge转成numpy数组,再提取像素值。
4.2 写一个简单的距离检测节点
下面这个节点订阅深度图,实时计算图像中心区域的平均距离,并以Float64消息发布出去,方便其他节点接收。完整流程是:订阅/camera/depth/image_raw,用cv_bridge转成numpy数组,取中心区域像素均值。
import rclpy import message_filters import numpy as np from rclpy.node import Node from std_msgs.msg import Float64 from sensor_msgs.msg import Image from cv_bridge import CvBridge class DistanceDetector(Node): def __init__(self): super().__init__('distance_detector') self.bridge = CvBridge() self.publisher = self.create_publisher(Float64, 'distance_to_center', 10) self.subscription = self.create_subscription( Image, '/camera/depth/image_raw', self.depth_callback, 10 ) def depth_callback(self, msg): depth = self.bridge.imgmsg_to_cv2(msg, desired_encoding='passthrough') h, w = depth.shape[:2] cx, cy = w // 2, h // 2 region = depth[cy-20:cy+20, cx-20:cx+20] # 深度有效值范围:0表示无效测量 valid = region[region > 0].astype(np.float32) if valid.size == 0: return distance_m = np.median(valid) / 1000.0 self.publisher.publish(Float64(data=distance_m)) self.get_logger().info(f'Distance: {distance_m:.3f} m') def main(args=None): rclpy.init(args=args) node = DistanceDetector() rclpy.spin(node) node.destroy_node() rclpy.shutdown() if __name__ == '__main__': main()这段代码里有几个关键选择:
- 用区域中位数而不是平均值。深度图里偶尔会出现孤立的异常点,个别像素跳变到几十米外,平均数会被拉偏,中位数则能很好地抵抗这种噪声。
- 先过滤掉值为0的像素。Astra Pro在检测不到有效反射时,深度值为0,直接参与计算会把距离拉近很多。
- 取图像中心40x40的小区域。对固定安装的机器人而言,中心区域通常对应正前方,取区域平均比单点更稳。
4.3 多点测距与区域测距:换成谁在测
如果你要检测的不只是中心点,而是整个图片中距离最近的物体,比如避障场景,可以用numpy一次性处理整幅深度图:
depth = self.bridge.imgmsg_to_cv2(msg, desired_encoding='passthrough') valid_depth = depth[depth > 0] if valid_depth.size == 0: return min_distance = np.min(valid_depth) / 1000.0这样得到的就是视野范围内最近的物体距离。注意直接用np.min会比较敏感,因为单个坏点可能造成误报,所以最好先做个中值滤波或者形态学开运算,把孤立噪点清掉。
关于距离检测,我想补充一个非常容易踩的认知误区:深度相机的测距范围不是均匀可靠的。Astra Pro标称0.6到8米,实际测试下来,0.3到0.6米范围内深度值虽然能出来,但噪声明显增大,误差可能抓到几十毫米级别;超过4米后,深度值开始变得稀疏,很多像素直接是0。做避障阈值判断时,需要针对你的实际安装场景重新标定一下“可用范围”,别直接按说明书上的标称值来做逻辑。
5. 远程可视化:人不用跟着机器人跑
5.1 ROS 2分布式通信是怎么让远程可视化成立的
很多人第一次接触ROS 2远程可视化时会有一个误解:以为必须在机器人本体上装远程桌面,然后人远程连上去看画面。其实ROS 2本身就是分布式的,DDS(Data Distribution Service)的通信模型决定了节点之间是peer-to-peer的,只要两台机器在同一个网络里能互相发现对方,A节点发布的话题数据可以直接传到B节点的RViz2里显示。
所以思路很简单:机器人本体上运行相机节点和距离检测节点,你在另一台电脑上运行RViz2订阅话题,就能直接看到机器人的“眼睛”看到的一切。
5.2 同一个局域网下的配置与踩坑
让两台机器互通,最简配置只需要三个步骤:
- 两台机器装相同的ROS 2版本,确保都源码编译过
orbbec_camera驱动。 - 设置相同的
ROS_DOMAIN_ID环境变量,默认是0,如果端口冲突可以改成别的,比如42。 - 确保两台机器在同一个网段,比如都用192.168.1.x,并且路由器或者交换机没有开启AP隔离。
然后机器人端正常启动相机节点,电脑端启动RViz2,在Fixed Frame里输入camera_depth_frame,添加到话题,深度图就能跨越网线出现在你面前。
但这里有个出现频率极高的坑:两台机器如果都装了多个RMW实现(比如同时装了Fast DDS和Cyclone DDS),默认选择的DDS实现不一致,节点之间就无法互相发现。排查方法很简单:
echo $RMW_IMPLEMENTATION如果输出不一致,在~/.bashrc里统一加上:
export RMW_IMPLEMENTATION=rmw_fastrtps_cpp两端重启终端即可。
5.3 Web可视化:用浏览器简化一切
如果你不想为了看个距离再装一套完整的ROS 2环境,或者你在地铁上用手机也想瞄一眼机器人状态,可以加一个web_video_server。它把ROS图像话题转成HTTP视频流,任何设备打开浏览器就能看。
安装:
sudo apt install ros-humble-web-video-server运行:
ros2 run web_video_server web_video_server然后浏览器访问:
http://<机器人IP>:8080/stream?topic=/camera/depth/image_raw这个方案特别适合不能直接SSH进机器人的场景,尤其是你想在手机上快速巡检、调试相机角度是否正确的时候,非常实用。
5.4 远程传输的带宽优化思路
Astra Pro在640x480分辨率下深度图一帧约600KB,按30fps计算,原始数据每秒接近18MB,在局域网勉强没问题,但要传到公网就会非常吃力。我用下来最有效的几个办法是:
- 把分辨率降到320x240,深度信息的精度损失对距离检测影响不大。
- 帧率从30降到10fps。
- 图像压缩格式从raw改成jpeg或者png。
orbbec_camera的launch启动参数里支持设置depth_width、depth_height和depth_fps,直接改就行。实测320x240@10fps的深度流,每帧只有约75KB,远程传输压力一下子小很多,RViz2里的可视化依然流畅。
6. 避坑指南:这些坑我替你们踩过了
6.1 USB线材和接口位置:一个隐藏的帧率杀手
Astra Pro对USB线很挑剔。我最初用了一根随机附送的USB 3.0线,插电脑前置面板,经常跑着跑着深度图就卡住不动,日志里也没有任何报错,重新插拔USB线又恢复。后来换了根短一点、带磁环的USB 3.0线,直接插主机背面主板接口,再也没出现过这个问题。
原因是Astra Pro在1280x1024@30fps时,USB 3.0的实际带宽需求很高,劣质线材或者延长线会造成供电不足和信号衰减,相机会自动降级或重启USB控制器。如果出现“相机每隔几十秒掉一次线”的情况,先查线材,大概率不是驱动问题。
6.2 近距离盲区:靠太近是真的测不到
结构光相机和双目相机一样,有最小工作距离。Astra Pro在0.35米以内基本没法得到有效深度,0.35到0.6米范围内深度图会有大量空洞,表现为黑色斑块。我这台S10386尤其明显,正前方10厘米处的手掌在深度图里就是一片漆黑。
如果你的机器人需要检测非常贴近相机的障碍物,单靠Astra Pro是不行的,要么加一个超声波传感器做近场补盲,要么把相机斜着安装增加近场视野。
6.3 阳光和强光:结构光的天然克星
结构光相机靠投射红外光斑来测量,环境红外光强度过高时,光斑会被淹没,深度值就测不准。实测在室外阳光直射下,Astra Pro的深度图基本是废的;室内靠近窗边、阳光透过窗户照射到的地方,深度图也会有明显噪点。
所以这东西适合场景明确:室内、稳定光照条件。如果要做室外机器人,请直接上激光雷达或者主动双目+红外补光的方案,别在这个问题上硬耗。
6.4 长时间运行的发热和掉帧
连续开机两小时以上,Astra Pro外壳会明显发热,这时候深度图偶尔会出现整帧丢失的情况,表现为话题消息间隔从33ms突然跳到200ms以上。我一开始以为是代码问题,后来发现是设备过热。
解决办法很朴素:加一个小散热片贴在后壳上,或者用USB风扇对着吹。加上散热后连续跑一整天也没再掉过帧。这个细节在官网资料里完全找不到,得靠自己试。
6.5 无效深度值的处理:不是每个像素都有意义
Astra Pro输出的深度图里,黑色区域是无效像素,值为0。在写距离检测算法时一定要先过滤,否则会把真实距离严重拉偏。我刚开始做区域平均测距时,就因为没有过滤0值,导致图像中有大片无效区域时,计算出来的距离偏小好几倍。
工程上的处理方法是:统计时剔除0值,同时统计有效像素占比,如果有效像素太少,说明这个区域的信息量不足,算法应该主动丢弃这一帧,而不是输出一个不可靠的数值。
7. 实测数据与扩展玩法
7.1 不同距离下的测距精度实测
我把相机固定在三脚架上,正对一面白墙,在不同距离下各采集100帧深度数据,取中心区域中位数和标准差,得到的结果如下表:
| 真实距离 | 测量中位数 | 标准差 | 有效像素率 |
|---|---|---|---|
| 0.50 m | 0.483 m | 18 mm | 62% |
| 1.00 m | 0.992 m | 6 mm | 98% |
| 1.50 m | 1.506 m | 5 mm | 99% |
| 2.00 m | 2.011 m | 8 mm | 97% |
| 3.00 m | 2.958 m | 21 mm | 89% |
| 5.00 m | 4.763 m | 55 mm | 57% |
从数据看,1到2米是黄金区间,标准差在10毫米以内,做避障阈值判断完全够用。0.5米处不仅标准差变大,有效像素率也掉到六成,近距离确实不行。5米处有效像素率只剩一半多,测距噪声已经大到不适合做精确控制。
7.2 扩展玩法:micro-ROS + ESP32做低成本测距节点
如果你想让Astra Pro的数据直接驱动小车上的一块ESP32,从而不依赖工控机处理所有逻辑,可以用micro-ROS。思路是:电脑上跑一个micro-ROS Agent节点,ESP32作为micro-ROS Client订阅距离检测节点发布的Float64话题,通过串口或WiFi传输数据,ESP32拿到距离值后直接控制电机减速或转向。
这个方案的工程价值很大:深度相机的复杂计算放在高性能平台上,底盘控制放在实时性要求更高的单片机上,两边通过话题解耦。你要是想把Astra Pro用到竞速小车或者桌面小机器人上,这个架构值得研究。
7.3 后续还能接什么
距离检测只是起点。Astra Pro的深度图接到nav2的costmap里可以做全向避障,接到MoveIt里可以做机械臂抓取的物体定位,配合RGB图做YOLO检测后,还能实现“检测到人→测距→跟随”的完整链路。
我自己的下一步计划是把Astra Pro和现在的差分底盘深度绑定,做一个跟随配送小车,让它跟着人走,距离保持在1.5米左右,远了就跟上,近了就停住。这套功能在ROS 2里也就是把现有节点串起来的事。
最后再分享一个小技巧:如果你把Astra Pro固定安装在机器上,一定要花点时间检查深度图和RGB图的对齐情况。我在调试时发现两个相机在出厂时会有个体差异,如果直接拿深度图中心点表示RGB画面中物体的距离,偏差最大能到5到10厘米。用棋盘格做一次手眼标定,或者干脆在应用中只用深度图坐标系来做距离判断,能省掉很多后续乱七八糟的调试问题。