1 概述

机器人都是由link和joint进行描述.

alt text

2 流程

1
2
3
4
5
6
7
8
9
10
# 建立工程

mkdir -p urdf_ws/src
cd urdf_ws/src

ros2 pkg create --build-type ament_cmake --dependencies urdf robot_state_publisher --license Apache-2.0 my_robot_description

cd my_robot_description
# 名称固定为 urdf
mkdir -p urdf

写入urdf代码.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# robot.urdf
<?xml version="1.0"?>
<robot name="my_robot">

<link name="base_link">
<visual>
<geometry>
<box size="0.2 0.2 0.05"/>
</geometry>
<origin xyz="0 0 0.025"/>
<material name="blue">
<color rgba="0 0 1 1"/>
</material>
</visual>
</link>

<joint name="joint1" type="fixed">
<parent link="base_link"/>
<child link="link1"/>
<origin xyz="0 0 0.05" rpy="0 0 0"/>
<axis xyz="0 0 1"/>
<limit lower="-1.57" upper="1.57" effort="1.0" velocity="1.0"/>
</joint>

<link name="link1">
<visual>
<geometry>
<cylinder length="0.4" radius="0.03"/>
</geometry>
<origin xyz="0 0 0.2" rpy="0 0 0"/>
<material name="green">
<color rgba="0 1 0 1"/>
</material>
</visual>
</link>

<joint name="joint2" type="fixed">
<parent link="link1"/>
<child link="link2"/>
<origin xyz="0 0 0.4" rpy="0 0 0"/>
<axis xyz="0 1 0"/>
<limit lower="-1.57" upper="1.57" effort="1.0" velocity="1.0"/>
</joint>

<link name="link2">
<visual>
<geometry>
<cylinder length="0.3" radius="0.02"/>
</geometry>
<origin xyz="0 0 0.15" rpy="0 0 0"/>
<material name="red">
<color rgba="1 0 0 1"/>
</material>
</visual>
</link>

</robot>

编译并构建.

1
2
colcon build
source install/setup.bash

创建 launch 目录, 在其中编写 launch.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# display.launch.py
from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument
from launch.substitutions import LaunchConfiguration, Command
from launch_ros.actions import Node
from launch_ros.parameter_descriptions import ParameterValue
from ament_index_python.packages import get_package_share_directory
import os

def generate_launch_description():
pkg_path = get_package_share_directory('my_robot_description')
default_model_path = os.path.join(pkg_path, 'urdf', 'robot.urdf')
rviz_config_path = os.path.join(pkg_path, 'config', 'display_config.rviz')

declare_model_arg_path = DeclareLaunchArgument(
name='model',
default_value=str(default_model_path),
description='Absolute path to robot urdf or xacro file'
)

robot_description_result = Command(['xacro ', LaunchConfiguration('model')])
robot_description_val = ParameterValue(robot_description_result, value_type=str)

return LaunchDescription([
declare_model_arg_path,

Node(
package='robot_state_publisher',
executable='robot_state_publisher',
parameters=[{'robot_description': robot_description_val}],
),

Node(
package='joint_state_publisher_gui',
executable='joint_state_publisher_gui',
),

Node(
package='rviz2',
executable='rviz2',
arguments=['-d', rviz_config_path],
)
])

添加安装指令.

1
2
3
4
5
6
7
8
9
10
11
install(DIRECTORY urdf
DESTINATION share/${PROJECT_NAME}
)

install(DIRECTORY launch
DESTINATION share/${PROJECT_NAME}
)

install(DIRECTORY config
DESTINATION share/${PROJECT_NAME}
)

目录结构如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
build
install
log
src/
└── my_robot_description
├── CMakeLists.txt
├── include
│   └── my_robot_description
├── launch
│   └── display.launch.py
│── config
│ └── display_config.rviz
├── LICENSE
├── package.xml
├── src
└── urdf
└── robot.urdf

3 描述机器人关系的组件

3.1 robot_state_publisher

  1. 输入

    • robot_description: 机器人模型(URDF / XACRO 的 XML)

    • /joint_states: 每个关节的角度

  2. 输出: /tf 和 /tf_static:每个 link 的位置变换

3.2 joint_state_publisher

/joint_states: 消息类型为 sensor_msgs/JointState

组件 功能说明
joint_state_publisher 自动发布所有关节角度为 0 的状态
joint_state_publisher_gui 提供滑块 GUI 让你拖动角度

joint_state_publisher_gui 的效果, 如果 joint 的关系是 revolute, 则可以滑动.

alt text

4 xacro

xacro 是对 urdf 的语法增强.

我们将上面的 urdf 换成 xacro 版本.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
<?xml version="1.0"?>
<robot xmlns:xacro="http://ros.org/wiki/xacro" name="my_robot">

<!-- 定义颜色 -->
<xacro:property name="blue" value="0 0 1 1"/>
<xacro:property name="green" value="0 1 0 1"/>
<xacro:property name="red" value="1 0 0 1"/>

<!-- 定义基本尺寸参数 -->
<xacro:property name="base_size" value="0.2 0.2 0.05"/>
<xacro:property name="link1_length" value="0.4"/>
<xacro:property name="link2_length" value="0.3"/>

<link name="base_link">
<visual>
<geometry>
<box size="${base_size}"/>
</geometry>
<origin xyz="0 0 0.025"/>
<material name="blue">
<color rgba="${blue}"/>
</material>
</visual>
</link>

<joint name="joint1" type="fixed">
<parent link="base_link"/>
<child link="link1"/>
<origin xyz="0 0 0.05" rpy="0 0 0"/>
</joint>

<link name="link1">
<visual>
<geometry>
<cylinder length="${link1_length}" radius="0.03"/>
</geometry>
<origin xyz="0 0 ${link1_length/2}" rpy="0 0 0"/>
<material name="green">
<color rgba="${green}"/>
</material>
</visual>
</link>

<joint name="joint2" type="revolute">
<parent link="link1"/>
<child link="link2"/>
<origin xyz="0 0 ${link1_length}" rpy="0 0 0"/>
<axis xyz="0 1 0"/>
<limit lower="-1.57" upper="1.57" effort="1.0" velocity="1.0"/>
</joint>

<link name="link2">
<visual>
<geometry>
<cylinder length="${link2_length}" radius="0.02"/>
</geometry>
<origin xyz="0 0 ${link2_length/2}" rpy="0 0 0"/>
<material name="red">
<color rgba="${red}"/>
</material>
</visual>
</link>

</robot>

4.1 xacro 的多文件结构

alt text

1
2
3
4
5
6
7
urdf/
├── base.xacro
├── macros.xacro
├── robot.xacro
├── robot.urdf
├── sensors.xacro
└── wheels.xacro

我们传入 robot.xacro 即可, xacro 会自动解析所有 include 文件.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# robot.xacro
<?xml version="1.0"?>
<robot xmlns:xacro="http://ros.org/wiki/xacro" name="my_wheeled_robot">

<!-- Include modules -->
<xacro:include filename="macros.xacro"/>
<xacro:include filename="base.xacro"/>
<xacro:include filename="wheels.xacro"/>
<xacro:include filename="sensors.xacro"/>

<!-- Define properties (used as macro arguments) -->
<xacro:property name="base_size" value="0.4 0.4 0.1"/>

<!-- Call base macro with required params -->
<xacro:base_link base_size="${base_size}"/>

<!-- Wheels -->
<xacro:wheel name="left_wheel" parent="base_link" xyz="0.1 0.2 0"/>
<xacro:wheel name="right_wheel" parent="base_link" xyz="0.1 -0.2 0"/>

<!-- Sensor -->
<xacro:lidar_sensor parent="base_link" xyz="0 0 0.1"/>

</robot>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# macros.xacro
<?xml version="1.0"?>
<robot xmlns:xacro="http://ros.org/wiki/xacro">

<!-- 颜色 -->
<xacro:property name="color_red" value="1 0 0 1"/>
<xacro:property name="color_green" value="0 1 0 1"/>
<xacro:property name="color_blue" value="0 0 1 1"/>

<!-- 底盘尺寸 -->
<xacro:property name="base_size" value="0.4 0.3 0.1"/>
<xacro:property name="wheel_radius" value="0.05"/>
<xacro:property name="wheel_width" value="0.02"/>
</robot>

对于 base.xacro 我们可以建立一个虚拟组件对地面进行贴合.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# base.xacro
<?xml version="1.0"?>
<robot xmlns:xacro="http://ros.org/wiki/xacro">
<xacro:macro name="base_link" params="base_size">

<xacro:property name="base_size_z" value="${float(base_size.split()[2])}"/>
<link name="base_footprint" />

<link name="base_link">
<visual>
<geometry>
<box size="${base_size}"/>
</geometry>
<origin xyz="0 0 ${base_size_z / 2}"/>
<material name="blue">
<color rgba="${color_blue}"/>
</material>
</visual>
<collision>
<geometry>
<box size="${base_size}"/>
</geometry>
<origin xyz="0 0 ${base_size_z / 2}"/>
<material name="blue">
<color rgba="${color_blue}"/>
</material>
</collision>
</link>

<joint name="joint_name" type="fixed">
<origin xyz="0 0 ${base_size_z / 2}" rpy="0 0 0"/>
<parent link="base_footprint"/>
<child link="base_link"/>
</joint>

</xacro:macro>
</robot>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# wheels.xacro
<?xml version="1.0"?>
<robot xmlns:xacro="http://ros.org/wiki/xacro">

<xacro:macro name="mul_wheel" params="name parent xyz">

<xacro:property name="wheel_mass" value="0.1"/>

<link name="${name}_link">
<visual>
<geometry>
<sphere radius="${wheel_radius}"/>
</geometry>
<origin xyz="0 0 ${wheel_radius}" rpy="0 0 0"/>
<material name="gray">
<color rgba="0.4 0.4 0.4 1"/>
</material>
</visual>
<collision>
<geometry>
<sphere radius="${wheel_radius}"/>
</geometry>
<origin xyz="0 0 0" rpy="0 0 0"/>
</collision>
<inertial>
<origin xyz="0 0 0" rpy="0 0 0"/>
<mass value="${wheel_mass}"/>
<inertia ixx="1e-5" ixy="0.0" ixz="0.0"
iyy="1e-5" iyz="0.0"
izz="1e-5"/>
</inertial>
</link>

<joint name="${name}_joint" type="fixed">
<parent link="${parent}"/>
<child link="${name}_link"/>
<origin xyz="${xyz}" rpy="0 0 0"/>
</joint>
</xacro:macro>

<xacro:macro name="wheel" params="name parent xyz">
<xacro:property name="wheel_mass" value="0.3"/>
<xacro:property name="inertia_ixx" value="${0.5 * wheel_mass * wheel_radius * wheel_radius}"/>
<xacro:property name="inertia_iyy" value="${0.5 * wheel_mass * wheel_radius * wheel_radius}"/>
<xacro:property name="inertia_izz" value="${0.5 * wheel_mass * wheel_radius * wheel_radius}"/>

<link name="${name}_link">
<visual>
<geometry>
<cylinder radius="${wheel_radius}" length="${wheel_width}"/>
</geometry>
<origin xyz="0 0 0" rpy="1.5708 0 0"/>
<material name="black">
<color rgba="0 0 0 1"/>
</material>
</visual>
<collision>
<geometry>
<cylinder radius="${wheel_radius}" length="${wheel_width}"/>
</geometry>
<origin xyz="0 0 0" rpy="1.5708 0 0"/>
<material name="black">
<color rgba="0 0 0 1"/>
</material>
</collision>
<inertial>
<origin xyz="0 0 0" rpy="0 0 0"/>
<mass value="${wheel_mass}"/>
<inertia
ixx="${inertia_ixx}" ixy="0.0" ixz="0.0"
iyy="${inertia_iyy}" iyz="0.0"
izz="${inertia_izz}"/>
</inertial>
</link>

<joint name="${name}_joint" type="continuous">
<parent link="${parent}"/>
<child link="${name}_link"/>
<origin xyz="${xyz}" rpy="0 0 0"/>
<axis xyz="0 1 0"/>
</joint>
</xacro:macro>

</robot>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# sensors.xacro
<?xml version="1.0"?>
<robot xmlns:xacro="http://ros.org/wiki/xacro">

<xacro:macro name="lidar_sensor" params="parent xyz">

<link name="lidar_link">
<visual>
<geometry>
<cylinder radius="0.03" length="0.02"/>
</geometry>
<origin xyz="0 0 0.01" rpy="0 0 0"/>
<material name="gray">
<color rgba="0.6 0.6 0.6 1"/>
</material>
</visual>
</link>

<joint name="lidar_joint" type="fixed">
<parent link="${parent}"/>
<child link="lidar_link"/>
<origin xyz="${xyz}" rpy="0 0 0"/>
</joint>

</xacro:macro>

</robot>

5 物理属性

5.1 碰撞

collision 标签可以给组件添加碰撞体积, 一般写法与 visual 一致, 对于不规则的物体我们也可以用 collision 将碰撞体积近似.

这里详见 base.xacro 和 wheels.xacro 的写法.

通过开关我们可以看到碰撞体积, 比如这里只写了 base.xacro 的碰撞体积.

alt text

5.2 惯性

这里可以查看 base.xacro 为例.

  • inertial 表示惯性: 需要用一个 3x3 的矩阵表示.

    • mass 的单位是千克.

    • origin 表示重心位置.

    • inertia 表示转动惯量.

alt text

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<link name="base_link">
<visual>
<geometry>
<cylinder length="0.6" radius="0.2"/>
</geometry>
<material name="blue">
<color rgba="0 0 .8 1"/>
</material>
</visual>
<collision>
<geometry>
<cylinder length="0.6" radius="0.2"/>
</geometry>
</collision>
<inertial>
<mass value="10"/>
<inertia ixx="0.4" ixy="0.0" ixz="0.0" iyy="0.4" iyz="0.0" izz="0.2"/>
</inertial>
</link>

这里可以看一下 base.xacro 和 wheels.xacro.

alt text

alt text

6 References