本文档是URDF的入门参考,进阶参考见:以并联腿为例学习实车URDF
XML 是一种用来传输和存储数据的标记语言。
在 ROS 中,XML 用来
XML 语法的核心是树结构,以下面的 urdf 文件为例:
<?xml version="1.0" encoding="UTF-8"?>
<robot name="myfirst">
<link name="base_link">
<visual>
<geometry>
<cylinder length="0.6" radius="0.2"/>
</geometry>
</visual>
</link>
<link name="link1">
...
</link>
</robot>
robot 是根,随后有 base_link 和 link 两个分叉,分叉往后又有别的枝叶。
这里引出元素和属性的概念:
robot, link, visual 等就是元素。元素指的是从(且包括)开始标签直到(且包括)结束标签的部分。一个元素可以包含:
name, radius 是属性。属性的数值一定要用双引号围着,不同的属性用空格分隔进一步看该文档的整体结构,最开始的 <?xml version="1.0" encoding="UTF-8"?> 声明了文件的 XML 版本和所使用的编码,这是可选的。
下一行就是文件的根元素,说明了本文档描述了一个机器人。元素的属性 name 说明了机器人名称。
需要明确的是,正如一棵树只有一个根,一个 XML 文件需要一个包裹一切(除了声明)的根元素,它是所有其他元素的父元素。
所有的 XML 元素一般都有一个关闭标签。以 visual 为例:有代表开始的 <visual>,以及代表结束的 </visual>
但 XML 也允许单标签的使用,写法如下:
<elementName attribute="value" />
现在我们回到 XML 的目的,用来存储和传输数据,通过以上的学习,可以尝试用一个 XML 文件来描述一个图书馆的数据:
首先我们需要一个根元素,想必就是 library 了
<library>
</library>
里面需要一些书:
<library>
<book name="Harry Potter">
</book>
<book name="Learning XML">
</book>
</library>
我还想记录书的作者,出版年份等信息:
<library>
<book name="Harry Potter">
<author>J K. Rowling</author>
<year>2005</year>
<price>29.99</price>
</book>
<book name="Learning XML">
<author>Erik T. Ray</author>
<year>2003</year>
<price>39.95</price>
</book>
</library>
很好,就是如此,我们得到了一个形式良好的 XML 文件,记录了图书馆的数据。
请记住以下书写形式良好的 XML 文件的要求:
官方 URDF 文档
翻译 URDF 文档
虽然上述文档是 ros 2 的,但他们在 ros 1 中同样适用
URDF XML 规范总览主页 (ROS Wiki):该页面列出了构成机器人模型的所有核心 XML 元素。
古月居的一篇记录帖
xacro文档
要写一个机器人,主要是以下的流程:
<robot>link:base_link 一般作为车的底盘,接着 1 轮子是一个 link ,云台是一个 link...类比人体,link 就是大腿、小腿之类的部位<robot>根元素包裹着其他所有元素,有一个 name 属性,代表我们机器人的名字
值得注意的是,如果要用 xacro,则需要加上 xmlns:xacro="http://www.ros.org/wiki/xacro" 这一属性。
综上,根元素的一个例子是:
<robot name="my_robot" xmlns:xacro="http://www.ros.org/wiki/xacro">
</robot>
<link>描述具有惯性、视觉特征和碰撞属性的刚体(由这句话我们可以知道 link 元素内部就是这几个东西),如下图所示

这里给出一个 link 的例子:
<link name="gimbal">
<inertial>
<origin xyz="0.0 0.0 0.0" rpy="0.0 0.0 0.0"/>
<mass value="0.1"/>
<inertia ixx="0.0000175" ixy="0.0" ixz="0.0" iyy="0.0000175" iyz="0.0" izz="0.00002"/>
</inertial>
<visual name="">
<origin xyz="0.0 0.0 0.0" rpy="0.0 0.0 0.0"/>
<geometry>
<cylinder length="${gimbal_height}" radius="${gimbal_radius}"/>
</geometry>
<material name="white"/>
</visual>
<collision>
<origin xyz="0.0 0.0 0.0" rpy="0.0 0.0 0.0"/>
<geometry>
<cylinder length="${gimbal_height}" radius="${gimbal_radius}"/>
</geometry>
</collision>
</link>
<inertial>本元素描述了物体的物理属性
<origin> 描述了物体质心相对于这个 link 坐标系的偏移,我们一般创建的都是标准的几何体,质心就在原点,所以全填 0 就好
<mass> 描述了物体的质量,urdf 中所有的数值单位都是国际单位,这里为 kg
<inertia> 3x3 转动惯量矩阵,我这次是让 ai 生一份填进去。以下引用嗣音的笔记内容:
我对这个没什么研究,只是平时都将ixx、iyy、izz设置为0.1或者0.001,如果仿真模型加载到gazebo里后原地乱飞就得想想这个是不是设置太小了。
<visual>本元素描述了物体的视觉属性
<origin> 描述了视觉几何元素的坐标系相对于这个link坐标系的位姿,这个一般也全填写 0。刚开始尝试时我选择修改并统一 visual 和 collision 元素的 origin 元素来修改物体位置,但是这样并不是好的实现,应当在 joint 的 origin 元素里定义
<geometry> 描述了这物体看起来是个什么形状,有几个可选标签:
<cylinder length="${gimbal_height}" radius="${gimbal_radius}"/>
这里使用了一点 xacro
<material> 描述了物体的外观材料
在 urdf 前半部分我们可以统一定义各种颜色,如:
<material name="white">
<color rgba="1 1 1 1"/>
</material>
我们给 <material> 元素设置属性 name="while",在 gazebo 以及 rviz 里这看起来就是白色的了。
还可以从文件加载材质。
<collision><origin> 描述了碰撞箱几何元素的坐标系相对于这个 link 坐标系的位姿,这个一般也全填写 0。
<geometry> 描述了这物体碰撞箱是个什么形状,设置的与visual一样即可。
我们可以通过此项来设置安全区,假设有个机器人头部是半球形,我们希望整个区域都不能靠近,那么可以把碰撞箱设置得更大,如设置为一个长方体
<joint>joint是链接link的关节,如下图所示,joint 描述了连接的 link 分别是谁,link 旋转的方向以及最为关键的,被之相连的 link 之间的坐标变换。

以下给出一个 joint 例:
<joint name="gimbal_joint" type="continuous">
<origin xyz="0.035 0 0.15" rpy="0 0.0 0.0"/>
<parent link="base_link"/>
<child link="gimbal"/>
<axis xyz="0.0 0.0 1"/>
<limit effort="1000" velocity="1000"/>
</joint>
type="continuous" 给出了关节的类型,轮子就用这个。更多类型参附录
<origin> 描述的是从父连杆(Parent Link)坐标系到子连杆(Child Link)坐标系的静态空间变换。通俗理解就是,我们现在研究两个 link 的坐标原点,从父 link 原点出发,走过 xyz 描述的距离就到了子 link 的坐标轴原点。进一步按 rpy 旋转,两个坐标系就重合了。
综上,我们 link与 link的关系不用自身的视觉和碰撞箱的位姿描述,而应该用 joint的位姿描述。
<axis> 描述了关节的运动轴。具体来说,是定义在子 link 坐标系下的。表明了 joint 是沿哪个轴运动。
xyz为一个正交向量。对revolute、continuous旋转关节为旋转轴,对prismatic滑动关节为平移轴,对planar平面关节为平面的法线。这个轴向是相对于两个link的接触面的。
<tranmission>定义了执行器(Actuator,如电机)与关节(Joint)之间的机械关系。
以下给出示例:
<transmission name="gimbal_trans">
<type>transmission_interface/SimpleTransmission</type>
<joint name="gimbal_joint">
<hardwareInterface>hardware_interface/VelocityJointInterface</hardwareInterface>
</joint>
<actuator name="gimbal_motor">
<mechanicalReduction>1</mechanicalReduction>
</actuator>
</transmission>
<type>transmission_interface/SimpleTransmission</type> 表示电机和关节之间是简单的线性缩放关系
<joint name="gimbal_joint"> 名字要匹配前面定义的 joint
<hardwareInterface> 定义了关节支持的控制模式:
EffortJointInterface:力矩控制VelocityJointInterface:速度控制PositionJointInterface:位置控制<mechanicalReduction> 减速比,这里填 n就表示执行器转 n 圈关节才转 1 圈。
通过以下代码段包含 libgazebo_ros_control.so 插件,这将硬件和 ros_control 连接在一起:
<gazebo>
<plugin name="gazebo_ros_control" filename="libgazebo_ros_control.so">
<robotNamespace>/</robotNamespace>
</plugin>
</gazebo>
这是一个用来命名和封装的工具,可以减少许多重复的工作。比如在画差速小车的过程中,我通过 xacro定义了轮子,然后分别对左轮和右轮实例化。
我还通过 xacro定义了车辆的各种参数,减少计算量以及方便统一更改。
属性定义
一般用于定义轮宽,轮半径之类的参数,定义方法为:
<xacro:property name="my_name" value="0.05" />
调用的时候将其写为${my_name},比如
<origin rpy="0 0 ${my_name}" xyz="0 0 0"/>
宏定义
类似于函数封装,定义方法为:
<xacro:macro name="default_inertial" params="mass">
<inertial>
<mass value="${mass}" />
<inertia ixx="0.1" ixy="0.0" ixz="0.0" iyy="0.1" iyz="0.0" izz="0.1" />
</inertial>
</xacro:macro>
params可以包含多个参数,用空格隔开就行。也可以设置默认值,加上:=即可。 调用方法为
<xacro:default_inertial mass="0.0001"/>
使用宏的时候可以仔细思考参数如何设置,譬如对于对称于车体两端的轮子,可以设置一个 reflect参数等于 1 或 -1,然后在计算 joint 的位姿的时候乘上。
以下给出差速小车的宏用例:
<xacro:macro name="wheel" params="prefix reflect num">
<link name="${prefix}_wheel">
<inertial>
<origin xyz="0.0 0.0 0.0" rpy="0.0 0.0 0.0"/>
<mass value="0.2"/>
<inertia ixx="0.00014" ixy="0.0" ixz="0.0" iyy="0.00014" iyz="0.0" izz="0.00025"/>
</inertial>
<visual>
<geometry>
<cylinder length="${wheel_length}" radius="${wheel_radius}"/>
</geometry>
<material name="white"/>
</visual>
<collision>
<geometry>
<cylinder length="${wheel_length}" radius="${wheel_radius}"/>
</geometry>
</collision>
</link>
<joint name="wheel_${num}_joint" type="continuous">
<origin xyz="0.035 ${reflect*(width/2+wheel_length/2)} 0.1" rpy="${-pi/2} 0.0 0.0"/>
<parent link="base_link"/>
<child link="${prefix}_wheel"/>
<axis xyz="0.0 0.0 1"/>
<limit effort="1000" velocity="1000"/>
</joint>
<transmission name="wheel_${num}_trans">
<type>transmission_interface/SimpleTransmission</type>
<joint name="wheel_${num}_joint">
<hardwareInterface>hardware_interface/VelocityJointInterface</hardwareInterface>
</joint>
<actuator name="wheel_${num}_motor">
<mechanicalReduction>1</mechanicalReduction>
</actuator>
</transmission>
</xacro:macro>
<xacro:wheel prefix="left" num="0" reflect="1"/>
<xacro:wheel prefix="right" num="1" reflect="-1"/>
这样通过 xacro,我们很方便地完成了两个轮子 link,joint和 transmission的定义。
xacro还有别的用法,参官方文档
upper 和 lower 限制)。加载 libgazebo_ros_control.so 插件实现了以下 4个核心功能
transmission 定义。该插件充当了虚拟硬件接口。
ros_control 能够理解的标准格式。这是最关键的一步。插件启动后,会初始化一个 controller_manager。
switch_controller 或 load_controller)动态地加载、启动或停止控制器。ros_control 计算控制输出。