跳到主要内容

在本章中,我们将学习如何构建机器人模型。在开始之前,请确保已安装 joint_state_publisher 功能包。如果你之前安装过 urdf_tutorial 二进制文件,该功能包通常已包含在内。若未安装,请执行以下命令:

sudo apt update
sudo apt install ros-jazzy-urdf-tutorial

本章涉及的所有机器人模型示例及源文件均可在 /opt/ros/jazzy/share/urdf_tutorial 中找到。此外,运行这些示例还需要 urdf_launch 功能包。

构建机器人模型

添加形状和材质

本节代码文件位于 /opt/ros/jazzy/share/urdf_tutorial/urdf/ 目录下,主要通过 XML 标签定义机器人的外观结构。

1. 添加形状

首先,使用 URDF 定义一个简单的几何形状:

<?xml version="1.0"?>
<robot name="myfirst">
<link name="base_link">
<visual>
<geometry>
<cylinder length="0.6" radius="0.2"/>
</geometry>
</visual>
</link>
</robot>

这段代码定义了一个名为 myfirst 的机器人。它仅包含一个 link(部件),即一个长 0.6 米、半径 0.2 米的圆柱体。

  • link 与坐标系:在 URDF 中,每个 link 对应一个坐标系。圆柱体的长度沿 Z 轴分布,几何中心默认与世界坐标系原点重合,因此圆柱体的一半会位于网格下方。

使用以下命令在 RViz 中查看模型:

cd ~/ros2_ws

ros2 launch urdf_tutorial display.launch.py model:=urdf/01-myfirst.urdf

启动 display.launch.py 后,会在 Rviz 中看到以下内容:

加载URDF

图 1: 加载URDF

2. 添加多个形状

创建完单个模型后,下一步是将多个link组合起来。机器人本质上是由多个 link 通过关节(Joint) 连接而成的链式结构。

首先,我们在 URDF 中增加一个名为 right_leg 的长方体 link,并通过一个固定关节(Fixed Joint)将其连接到主体上:

<?xml version="1.0"?>
<robot name="multipleshapes">
<link name="base_link">
<visual>
<geometry>
<cylinder length="0.6" radius="0.2"/>
</geometry>
</visual>
</link>

<link name="right_leg">
<visual>
<geometry>
<box size="0.6 0.1 0.2"/>
</geometry>
</visual>
</link>

<joint name="base_to_right_leg" type="fixed">
<parent link="base_link"/>
<child link="right_leg"/>
</joint>
</robot>

代码解析:

  • 定义了一个长 0.6m、宽 0.1m、高 0.2m 的长方体 (box)。

  • 定义了一个关节,父级是 base_link,子级是 right_leg

使用以下命令启动 RViz 查看效果::

ros2 launch urdf_tutorial display.launch.py model:=urdf/02-multipleshapes.urdf
显示多个形状

图 2: 显示多个形状

会看到圆柱体和长方体“重叠”在了一起(俗称“穿模”),这是因为在 URDF 中,如果不指定偏移量,所有 link 的中心都默认位于坐标系的原点 (0,0,0)。要构建正确的机器人外形,我们需要设置坐标原点(Origin)

为了把“腿”安在正确的位置,可以分两步走:

  1. 移动关节(Joint Origin):确定“腿”安装在“身体”的哪个部位。

  2. 移动视觉(Visual Origin):确定“腿的形状”相对于“安装点”的位置。

修改如下代码,引入 <origin> 标签:

<?xml version="1.0"?>
<robot name="multipleshapes">
<link name="base_link">
<visual>
<geometry>
<cylinder length="0.6" radius="0.2"/>
</geometry>
</visual>
</link>

<link name="right_leg">
<visual>
<geometry>
<box size="0.6 0.1 0.2"/>
</geometry>
<origin rpy="0 1.57075 0" xyz="0 0 -0.3"/>
</visual>
</link>

<joint name="base_to_right_leg" type="fixed">
<parent link="base_link"/>
<child link="right_leg"/>
<origin xyz="0 -0.22 0.25"/>

</joint>
</robot>

这一部分是 URDF 学习的重难点,需要明白:

  1. 关节原点(Joint Origin)

<joint> 标签中的 <origin> 定义了子坐标系相对于父坐标系的位置。

  • xyz="0 -0.22 0.25":这意味着我们将关节的安装点(也就是 right_leg 的参考原点)移动到了 base_link 中心上方 0.25米右侧(Y轴负方向)0.22米的位置。 因为没有设置 rpy(Roll, Pitch, Yaw),子坐标系的轴向方向默认与父坐标系保持一致。
  1. 视觉原点(Visual Origin)

<link><visual> 标签中的 <origin> 定义了几何形状相对于自身关节原点的位置。

  • 旋转 (rpy="0 1.57075 0")

    • 长方体的默认长边是沿 X 轴的。我们需要让它充当“腿”,所以需要将其竖起来。

    • 这里我们将长方体绕 Y 轴旋转了 1.57075 弧度(即 π/2\pi/2 或 90度)。旋转后,长边变为了沿 Z 轴方向。

  • 平移 (xyz="0 0 -0.3")

    • 长方体的长度是 0.6米,几何中心在中间。如果不平移,关节会连接在腿的中间位置(就像跷跷板一样)。

    • 为了让关节连接在腿的顶部,我们需要把长方体向下移动一半的长度,即 -0.3米

运行修改后的文件:

ros2 launch urdf_tutorial display.launch.py model:=urdf/03-origins.urdf

此时,在 RViz 中应该能看到一个结构合理的机器人:腿部正确地连接在身体的右上方,且垂直向下。

启动文件 (launch file) 会根据 URDF 中的关节信息自动发布 TF (Transform) 变换树。RViz 正是读取了这些 TF 信息,才能知道如何在 3D 空间中正确地画出每一个部件的位置

3. 添加材质

在 URDF 中,我们可以通过 <material> 标签为机器人添加颜色或纹理,使其看起来更逼真。此外,为了让机器人更完整,本节添加另一条腿(left_leg),并介绍利用坐标对称性来快速构建模型。 下面的代码在文件顶部定义了颜色,并在具体的 link 中使用了它们:

<?xml version="1.0"?>
<robot name="materials">

<material name="blue">
<color rgba="0 0 0.8 1"/>
</material>

<material name="white">
<color rgba="1 1 1 1"/>
</material>

<link name="base_link">
<visual>
<geometry>
<cylinder length="0.6" radius="0.2"/>
</geometry>
<material name="blue"/>
</visual>
</link>

<link name="right_leg">
<visual>
<geometry>
<box size="0.6 0.1 0.2"/>
</geometry>
<origin rpy="0 1.57075 0" xyz="0 0 -0.3"/>
<material name="white"/>
</visual>
</link>

<joint name="base_to_right_leg" type="fixed">
<parent link="base_link"/>
<child link="right_leg"/>
<origin xyz="0 -0.22 0.25"/>
</joint>

<link name="left_leg">
<visual>
<geometry>
<box size="0.6 0.1 0.2"/>
</geometry>
<origin rpy="0 1.57075 0" xyz="0 0 -0.3"/>
<material name="white"/>
</visual>
</link>

<joint name="base_to_left_leg" type="fixed">
<parent link="base_link"/>
<child link="left_leg"/>
<origin xyz="0 0.22 0.25"/>
</joint>
</robot>

为了代码整洁,通常会在 <robot> 标签的最上方集中定义材质。URDF 使用红 (R)、绿 (G)、蓝 (B)、透明度 (A) 来定义颜色, 数值范围是 0 到 1。例如 <color rgba="0 0 0.8 1"/> 表示:无红色,无绿色,80% 蓝色,100% 不透明。在 link 内部,只需要使用 <material name="blue"/> 即可引用之前定义的颜色。这样做的好处是,如果要把机器人从蓝色改成红色,只需要修改顶部的定义,不需要逐个修改 link。

不要忘记最后一位的透明度(Alpha),如果设为 0,物体就会变成透明看不见!

本段代码增加了一个 left_leg,它和 right_leg 几乎一模一样,唯一的区别在于关节的位置

  • Right Leg Joint: <origin xyz="0 -0.22 0.25"/> (在 Y 轴负方向,即右侧)

  • Left Leg Joint: <origin xyz="0 0.22 0.25"/> (在 Y 轴正方向,即左侧)

这是机器人建模中常用的技巧:利用几何对称性,只需要反转坐标轴的数值,就能快速创建对称的部件。运行以下命令,将在 RViz 中看到一个蓝白相间、拥有双腿的机器人:

ros2 launch urdf_tutorial display.launch.py model:=urdf/04-materials.urdf

现在,机器人已经初具雏形,不再是单调的白色几何体了。

完成机器人的外形

现在的机器人只有身体和腿,看起来还很简陋。为了让它看起来像一个真正的机器人(比如 R2D2),我们需要添加更多的细节:

  1. 头部:使用球体(Sphere)几何体。

  2. 轮子:使用旋转过的圆柱体。

  3. 夹爪:使用网格文件(Mesh) 来引入复杂的 3D 模型。

查看以下机器人定义,请注意新加的 <sphere> 标签和 <mesh> 标签:

<?xml version="1.0"?>
<robot name="visual">

<link name="base_link"> ... </link>
<link name="right_leg"> ... </link>

<joint name="gripper_extension" type="fixed">
<parent link="base_link"/>
<child link="gripper_pole"/>
<origin rpy="0 0 0" xyz="0.19 0 0.2"/>
</joint>

<link name="gripper_pole">
<visual>
<geometry>
<cylinder length="0.2" radius="0.01"/>
</geometry>
<origin rpy="0 1.57075 0 " xyz="0.1 0 0"/>
</visual>
</link>

<joint name="left_gripper_joint" type="fixed">
<origin rpy="0 0 0" xyz="0.2 0.01 0"/>
<parent link="gripper_pole"/>
<child link="left_gripper"/>
</joint>

<link name="left_gripper">
<visual>
<origin rpy="0.0 0 0" xyz="0 0 0"/>
<geometry>
<mesh filename="package://urdf_tutorial/meshes/l_finger.dae"/>
</geometry>
</visual>
</link>
<link name="head">
<visual>
<geometry>
<sphere radius="0.2"/>
</geometry>
<material name="white"/>
</visual>
</link>
<joint name="head_swivel" type="fixed">
<parent link="base_link"/>
<child link="head"/>
<origin xyz="0 0 0.3"/>
</joint>

</robot>

提示:完整的代码文件位于 urdf_tutorial 包的 urdf/05-visual.urdf 中,可以直接查看源文件。

除了立方体(Box)和圆柱体(Cylinder),URDF 还支持球体,添加球体方法如下:

<link name="head">
<visual>
<geometry>
<sphere radius="0.2"/>
</geometry>
<material name="white"/>
</visual>
</link>

球体非常简单,只需要定义 radius(半径)。简单的几何体无法表示复杂的机械结构(如机械爪、激光雷达外壳)。这时,我们需要导入外部的 3D 模型文件。

<link name="left_gripper">
<visual>
<origin rpy="0.0 0 0" xyz="0 0 0"/>
<geometry>
<mesh filename="package://urdf_tutorial/meshes/l_finger.dae"/>
</geometry>
</visual>
</link>
  • 路径格式:必须使用 package://功能包名/文件路径 的格式。ROS2 会根据功能包名自动查找安装路径。

  • 文件格式

    • STL:最通用的 3D 格式,只有形状,没有颜色。如果在 URDF 中使用 STL,需要在 <visual> 标签中再指定 <material> 颜色。

    • DAE (Collada):不仅包含形状,还包含颜色和纹理数据。如代码中的 .dae 文件,它自带了深灰色,因此不需要在 URDF 中再指定 <material>

  • 缩放 (Scale):如果导入的模型太大或太小,可以使用 <mesh filename="..." scale="1 1 1"/> 进行 X/Y/Z 轴的缩放。

运行以下命令,在 Rviz 中可以看到看到一个拥有完整细节的机器人:

ros2 launch urdf_tutorial display.launch.py model:=urdf/05-visual.urdf

现在,我们的机器人已经拥有了类似 R2D2 的外观。但所有的关节都是 fixed(固定)类型,机器人无法移动。在下一节中,我们将修改关节类型,让它真正“动”起来。