【ROS2速成 - Day2】ROS2五大核心概念吃透(嵌入式类比记忆,超好懂)
前言大家好我是深耕嵌入式 15 年的老林。上一篇 Day1 我们搭好了 ROS2 的开发环境很多同学私信我说ROS2 的概念太多了什么节点、话题、服务听着就头大完全不知道和我们平时写单片机代码有什么关系。其实我刚开始学 ROS2 的时候也有同样的感觉觉得这东西太 上层 了和我们天天写的寄存器操作、任务调度、串口通信完全是两个世界。但后来我发现ROS2 的核心设计思想和嵌入式开发是完全相通的只是换了一套名词而已。今天这篇文章我就用嵌入式工程师最熟悉的语言把 ROS2 的五大核心组件全部翻译过来。看完你会发现原来 ROS2 这么简单就是把我们嵌入式里天天用的东西封装成了一套标准的框架而已。一、核心思想ROS2 就是一个分布式的嵌入式操作系统先给大家建立一个整体认知ROS2 本质上就是一个分布式的、跨平台的嵌入式操作系统。我们平时用的 FreeRTOS 是单芯片的操作系统管理同一个 MCU 上的多个任务ROS2 是跨设备的操作系统管理同一个网络上的多个节点可以是 PC、单片机、树莓派、机器人所有的 ROS2 程序都是围绕着 节点之间如何通信 这个核心问题展开的。而通信的方式就是我们今天要讲的话题、服务、参数。二、五大核心组件详解嵌入式类比记忆法2.1 节点 (Node) FreeRTOS 独立任务线程最核心的类比一个 ROS2 节点就相当于 FreeRTOS 里用 xTaskCreate 创建的一个独立任务。我们写嵌入式代码的时候都知道不能把所有功能都写在 main 函数的 while (1) 里那样耦合度太高出了问题不好排查我们会把不同的功能拆成不同的任务比如一个任务负责采集传感器数据一个任务负责电机控制一个任务负责串口通信每个任务有自己的栈空间独立运行互不干扰任务之间通过消息队列、信号量等方式通信ROS2 的节点就是完全一样的设计思想一个节点只负责一个功能比如camera_node负责采集摄像头数据imu_node负责采集 IMU 数据motor_control_node负责控制电机每个节点是一个独立的进程有自己的内存空间独立运行崩溃了不会影响其他节点节点可以运行在同一个设备上也可以运行在不同的设备上比如树莓派跑传感器节点PC 跑路径规划节点节点之间通过话题、服务、参数等方式通信举个例子一个最简单的差分驱动机器人至少需要 3 个节点imu_node采集 IMU 数据发布 IMU 话题motor_driver_node订阅速度话题控制电机转动teleop_node接收键盘输入发布速度话题这就相当于我们在 FreeRTOS 里创建了 3 个任务每个任务做一件事通过消息队列传递数据。2.2 话题 (Topic) 消息队列 串口广播最核心的类比一个 ROS2 话题就相当于一个带广播功能的消息队列或者说一个串口总线。我们先回忆一下嵌入式里的消息队列一个任务往消息队列里发数据生产者一个或多个任务从消息队列里取数据消费者异步通信发完就走不用等对方接收数据是先进先出的再回忆一下串口广播一个设备往串口总线上发数据所有挂在总线上的设备都能收到这个数据谁需要谁处理不需要的可以忽略ROS2 的话题就是把这两个东西结合起来了有一个 ** 发布者 (Publisher)** 往话题里发消息相当于往消息队列里写数据或者往串口总线上发数据有一个或多个 ** 订阅者 (Subscriber)** 从话题里收消息相当于从消息队列里读数据或者监听串口总线异步通信发布者发完消息就继续干自己的事不用等订阅者处理一对多通信一个发布者可以对应多个订阅者数据是先进先出的举个例子上面的teleop_node往/cmd_vel话题里发布速度消息motor_driver_node订阅这个话题收到消息后控制电机转动。如果我们再加一个odometry_node也订阅/cmd_vel话题就可以根据速度计算机器人的里程计。这就相当于teleop_task往一个消息队列里发速度数据motor_control_task和odometry_task都从这个消息队列里取数据。注意话题的命名很重要就像串口的设备名/dev/ttyUSB0一样只有发布者和订阅者使用完全相同的话题名才能通信。2.3 消息 (Message) C 语言通信结构体最核心的类比一个 ROS2 消息就相当于我们在 C 语言里定义的一个通信结构体。这个类比绝对是 ROS2 入门最重要的一个点理解了这个你就理解了 ROS2 通信的本质。我们写嵌入式代码的时候两个任务之间要传递数据或者两个设备之间通过串口通信会怎么做我们会先定义一个结构体约定好数据的格式比如第 1 个字节是命令字第 2-3 个字节是速度第 4-5 个字节是角度发送方把数据打包成这个结构体然后发送出去接收方按照同样的结构体格式解析数据如果双方约定的结构体不一样就会解析出乱码通信失败。这是我们写嵌入式代码天天都会遇到的问题。ROS2 的消息就是帮你把这个 约定结构体 的过程标准化了。ROS2 已经预定义了大量常用的消息类型你直接用就行不用自己再去定义协议了。举个例子最常用的速度消息geometry_msgs/Twist它的结构体定义是这样的// 对应C语言的结构体 typedef struct Twist { Vector3 linear; // 线速度单位m/s Vector3 angular; // 角速度单位rad/s } Twist; typedef struct Vector3 { float x; float y; float z; } Vector3;对于差分驱动机器人来说我们只需要用linear.x前后速度和angular.z转向速度就够了。当teleop_node往/cmd_vel话题发布一个Twist消息时就相当于它把一个这样的结构体打包发了出去。motor_driver_node收到这个结构体后解析出linear.x和angular.z的值然后转换成左右轮的 PWM 输出。就是这么简单没有任何魔法就是结构体的打包和解包而已。2.4 服务 (Service) 函数调用 串口问答最核心的类比一个 ROS2 服务就相当于一个远程函数调用或者说串口的问答式通信。我们先回忆一下函数调用你调用一个函数传入参数函数执行相应的操作函数返回一个结果给你再回忆一下串口的问答式通信你给设备发一个指令请读取传感器 A 的值设备收到指令后读取传感器的值设备给你回复传感器 A 的值是 123这两种都是同步通信你发完请求后必须等对方回复才能继续往下执行。ROS2 的服务就是这种同步的、一对一的通信方式有一个 ** 客户端 (Client)** 发送请求相当于调用函数传入参数有一个 ** 服务端 (Server)** 处理请求执行相应的操作服务端给客户端返回一个响应相当于函数的返回值一对一通信一个服务端只能同时处理一个客户端的请求同步通信客户端发完请求后会阻塞直到收到响应举个例子我们想让机器人执行一个拍照的操作就可以用服务来实现客户端发送请求请拍一张照片服务端摄像头节点收到请求后控制摄像头拍照服务端返回响应拍照成功照片数据是 xxx话题 vs 服务 怎么选通信方式适用场景嵌入式类比话题连续不断的数据流消息队列、串口广播服务一次性的、有返回值的操作函数调用、串口问答简单来说一直发的用话题偶尔问的用服务。2.5 参数 (Parameter) 全局变量 Flash 配置参数最核心的类比ROS2 参数就相当于嵌入式里的全局变量或者存在 Flash 里的配置参数。我们写嵌入式代码的时候经常会用到一些配置参数比如电机的 PID 参数、传感器的校准值、串口的波特率。这些参数我们会定义成全局变量程序运行时可以读写保存在 Flash 里掉电不丢失可以通过串口指令修改不用重新烧写程序ROS2 的参数就是实现了这个功能参数是节点的配置项以键值对的形式存在比如motor_pid_p: 1.0节点运行时可以读写自己的参数参数可以保存在 YAML 文件里启动节点时加载可以通过命令行或其他节点修改参数不用重新编译代码举个例子我们的motor_control_node可以定义三个参数pid_p、pid_i、pid_d。启动节点时从配置文件加载这些参数运行时如果发现 PID 参数不合适可以直接通过命令行修改不用重新编译代码非常方便。三、ROS2 常用基础消息类型必须熟记ROS2 已经为我们预定义了大量常用的消息类型覆盖了机器人开发的绝大多数场景。下面这几个是你开发中天天都会用到的必须熟记。3.1 基础数据类型 (std_msgs)最基础的消息类型封装了 C 语言的基本数据类型std_msgs/String字符串消息最常用的调试消息string datastd_msgs/Int32、std_msgs/Float32整数、浮点数消息std_msgs/Bool布尔值消息3.2 运动控制类型 (geometry_msgs)机器人运动控制相关的消息类型geometry_msgs/Twist速度消息控制机器人运动的核心消息Vector3 linear # 线速度(x:前后, y:左右, z:上下) Vector3 angular # 角速度(x:翻滚, y:俯仰, z:偏航)geometry_msgs/Pose位姿消息表示机器人的位置和姿态Point position # 位置(x,y,z) Quaternion orientation # 姿态(四元数)geometry_msgs/PoseStamped带时间戳的位姿消息最常用的位姿消息Header header # 包含时间戳和坐标系 Pose pose3.3 传感器类型 (sensor_msgs)各种传感器数据的消息类型sensor_msgs/ImuIMU 数据消息包含加速度、角速度、姿态Header header Quaternion orientation # 姿态四元数 Vector3 angular_velocity # 角速度 Vector3 linear_acceleration # 线加速度sensor_msgs/Image图像消息摄像头数据sensor_msgs/LaserScan激光雷达数据消息四、总结一张表记住五大核心组件最后给大家整理了一张对比表把今天讲的所有内容都浓缩进去了建议截图保存。ROS2 组件嵌入式类比核心功能通信模式典型使用场景节点 (Node)FreeRTOS 任务执行具体功能无传感器采集、电机控制、路径规划话题 (Topic)消息队列 串口广播连续数据流传输异步、一对多发布传感器数据、速度指令消息 (Message)C 语言结构体定义数据格式无所有通信的数据载体服务 (Service)函数调用 串口问答一次性请求响应同步、一对一拍照、启动设备、查询状态参数 (Parameter)全局变量 Flash 配置节点配置管理无PID 参数、校准值、设备地址写在最后今天这篇文章我用嵌入式工程师最熟悉的语言把 ROS2 的五大核心组件全部翻译了一遍。相信你现在已经对 ROS2 有了一个清晰的整体认知再也不会觉得 ROS2 的概念晦涩难懂了。其实 ROS2 的本质就是一套标准化的通信框架它把我们嵌入式开发中天天都在重复做的 拆任务、定义协议、写通信 这些工作全部封装成了标准的组件。我们不用再去关心底层的通信细节只需要专注于自己的业务逻辑就行。这就是 ROS2 最大的价值让机器人开发变得像搭积木一样简单。 下期连载预告【ROS2 速成 - Day3】ROS2 命令行全套实操开发天天用必须练熟下一篇我会带大家手把手实操 ROS2 的所有常用命令包括如何查看节点、话题、服务如何发布消息、调用服务如何设置参数。这些命令是你开发 ROS2 的时候天天都会用到的必须练熟。码字不易欢迎点赞 收藏 关注后续我会按【ROS2 速成 - DayX】的格式持续连载更新分享 ROS2 嵌入式实战代码、硬件对接源码、项目工程模板帮大家少踩坑快速从嵌入式工程师转型 ROS2 开发。有任何问题欢迎在评论区留言我会一一回复。