先看最终目标:

                    ROS2机器人系统
                         │
                    ros2 launch
                         │
              ┌──────────┼──────────┐
              ↓          ↓          ↓
           Node A      Node B      Node C
              │          │          │
              └────── Topic / Service ──────┘
                         │
                    数据和命令流动
                         │
          ┌──────────────┼──────────────┐
          ↓              ↓              ↓
        TF2            URDF        ros2_control
          │              │              │
          ↓              ↓              ↓
       坐标关系       机器人模型       控制器
                                        │
                                        ↓
                                  Hardware Interface
                                        │
                                        ↓
                                   EtherCAT/CAN
                                        │
                                        ↓
                                      电机

你现在先不要研究每一个节点怎么写。先搞清楚:

启动 → 数据 → 坐标 → 控制 → 硬件

这条主线。


1、第一层:Bringup

真实机器人 ROS2 项目里面,你以后经常看到:

robot_bringup

或者:

xxx_bringup

这个名字非常值得记。

它通常不是一个核心算法包,而是:

负责把整个机器人系统启动起来。

比如:

robot_bringup/
├── launch/
│   ├── robot.launch.py
│   ├── simulation.launch.py
│   └── hardware.launch.py
│
└── config/
    ├── controller.yaml
    └── robot.yaml

于是:

ros2 launch robot_bringup robot.launch.py

可能一次启动:

robot_state_publisher
controller_manager
joint_state_broadcaster
robot_controller
RViz
hardware_interface

所以以后看到:

bringup

你脑子里直接翻译成:

系统启动入口。


2、第二层:URDF——机器人“长什么样”

现在假设机器人已经启动,ROS2 需要知道:

机器人有多少个关节?
每个关节连接什么?
每个 link 多大?
坐标系在哪里?
关节怎么运动?

这就是:

URDF

例如一个简单机械臂:

base_link
    │
 joint1
    │
 link1
    │
 joint2
    │
 link2
    │
 joint3
    │
 link3

URDF 描述的是:

Link
Joint
几何模型
惯量
碰撞模型
关节限制

真实机器人 URDF 往往很长。

例如几十个关节:

left_hip
left_knee
left_ankle
right_hip
right_knee
right_ankle
...

直接写 URDF 很麻烦。

所以工程里经常看到:

.xacro

你可以暂时把它理解成:

更方便生成 URDF 的模板语言。

因此以后看到:

urdf/
├── robot.urdf.xacro
├── leg.xacro
├── arm.xacro
└── sensor.xacro

不要懵。

本质上:

Xacro
 ↓
生成
 ↓
URDF
 ↓
机器人描述

3、第三层:robot_state_publisher

这是一个非常重要的 Node。

它的任务可以简单理解为:

根据机器人模型和关节状态,告诉 ROS2 每个 link 当前在哪里。

例如:

base_link
   ↓
left_hip
   ↓
left_thigh
   ↓
left_knee
   ↓
left_shank

如果膝关节转了:

joint position变化
        ↓
robot_state_publisher
        ↓
TF变化
        ↓
RViz里的机器人姿态变化

4、TF2:机器人坐标系的“关系网络”

你可以把 TF2 理解成:

机器人所有坐标系之间的关系管理系统。

例如人形机器人:

base_link
   │
   ├── torso
   │
   ├── left_leg
   │      ├── left_thigh
   │      ├── left_shin
   │      └── left_foot
   │
   └── right_leg
          ├── right_thigh
          ├── right_shin
          └── right_foot

相机:

head
  │
  └── camera_link

IMU:

torso
  │
  └── imu_link

TF2 就是在维护:

camera_link
    ↓
head
    ↓
torso
    ↓
base_link

这样的坐标关系。


5、ros2_control

到这里:

URDF
 ↓
机器人是什么样
TF2
 ↓
机器人各部分在哪里

但是还缺一个东西:

怎么让机器人动?

这就是:ros2_control

你可以先把它理解成:

ROS2 和真实机器人硬件之间的标准控制框架。

整体:

                    ROS2
                     │
                Controller
                     │
                     ↓
               ros2_control
                     │
             Hardware Interface
                     │
             ┌───────┴───────┐
             ↓               ↓
          EtherCAT           CAN
             ↓               ↓
           电机              电机

所以:

Controller

负责:

我要机器人怎么运动。

而:

Hardware Interface

负责:

怎么把这个运动命令真正送给电机。


现在把整条链真正串起来,你现在应该能看懂下面这个架构:

                 robot_bringup
                      │
                 Launch启动
                      │
       ┌──────────────┼──────────────┐
       ↓              ↓              ↓
     GMR          robot_state     controller
     Node          publisher        manager
       │              │              │
       ↓              ↓              ↓
 /joint_command      TF2       ros2_control
                                      │
                                      ↓
                              Hardware Interface
                                      │
                              ┌───────┴───────┐
                              ↓               ↓
                           MuJoCo         EtherCAT
                              │               │
                              ↓               ↓
                            仿真            真实机器人

旁边:

URDF/Xacro
   ↓
机器人模型
   ↓
robot_state_publisher
   ↓
TF2

而参数:

YAML
 ↓
Controller / GMR / Driver

最终:

Launch
 ↓
把所有东西启动起来

6、你现在看真实 ROS2 仓库,应该按照这个顺序

以后打开一个陌生机器人项目,不要从 .cpp 开始。按照:

① README
   ↓
② Workspace
   ↓
③ Package
   ↓
④ bringup
   ↓
⑤ launch
   ↓
⑥ config/YAML
   ↓
⑦ URDF/Xacro
   ↓
⑧ Node
   ↓
⑨ Topic
   ↓
⑩ TF2
   ↓
⑪ Controller
   ↓
⑫ ros2_control
   ↓
⑬ Hardware

这就是你现在最需要建立的读 ROS2 项目的方法论。

ROS2 为什么需要 URDF?

因为 ROS2 本身并不知道:

“你的机器人到底长什么样?”

比如宇树 G1:

             head
              │
            torso
              │
            pelvis
          /       \
      left_leg   right_leg
         │          │
       foot        foot

ROS2 需要知道:

  • 有哪些关节?

  • 有哪些身体部件?

  • 谁连接谁?

  • 关节能不能转?

  • 能转多少?

  • 每个部件的坐标系在哪里?

  • 碰撞模型是什么?

  • 质量、惯量是什么?

这就是 URDF 主要解决的问题。

你只需要先记住:

URDF = 机器人结构说明书

它描述的是机器人本身。

例如:

机器人
│
├── base_link
│
├── pelvis
│
├── torso
│
├── left_arm
│
├── right_arm
│
├── left_leg
│
└── right_leg

这些叫:

Link

也就是机器人的“身体部件”。


7、Link 是什么?

非常简单:

Link = 一块刚体

例如:

大腿
小腿
脚
手臂
前臂
手
躯干

都可以看成 Link。

例如:

大腿
 ↓
小腿
 ↓
脚

可以理解成:

Link
 ↓
Joint
 ↓
Link
 ↓
Joint
 ↓
Link

8、Joint 是什么?

Joint 就是:

两个 Link 之间怎么连接、怎么运动。

例如机器人腿:

大腿
 │
 │  髋关节
 ↓
小腿
 │
 │  膝关节
 ↓
脚

对应:

thigh
  │
 hip_joint
  │
 shin
  │
 knee_joint
  │
 foot

所以:

Link 是东西,Joint 是东西之间的连接和运动关系。

这个一定记住。


Joint 有哪些类型?目前最需要知道三个:

1. Fixed

固定连接:

torso
  │
 fixed
  │
camera

摄像头固定在头上,就可以是 Fixed。


2. Revolute

旋转关节。

机器人绝大部分关节都属于这一类。

例如:

大腿
 │
髋关节 ↻
 │
小腿

3. Prismatic

直线运动:

←────→

机器人传统机械臂里可能比较常见。

对于你现在的 humanoid 运控工作:

重点理解 Fixed + Revolute 就够了。

Xacro = 帮你生成 URDF 的模板语言

你可以理解成:

Xacro
  ↓
生成
  ↓
URDF

所以:

URDF 是最终机器人模型,Xacro 是更方便写 URDF 的方式。


9、robot_state_publisher

现在我们有了:

URDF
 ↓
知道机器人有哪些 Link / Joint

但是还有一个问题:

机器人现在的关节到底转到了哪里?

比如:

左膝关节 = 30°

那么小腿应该在哪里?这时候就需要:

robot_state_publisher

它的作用可以简单理解成:

URDF
 +
Joint State
 ↓
计算
 ↓
各个 Link 的空间关系
 ↓
发布 TF

10、TF2

TF2 = 管理机器人各个坐标系之间空间关系的系统。

例如:

base_link
   ↓
pelvis
   ↓
torso
   ↓
head
   ↓
camera_link

TF2 就是在告诉 ROS2:

camera_link
相对于
head
在哪里?

head
相对于
torso
在哪里?

torso
相对于
base_link
在哪里?
机器人学ROS2
连杆Link
关节Joint
坐标系TF Frame
坐标变换TF Transform
机器人结构URDF
参数化机器人模型Xacro
根据关节状态计算 TFrobot_state_publisher

这个地方非常重要。

Joint State

告诉你:

左膝:
position = 0.5 rad
velocity = ...
effort = ...

它描述的是:

关节状态


TF

告诉你:

left_foot
相对于
base_link

在哪里?

它描述的是:

坐标系之间的位置和姿态关系

所以:

Joint State
     ↓
关节转了多少
     ↓
robot_state_publisher
     ↓
TF
     ↓
各个坐标系在哪里

11、RViz 

以后打开:

rviz2

你会看到机器人模型。

背后的逻辑其实就是:

URDF
 ↓
Robot Model
 ↓
TF
 ↓
RViz
 ↓
显示机器人

所以 RViz 不是什么“神奇的机器人软件”。

它主要是在:

把 ROS2 中的数据可视化出来。

              URDF / Xacro
                   │
          机器人结构模型
                   │
          Link + Joint
                   │
                   ↓
        robot_state_publisher
                   │
                   ↓
                  TF2
                   │
       各坐标系之间的空间关系
                   │
                   ↓
                 RViz


Joint State ───────┘

而控制链是另外一条:

GMR
 ↓
Joint Command
 ↓
Controller
 ↓
ros2_control
 ↓
Hardware Interface
 ↓
EtherCAT
 ↓
电机

两条线最后共同组成一个机器人系统。


12、Static TF 和 Dynamic TF

这个也不用学复杂。

Static TF

不会变化。

例如:

head
 ↓
camera_link

摄像头牢牢固定在头上。

那么:

head → camera_link

基本不会变化。

这就是静态 TF。


Dynamic TF

会随着机器人运动变化。

例如:

torso
 ↓
upper_arm
 ↓
forearm
 ↓
hand

手臂动起来以后:

torso → hand

一直在变化。所以这是动态 TF,以后调机器人系统,你会经常用。

查看 TF:

ros2 topic echo /tf

静态 TF:

ros2 topic echo /tf_static

查看节点:

ros2 node list

查看机器人状态:

ros2 topic echo /joint_states

查看 TF 树:

ros2 run tf2_tools view_frames

查看两个坐标系之间的关系:

ros2 run tf2_ros tf2_echo base_link camera_link

不用死记,以后真正调系统的时候再查命令即可。


13、ros2_control

                 ROS2
                   │
                   ↓
              Controller
                   │
                   ↓
             ros2_control
                   │
          ┌────────┴────────┐
          ↓                 ↓
    Command Interface   State Interface
          ↓                 ↑
          └──── Hardware ───┘
                   │
                   ↓
          Hardware Interface
                   │
          ┌────────┴────────┐
          ↓                 ↓
       EtherCAT            MuJoCo
          ↓                 ↓
        电机                 仿真
上面
控制算法
 ↓
Controller
 ↓
ros2_control
 ↓
Hardware Interface
 ↓
具体硬件

所以:

上面的控制逻辑尽量不要关心底层电机到底是 EtherCAT、CAN 还是仿真。

底层通过 Hardware Interface 对接。

Controller

它不希望知道:

“下面到底是 EtherCAT 还是 MuJoCo?”

所以:

Controller
     ↓
ros2_control
     ↓
Hardware Interface
     ↓
具体硬件

例如:

真机

Controller
 ↓
ros2_control
 ↓
G1 Hardware Interface
 ↓
EtherCAT
 ↓
电机

仿真

Controller
 ↓
ros2_control
 ↓
Simulation Interface
 ↓
MuJoCo

上面的 Controller 可以保持基本一致。

这就是 ros2_control 很重要的意义。


14、 Hardware Interface 

简单理解:

Hardware Interface = ROS2 和真实硬件之间的翻译层。

上面说:

左膝目标位置 = 0.5 rad

Hardware Interface 负责把这个东西变成底层硬件能理解的东西。

例如:

ROS2
 ↓
position command
 ↓
Hardware Interface
 ↓
EtherCAT PDO
 ↓
电机驱动器
 ↓
电机

然后电机反馈:

编码器
 ↓
EtherCAT
 ↓
Hardware Interface
 ↓
ROS2
 ↓
joint_state

于是形成闭环:

        Command
           ↓
ROS2 → Hardware → 电机
           ↑
        Feedback
Joint Command
      ↓
Controller
      ↓
ros2_control
      ↓
Hardware Interface
      ↓
EtherCAT
      ↓
Motor
      ↓
Encoder
      ↓
Hardware Interface
      ↓
Joint State
      ↓
ROS2

所以你会发现:

ROS2 并没有改变机器人控制的本质。

它主要是在把整个机器人软件系统组织起来。

名称大白话
Controller决定机器人应该怎么动
ros2_control控制框架
Hardware InterfaceROS2 和硬件之间的翻译层
Command Interface给硬件的目标
State Interface硬件反馈回来的状态

这五个概念搞明白,后面学习就会非常顺。


15、 Controller 到底怎么控制关节

你现在最需要搞明白的是这一条:

上层算法
   ↓
Controller
   ↓
Command Interface
   ↓
Hardware Interface
   ↓
电机

以及反馈:

编码器
   ↓
Hardware Interface
   ↓
State Interface
   ↓
Controller

Controller 不等于“电机控制器”,这是刚接触 ros2_control 很容易混淆的地方。

这里的 Controller 更接近:

ROS2 中负责产生关节控制目标的软件模块。

例如:

Joint Trajectory Controller

收到:

左膝 → 0.5 rad
右膝 → 0.6 rad

然后产生对应的关节命令。

真正和电机通信的是后面的:

Hardware Interface

所以:

Controller
= 算“我要什么”

Hardware Interface
= 想办法把这个要求送到硬件

16、 Command Interface

假设一个关节:

left_knee

可能存在:

position
velocity
effort

这就是不同的 Command Interface。

例如:

position command

意思:

“我希望这个关节到这个位置。”

或者:

velocity command

意思:

“我希望这个关节以这个速度运动。”

或者:

effort command

意思:

“我希望给这个关节这个力/力矩控制量。”

你可以把它理解成:

Controller
   ↓
“给左膝 0.5 rad”
   ↓
position command interface

17、  State Interface

反过来,机器人会告诉 ROS2:

left_knee position = 0.48
left_knee velocity = 0.12
left_knee effort = ...

这些就是:

State Interface

所以:

Command Interface
        ↓
       输出
        ↓
      Hardware
        ↑
       反馈
        ↑
 State Interface

这是 ros2_control 最核心的一组概念。


18、  Controller Manager

现在再加入一个东西:

Controller Manager

它可以理解成:

Controller 的管理者。

例如你的机器人可能同时有:

joint_state_broadcaster
arm_controller
leg_controller
head_controller

Controller Manager 负责:

  • 加载 Controller

  • 启动 Controller

  • 停止 Controller

  • 切换 Controller

  • 管理 Controller 使用哪些接口

所以结构变成:

             Controller Manager
              /      |       \
             ↓       ↓        ↓
          Controller Controller Controller
                 ↓
          ros2_control
                 ↓
        Hardware Interface

假设控制频率:

500 Hz

一个周期大致可以理解成:

① 读取硬件反馈
       ↓
② 更新 State Interface
       ↓
③ Controller 读取状态
       ↓
④ Controller 计算
       ↓
⑤ 产生 Command Interface
       ↓
⑥ Hardware Interface 写入硬件
       ↓
⑦ 电机执行
       ↓
⑧ 编码器反馈
       ↓
       回到①

形成闭环:

        ┌──────────────────────┐
        │                      ↓
反馈 → State → Controller → Command
 ↑                         ↓
 └──── Hardware ←──────────┘

这就是你之前学的:

闭环控制

在 ROS2 中的一个软件实现形式。

                 上层算法
                    ↓
             Joint Target
                    ↓
               Controller
                    ↓
          Command Interface
                    ↓
              ros2_control
                    ↓
           Hardware Interface
                    ↓
             EtherCAT/CAN
                    ↓
                  电机
                    ↑
               Encoder
                    ↑
           State Interface
                    ↑
              ros2_control

Controller 是什么?

→ 产生控制目标的软件模块。

Command Interface 是什么?

→ 向硬件发送什么类型的控制命令。

State Interface 是什么?

→ 从硬件读取什么状态。

Controller Manager 是什么?

→ 管理 Controller 的加载、启动、停止和切换。


19、  Hardware Interface 

你可以把它理解成一个“适配器”:

ROS2世界
   ↓
Command / State Interface
   ↓
Hardware Interface
   ↓
机器人硬件世界

上面只认识:

position
velocity
effort

下面可能是:

EtherCAT PDO
CAN
串口
SDK
MuJoCo

所以 Hardware Interface 的任务就是:

把 ROS2 的标准接口翻译成具体硬件能理解的通信。

read()

从机器人读取状态:

电机
 ↓
EtherCAT
 ↓
Hardware Interface
 ↓
read()
 ↓
State Interface
 ↓
ROS2

例如:

position = 0.52
velocity = 0.13
effort = ...

write()

把 ROS2 的目标写给机器人:

Controller
 ↓
Command Interface
 ↓
write()
 ↓
Hardware Interface
 ↓
EtherCAT
 ↓
电机

例如:

left_knee position command = 0.6

所以你可以直接记:

read = 从硬件读

write = 向硬件写

                 Controller
                     │
                     ↓
              Command Interface
                     │
                     ↓
              Hardware Interface
                     │
                    write
                     │
                     ↓
                  EtherCAT
                     │
                     ↓
                  电机驱动
                     │
                     ↓
                    电机
                     │
                  编码器
                     │
                     ↓
                  EtherCAT
                     │
                    read
                     │
                     ↓
              Hardware Interface
                     │
                     ↓
               State Interface
                     │
                     ↓
                 Controller

20、 以后遇到机器人“不动”,你应该怎么想?

不要第一反应就怀疑电机。

按照链路检查:

① Controller启动了吗?
        ↓
② Controller有没有产生Command?
        ↓
③ Command Interface存在吗?
        ↓
④ Hardware Interface激活了吗?
        ↓
⑤ write()有没有执行?
        ↓
⑥ EtherCAT有没有发送?
        ↓
⑦ 驱动器有没有收到?
        ↓
⑧ 电机有没有执行?

反馈则反过来:

电机
 ↓
编码器
 ↓
EtherCAT
 ↓
read()
 ↓
State Interface
 ↓
ROS2

沿着数据链逐段定位,而不是一上来猜问题。


21、 Noitom和宇树G1完整机器人系统

先看最终全貌:

                    人体
                     ↓
              Motion Capture
             /             \
          Xsens           Noitom
             \             /
              ↓           ↓
                MotionInput
                     ↓
                 NoitomFrame
                     ↓
                    GMR
                     ↓
               Joint Target
                     ↓
                Controller
                     ↓
              ros2_control
                     ↓
            Hardware Interface
                     ↓
             ┌───────┴────────┐
             ↓                ↓
          EtherCAT          MuJoCo
             ↓                ↓
          G1 真机            仿真

1. Motion Capture 层

Xsens / Noitom
      ↓
人体骨骼运动数据

例如:

人体左手在哪里?
人体膝盖怎么弯?
人体躯干怎么旋转?

这些数据本质上是:

人体运动状态。


2. MotionInput

你之前已经做过这个抽象:

XsensUdpSource
       ↓
   NoitomFrame

或者:

NoitomMocapApiSource
       ↓
   NoitomFrame

所以 GMR 不需要关心:

“数据到底来自 Xsens 还是 Noitom?”

它只需要:

NoitomFrame

这就是软件工程里面非常典型的:

接口抽象 + 多种实现。


3. GMR

然后:

NoitomFrame
      ↓
     GMR
      ↓
Robot Joint Target

这里发生的是:

人体动作 → 机器人动作

例如:

人体膝盖角度
       ↓
机器人腿部目标关节角

所以 GMR 属于:

运动重定向 / IK / 运动映射层。

它不是:

  • EtherCAT

  • 电机驱动

  • Controller

  • Hardware Interface

这些东西不要混。


4. Joint Target 到 Controller

GMR 算出来:

q_target =
[
 q1,
 q2,
 q3,
 ...
]

这个东西进入控制系统。

可以理解为:

GMR
 ↓
“我希望机器人每个关节到这些位置”

然后 Controller 再负责:

目标
 ↓
控制逻辑
 ↓
输出控制命令

5. ros2_control

接下来:

Controller
      ↓
ros2_control

这里的核心作用已经学过:

把上层控制和底层硬件解耦。

于是上面不需要关心:

EtherCAT PDO
CAN
驱动器寄存器
编码器协议

这些交给下面。


6. Hardware Interface

继续:

ros2_control
      ↓
Hardware Interface

Hardware Interface 把:

position command
velocity command
effort command

转换成底层硬件需要的数据。

例如:

Command
 ↓
write()
 ↓
EtherCAT PDO

反馈:

EtherCAT PDO
 ↓
read()
 ↓
Joint State

7. 真机路线

于是完整真机链路就是:

人体
 ↓
动捕
 ↓
NoitomFrame
 ↓
GMR
 ↓
Joint Target
 ↓
Controller
 ↓
ros2_control
 ↓
Hardware Interface
 ↓
EtherCAT
 ↓
电机
 ↓
编码器
 ↓
EtherCAT
 ↓
Hardware Interface
 ↓
Joint State
 ↓
Controller

这就是一个完整闭环。


8. MuJoCo 路线

如果换成仿真:

人体
 ↓
动捕
 ↓
GMR
 ↓
Joint Target
 ↓
Controller
 ↓
ros2_control
 ↓
Simulation Hardware
 ↓
MuJoCo

因此你之前使用 MuJoCo 的经验现在就接上了。

你可以把:

真实 Hardware Interface

换成:

仿真 Hardware Interface

上层控制逻辑仍然可以保持一致。


9. 那么 ROS2 在整个系统中到底是什么?

ROS2 更像整个机器人软件系统的:

通信 + 节点组织 + 参数配置 + 启动管理 + 控制框架集成平台。

所以你可以把整个系统看成:

┌─────────────────────────────┐
│           ROS2              │
│                             │
│  Motion Capture              │
│       ↓                     │
│      GMR                    │
│       ↓                     │
│   Controller                │
│       ↓                     │
│   ros2_control              │
│       ↓                     │
│ Hardware Interface          │
└────────────┬────────────────┘
             ↓
          EtherCAT
             ↓
            G1

22、 以后拿到一个陌生 ROS2 机器人项目怎么办?

这是这套学习路线最终要达到的能力。

不要一上来就看源码。

先找这几个东西:

第一:启动入口

找:

launch/
bringup/

问:

系统怎么启动?


第二:机器人模型

找:

urdf/
xacro/

问:

机器人结构是什么?


第三:控制器

找:

controller/
config/
yaml

问:

谁产生控制命令?


第四:硬件

找:

hardware/
hardware_interface/

问:

ROS2 怎么连接真实机器人?


第五:上层算法

找:

gmr/
ik/
motion/
planner/

问:

机器人为什么要这么运动?


第六:通信

找:

msg/
srv/
action/
topic

问:

数据怎么流动?

看到:

robot_bringup
robot_description
robot_controller
robot_hardware
gmr
xxx_msgs

你应该能大概判断:

robot_bringup
    → 系统启动

robot_description
    → URDF/Xacro

robot_controller
    → 控制器

robot_hardware
    → 硬件接口

gmr
    → 运动重定向

xxx_msgs
    → ROS2数据接口

然后再进入源码。

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐