继"ROS2 Jazzy + C++ 实战路线------基础学习2"继续学习
ROS2 工程里常见的是:
代码负责逻辑
参数负责配置
Launch负责启动
1. Parameter:节点自己的配置项
Parameter 就是:
Node 运行时使用的配置。
比如:
source_type = axis
primary_port = 7014
publish_rate = 50
Node 内部会读取这些参数。
以后你的动捕节点:
mocap_driver_node
可能有:
source_type
primary_port
backup_port
queue_capacity
publish_rate
这样代码不用改,只改参数。
2. YAML:把一堆 Parameter 放到文件里
比如:
mocap_driver:
ros__parameters:
source_type: "axis"
primary_port: 7014
backup_port: 7015
publish_rate: 50.0
这个文件可能叫:
mocap.yaml
你可以理解:
Parameter
= 单个配置
YAML
= 一堆配置保存成文件
这对真实项目非常重要,因为机器人通常有几十甚至几百个参数。
3. Launch:负责把整个系统启动起来
现在假设系统有三个节点:
mocap_driver_node
gmr_retarget_node
robot_output_node
不用 Launch 的话:
终端1:启动动捕
终端2:启动GMR
终端3:启动输出
很麻烦。
所以 ROS2 有 Launch:
Launch
↓
一次启动多个 Node
例如:
humanoid.launch.py
里面描述:
启动 mocap_driver
启动 gmr_retarget
启动 robot_output
加载 YAML
最终你只需要:
ros2 launch humanoid_bringup humanoid.launch.py
整个系统就起来了。
4. 三者关系一定要搞懂
Launch
↓
┌───────┼────────┐
↓ ↓ ↓
Node A Node B Node C
↓
加载 Parameter
↓
YAML
准确一点是:
YAML
↓
提供参数
↓
Launch
↓
把参数交给 Node
↓
Node 根据参数运行
这是非常典型的 ROS2 工程结构。
5. bringup 是什么?
以后你会经常看到:
xxx_bringup
比如:
humanoid_bringup
它通常不是核心算法。
主要负责:
Launch
Config
系统启动
节点组合
所以你看到:
xxx_bringup
可以直接想到:
这个包负责把整个系统拉起来。
这是 ROS2 项目里很常见的组织方式。
9. 为什么 Parameter 不是什么东西都能改?
这里提前建立一个概念。
有些参数:
日志等级
阈值
滤波参数
运行时改可能没问题。
但有些:
设备类型
EtherCAT网卡
模型文件
底层驱动
通常更适合:
启动之前配置
而不是系统运行过程中随便改。
所以:
Parameter 是配置机制,不代表所有参数都应该动态修改。
10. 到现在 ROS2 大框架已经越来越完整
你已经知道:
Workspace
│
├── Package
│
├── Node
│
├── Topic
│
├── Service
│
├── Action
│
├── Parameter
│
├── Interface
│
├── YAML
└── Launch
它们各自解决的问题:
| 东西 | 解决什么 |
|---|---|
| Node | 谁来干活 |
| Topic | 持续数据怎么传 |
| Service | 一问一答 |
| Action | 长任务 |
| msg/srv/action | 数据格式 |
| Parameter | 节点怎么配置 |
| YAML | 配置怎么保存 |
| Launch | 整个系统怎么启动 |
这张表比目前写多少代码更重要。
继续。现在讲 第 11 课:rosbag2 + ROS2 调试工具体系,还是只抓大框架和必要命令。
1. rosbag2 是什么
你可以把它理解成:
ROS2 的"数据录像机"。
它可以把 Topic 上的数据录下来,之后再回放。
比如真实机器人运行时:
Axis
↓
/mocap/human_pose
↓
GMR
你可以同时录:
/mocap/human_pose
以后即使没有 Axis,也能:
rosbag2 回放
↓
/mocap/human_pose
↓
GMR
所以它特别适合:
复现问题
调算法
分析掉帧
离线测试
保存实验数据
对你的动捕链路尤其重要。
2. 最必要的几个命令
录一个 Topic:
ros2 bag record /mocap/human_pose
录多个:
ros2 bag record \
/mocap/human_pose \
/g1/joint_targets
录全部 Topic:
ros2 bag record -a
查看 bag 信息:
ros2 bag info <bag目录>
回放:
ros2 bag play <bag目录>
现在不需要记参数细节。
只要理解:
record = 录
info = 看
play = 放
3. rosbag2 为什么对算法开发很重要
比如某次机器人出现异常:
第 2 分钟
突然动作抖动
如果没有录数据:
问题过去了
↓
很难重现
有 rosbag2:
真实运行
↓
记录传感器、状态、控制目标
↓
保存
↓
办公室重新播放
↓
分析
这就是机器人开发里非常典型的流程。
4. 它还能帮你把"硬件"和"算法"分开
比如正常流程:
真实动捕设备
↓
mocap_driver
↓
GMR
调 GMR 时不一定需要每次都接真实设备。
可以:
以前录好的 bag
↓
播放
↓
GMR
于是:
硬件团队
和
算法团队
可以相对独立工作。
这就是为什么 rosbag2 不只是"录像工具",也是一个非常重要的测试工具。
5. ROS2 调试工具整体框架
以后遇到问题,不要一上来就看源码。
先用 ROS2 自带工具观察系统。
你重点记这几类:
| 想看什么 | 命令 |
|---|---|
| 有哪些 Node | ros2 node list |
| Node 在干什么 | ros2 node info |
| 有哪些 Topic | ros2 topic list |
| Topic 类型和连接 | ros2 topic info |
| Topic 数据 | ros2 topic echo |
| Topic 频率 | ros2 topic hz |
| 参数 | ros2 param |
| 接口定义 | ros2 interface show |
| 数据录制/回放 | ros2 bag |
这张表以后比背 API 更实用。
6. 一个实际排查思路
例如:
GMR 没有输出。
不要马上改代码。
先检查:
1. mocap_driver 在不在?
↓
ros2 node list
2. /mocap/human_pose 在不在?
↓
ros2 topic list
3. 有没有数据?
↓
ros2 topic echo
4. 频率正常吗?
↓
ros2 topic hz
5. GMR 有没有订阅?
↓
ros2 topic info
6. 输入正常但没输出?
↓
再去看 GMR
这个思维非常重要:
先判断问题在哪一层,再看代码。
7. ROS2 系统调试其实是在看"数据流"
以后你的系统:
传感器
↓
Driver
↓
Topic
↓
算法
↓
Topic
↓
控制
↓
机器人
你要学会逐段确认:
这里有没有数据?
↓
数据类型对不对?
↓
频率对不对?
↓
谁发布?
↓
谁订阅?
这和你之前排查动捕掉帧时做"源端 → GMR → 输出端计数守恒"其实是同一种工程思路。
8. 到这里,ROS2"通信与工程工具"这一层基本完整
目前你已经掌握整体:
Node
↓
Topic / Service / Action
↓
Interface
↓
Parameter + YAML
↓
Launch
↓
rosbag2
↓
调试命令
下一块开始进入机器人本体相关内容:
TF2
↓
URDF
↓
RViz2
这一块非常重要,因为前面学的是:
程序之间怎么通信。
下一块开始学:
机器人各个坐标系、Link、Joint、模型之间是什么关系。
9. TF2 是什么?
一句话:
TF2 用来管理机器人不同坐标系之间的空间关系。
比如一个人形机器人:
world
↓
base_link
↓
torso
↓
head
↓
camera_link
↓
camera optical frame
每个东西都有自己的"坐标系"。
TF2 就负责告诉 ROS2:
camera 相对于 head 在哪里?
head 相对于 torso 在哪里?
torso 相对于 base_link 在哪里?
base_link 相对于 world 在哪里?
10. TF2 最核心的东西:TF Tree
你以后经常看到:
base_link
/ \
left_leg right_leg
↓ ↓
foot foot
↓
torso
↓
head
↓
camera
这叫:
TF Tree,坐标变换树。
它描述整个机器人各部分之间的空间关系。
11. TF2 最重要的两个概念
你暂时只记:
静态 TF
两个坐标系之间基本不会变化。
例如:
head
↓
camera
相机固定安装在头部。
这种关系通常是:
static transform
动态 TF
两个坐标系之间会不断变化。
例如:
base_link
↓
left_foot
机器人走路的时候:
位置变化
姿态变化
所以需要不断更新。TF2 最终解决什么问题?
你以后可以用一句话解释:
TF2 管理机器人系统中不同坐标系之间随时间变化的位姿关系,并提供坐标转换。
这句话已经足够工程化。TF2 常用工具
现在先知道名字:
ros2 run tf2_tools view_frames
用于查看 TF Tree。
以及:
ros2 run tf2_ros tf2_echo frame_a frame_b
查看两个坐标系之间的变换。
例如:
ros2 run tf2_ros tf2_echo base_link camera_link
意思:
查看
camera_link和base_link之间的关系。
不用现在背参数。
11. TF2 和 URDF 的关系
这一点非常重要。
接下来我们会学:
URDF
你可以先理解:
URDF
= 描述"机器人长什么样"
TF2
= 描述"机器人现在各部分在哪里"
例如 URDF 描述:
base_link
↓
torso
↓
head
↓
camera
以及:
Joint
Link
尺寸
惯量
碰撞模型
而机器人真正运行时:
TF2
告诉你:
现在 head 在哪里?
现在 camera 在哪里?
现在 left_foot 在哪里?
所以:
URDF
+
TF2
是机器人模型和空间关系的核心组合。
12. 再往后就是 RViz2
当你有:
URDF
+
TF2
就可以在:
RViz2
里把机器人显示出来。
最终类似:
ROS2
↓
URDF
↓
Robot Model
↓
TF2
↓
RViz2
↓
看到机器人当前姿态
RViz2 本质上就是:
ROS2 的三维可视化和调试工具。
ROS2
│
┌──────────┴──────────┐
↓ ↓
通信系统 机器人描述
│ │
Topic / Service URDF
Action / msg │
│ ↓
Parameter TF2
Launch │
rosbag2 ↓
│ RViz2
└──────────┬──────────┘
↓
机器人系统
13. URDF
URDF = 告诉 ROS2:机器人由哪些零件组成,这些零件之间怎么连接。
例如一个机械臂:
base
↓
link1
↓
link2
↓
link3
↓
end_effector
一个人形机器人:
head
│
torso
/ \
left_arm right_arm
│ │
left_hand right_hand
│
pelvis
/ \
left_leg right_leg
│ │
left_foot right_foot
URDF 就是在描述这些东西。
14. URDF 最核心的两个东西
你现在只需要牢牢记住:
Link
Joint
Link = 一块"刚体"
比如:
头
躯干
大腿
小腿
脚
手臂
都可以看成 Link。
例如:
<link name="base_link"/>
<link name="left_thigh"/>
<link name="left_shin"/>
<link name="left_foot"/>
就是定义了几个刚体。
Joint = 两个 Link 怎么连接
例如:
pelvis
│
hip joint
│
left_thigh
这里:
pelvis
↓
joint
↓
left_thigh
Joint 描述:
两个 Link 之间是什么关系,以及能不能动、怎么动。
15. Joint 有哪些类型?
ROS2 里面常见的:
fixed
revolute
continuous
prismatic
你现在先理解前三个。
fixed
固定不动。
base
│
└── camera
比如机器人身体上的固定相机。
revolute
旋转关节。
例如:
大腿
│
● ← 髋关节
│
小腿
机械臂关节、人形机器人关节,大量都是这种。
continuous
也是旋转,但可以无限旋转。
比如:
轮子
所以看到:
joint
你的第一反应应该是:
"这是在描述两个 Link 怎么连接、怎么运动。"
16. 一个最小 URDF
你不用现在研究 XML 语法,只看结构:
<robot name="demo">
<link name="base_link"/>
<link name="link1"/>
<joint name="joint1" type="revolute">
<parent link="base_link"/>
<child link="link1"/>
<origin xyz="0 0 0.1" rpy="0 0 0"/>
<axis xyz="0 0 1"/>
</joint>
</robot>
整体关系就是:
base_link
│
│ joint1
↓
link1
其中:
parent = 父 Link
child = 子 Link
这个概念以后 TF2 会一直遇到。
17. URDF 还有三个重要东西
一个 Link 不只是"名字"。
通常还会描述:
visual
collision
inertial
visual
机器人"长什么样"。
比如:
机器人身体模型
机械臂模型
脚模型
主要用于显示。
collision
机器人"碰撞用什么形状"。
例如真实机器人腿可能非常复杂:
█████
███████
███
████
但是碰撞检测没必要这么复杂。
可以简化成:
┌─────┐
│ │
│ │
└─────┘
这样计算更快。
inertial
物理属性。
比如:
质量
质心
惯量
这个在 MuJoCo / Gazebo / 物理仿真 里面特别重要。
所以可以简单记:
visual → 看起来什么样
collision → 碰撞怎么算
inertial → 物理属性
URDF
描述:
机器人是什么结构。
例如:
base_link
↓
torso
↓
head
↓
camera_link
TF2
描述:
这些坐标系当前是什么空间关系。
例如:
base_link
↓
head
↓
camera_link
机器人运动以后:
camera_link
的位置和姿态会发生变化。
TF2 就负责表达这种变化。
robot_state_publisher
它可以简单理解成:
根据 URDF + 关节状态,把机器人的 TF 关系发布出来。
整体:
URDF
│
↓
robot_state_publisher
│
↓
TF2
│
↓
RViz2
所以你现在应该建立这个认识:
URDF
↓
机器人结构
robot_state_publisher
↓
把结构转换成 TF
TF2
↓
管理坐标系关系
RViz2
↓
把机器人显示出来
18. 那 joint_state 又是什么?
这个概念也很重要。
假设机器人有:
left_hip
left_knee
left_ankle
现在机器人实际运动:
left_hip = 0.2 rad
left_knee = 0.8 rad
left_ankle = -0.3 rad
这些就是:
Joint State(关节状态)
简单理解:
URDF
↓
告诉 ROS:
"机器人有哪些关节"
Joint State
↓
告诉 ROS:
"这些关节现在转到了哪里"
robot_state_publisher
↓
根据这些信息计算 TF
19. Xacro 是什么?
实际机器人一般不会直接手写一个几千行的 URDF。
因为太痛苦。
所以会出现:
Xacro
你可以暂时理解成:
Xacro = 给 URDF 加上"变量、宏、复用"的工具。
比如机器人左右腿结构非常类似:
left_leg
right_leg
如果直接 URDF 写两遍,会非常重复。
Xacro 可以把公共结构抽出来。
所以实际项目经常是:
xxx.xacro
↓
生成
↓
URDF
你以后看机器人 ROS2 项目时,看到:
urdf/
├── robot.urdf
├── robot.xacro
├── arm.xacro
├── leg.xacro
└── sensor.xacro
不要慌。
本质还是在描述:
Link
+
Joint
+
机器人结构
现在可以形成一个完整 ROS2 机器人软件框架:
ROS2
│
┌─────────────┼─────────────┐
│ │ │
Topic Service Action
│
↓
节点之间通信
机器人模型
│
URDF
↓
TF2
↓
RViz2
机器人控制
│
ros2_control
↓
Controller
↓
Hardware
↓
电机