4. Joint详解:类型与父子关系

关节——说白了就是机器人身上“会动的地方”。你想想看,一个机械臂如果没有关节,那就是一根铁棍。URDF 里的 <joint> 标签,就是用来描述两个连杆之间怎么相对运动的。我做了这么多年机器人,每次拿到一个新模型,第一件事就是检查关节定义,因为这里出错了,后面 TF 全乱套。

这一章咱们就把 <joint> 的六种类型掰开揉碎讲清楚,顺便聊聊父子关系这个核心概念。嗯,内容有点干,但都是硬货。

Joint 类型与父子关系 <joint> 六种类型 continuous revolute prismatic fixed floating planar 父子关系 parent → child TF 树方向 子随父动 每个 joint 连接一个 parent link 和一个 child link

4.1 六种关节类型,一个都不能少

URDF 一共定义了六种关节类型。我刚开始学的时候也觉得多,但用久了就会发现——每种类型都对应着真实的机械结构。咱们一个一个过。

类型运动方式典型应用自由度
continuous连续旋转,无角度限制车轮、转台1 (旋转)
revolute旋转,有角度限制关节臂、肘关节1 (旋转)
prismatic直线滑动,有行程限制伸缩臂、滑台1 (平移)
fixed完全固定,无运动传感器支架、结构件0
floating6自由度自由运动无人机、水下机器人6
planar平面内运动 (x,y + 偏航)AGV 全向轮底盘3
💡 我的习惯: 只要关节能转一整圈,就用 continuous;如果机械限位在 ±180° 以内,用 revolute 并老老实实填 <limit>。曾经有个项目,我把连续旋转的转台写成了 revolute,结果仿真时老是撞限位,排查了半天……

4.2 父子关系——关节的“上下级”

每个 <joint> 必须指定 parent 和 child。这可不是随便写的。父连杆固定,子连杆相对父连杆运动。说白了,关节就是“子连杆在父连杆坐标系下的运动描述”。

举个例子:

<joint name="shoulder_pan" type="revolute">
    <parent link="base_link"/>
    <child link="shoulder_link"/>
    <origin xyz="0 0 0.2" rpy="0 0 0"/>
    <axis xyz="0 0 1"/>
    <limit lower="-3.14" upper="3.14" effort="100" velocity="1.0"/>
</joint>

这里 base_link 是父,shoulder_link 是子。关节旋转轴是 Z 轴。你想想看,如果我把父子写反了,整个 TF 树就倒挂了——我遇到过不止一次,新手经常在这里翻车。

⚠️ 我曾经踩过的坑: 有一次给一个六轴机械臂建模,把第二关节的 parent 和 child 写反了。结果在 RViz 里看,整个臂像麻花一样拧着。后来用 tf_echo 一查,才发现坐标系方向全反了。所以我的建议是:画一个树形图,标清楚每个 joint 的 parent 和 child,再写 URDF。

4.3 每个类型的细节与避坑

continuous —— 没有 <limit> 的上下界,但必须给 effort 和 velocity。嗯,ROS 里有些控制器会检查这两个字段,别漏了。

revolute —— 必须设置 lower 和 upper。我习惯把单位统一成弧度,避免角度/弧度混用。曾经有个同事用了角度值,结果仿真时关节直接飞出去了……

prismatic —— 平移关节,axis 指定移动方向。注意 xyz 是单位向量。如果你想让滑台沿 X 轴滑动,就写 axis xyz="1 0 0"。

fixed —— 最简单,但最容易忽略。比如相机支架、激光雷达底座,都用 fixed。别小看它,TF 树里 fixed joint 能帮你把坐标系整理得清清楚楚。

floating —— 6自由度,一般用在移动机器人或者无人机上。但说实话,我很少在 URDF 里直接用 floating,因为大多数时候我们用 base_link 配合 odom 来做。不过如果你在做水下机器人仿真,floating 就很有用了。

planar —— 平面三自由度 (x, y, yaw)。适合全向轮底盘。注意,planar 关节在 Gazebo 里需要额外的插件支持,否则可能动不了。

🔧 小技巧: 如果你不确定关节类型,先用 fixed 占位,等运动学模型确定后再改。我经常在项目初期用 fixed 搭骨架,后面再替换成运动关节。

4.4 关节的坐标轴与原点

每个 joint 都有一个 <origin>,它定义了子连杆坐标系相对于父连杆坐标系的偏移和旋转。这个 xyz 和 rpy 非常关键——它直接决定了关节的位置和朝向。

举个例子:如果 origin xyz="0 0 0.5" rpy="0 0 0",表示子连杆的原点位于父连杆上方 0.5 米处,且姿态相同。而 axis 则是在子连杆坐标系下定义的旋转轴或平移轴。

<!-- 一个典型的轮式关节 -->
<joint name="left_wheel_joint" type="continuous">
    <parent link="base_link"/>
    <child link="left_wheel"/>
    <origin xyz="0.2 0.3 0" rpy="0 0 0"/>
    <axis xyz="0 1 0"/>   <!-- 绕 Y 轴旋转 -->
</joint>

这里车轮绕 Y 轴旋转,意味着车轮向前滚动。如果你把 axis 写成 0 0 1,那车轮就会像陀螺一样原地打转——嗯,我见过有人这么干过,结果车子在仿真里横着走。

4.5 关节与 TF 树的关系

URDF 里的每个 joint 都会在 TF 树中生成一个坐标变换。父连杆坐标系到子连杆坐标系的变换,就是由 joint 的 origin 加上当前关节变量(角度/位移)决定的。所以,joint 定义得越准确,TF 树就越清晰。

我个人习惯在写完 URDF 后,立刻用 urdf_to_graphviz 生成一张图,检查父子关系是否合理。你也可以用 check_urdf 工具做语法检查,但它查不出逻辑错误——比如父子颠倒。

📌 核心要点:
  • 关节类型决定了运动自由度与约束
  • 父子关系定义了 TF 树的流向(父→子)
  • origin 是子坐标系在父坐标系中的位姿
  • axis 在子坐标系下定义,注意单位向量
  • fixed 关节虽然不动,但能帮你整理坐标系结构

好了,这一章的内容就是这些。关节是 URDF 的灵魂,把 joint 搞明白了,你的机器人模型就成功了一半。下一章我们会深入坐标变换,看看 TF 到底是怎么把关节串起来的。