ROS 2 软件架构:把机器人能力组织成可协作的系统
ROS 2 用节点、接口、坐标变换、生命周期和工具链,把分散的传感、算法与执行模块组织成可观察、可替换的机器人软件图。
ROS 2 的角色与边界
ROS 2 是一套机器人软件中间件、库、工具与约定。它帮助进程发现彼此、交换有类型的数据、记录回放、管理包和观察运行图;它不会自动提供正确的架构、实时性或安全认证。项目仍需定义接口语义、时间边界、错误处理和部署责任。
节点是 ROS 图中的计算参与者,一个节点宜承担一个清楚的逻辑职责,例如相机驱动、里程计、定位或路径规划。节点可以在不同进程或机器上,也可组合进同一进程减少序列化与调度开销。把整个机器人写成一个节点,接口难观察、故障难隔离;把每个小函数拆成节点,则增加通信和配置复杂度。边界应围绕独立生命周期、计算资源、故障隔离和可替换性划分。
Topic、Service 与 Action
Topic 使用发布—订阅,适合连续数据流,如图像、激光扫描和机器人状态。发布者无需知道具体订阅者,多个生产者与消费者可共享主题。Service 是短时请求—响应,适合查询或快速计算;调用方等待结果,不宜承载长时间运动。Action 表达持续较久的目标,带反馈、结果和取消/抢占,例如“导航到房间 301”。
选择接口应从语义出发。相机帧是持续流,用 Topic;查询一次当前地图可用 Service;机械臂执行轨迹需要进度和取消,用 Action。用 Service 执行几十秒任务会让超时与取消语义混乱;用 Topic 模拟事务请求,需要自己重造关联 ID、确认和重试。
接口定义是系统契约。消息字段应带单位、坐标系、时间戳和有效性约定。geometry_msgs/Pose 没有 header,若位姿可能来自不同时间或坐标系,通常应使用带 Header 的类型或在上层协议补足。自定义消息要区分“未知”“零”和“未提供”,避免默认值制造假证据。
DDS、QoS 与数据传输
ROS 2 通常通过 DDS 实现发现与数据分发。QoS 决定通信行为,包括可靠性、历史深度、耐久性、截止时间、生命周期和活跃性。传感器高速流常选择 best effort,允许丢旧帧以减少阻塞;低频关键命令可能选择 reliable;静态地图或配置可用 transient local,让新订阅者收到历史样本。
发布与订阅 QoS 不兼容时,节点可能看得到主题名却收不到数据。可靠也不等于业务可靠:网络层确认传递不能证明控制器执行成功,因此任务仍需 Action 结果或状态回读。队列过深会让消费者处理过期数据;实时感知通常宁可丢旧帧,也不要积压几秒。
大图像与点云可能占满内存带宽。进程内组合与 loaned message/零拷贝能减少复制,但支持程度依赖中间件、消息类型和硬件。优化前应测端到端延迟、CPU、内存与丢帧,避免仅按理论选择。
tf2:管理随时间变化的坐标变换
tf2 维护带时间的坐标树。动态变换如 odom→base_link 持续发布,静态外参如 base_link→camera_link 发布一次。查询“图像曝光时相机到地图的变换”时,应使用图像时间戳;查询最新变换会在机器人运动时造成空间错位。
坐标树必须是一棵有清晰权威来源的结构。两个节点同时发布同一变换会造成跳动;环形父子关系不可定义;坐标命名与轴向应遵循 REP 103/105。tf2 缓冲区只能保存有限历史,延迟过大的消息可能查不到对应变换,此时应丢弃、延长缓冲或修正上游时延;最新值无法代表过去的曝光时刻。
机器人模型、包与启动编排
URDF 描述 link、joint、视觉模型、碰撞几何和惯性参数,Xacro 用宏减少重复。视觉模型可以精细,碰撞模型应服务实时碰撞检查;惯性参数必须物理合理,质量为正、惯量矩阵满足约束。URDF 中的关节零位、轴向和限位要与驱动一致,模型方向错一处会沿 tf、运动学和控制扩散。
ROS 2 package 是源代码、接口、配置与依赖的发布单元,ament 与 colcon 负责构建工作空间。包边界宜围绕可复用能力与依赖关系,消息接口包可单独存在,避免感知和控制形成循环依赖。pluginlib 允许运行时选择控制器或规划器实现,但插件 ABI、参数和生命周期仍需测试。
Launch 文件负责把节点、参数、命名空间、重映射和生命周期顺序组装成系统。启动成功只能证明进程被创建;使能执行器前还要等待驱动、标定、tf、定位与安全监控进入健康状态。多机器人系统使用命名空间隔离主题与参数,坐标 frame 名仍要避免冲突。
参数、Lifecycle 与组件
参数用于运行时配置节点行为。参数文件应版本化,并区分设备标定、环境配置与算法调参;把标定常数散落在启动脚本里会失去来源。参数改变若影响安全或数据结构,应验证后再应用,不能任意热更新。
Lifecycle Node 有未配置、非激活、激活、清理和结束等受控状态。驱动可以在 configure 时申请设备,在 activate 后才发布有效数据;导航系统可确保地图、定位和控制器按依赖顺序激活。生命周期转移失败要上报并阻止下游误用。
Composable Node 把多个组件放入同一进程,提高通信效率;代价是故障隔离变弱,一个进程崩溃可能带走所有组件。安全关键驱动、资源密集推理和易崩溃第三方库是否共进程,需要依据恢复与性能取舍。
执行器、回调与并发
Executor 选择何时执行订阅、定时器、Service 和 Action 回调。单线程执行器易理解,某个长回调会阻塞其他回调;多线程执行器提高并发,但共享状态需要同步,回调顺序更难预测。Callback Group 可以指定互斥或可重入关系。
常见反模式是在订阅回调中直接做数百毫秒推理,导致控制状态与心跳都延迟。可将重计算放入独立线程/进程,回调只交换带时间戳的数据;控制循环不要等待远程 Service;持锁时不要调用可能阻塞的接口。并发设计应先画出共享数据所有权,逐个增加互斥锁很难修复混乱的所有权。
rosbag、日志与可复现性
rosbag2 可记录主题并回放,是复现感知和导航问题的重要工具。记录方案要包含 /tf、/tf_static、时钟、关键参数版本和软件提交标识,仅录最终结果常无法重建问题。涉及图像与点云时要估算带宽、磁盘和压缩开销,防止记录行为改变系统时序。
日志应带节点、时间、任务 ID 和严重级别;指标需要覆盖消息年龄、周期、队列积压、丢帧、控制超时和状态机转移。机器人现场问题常无法重复,观测设计必须在故障前完成。
工程例子:导航偶尔使用两秒前的激光数据
激光 Topic 配置 reliable 且队列深度 100,局部规划器在 CPU 峰值时处理不过来,消息没有丢失,却逐步积压。表面上每帧都“可靠到达”,实际上机器人正在根据两秒前的障碍行动。
修复先监控消息年龄,丢包率作为另一项指标;对实时传感数据改用适当 QoS 和浅队列,让旧帧被新帧替代;把重计算与控制回调隔离;过期测量在消费端明确拒绝。任务状态类消息仍可保留可靠传输。QoS 要按数据语义逐主题设计。