把地瓜机器人 RDK X5 拿到手之后,我最关心的不是它跑多少分,而是能不能顺畅跑起 ROS 2。机器人开发这种事情,外围工具再花哨,最后全靠环境的稳定性和传感器数据的质量撑着。这篇文章把我从零开始搭 ROS 2 Humble、接摄像头、接激光雷达、接 IMU,再把几路数据放到 RViz2 里一起看、录成 ros2 bag 拿去回放的全过程整理出来。内容不长,但都是能直接复现的操作,适合刚入手 RDK X5 的新手,也适合准备给项目做传感器预研的朋友。我会把每一步的意图、常见坑、以及我踩过的雷都写出来,尽量让后来的人少走弯路。
1. 为什么拿 RDK X5 跑 ROS 2 比较顺手
1.1 先搞清楚 RDK X5 的硬件底子
RDK X5 是地瓜机器人(原地平线机器人)出的开发者套件,名字里的 RDK 就是 Robot Development Kit。它和前代 X3 相比最大的变化是处理器的线程能力上了一个台阶,官方标称的 CPU 是 8 核 Arm Cortex-A55,频率最高能到 2.0GHz 左右。我手里这套是 8GB 内存版本,跑 Ubuntu 22.04 加上桌面包,日常开发剩余内存还是比较充裕的,这一点对 ROS 2 这种多进程、多 DDS 通信的框架很关键,毕竟机器人里经常同时挂着十几个节点。
除了 CPU,这块板子真正值钱的部分是 BPU,也就是板载的神经网络加速单元。官方数据是 10 TOPS 级别的 AI 算力,用来跑目标检测、语义分割这类常见的视觉模型非常合适。装上 ROS 2 之后,摄像头采集图像、模型推理、结果发布,这套链路完全可以在这块板子上独立完成,而不需要把数据全部送到电脑上处理。板卡接口方面,HDMI、千兆网口、USB 3.0、双 MIPI CSI、40pin GPIO 基本都齐了,SSD 可以走 M.2 扩展,Wi-Fi 和蓝牙也是板载的。对我这种喜欢折腾各种传感器的开发者来说,接口齐全比参数好看更重要,不然传感器接不上,算力再强也白搭。
1.2 选 ROS 2 而不是 ROS 1 的决策逻辑
做了这么多年机器人开发,我的建议很直接:新项目一律 ROS 2。ROS 1 虽然资料多、历史包袱轻,但官方已经停止维护,Noetic 之后就没有正式版本了。RDK X5 出厂镜像直接适配的就是 Ubuntu 22.04,而 ROS 2 的 Humble 版本正是 22.04 的 LTS 配套版本,系统和框架的生命周期完全对得上,依赖冲突最少,社区踩坑资料也最全。
ROS 2 引入 DDS 之后,节点间的通信变成了真正意义上的分布式:不同进程、不同设备之间的话题传输都走 QoS 策略,不再像 ROS 1 那样依赖一个中心化的 master 节点。这点在机器人上很实用,比如你后面要把传感器数据从板子发到工控机、发到地面站,ROS 1 还要折腾网络配置,ROS 2 只需要保证 DDS 域一致就行。再加上 Micro-ROS、组件化节点、生命周期管理这些新特性,整个框架的设计确实更贴近现代机器人软件工程。没必要犹豫,选 Humble 就对了。
1.3 搭建前的整体规划
在动手之前,我先在脑子里过了几遍方案,核心原则是"不折腾"。第一步,只装官方镜像对应的系统版本,不自己从零裁剪内核,等把 ROS 2 跑通、传感器接完,再考虑精简系统的问题。第二步,能 apt 装的依赖绝对不手动编译,ROS 2 的包管理已经比较成熟,很多驱动和工具都有官方二进制包,编译源码只会浪费时间和精力。第三步,传感器接入顺序按难度排:先接 USB 摄像头,再接串口雷达,最后搞 I2C 的 IMU,每一路单独验证通过之后再进行下一步,避免多路传感器同时出问题时不知道问题出在谁身上。
还有一个容易被忽略的点是磁盘空间。ROS 2 Humble 的桌面包装完大概要占几个 GB 空间,如果板子上的 eMMC 容量紧张,最好先检查 df -h,不够的话提前插一张 TF 卡或者挂载 SSD,否则装到一半提示磁盘满就很尴尬。整体规划做好之后,后面的操作基本就是流水线作业。
2. ROS 2 Humble 环境搭建全流程
2.1 系统准备与软件源检查
RDK X5 拿到手之后,我先确认了系统版本。在终端里执行 uname -a 和 cat /etc/os-release,看到内核版本和系统代号都是正常的 Ubuntu 22.04 环境,说明出厂镜像已经是适配好的版本,可以直接进入下一步。如果你的板子系统比较旧或者已经被折腾过,建议重新烧录官方镜像,干净系统的优先级比什么都高,一个残留依赖混乱的环境会浪费你大量时间。
接下来做 locale 设置。ROS 2 对 locale 比较敏感,某些非 UTF-8 环境会导致编译和运行报出奇怪的错误,所以最好统一改成 UTF-8。这一步虽然看起来无关痛痒,但我确实遇到过不同开发板默认 locale 不同导致 colcon 编译失败的情况,所以还是提前处理掉。
sudo apt update sudo apt install -y locales sudo locale-gen en_US en_US.UTF-8 sudo update-locale LC_ALL=en_US.UTF-8 LANG=en_US.UTF-8改完之后最好重新登录一次终端,让 locale 生效。然后添加 ROS 2 官方软件源,这一步的关键是先把 ROS 2 的 GPG key 下载到系统,再把软件源地址写进 /etc/apt/sources.list.d/。如果 apt update 速度很慢,就把系统自身的源切换到国内常用的镜像源,这个可以根据自己的网络情况选择,原则是找一个延迟低、更新快的源,不要在换源这件事上反复折腾。
2.2 安装 ROS 2 Humble Desktop
软件源准备好之后,直接 apt 安装。ROS 2 在 Ubuntu 22.04 下有几个安装粒度,最常用的是 ros-humble-desktop,里面包含了 RViz2、demo 节点、常用可视化和仿真工具,适合开发调试;如果只想跑最小系统,可以装 ros-humble-ros-base,只包含通信、话题、服务等基础功能。我第一次搭建建议直接上 desktop 包,后面接传感器时这些工具全用得上。
sudo apt install -y ros-humble-desktop安装过程中会自动拉取很多依赖包,包括 DDS 实现(默认是 Fast DDS)、Python 客户端、tf2 坐标变换库等,这些都不用单独处理。装完之后最关键的一步是加载环境变量。因为 ROS 2 的安装路径在 /opt/ros/humble,里面的可执行文件和脚本需要环境变量才能暴露给 shell。每次打开新终端要想直接用 ros2、colcon 这些命令,就把下面这行写进 ~/.bashrc:
echo "source /opt/ros/humble/setup.bash" >> ~/.bashrc source ~/.bashrc这里有一个常见误区:很多人只执行了手动 source,而没有写进 .bashrc,结果每次重新开终端都提示 command not found。写进去之后,再验证一下:
ros2 --help能输出帮助信息,说明安装成功。如果还没有,检查一下 .bashrc 文件内容以及有没有其他地方对你 PATH 变量做了不合理的覆盖。
2.3 创建工作空间与 colcon 配置
环境变量通了之后,我再创建一个 ROS 2 工作空间。工作空间是所有自建包的根目录,ROS 2 默认按 src、build、install、log 四个子目录组织。当中 src 放源码包,build 是构建中间产物,install 是最终安装好的包,log 是构建日志。一般我们只手动建 src,后面构建时 colcon 会自动生成另外几个目录。
mkdir -p ~/ros2_ws/src cd ~/ros2_ws colcon build第一次构建空工作空间,colcon 会生成 install、build、log 目录。此时如果提示 colcon 命令不存在,需要单独安装:
sudo apt install -y python3-colcon-common-extensions构建完成之后,建议把 install/setup.bash 也加进 .bashrc。这样每次打开终端,当前工作空间的包就可以直接被 ros2 run 找到。需要注意的是,不要手动去改 install 目录里的内容,所有修改都要回到 src 里去改,然后重新 colcon build,否则会出现"改了代码但跑起来还是旧版本"的灵异问题。ROS 2 的构建系统对这种覆盖关系非常严格,我踩过好几次这种坑。
2.4 用 talker/listener 验证核心通信
环境搭好之后,我用最经典的 talker/listener 验证 ROS 2 核心通信链路。开两个终端,分别执行:
ros2 run demo_nodes_cpp talkerros2 run demo_nodes_py listener如果能看到 talker 每 0.5 秒发布一条消息,listener 同步收到,说明从节点管理到底层 DDS 通信都正常。这里我要多说一句:ROS 2 的通信走的是 DDS,默认域 ID 是 0。如果两台设备上的 ROS 2 互相看不到,先检查 ROS_DOMAIN_ID 环境变量是不是一致。这在后面真正部署到多机协作时会经常遇到,现在养成检查域 ID 的习惯,后面能少掉很多头发。
另外,打开第三个终端,还可以用 ros2 node list、ros2 topic list 查看当前节点和话题。这算是我每次搭建完环境后的固定自检流程:节点能不能起来、话题能不能互通、list 命令能不能看到,三关全过,就说明环境基本干净可用。
ros2 topic list ros2 topic echo /chatter3. 传感器集成:把物理世界变成话题
3.1 我这次使用的传感器组合
环境跑通后,接着做传感器集成。我手边正好有三类典型传感器,覆盖了机器人感知的大部分场景:一个 USB 摄像头(UVC 协议,接入 /dev/video0)、一个 SLAMTEC RPLIDAR A1 激光雷达(USB 转串口,接入 /dev/ttyUSB0)、一个 MPU6050 IMU(走 I2C,挂在 40pin GPIO 上)。这三路数据分别对应视觉、测距、姿态,最后都可以通过 ROS 2 话题发布出去,方便下游节点订阅处理。
| 传感器 | 型号/类型 | 接口 | 使用的驱动包 | 主要话题 |
|---|---|---|---|---|
| 摄像头 | USB UVC 摄像头 | USB 3.0 | usb_cam | /camera/image_raw |
| 激光雷达 | RPLIDAR A1 | USB 转串口 | sllidar_ros / rplidar_ros | /scan |
| IMU | MPU6050 | I2C(40pin) | 自写 Python 节点 | /imu/data |
这个组合并不是随意选的。USB 摄像头驱动成熟、即插即用,适合先打通图像链路;串口雷达能提供激光扫描数据,是 2D SLAM 的基础;MPU6050 虽然是老芯片,但寄存器简单、资料多,适合用来理解 IMU 数据上话题的完整流程。三路传感器从不同硬件协议切入,接完之后你对 ROS 2 的驱动框架基本就有感觉了。
3.2 摄像头接入:usb_cam 与图像话题
摄像头是最好验证的一路。插入 USB 摄像头后,先检查系统是否识别到设备:
lsusb ls /dev/video*正常情况下会出现 /dev/video0,如果插了多个摄像头,会是 video0、video1 这样的编号。然后我用 v4l2-ctl 查看摄像头支持的图像格式,这一步很重要,因为不同摄像头的像素格式差异很大,有的只支持 MJPG,有的只支持 YUYV,如果你在驱动里指定了不支持的格式,画面就会黑屏或者花屏。
sudo apt install -y v4l-utils v4l2-ctl --list-formats-ext -d /dev/video0确认支持格式后,安装 usb_cam 驱动包并启动:
sudo apt install -y ros-humble-usb-cam ros2 run usb_cam usb_cam_node_exe --ros-args \ -p video_device:=/dev/video0 \ -p pixel_format:=yuyv \ -p image_width:=640 \ -p image_height:=480usb_cam 启动后,会发布一系列话题,其中核心的是 /camera/image_raw,消息类型是 sensor_msgs/Image。我用 ros2 topic hz 检查发布频率,再用 rqt_image_view 直接看画面:
ros2 topic hz /camera/image_raw ros2 run rqt_image_view rqt_image_view如果频率稳定、画面正常,说明摄像头已经成功接入 ROS 2。这里我总结几个常见的坑:第一个是权限问题,某些摄像头需要 video 组权限,可以用 usermod -aG video 解决;第二个是像素格式不一致导致画面只有一半或者绿屏,解决办法是 title 里指定摄像头真正支持的格式;第三个是分辨率拉太高导致帧率掉到个位数,开发阶段用 640x480 就够了,别急着上 1080P。
3.3 激光雷达接入:串口权限与扫描话题
激光雷达这块要比摄像头稍微多一点步骤,因为涉及串口设备。RPLIDAR A1 插上之后,系统会识别成一个 USB 转串口设备,也就是 /dev/ttyUSB0。如果插上后没看到这个设备,可以先检查 USB 识别情况和驱动是否加载:
ls -l /dev/ttyUSB* dmesg | grep ttyUSB串口设备的第一个经典坑是权限。默认情况下普通用户是打不开 /dev/ttyUSB0 的,必须加入 dialout 用户组。我一开始没注意这个,导致雷达驱动一直报 open failed,后来才发现是权限问题。
sudo usermod -aG dialout $USER执行之后要重新登录或者重启终端才能生效。接着安装驱动。RPLIDAR A1 在 ROS 2 Humble 下有两个常用选择:sllidar_ros 是老牌驱动,包名叫 ros-humble-sllidar-ros;也可以从源码编译 rplidar_ros。我用的是系统 apt 安装方式:
sudo apt install -y ros-humble-sllidar-ros ros2 launch sllidar_ros sllidar_launch.py启动之后检查 /scan 话题:
ros2 topic echo /scan --once ros2 topic hz /scan如果能看到 ranges 数组里有非零值,并且频率在 10Hz 左右,说明雷达已经在工作了。RPLIDAR A1 的量程大概是 0.15 米到 12 米,扫描角度 360 度,点云密度取决于电机转速和测距采样率。实际测试中我发现,雷达对供电稳定性非常敏感,如果插在 USB 扩展坞上,偶尔会出现扫描频率跳变或者中间断掉的情况,后来直接插到板子原生的 USB 口就稳定了。做机器人项目一定要记住,传感器供电质量直接影响数据质量,这是一个经常被忽略的坑。
3.4 IMU 接入:用 MPU6050 自己写一个驱动节点
摄像头和雷达走的是官方驱动包,IMU 这块我打算自己写一个最小驱动节点,原因很简单:MPU6050 这类老芯片的 ROS 2 驱动包质量参差不齐,有的版本依赖旧接口,与其去修别人的包,不如自己把寄存器读一遍再发布成标准消息,整个过程反而能让你把 ROS 2 的发布机制吃透。
首先接线。MPU6050 的 SDA、SCL 分别接到 RDK X5 40pin GPIO 上的 I2C 引脚,VCC 接 3.3V,GND 接 GND。接好之后用 i2cdetect 扫描 I2C 总线,确认设备挂在 0x68 地址:
sudo apt install -y i2c-tools sudo i2cdetect -y 1如果能看到 0x68,说明硬件通路正常。接下来写一个简单的 Python 节点,核心逻辑是读取加速度计和陀螺仪的原始寄存器值,转换成物理单位,然后封装成 sensor_msgs/Imu 消息发布。这里有一个基础概念顺便提一下:sensor_msgs/Imu 里的线性加速度单位是 m/s²,角速度单位是 rad/s,而 MPU6050 的原始寄存器值是整数,需要根据量程除以灵敏度系数。我配置的量程是加速度 ±2g,陀螺仪 ±250°/s,所以加速度除以 16384,陀螺仪除以 131,然后再做单位换算。
#!/usr/bin/env python3 import math import smbus import rclpy from rclpy.node import Node from sensor_msgs.msg import Imu ADDR = 0x68 REG_PWR = 0x6B bus = smbus.SMBus(1) bus.write_byte_data(ADDR, REG_PWR, 0) def read_axis(reg): data = bus.read_i2c_block_data(ADDR, reg, 6) raw = [] for i in (0, 2, 4): v = data[i] << 8 | data[i + 1] if v >= 0x8000: v -= 0x10000 raw.append(v) return raw class ImuNode(Node): def __init__(self): super().__init__("mpu6050_node") self.pub = self.create_publisher(Imu, "/imu/data", 10) self.timer = self.create_timer(0.02, self.publish_imu) def publish_imu(self): acc = read_axis(0x3B) gyr = read_axis(0x43) msg = Imu() msg.header.stamp = self.get_clock().now().to_msg() msg.header.frame_id = "imu_link" msg.linear_acceleration.x = acc[0] / 16384.0 * 9.81 msg.linear_acceleration.y = acc[1] / 16384.0 * 9.81 msg.linear_acceleration.z = acc[2] / 16384.0 * 9.81 msg.angular_velocity.x = gyr[0] / 131.0 * math.pi / 180.0 msg.angular_velocity.y = gyr[1] / 131.0 * math.pi / 180.0 msg.angular_velocity.z = gyr[2] / 131.0 * math.pi / 180.0 self.pub.publish(msg) def main(): rclpy.init() node = ImuNode() rclpy.spin(node) node.destroy_node() rclpy.shutdown() if __name__ == "__main__": main()保存为 mpu6050_node.py,然后运行:
sudo apt install -y python3-smbus python3 mpu6050_node.py再开一个终端用 ros2 topic echo /imu/data 查看数据。转动板子时,角速度三个分量会跟着变化,说明 IMU 已经正常工作。需要提醒的是,这个节点只做了最简单的数据读取,没有做零偏校准和滤波,真实项目里还需要在静止状态下采集一段数据求均值作为零偏,再考虑要不要上卡尔曼滤波或者 Madgwick 算法。对于验证传感器链路来说,现在的状态已经够用了。
3.5 在 RViz2 里把三类数据叠加看
三路传感器都出数据之后,我打开 RViz2,把图像、激光扫描、IMU 姿态放到同一个视图里看。RViz2 是 ROS 2 的官方可视化工具,安装 desktop 包时已经带上了,直接在终端运行:
rviz2RViz2 打开后,先用 Add 按钮添加三个显示组件:Image、LaserScan、Imu。分别设置话题为 /camera/image_raw、/scan、/imu/data。这里会遇到一个固定坐标系的问题。RViz2 默认的 Fixed Frame 是 map,如果当前没有节点发布 TF,雷达数据显示不出来。最简单的办法是把 Fixed Frame 改成雷达驱动发布数据时使用的坐标系,通常是 laser_link,这样 scan 就能显示出来。如果图像话题里填了的 frame_id 和全局坐标系不一致,图像会显示为黑屏或者没有图像帧,点开 Image 面板的 transport 状态看一下就知道原因了。
把三个显示组件叠加起来之后,你会特别直观地看到机器人的"感知状态":摄像头画面出现在左边的图像窗口,激光雷达的点云围绕在中心,IMU 的坐标轴在空间里跟着板子翻转。这一刻我才觉得环境搭建真的算是完成了,后面的开发全是围绕这些数据展开的。
4. 把传感器数据串成一个小闭环
4.1 用 message_filters 做时间同步
传感器全部接入后,我顺手搭了一个小闭环,用来验证多传感器数据的时间同步。时间同步是机器人感知里面很核心的一个问题,因为摄像头图像和雷达扫描不是同一个时刻采样的,如果直接把两路数据发到下游算法,时间戳对不齐,SLAM 或者目标检测的结果就会抖动。
ROS 2 里做时间同步最常用的工具是 message_filters,它提供了 ApproximateTimeSynchronizer,可以在多个话题之间根据时间戳找到最接近的消息组合。下面是一个示意代码,订阅 /camera/image_raw 和 /scan,当两路消息的时间差在一定范围内时,触发回调函数:
from message_filters import Subscriber, ApproximateTimeSynchronizer from sensor_msgs.msg import Image, LaserScan def callback(image_msg, scan_msg): # 在此处处理同步后的图像和雷达数据 pass image_sub = Subscriber(node, Image, "/camera/image_raw") scan_sub = Subscriber(node, LaserScan, "/scan") ats = ApproximateTimeSynchronizer([image_sub, scan_sub], 10, 0.1) ats.registerCallback(callback)这个代码的核心思想是缓存最近一段时间的消息,然后在时间窗(这里是 0.1 秒)内寻找最匹配的消息对。实际项目中,你可以在这个回调里做相机和激光雷达的联合标定、做多模态目标检测,或者生成带时间戳的融合特征。我在本地的实验里,把同步后的数据直接通过一个新话题 /synchronized_data 发出去,下游节点只需要订阅一个话题就能同时拿到两路数据,逻辑上清爽很多。
4.2 用 ros2 bag 录制和回放现场数据
另一个我强烈建议养成的习惯是录 ros2 bag。ROS 2 的 ros2 bag 工具可以把指定话题的数据按时间顺序记录到磁盘上,之后随时可以回放,相当于给传感器数据做了"黑匣子"。这样在户外跑机器人时,不用现场调试,先把数据录回来,回到实验室慢慢分析。
录 bag 的命令很简单:
ros2 bag record /camera/image_raw /scan /imu/data执行后会生成一个以时间戳命名的目录,里面包含了数据文件和元信息文件。录制结束后,用 ros2 bag info 查看包信息,确认话题和消息数量:
ros2 bag info <bag目录名>回放时,直接 ros2 bag play <bag目录名>,数据会按照原始时间序列重新发布到对应话题,RViz2 里也能正常看到图像和雷达数据。这个过程最方便的地方在于,它不依赖真实硬件,回放时你甚至可以只开一个终端,不用关心传感器是不是还接着板子上。我在做传感器集成调试时,多次靠 bag 回放复现了现场才出现的问题,比一群人围着设备猜原因高效得多。
4.3 给闭环加一个模型加速的"眼睛"
数据链路通了以后,我顺便把 RDK X5 的 BPU 用了起来,在板端跑了一个轻量的目标检测模型,把摄像头图像送入模型,检测结果通过话题再发出来。这一步算是传感器集成之后很自然的延伸,因为 RDK X5 的核心卖点本来就是边缘 AI 推理,如果只是拿它当普通 ARM 板子用,10 TOPS 的 BPU 就浪费了。
具体实现方式上,可以走地瓜官方提供的推理工具链,把 YOLO 这类模型转换到 BPU 上运行,再把结果封装成可视化话题。相比在 CPU 上跑同样的模型,BPU 的推理帧率会提升非常明显,这个主要由板内 AI 加速器负责计算,CPU 只做预处理和后处理。我在实际测试中,跑一个 YOLOv8 量级的小模型时,CPU 占用没有明显飙高,整体节奏依然很顺。不过这块内容展开讲又是一篇长文,这里先留个引子,等环境搭建这篇写完后再专门聊 NPU 推理的部署细节。
5. 常见问题速查与避坑记录
5.1 权限与设备识别类
传感器接入阶段遇到的问题,大部分集中在设备权限和识别上。我整理了一个速查表,基本覆盖了最常见的几种情况。
| 现象 | 原因 | 解决方案 |
|---|---|---|
| ros2: command not found | 环境变量未加载 | 确认 ~/.bashrc 有 source /opt/ros/humble/setup.bash |
| 打不开 /dev/ttyUSB0 | 当前用户不在 dialout 组 | sudo usermod -aG dialout $USER 后重新登录 |
| 摄像头黑屏或花屏 | 像素格式设置不对 | 用 v4l2-ctl --list-formats-ext 查实际支持格式 |
| 摄像头找不到 /dev/video0 | 设备被占用或未识别 | lsusb 确认硬件,必要时重启板子 |
| 雷达扫描频率跳动 | USB 供电不足 | 把雷达插到板子原生 USB 口或使用带供电的 HUB |
| 雷达驱动启动报 open failed | 串口号不对或权限问题 | ls -l /dev/ttyUSB*,再检查 dialout 组 |
| I2C 设备扫描不到 | 接线错误或地址不对 | 检查 SDA/SCL 接线,用 i2cdetect 扫描 0x48~0x68 |
这些坑几乎没有一个是真正难到解不了的,但每一个都可能卡住你半小时以上。我的经验是:每换一个设备,先独立验证设备本身,再进 ROS 2 层验证话题,最后再接入整体链路。设备本身的测试可以用系统工具完成(比如 v4l2-ctl、i2cdetect),这样可以快速把问题定位在硬件还是软件层。
5.2 通信与环境变量类
ROS 2 项目做到后面,遇到最多的问题反而是通信层面的。比如两个节点明明都在运行,但一个发的话题另一个就是收不到。这种问题 90% 出在 ROS_DOMAIN_ID 上。ROS 2 多个应用如果使用不同的域 ID,互相之间是完全隔离的,就像两个房间里的对讲机用了不同频道。检查办法很简单:
echo $ROS_DOMAIN_ID如果两个节点的环境变量不一样,话题就互相不可见。另外,如果你的设备上开启了防火墙,也要确保 UDP 端口没有被拦截,不过本地单机调试一般不会遇到这个问题,多机通信时才会出现。
还有一个我经常提醒自己的点:写代码时注意 QoS 策略。ROS 2 的发布者和订阅者可以通过 QoS 配置不同的可靠性、持久性策略,如果发布端和订阅端的 QoS 不匹配,默认情况下会报 warning,有些组合甚至收不到数据。usb_cam 这类驱动默认 QoS 通常和 sensor_msgs 的标准配置一致,但如果你自己写节点,要记得设置合适的 QoS,否则下游订阅方会静默收不到图像。
5.3 提高效率的几个小细节
文章最后,分享几个提升开发效率的小细节。第一个是 aliases,把常用命令缩短。我在 ~/.bashrc 里加了下面几个别名,省了不少事:
alias cm='colcon build --symlink-install' alias sb='source install/setup.bash' alias rv='rviz2'第二个是 RViz2 配置保存。手动把视图布局调好之后,点左上角 File -> Save Config As 保存成 rviz 文件,下次启动时直接加载,不用每次重新添加显示组件。
第三个是 tmux。在板子上做开发,ssh 进去之后所有操作都在一个终端里,要用 tmux 分屏管理多个节点的启动,尤其是同时跑 RViz2、雷达驱动、bag 录制时,切来切去会非常痛苦。tmux 学习成本不高,但一旦用起来就回不去了。
第四点关于 colcon 构建参数。在开发自己包的过程中,推荐用 --symlink-install 参数,这样源码修改后不需要每次重新构建就能生效,对于 Python 节点来说尤其好用。代价是构建目录里放的是符号链接,不能随意删 src,但正常开发流程下这完全够用。
第五个是记录的习惯。每次跑通一个环节,我会用 ros2 topic list 和 ros2 topic echo 把关键话题的状态打出来留档,同时把 bag 录下来。真出了问题,回放 bag 比现场复现可靠多了,这也是一开始说的"黑匣子"思路。
我自己的体会是,RDK X5 这套板子的硬件底子是够的,真正决定开发效率的其实是软件链路通不通。先保证 ROS 2 环境干净,再一路一路把传感器接进去,遇到问题先从权限和设备识别查起,然后查环境和通信,最后再怀疑驱动本身。这套排查顺序帮我解决掉了很多看似灵异的故障。最后再补充一个建议:别急着在一开始就加入太多新概念,把摄像头、雷达、IMU 这三类最基础的数据真正吃透,后续做 SLAM、导航、多传感器融合都会顺很多。