3、机器人软件架构:ROS2基础、节点与话题通信、机器人软件分层设计

好,咱们进入正题。这一章聊的是机器人软件架构,说白了就是机器人的“大脑”和“神经系统”怎么搭。我见过不少刚入行的朋友,一上来就写死循环、用全局变量传数据,结果项目一复杂,代码改都改不动。嗯,今天咱们就用ROS2这套成熟框架,把这事理顺。

3.1 ROS2 到底是什么?

ROS2,全称 Robot Operating System 2,但它不是真正的操作系统。它更像一套“通信中间件”,帮我们把传感器、电机、算法模块串起来。我个人习惯把它理解成“机器人界的微信”——各个模块就是不同的群友,通过话题(Topic)和服务(Service)来聊天。

为什么用ROS2而不是ROS1?我踩过坑。ROS1在实时性、多机通信、跨平台支持上都有短板。比如你在风场里部署巡检机器人,需要远程监控,ROS1的通信机制在弱网环境下经常掉线。ROS2基于DDS(数据分发服务),天生支持分布式、高可靠通信,说白了就是更稳。

核心概念速览:

  • 节点(Node):每个可执行程序就是一个节点,比如“激光雷达驱动节点”、“路径规划节点”。
  • 话题(Topic):节点之间发布/订阅的通道,比如“/scan”话题传输激光雷达数据。
  • 服务(Service):一问一答的通信模式,适合请求-响应场景,比如“/get_battery_status”。
  • 动作(Action):带反馈的长时间任务,比如“/navigate_to_waypoint”。

3.2 节点与话题通信:机器人的“对话”方式

咱们直接看代码。假设你有一个温度传感器节点,它每隔1秒发布一次温度数据。另一个节点负责接收并显示。这就是最典型的发布-订阅模式。

发布者节点(publisher)示例:

import rclpy
from rclpy.node import Node
from std_msgs.msg import Float32

class TemperaturePublisher(Node):
    def __init__(self):
        super().__init__('temperature_publisher')
        self.publisher_ = self.create_publisher(Float32, 'temperature', 10)
        self.timer = self.create_timer(1.0, self.publish_temp)
        self.temp = 25.0

    def publish_temp(self):
        msg = Float32()
        msg.data = self.temp
        self.publisher_.publish(msg)
        self.get_logger().info(f'发布温度: {self.temp}°C')
        self.temp += 0.1  # 模拟温度变化

def main(args=None):
    rclpy.init(args=args)
    node = TemperaturePublisher()
    rclpy.spin(node)
    node.destroy_node()
    rclpy.shutdown()

if __name__ == '__main__':
    main()

订阅者节点(subscriber)示例:

import rclpy
from rclpy.node import Node
from std_msgs.msg import Float32

class TemperatureSubscriber(Node):
    def __init__(self):
        super().__init__('temperature_subscriber')
        self.subscription = self.create_subscription(
            Float32, 'temperature', self.listener_callback, 10)

    def listener_callback(self, msg):
        self.get_logger().info(f'收到温度: {msg.data:.2f}°C')

def main(args=None):
    rclpy.init(args=args)
    node = TemperatureSubscriber()
    rclpy.spin(node)
    node.destroy_node()
    rclpy.shutdown()

我的经验: 刚开始写ROS2节点时,容易忘记设置QoS(服务质量)。比如在风场巡检中,激光雷达数据丢几帧可能问题不大,但电机控制指令丢一帧就可能撞机。所以,关键控制话题建议用 rclcpp::QoS(10).reliable(),保证数据可靠到达。

3.3 机器人软件分层设计

写机器人软件最怕什么?怕耦合。传感器驱动、数据处理、决策规划、执行控制全揉在一个文件里。改一个地方,崩一片。我早期做项目时就吃过这个亏——为了加一个超声波传感器,结果把路径规划模块搞崩了。

所以,分层设计是必须的。咱们把软件分成三层:

层次 职责 典型模块
驱动层 直接与硬件交互,读取传感器数据,发送控制指令 激光雷达驱动、IMU驱动、电机驱动
功能层 数据处理、状态估计、决策规划 SLAM建图、路径规划、避障算法
应用层 任务调度、人机交互、业务逻辑 巡检任务管理、远程监控界面

每一层之间通过ROS2话题通信。驱动层发布原始数据,功能层订阅并处理,应用层再订阅处理结果。这样,你想换一个激光雷达,只需要改驱动层,功能层和应用层完全不用动。

下面这张图,是我自己画的分层架构图,你看一眼就明白了:

应用层 巡检任务管理 | 远程监控 | 告警系统 话题:/task_command, /alarm_status 功能层 SLAM建图 | 路径规划 | 避障 | 状态估计 话题:/map, /plan, /odom 服务:/get_pose, /set_goal 驱动层 激光雷达驱动 | IMU驱动 | 电机驱动 话题:/scan, /imu, /cmd_vel 数据流方向

避坑指南: 我曾经在一个风场项目里,把驱动层和功能层写在了同一个节点里。结果换了一个更高精度的激光雷达,驱动接口变了,不得不把整个节点重写。后来我强制自己遵守分层原则,每个节点只做一件事。记住:高内聚、低耦合,这是软件工程的金科玉律。

3.4 小结:ROS2 让机器人开发更“模块化”

说白了,ROS2 就是帮你把机器人软件拆成一个个小积木。节点是积木块,话题是积木之间的接口。你搭积木的时候,换一块不影响其他块,这就是分层设计的好处。

嗯,这一章的内容就到这里。下一章咱们会深入聊一聊ROS2的通信机制细节,包括QoS配置、多线程回调,以及怎么在风场这种复杂环境下保证通信稳定。到时候我会分享一些实际调参的经验。


专注资料整理