ROS2之URDF建模
ROS是机器人操作系统,当然要给机器人使用啦,不过在使用之前,还得让ROS认识下我们使用的机器人,如何把一个机器人介绍给ROS呢?
为此,ROS专门提供了一种机器人建模方法——URDF,用来描述机器人外观、性能等各方面属性。
一、URDF语法
1.1 机器人的组成
建模描述机器人的过程中,我们自己需要先熟悉机器人的组成和参数,比如机器人一般是由硬件结构、驱动系统、传感器系统、控制系统四大部分组成,市面上一些常见的机器人,无论是移动机器人还是机械臂,我们都可以按照这四大组成部分进行分解。
其中:
- 硬件结构就是底盘、外壳、电机等实打实可以看到的设备;
- 驱动系统就是可以驱使这些设备正常使用的装置,比如电机的驱动器,电源管理系统等;
- 传感系统包括电机上的编码器、板载的
IMU、安装的摄像头、雷达等等,便于机器人感知自己的状态和外部的环境; - 控制系统就是我们开发过程的主要载体了,一般是树莓派、电脑等计算平台,以及里边的操作系统和应用软件。
机器人建模的过程,其实就是按照类似的思路,通过建模语言,把机器人每一个部分都描述清楚,再组合起来的过程。
1.2 什么是URDF
URDF(Unified Robot Description Format,统一机器人描述格式)是一种XML格式的文件,用于从物理和逻辑上描述一个机器人模型。
你可以把它理解为机器人的数字说明书或三维装配图,它告诉计算机:
- 机器人由哪些零件(连杆、关节)组成;
- 这些零件长什么样(形状、尺寸、颜色);
- 零件之间如何连接(位置、旋转、运动类型);
- 零件的物理属性(质量、惯性);
1.3 URDF组成
URDF主要描述两大核心元素:
- 连杆(
Link):代表机器人的刚性部件,如机械臂的每一节臂杆、底盘、轮子、传感器支架等,包括:- 视觉属性:形状(立方体、圆柱体、网格模型)、尺寸、颜色、纹理;
- 碰撞属性:用于物理仿真的简化几何形状;
- 惯性属性:质量、转动惯量(对仿真至关重要)。
- 关节(
Joint):定义连杆之间的连接方式和运动关系,主要类型:- 固定关节(
fixed):完全固定连接; - 旋转关节(
revolute):绕单轴旋转,有角度限制(如机械臂关节); - 连续关节(
continuous):无限旋转(如轮子); - 平移关节(
prismatic):线性滑动; - 平面关节(
planar):在平面内运动; - 浮动关节(
floating):完全自由(6自由度)。
- 固定关节(
机器人描述由一组连杆元素和一组将连杆连接起来的关节元素组成;
因此,典型的机器人描述大致如下所示:
<?xml version="1.0"?>
<?xml-model href="https://raw.githubusercontent.com/ros/urdfdom/master/xsd/urdf.xsd" ?>
<robot name="pr2" xmlns="http://www.ros.org">
<link> ... </link>
<link> ... </link>
<link> ... </link>
<joint> .... </joint>
<joint> .... </joint>
<joint> .... </joint>
</robot>
可以看到,URDF格式的根元素是一个 <robot> 元素。
1.3.1 link元素
link元素描述了一个具有惯性、视觉特征和碰撞属性的刚体。以下是一个link元素的示例:
<link name="my_link">
<inertial>
<origin xyz="0 0 0.5" rpy="0 0 0"/>
<mass value="1"/>
<inertia ixx="100" ixy="0" ixz="0" iyy="100" iyz="0" izz="100" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<box size="1 1 1" />
</geometry>
<material name="Cyan">
<color rgba="0 1.0 1.0 1.0"/>
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0"/>
<geometry>
<cylinder radius="1" length="0.5"/>
</geometry>
</collision>
</link>
link元素有一个属性,即name(必选):用来描述连杆本身的名称.。此外,link包含若干部元素,接下来我们一一介绍。
1.3.1.1 惯性属性inertial
inertial(可选):用于描述连杆的质量、其质心的位置以及其中心惯性属性。
其下可以包含如下元素:
origin(可选):此位姿(平移、旋转)描述了连杆质心坐标系C相对于连杆坐标系L的位置和方向。xyz(可选):表示从Lo(连杆坐标系原点)到Co(连杆质心)的位置向量,形式为x L̂x + y L̂y + z L̂z,其中L̂x、L̂y、L̂z是连杆坐标系L的正交单位向量;rpy(可选):表示C的单位向量Ĉx、Ĉy、Ĉz相对于连杆坐标系L的方向,以欧拉旋转(横滚roll、俯仰pitch、偏航yaw)序列表示,单位为弧度。注意:Ĉx、Ĉy、Ĉz不需要与连杆的惯性主轴对齐;
mass:连杆的质量由该元素的value属性表示;inertia:此连杆关于Co(连杆质心)的惯性矩ixx、iyy、izz和惯性积ixy、ixz、iyz,这些值对应于固定在质心坐标系C中的单位向量Ĉx、Ĉy、Ĉz。注意:Ĉx、Ĉy、Ĉz相对于L̂x、L̂y、L̂z的方向由<origin>标签中的rpy值指定。
1.3.1.2 视觉属性visual
visual(可选):连杆的视觉属性,此元素指定了用于可视化目的的对象形状(长方体、圆柱体等)。
注意:同一个连杆可以存在多个 <visual> 标签实例。它们定义的几何体的并集形成了该连杆的视觉表示。
其下可以包含如下元素:
origin(可选):视觉元素相对于连杆坐标系的参考坐标系;xyz(可选):分别是x、y、z方向上的平移;py(可选):表示固定轴的横滚、俯仰和偏航角度,单位为弧度;
geometry(必选):表示几何形状,可以是以下之一:<box>size属性包含长方体的三个边长,长方体的原点位于其中心;
<cylinder>- 指定半径和长度。圆柱体的原点位于其中心;
<sphere>- 指定半径。球体的原点位于其中心;
<mesh>- 由文件名指定的三角网格元素,以及一个可选的缩放比例,用于缩放网格的轴对齐边界框。任何几何格式都可以接受,但具体应用程序的兼容性取决于实现。对于最佳纹理和颜色支持,推荐的格式是
Collada .dae文件。引用同一模型的机器之间不会传输网格文件。它必须是本地文件。在文件名前加上package://<packagename>/<path>可以使网格文件的路径相对于包<packagename>。
- 由文件名指定的三角网格元素,以及一个可选的缩放比例,用于缩放网格的轴对齐边界框。任何几何格式都可以接受,但具体应用程序的兼容性取决于实现。对于最佳纹理和颜色支持,推荐的格式是
material(可选):视觉元素的材质,其name属性可以用于指定材质的名称;<color>(可选)rgba由一组四个数字(代表红/绿/蓝/透明度alpha)指定的材质颜色,每个数字的范围为 [0,1];
<texture>(可选)- 材质的纹理由文件名指定。
1.3.1.3 碰撞属性collision
collision用于描述碰撞参数,里边的内容似乎和<visual>一样,也有<geometry>和<origin>,看似相同,其实区别还是比较大的;
origin(可选):碰撞元素相对于连杆坐标系的参考坐标系;xyz(可选):分别是x、y、z方向上的平移;py(可选):表示固定轴的横滚、俯仰和偏航角度,单位为弧度;
geometry:请参见上述视觉元素中的几何描述。
1.3.2 joint元素
joint元素描述了关节的运动学和动力学特性,并指定了关节的安全限制。
以下是一个 joint元素的示例:
<joint name="my_joint" type="floating">
<origin xyz="0 0 1" rpy="0 0 3.1416"/>
<parent link="link1"/>
<child link="link2"/>
<calibration rising="0.0"/>
<dynamics damping="0.0" friction="0.0"/>
<limit effort="30" velocity="1.0" lower="-2.2" upper="0.7" />
<safety_controller k_velocity="10" k_position="15" soft_lower_limit="-2.0" soft_upper_limit="0.5" />
</joint>
下面图中,蓝色关节表示出一个运动的轴,蓝色关节是可以围绕蓝色的轴旋转的,也就是child这个link是可以围绕joint上下旋转。
joint连接两个link,需要分一个主次关系,主关节是parent link,子关节是child link, 在xml形式的描述中这个两个link是必须存在的。
joint元素有两个属性:
-
name(必须):指定关节的唯一名称; -
type(必须):指定关节的类型,类型可以是以下之一;-
revolute:旋转关节,和continuous类型的区别在于不能无限旋转,而是带有角度限制,比如机械臂的两个连杆,就属于这种运动; -
continuous:旋转关节,可以围绕单轴无限旋转;比如小车的轮子,就属于这种类型; -
prismatic:滑动关节,可以沿某一个轴平移,也带有位置的极限,一般直线电机就是这种运动方式; -
fixed:固定关节,是唯一一种不允许运动的关节,不过使用还是比较频繁的,比如相机这个连杆,安装在机器人上,相对位置是不会变化的,此时使用的连接方式就是fixed; -
floating:浮动关节,允许进行平移、旋转运动; -
planar:平面关节,允许在平面正交方向上平移或者旋转;
-
此外,joint包含若干部元素,接下来我们挑选部分介绍介绍。
1.3.2.1 origin
origin(可选):表示从父连杆到子连杆的变换,元素属性有:
xyz(可选):表示x、y、z偏移量,所有位置均以米为单位指定;rpy(可选):表示绕固定轴的旋转:先绕x轴横滚(roll),然后绕y轴俯仰(pitch),最后绕z轴偏航(yaw),所有角度均以弧度为单位指定。
1.3.2.2 parent
parent描述父连杆名称,元素属性有:
link:在机器人树结构中作为此连杆父连杆的连杆名称。
1.3.2.3 child
child描述子连杆名称,元素属性有:
link:作为子连杆的连杆名称。
1.3.2.4 calibration
关节的参考位置,用来校准关节的绝对位置。
1.3.2.5 dynamics
描述关节的物理属性,例如阻尼值、物理静摩擦力等,经常在动力学仿真中用到。
1.3.2.6 limit
描述运动的一些极限值,包括关节运动的上下限位置、速度限制、力矩限制等。
1.3.2.7 mimic
描述该关节与已有关节的关系。
1.3.2.8 safety_controller
描述安全控制器参数。保护机器人关节的运动。
二、URDF案例
创建my_learning_urdf的Python版本的功能包;
pi@NanoPC-T6:~/dev_ws$ cd src
pi@NanoPC-T6:~/dev_ws/src$ ros2 pkg create --build-type ament_python my_learning_urdf
在包中创建如下文件夹:
urdf:存放机器人模型的URDF或xacro文件;meshes:放置URDF中引用的模型渲染文件;launch:保存相关启动文件;rviz:保存rviz的配置文件。
我们需要修改setup.py文件,添加配置文件:
import os
from glob import glob
...
data_files=[
('share/ament_index/resource_index/packages',
['resource/' + package_name]),
('share/' + package_name, ['package.xml']),
(os.path.join('share', package_name, 'launch'), glob(os.path.join('launch', '*.launch.py'))),
(os.path.join('share', package_name, 'urdf'), glob(os.path.join('urdf', '*.*'))),
(os.path.join('share', package_name, 'urdf/sensors'), glob(os.path.join('urdf/sensors', '*.*'))),
(os.path.join('share', package_name, 'meshes'), glob(os.path.join('meshes', '*.*'))),
(os.path.join('share', package_name, 'rviz'), glob(os.path.join('rviz', '*.rviz'))),
],
...
2.1 模型文件
在urdf下新建文件mbot_base.urdf;
<?xml version="1.0" ?>
<robot name="mbot">
<link name="base_link">
<visual>
<origin xyz=" 0 0 0" rpy="0 0 0" /> <!-- 机器人中心,坐标系原点 -->
<geometry>
<cylinder length="0.16" radius="0.20"/> <!-- 直径0.4m,高0.16m的圆柱 -->
</geometry>
<material name="yellow">
<color rgba="1 0.4 0 1"/> <!-- 橙黄色 -->
</material>
</visual>
</link>
<joint name="left_wheel_joint" type="continuous">
<origin xyz="0 0.19 -0.05" rpy="0 0 0"/> <!-- 位置:Y轴+0.19,Z轴-0.05 -->
<parent link="base_link"/>
<child link="left_wheel_link"/>
<axis xyz="0 1 0"/> <!-- 绕Y轴旋转 -->
</joint>
<link name="left_wheel_link">
<visual>
<origin xyz="0 0 0" rpy="1.5707 0 0" /> <!-- 旋转90度(π/2=1.5707) -->
<geometry>
<cylinder radius="0.06" length = "0.025"/> <!-- 半径0.06m,厚0.025m -->
</geometry>
<material name="white">
<color rgba="1 1 1 0.9"/>
</material>
</visual>
</link>
<joint name="right_wheel_joint" type="continuous">
<origin xyz="0 -0.19 -0.05" rpy="0 0 0"/>
<parent link="base_link"/>
<child link="right_wheel_link"/>
<axis xyz="0 1 0"/>
</joint>
<link name="right_wheel_link">
<visual>
<origin xyz="0 0 0" rpy="1.5707 0 0" />
<geometry>
<cylinder radius="0.06" length = "0.025"/>
</geometry>
<material name="white">
<color rgba="1 1 1 0.9"/>
</material>
</visual>
</link>
<joint name="front_caster_joint" type="continuous">
<origin xyz="0.18 0 -0.095" rpy="0 0 0"/>
<parent link="base_link"/>
<child link="front_caster_link"/>
<axis xyz="0 1 0"/>
</joint>
<link name="front_caster_link">
<visual>
<origin xyz="0 0 0" rpy="0 0 0"/>
<geometry>
<sphere radius="0.015" />
</geometry>
<material name="black">
<color rgba="0 0 0 0.95"/>
</material>
</visual>
</link>
<joint name="back_caster_joint" type="continuous">
<origin xyz="-0.18 0 -0.095" rpy="0 0 0"/>
<parent link="base_link"/>
<child link="back_caster_link"/>
<axis xyz="0 1 0"/>
</joint>
<link name="back_caster_link">
<visual>
<origin xyz="0 0 0" rpy="0 0 0"/>
<geometry>
<sphere radius="0.015" />
</geometry>
<material name="black">
<color rgba="0 0 0 0.95"/>
</material>
</visual>
</link>
</robot>
这将会创建一个两轮差分驱动机器人的URDF模型,包含若干个部分。
2.1.1 1个主体base_link
圆柱体(直径0.4米,高0.16米),橙黄色,机器人中心,坐标系原点。
2.1.2 2个驱动轮(左右轮)
分别为left_wheel_link、right_wheel_link:
left_wheel_link:- 圆柱体(半径
0.06米,厚度0.025米); - 在基座坐标系中
(0, 0.19, -0.05); - 关节类型:
continuous(连续旋转关节,无限旋转); - 旋转轴:绕
Y轴(0, 1, 0); - 视觉旋转:
rpy="1.5707 0 0"将圆柱旋转90度,使其直立(假设原始圆柱是平放的);
- 圆柱体(半径
right_wheel_link:与左轮对称布置,Y坐标为负;
2.1.3 2个万向轮(前后脚轮,用于支撑)
分别为front_caster_link、back_caster_link:
front_caster_link:- 形状:小球体(半径
0.015米),模拟万向轮; - 在基座坐标系中前方
(0.18, 0, -0.095); - 颜色:黑色;
- 形状:小球体(半径
back_caster_link:与前万向轮对称布置,X坐标为负(-0.18)。
2.2 launch文件
在launch文件夹下创建display.launch.py文件;
from ament_index_python.packages import get_package_share_path
from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument
from launch.conditions import IfCondition, UnlessCondition
from launch.substitutions import Command, LaunchConfiguration
from launch_ros.actions import Node
from launch_ros.parameter_descriptions import ParameterValue
def generate_launch_description():
urdf_tutorial_path = get_package_share_path('my_learning_urdf')
# 设置默认的URDF文件和RViz配置文件路径
default_model_path = urdf_tutorial_path / 'urdf/mbot_base.urdf'
default_rviz_config_path = urdf_tutorial_path / 'rviz/urdf.rviz'
# 命令行参数:--gui true/false,控制是否使用GUI界面发布关节状态
gui_arg = DeclareLaunchArgument(name='gui', default_value='false', choices=['true', 'false'],
description='Flag to enable joint_state_publisher_gui')
# 命令行参数:--model <路径>,指定URDF文件路径
model_arg = DeclareLaunchArgument(name='model', default_value=str(default_model_path),
description='Absolute path to robot urdf file')
# 命令行参数:--rvizconfig <路径>,指定RViz配置文件
rviz_arg = DeclareLaunchArgument(name='rvizconfig', default_value=str(default_rviz_config_path),
description='Absolute path to rviz config file')
# 关键:使用xacro命令解析URDF文件(支持参数化、宏等高级特性)
robot_description = ParameterValue(Command(['xacro ', LaunchConfiguration('model')]),
value_type=str)
# robot_state_publisher 节点
robot_state_publisher_node = Node(
package='robot_state_publisher',
executable='robot_state_publisher',
parameters=[{'robot_description': robot_description}]
)
# 关节状态发布器(二选一)
# 文本版本(无GUI)
joint_state_publisher_node = Node(
package='joint_state_publisher',
executable='joint_state_publisher',
condition=UnlessCondition(LaunchConfiguration('gui'))
)
# GUI版本(带滑动条控制)
joint_state_publisher_gui_node = Node(
package='joint_state_publisher_gui',
executable='joint_state_publisher_gui',
condition=IfCondition(LaunchConfiguration('gui'))
)
# rviz2 可视化节点
rviz_node = Node(
package='rviz2',
executable='rviz2',
name='rviz2',
output='screen',
arguments=['-d', LaunchConfiguration('rvizconfig')],
)
return LaunchDescription([
gui_arg,
model_arg,
rviz_arg,
joint_state_publisher_node,
joint_state_publisher_gui_node,
robot_state_publisher_node,
rviz_node
])
这个Launch文件主要做三件事:
- 加载机器人
URDF模型(支持xacro格式); - 发布机器人的状态变换(
TF); - 在
rviz2中可视化机器人。
2.2.1 节点
脚本运行会创建以下几个节点:
joint_state_publisher:发布每个joint(除fixed类型)的状态,一个无界面的、基础版的关节状态发布器;joint_state_publisher_gui:发布每个joint(除fixed类型)的状态,可以通过UI界面对joint进行控制;robot_state_publisher:将机器人各个links、joints之间的关系,通过TF的形式,整理成三维姿态信息发布。rviz2:在rviz2中可视化机器人;
joint_state_publisher这是一个官方ROS2包,主要功能:
-
输入:
-
读取
URDF中的关节定义; -
接收用户或程序指定的关节角度;
-
-
输出:
-
发布
/joint_states话题,消息类型为sensor_msgs/msg/JointState; -
包含所有关节的名称、位置、速度、力等信息。
-
2.2.2 数据流与节点关系
数据流与节点关系:
用户通过滑动条/GUI或程序 → joint_state_publisher(_gui)
↓ 发布/joint_states话题
robot_state_publisher
↓ 计算并发布TF变换
rviz2 和其他节点
↓ 接收TF并可视化
2.3 urdf.rviz
在rviz目录下新建urdf.rviz文件;
Panels:
- Class: rviz_common/Displays
Name: Displays
- Class: rviz_common/Views
Name: Views
Visualization Manager:
Class: ""
Displays:
- Class: rviz_default_plugins/Grid
Name: Grid
Value: true
- Alpha: 0.8
Class: rviz_default_plugins/RobotModel
Description Source: Topic
Description Topic:
Value: /robot_description
Enabled: true
Name: RobotModel
Value: true
- Class: rviz_default_plugins/TF
Name: TF
Value: true
Global Options:
Fixed Frame: base_link
Frame Rate: 30
Name: root
Tools:
- Class: rviz_default_plugins/MoveCamera
Value: true
Views:
Current:
Class: rviz_default_plugins/Orbit
Distance: 1.7
Name: Current View
Pitch: 0.33
Value: Orbit (rviz)
Yaw: 5.5
Window Geometry:
Height: 800
Width: 1200
2.4 编译运行
编译程序:
pi@NanoPC-T6:~/dev_ws$ colcon build --paths src/my_learning_urdf
pi@NanoPC-T6:~/dev_ws$ source install/setup.sh
2.4.1 可视化
启动终端,运行如下命令;
pi@NanoPC-T6:~/dev_ws$ ros2 launch my_learning_urdf display.launch.py
很快就可以看到rviz中显示的机器人模型啦,大家可以使用鼠标拖拽观察;
从可视化的效果来看,这个机器人由5个link和4个joint组成。
2.4.2 查看URDF模型结构
我们分析的对不对呢,可以在模型文件的路径下,使用urdf_to_graphviz这个小工具来分析下;
pi@NanoPC-T6:~/dev_ws/src/my_learning_urdf/urdf$ urdf_to_graphviz mbot_base.urdf
WARNING: OUTPUT not given. This type of usage is deprecated!Usage: urdf_to_graphviz input.xml [OUTPUT] Will create either $ROBOT_NAME.gv & $ROBOT_NAME.pdf in CWD or OUTPUT.gv & OUTPUT.pdf.
Created file mbot.gv
Created file mbot.pdf
pi@NanoPC-T6:~/dev_ws/src/my_learning_urdf/urdf$ ls
mbot_base.urdf mbot.gv mbot.pdf
运行成功后会产生一个pdf文件,打开之后就可以看到URDF模型分析的结果啦,是不是和我们的猜测完全相同呢!
参考文章
[1] 古月居ROS2入门教程学习笔记

浙公网安备 33010602011771号