Wiki
Visualization platform RVIZ2 and URDF modeling language
Existing English translations were reused. 16 current source blocks remain in Simplified Chinese, without triggering paid translation.
1.Visualization Introduction



rviz2 plugins related to coordinates, LiDAR, and cameras





2.Basic usage of rviz2

Withsudo apt install ros-[ROS_DISTRO]-desktopformat installed, RViz is already installed by default at ROS2.
sudo apt install ros-[ROS_DISTRO]-rviz2
备注: 命令中的 指代ROS2版本。

Method 1:rviz2;
Method 2:ros2 run rviz2 rviz2.

After rviz2 starts, the default interface is as follows:
-
The upper part is the toolbar: it includes view control, estimated pose settings, target settings, etc., and custom plugins can also be added;
-
The left side is the plugin display area: it includes functions such as adding, deleting, copying, and renaming plugins, displaying plugins, and setting plugin properties;
-
The middle is the 3D view display area: it displays the added plugin information in a visual manner;
-
The right side is the observation view settings area: different observation views can be set;
-
The lower part is the time display area: it includes system time and ROS time.
The left plugin display area has two plugins by default:
-
Global Options: This plugin is used to set parameters related to global display. In general, the option that needs to be set manually is Fixed Frame, which is the global coordinate system referenced by all other data during visualization;
-
Global Status: This plugin is used to display whether all coordinate transforms are normal after Fixed Frame has been set in Global Options.

At the top is the menu area, on the left is the plugin display area, in the middle is the 3D debugging area, on the right is the view switching area, and at the bottom is the time area. ROS Time is ROS2 time, Wall Time is system time, and Elapsed is the time Rviz2 has been running.

You can save the rviz2 configuration, and you can also open a configuration

Set display panel

The number of planar grids can be set.

The number of grids in the vertical direction can be set.

You can set how large the grid side length is

Change the 4 channels of sRGB

Can change the perspective

Set the offset. If Z is -1, the grid will sink 1 unit relative to the coordinate system.

The fixed name is generally the name of the root coordinate system.
Background color is the background color.
The frame rate is the publishing frequency of the coordinate frame.

Global state; once the fixed name is set correctly, there will be no warning.

Viewpoint switching (generally default)

The Z-axis can be flipped
Common plugins:
| 序号 | 名称 | 功能 | 消息类型 |
|---|---|---|---|
| 1 | Axes | 显示 rviz2 默认的坐标系。 | |
| 2 | Camera | 显示相机图像,必须需要使用消息:CameraInfo。 | sensor_msgs/msg/Image,sensor_msgs/msg/CameraInfo |
| 3 | Grid | 显示以参考坐标系原点为中心的网格。 | |
| 4 | Grid Cells | 从网格中绘制单元格,通常是导航堆栈中成本地图中的障碍物。 | nav_msgs/msg/GridCells |
| 5 | Image | 显示相机图像,但是和Camera插件不同,它不需要使用 CameraInfo 消息。 | sensor_msgs/msg/Image |
| 6 | InteractiveMarker | 显示来自一个或多个交互式标记服务器的 3D 对象,并允许与它们进行鼠标交互。 | visualization_msgs/msg/InteractiveMarker |
| 7 | Laser Scan | 显示激光雷达数据。 | sensor_msgs/msg/LaserScan |
| 8 | Map | 显示地图数据。 | nav_msgs/msg/OccupancyGrid |
| 9 | Markers | 允许开发者通过主题显示任意原始形状的几何体。 | visualization_msgs/msg/Marker,visualization_msgs/msg/MarkerArray |
| 10 | Path | 显示机器人导航中的路径相关数据。 | nav_msgs/msg/Path |
| 11 | PointStamped | 以小球的形式绘制一个点。 | geometry_msgs/msg/PointStamped |
| 12 | Pose | 以箭头或坐标轴的方式绘制位姿。 | geometry_msgs/msg/PoseStamped |
| 13 | Pose Array | 绘制一组 Pose。 | geometry_msgs/msg/PoseArray |
| 14 | Point Cloud2 | 绘制点云数据。 | sensor_msgs/msg/PointCloud,sensor_msgs/msg/PointCloud2 |
| 15 | Polygon | 将多边形的轮廓绘制为线。 | geometry_msgs/msg/Polygon |
| 16 | Odometry | 显示随着时间推移累积的里程计消息。 | nav_msgs/msg/Odometry |
| 17 | Range | 显示表示来自声纳或红外距离传感器的距离测量值的圆锥。 | sensor_msgs/msg/Range |
| 18 | RobotModel | 显示机器人模型。 | |
| 19 | TF | 显示 tf 变换层次结构。 | |
| 20 | Wrench | 将geometry_msgs /WrenchStamped消息显示为表示力的箭头和表示扭矩的箭头加圆圈。 | geometry_msgs/msg/WrenchStamped |
| 21 | Oculus | 将 RViz 场景渲染到 Oculus 头戴设备。 |
Each of the aforementioned plugins also contains many properties; the final display effect of the plugin can be controlled by setting its properties.

Image is a camera data plugin
LaserScan is a lidar data plugin
TF is a coordinate transformation plugin
RobotModel is a robot model plugin
ros2 run rviz2 rviz2 -d xxx.rviz
#可以读取自己保存的rviz配置
3.Basic process of integrating URDF with rviz2
3.1.Case Analysis





Please run the following command to install the two feature packages required for the example (which can control the robot joint motion):
sudo apt install ros-humble-joint-state-publisher
sudo apt install ros-humble-joint-state-publisher-gui
In the terminal, enter the src directory of the workspace and run the following command to create the C++ package.
ros2 pkg create cpp06_urdf --build-type ament_cmake
Create urdf, rviz, launch, and meshes directories under the package for later use. Within the urdf directory, create the subdirectories urdf and xacro to store URDF files and Xacro files, respectively.

launch stores launch files
The urdf file stores the URDF 3D model file
meshes stores STL models
xacro can simplify URDF files and enhance their flexibility.
rviz stores the configuration of rviz2
3.2.Framework setup

<robot> name="hello_world"
<link> name="base_link"
<visual>
<geometry>
<box size="0.5 0.2 0.1"/>
</geometry>
</visual>
</link>
</robot>
Standard XML file


from launch import LaunchDescription
from launch_ros.actions import Node
import os
from ament_index_python.packages import get_package_share_directory
from launch_ros.parameter_descriptions import ParameterValue
from launch.substitutions import Command,LaunchConfiguration
from launch.actions import DeclareLaunchArgument
#示例:ros2 launch cpp06_urdf display.launch.py model:=`ros2 pkg prefix --share cpp06_urdf`/urdf/urdf/demo01_helloworld.urdf
def generate_launch_description():
cpp06_urdf_dir = get_package_share_directory("cpp06_urdf")
default_model_path = os.path.join(cpp06_urdf_dir,"urdf/urdf","demo01_helloworld.urdf")
default_rviz_path = os.path.join(cpp06_urdf_dir,"rviz","display.rviz")
model = DeclareLaunchArgument(name="model", default_value=default_model_path)
# 加载机器人模型
# 1.启动 robot_state_publisher 节点并以参数方式加载 urdf 文件
robot_description = ParameterValue(Command(["xacro ",LaunchConfiguration("model")]))
robot_state_publisher = Node(
package="robot_state_publisher",
executable="robot_state_publisher",
parameters=[{"robot_description": robot_description}]
)
# 2.启动 joint_state_publisher 节点发布非固定关节状态
joint_state_publisher = Node(
package="joint_state_publisher",
executable="joint_state_publisher"
)
# rviz2 节点
rviz2 = Node(
package="rviz2",
executable="rviz2"
# arguments=["-d", default_rviz_path]
)
return LaunchDescription([
model,
robot_state_publisher,
joint_state_publisher,
rviz2
])

<exec_depend>rviz2</exec_depend>
<exec_depend>xacro</exec_depend>
<exec_depend>robot_state_publisher</exec_depend>
<exec_depend>joint_state_publisher</exec_depend>
<exec_depend>ros2launch</exec_depend>

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

colcon build --packages-select cpp06_urdf

source install/setup.bash
ros2 launch cpp06_urdf display.launch.py

Tip:
In the subsequent cases of this chapter, all implementations follow the above steps; in the subsequent cases we only need to focus on the URDF implementation, and the launch files and configuration files need not be modified.




3.3.URDF file
Press ctrl+\\ to generate comments



Because the urdf plugin has been installed, there is a prompt indicating that a robot root tag needs to be created

The first attribute is the robot name.

The second one has an XML namespace pointing to xacro.


The third one has an XML namespace pointing to xacro, and then there's also a robot name.
(For now, use the simplest one, that is, the first one)

The link tag is called a link, and it also needs a name; a link generally refers to a rigid body part.
link has a subset tag called visual
Write the robot shape under the visual tag.
Then under that label, there is another subset label called geometry (geometric shape).
Then there is a subset tag called box (rectangular box shape), and the size that follows corresponds to length, width, and height.

<robot name="boxrobot">
<link name="base_link">
<visual>
<geometry>
<box size="1.0 0.5 0.1"/>
</geometry>
</visual>
</link>
</robot>
3.4.xacro tool (a tool for loading disk files into ROS2)
Check whether xacro is installed
ros2 pkg list | grep -i xacro

If xacro is printed, it indicates that it is installed; if not printed, it must be installed manually.
sudo apt-get update
sudo apt-get install ros-humble-xacro
Read the file using xacro

The content in the file has been output to the terminal; we usually integrate it into the launch file. In the terminal, we can only view the content, but with launch we can get the file into the node, that is, integrate it into ROS2.
3.5.launch core implementation
The core implementation consists of just three steps: load the robot model, have the node publish the states of the non-fixed joints, and start the rviz2 node.

Creating an rviz2 node is very simple: just declare the package name and declare the executable.
Loading a robot model is relatively complex. To load a robot model, you also need to create a node,
Then there is a parameter, and within the parameter there is a key called robot_description, and this key corresponds to a value
The value is a ParameterValue object, in which a command called xacro is executed, and then there is a Launch configuration, which is actually the path to the URDF file.




This value is actually the content in the URDF file, but the content is too long, so we encapsulate it into an object.


command line; it cannot be directly used as an object parameter value, so it still needs to be encapsulated.

Comand is specifically designed to encapsulate terminal command execution.


Remember there must be a space after xacro, and fill in the path here


此时已经定位到cpp06_urdf的share路径下的cpp06_urdf路径了,返回的也就是该路径的字符串



from launch import LaunchDescription
from launch_ros.actions import Node
# 封装终端指令相关类
# from launch.actions import ExecuteProcess
# from launch.substitutions import FindExecutable
# 参数声明与获取
# from launch.actions import DeclareLaunchArgument
# from launch.substitutions import LaunchConfiguration
# 文件包含相关
# from launch.actions import IncludeLaunchDescription
# from launch.launch_description_sources import PythonLaunchDescriptionSource
# 分组相关
# from launch_ros.actions import PushRosNamespace
# from launch.actions import GroupAction
# 事件相关
# from launch.event_handlers import OnProcessStart,OnProcessExit
# from launch.actions import ExecuteProcess,RegisterEventHandler,LogInfo
# 获取功能包下share目录或路径
from ament_index_python.packages import get_package_share_directory
from launch_ros.parameter_descriptions import ParameterValue
from launch.substitutions import Command
p_value = ParameterValue(Command(["xacro ",get_package_share_directory("cpp06_urdf") + "/urdf/urdf/demo01_boxrobot.urdf"]))
robot_state_pub = Node(
package="robot_state_publisher",
executable="robot_state_publisher",
parameters=[{"robot_description":p_value}]
)
rviz2 = Node(
package="rviz2",
executable="rviz2"
)
def generate_launch_description():
return LaunchDescription([robot_state_pub,rviz2])




Click Add in the lower left corner to add the RobotModel plugin.







Create a new coordinate system plugin: length along X, width along Y, height along Z
3.6.Launch optimization description and implementation
We also need to optimize three points. The first is to open the joint node, the second is to set the default configuration file for rviz2, and the third is that in the launch file, the URDF file we read is hardcoded, so the structure needs to be optimized.

This node also needs to be started to control joint motion. It can be changed to joint_state_publisher_gui, and a graphical interface will appear.





Save the rviz2 configuration.

The normal instruction is
ros2 run rviz2 rviz2 -d rviz2配置的路径



Create a parameter called model, with the value being that long string after it.

LaunchConfiguration is for parsing parameters

Remember to put the model at the very front; putting it later is not allowed. The path has now been encapsulated.
Currently, startup is normal startup with the default URDF path.

To parse a non-default URDF, in the terminal there is also something similar to get_package_share_directory, as follows (here the parameter model is missing an L): enclose the parameter value with backticks (the key between ESC and TAB).

ros2 pkg prefix --share cpp06_urdf
ros2 run launch cpp06_urdf display.launch.py model:=`ros2 pkg prefix --share cpp06_urdf`/urdf/urdf/hahah.urdf



from launch import LaunchDescription
from launch_ros.actions import Node
# 封装终端指令相关类
# from launch.actions import ExecuteProcess
# from launch.substitutions import FindExecutable
# 参数声明与获取
from launch.actions import DeclareLaunchArgument
from launch.substitutions import LaunchConfiguration
# 文件包含相关
# from launch.actions import IncludeLaunchDescription
# from launch.launch_description_sources import PythonLaunchDescriptionSource
# 分组相关
# from launch_ros.actions import PushRosNamespace
# from launch.actions import GroupAction
# 事件相关
# from launch.event_handlers import OnProcessStart,OnProcessExit
# from launch.actions import ExecuteProcess,RegisterEventHandler,LogInfo
# 获取功能包下share目录或路径
from ament_index_python.packages import get_package_share_directory
from launch_ros.parameter_descriptions import ParameterValue
from launch.substitutions import Command
model = DeclareLaunchArgument(name="model",default_value=get_package_share_directory("cpp06_urdf") + "/urdf/urdf/demo01_boxrobot.urdf")
p_value = ParameterValue(Command(["xacro ",LaunchConfiguration("model")]))
robot_state_pub = Node(
package="robot_state_publisher",
executable="robot_state_publisher",
parameters=[{"robot_description":p_value}]
)
joint_state_pub = Node(
package="joint_state_publisher",
executable="joint_state_publisher"
)
rviz2 = Node(
package="rviz2",
executable="rviz2",
arguments=["-d",get_package_share_directory("cpp06_urdf") + "/rviz/urdf.rviz"]
)
def generate_launch_description():
return LaunchDescription([model,robot_state_pub,joint_state_pub,rviz2])
Continue optimizing the final code as follows:
from launch import LaunchDescription
from launch_ros.actions import Node
# 封装终端指令相关类
# from launch.actions import ExecuteProcess
# from launch.substitutions import FindExecutable
# 参数声明与获取
from launch.actions import DeclareLaunchArgument
from launch.substitutions import LaunchConfiguration
# 文件包含相关
# from launch.actions import IncludeLaunchDescription
# from launch.launch_description_sources import PythonLaunchDescriptionSource
# 分组相关
# from launch_ros.actions import PushRosNamespace
# from launch.actions import GroupAction
# 事件相关
# from launch.event_handlers import OnProcessStart,OnProcessExit
# from launch.actions import ExecuteProcess,RegisterEventHandler,LogInfo
# 获取功能包下share目录或路径
from ament_index_python.packages import get_package_share_directory
from launch_ros.parameter_descriptions import ParameterValue
from launch.substitutions import Command
import os
cpp06_urdf_dir = get_package_share_directory("cpp06_urdf")
default_model_path = os.path.join(cpp06_urdf_dir,"urdf/urdf","demo01_boxrobot.urdf")
default_rviz_path = os.path.join(cpp06_urdf_dir,"rviz","urdf.rviz")
model = DeclareLaunchArgument(name="model",default_value=default_model_path)
p_value = ParameterValue(Command(["xacro ",LaunchConfiguration("model")]))
robot_state_pub = Node(
package="robot_state_publisher",
executable="robot_state_publisher",
parameters=[{"robot_description":p_value}]
)
# 关节信息节点
# joint_state_pub = Node(
# package="joint_state_publisher",
# executable="joint_state_publisher"
# )
# 关节信息节点图形界面(建议)
joint_state_pub = Node(
package="joint_state_publisher_gui",
executable="joint_state_publisher_gui"
)
rviz2 = Node(
package="rviz2",
executable="rviz2",
# arguments=["-d",get_package_share_directory("cpp06_urdf") + "/rviz/urdf.rviz"]
arguments=["-d",default_rviz_path]
)
def generate_launch_description():
return LaunchDescription([model,robot_state_pub,joint_state_pub,rviz2])
4.URDF syntax
4.1.Introduction

4.2.robot root tag


Divide the robot into several subsets, for example, one subset describes the head, one subset describes the body, and finally combine them into a set to form the robot.
Although my sub-file and main file are logically in an inclusion relationship, in reality, they are both separate URDF files. In the main file, the name attribute of the robot tag must be written; the sub-file may omit it. If it is written, the value of the sub-file's name must be the same as that of the main file!


4.3.link tag
4.3.1.Introduction

Each link is a rigid body and an independent component.
Links are connected through joints
A Link mainly consists of three parts: Visual, Collision, and Inertial

Those without angle brackets are attributes; those with them are tags.
(Optional): Used to describe the visual attributes of the link; you can set the link's shape (cube, sphere, cylinder, etc.).
-
name (Optional): Specifies the link name. This name is mapped to a coordinate frame with the same name and can also be used to locate the link by referencing this value.
-
(Required): Used to set the shape of the link, such as a cube, sphere, or cylinder.
-
: Cube tag. The side length of the cube is set via the size attribute, with its origin at the geometric center.
-
: Cylinder tag. The cylinder radius is set via the radius attribute, and the cylinder height is set via the length attribute, with its origin at the geometric center.
-
: Sphere tag. The sphere radius is set via the radius attribute, with its origin at the geometric center.
-
: References a "skin" file via the filename attribute to set the appearance of the link. The file must be a local file. Use package:///as a prefix for the filename.
-
(Optional): Used to set the relative offset and rotation angle of the link. If not specified, default values are used (no offset and no rotation).
-
xyz : Represents the offset in the three dimensions x, y, and z (in meters). Separate different values with spaces. If not specified, the default value is used (no offset in any of the three dimensions).
-
rpy : Represents the roll, pitch, and yaw angles (in radians). Separate different values with spaces. If not specified, the default value is used (no rotation in any of the three dimensions).
-
-
(Optional): The material of the visual element. A material tag can also be defined in the root robot tag and then referenced by name within a link.
-
name (Optional): Specifies a name for the material, which can be referenced via this value.
-
(Optional): The color of the rgba material, consisting of four numbers representing red/green/blue/alpha, with each number ranging from .
-
(Optional): The texture of the material, which can be set via the filename attribute.
-
-
When there are multiple Visuals, you need to set a name for the Visual, so it is optional.
Collision is related to simulation. We can set a collision interval for our robot's rigid body. As long as an obstacle enters the interval, a collision occurs. Generally, the collision interval is larger than the actual size.
(可选):link的碰撞属性。可以与link的视觉属性一致,也可以不同,比如:我们会通常使用更简单的碰撞模型来减少计算时间,或者设置的值大于link的视觉属性,以尽量避免碰撞。另外,同一链接可以存在多个 标签实例,多个几何图形组合表示link的碰撞属性。
-
name (Optional): Set a name for the collision.
-
(Required): Refer to the usage rules for geometry in the visual tag.
-
(Optional): Refer to the usage rules for origin in the visual tag.
Inertial is for setting the inertia matrix, and it is also related to simulation. For example, when a robot brakes, it may tilt forward; for instance, if the center of mass of the inertia matrix is higher, then sudden braking may cause it to tip over.
(Optional): Used to set the link's mass, center of mass position, and central inertia properties. If not specified, it defaults to zero mass and zero inertia.
-
(Optional): The pose (translation, rotation) describes the position and orientation of the link's center-of-mass frame C relative to the link frame L.
-
xyz : Indicates that the position vector from Lo (the link frame origin) to Co (the link's center of mass) is x L̂x + y L̂y + z L̂z, where L̂x, L̂y, L̂z are the orthogonal unit vectors of the link frame L.
-
rpy : Represents the orientation of C's unit vectors Ĉx, Ĉy, Ĉz relative to the link frame L as an Euler rotation sequence (r p y) in radians. Note: Ĉx, Ĉy, Ĉz do not need to be aligned with the link's principal axes of inertia.
-
-
(Required): Sets the mass of the link via its value attribute.
-
(Required): For the unit vectors Ĉx, Ĉy, Ĉz fixed in the center-of-mass coordinate system C, the link's moments of inertia ixx, iyy, izz and the products of inertia ixy, ixz, iyz about Co (the link's center of mass).
Note: and are required only in a simulation environment. If you are only integrating the URDF in rviz2, you do not need to define these two tags for the link.
4.3.2.Use

<robot name="link_demo">
<material name="yellow">
<color rgba="0.7 0.7 0 0.8" />
</material>
<link name="base_link">
<visual>
<geometry>
<box size="0.5 0.3 0.1" />
</geometry>
<origin xyz="0 0 0" rpy="0 0 0" />
<material name="yellow"/>
</visual>
</link>
</robot>

ros2 launch cpp06_urdf display.launch.py model:=`ros2 pkg prefix --share cpp06_urdf`/urdf/urdf/demo02_link.urdf


Rectangular solid, sphere, cylinder






Translation amounts: X translation 1, Y and Z translation 0, rpy rotation degrees set to 0 (the rotation degrees are respectively the roll angle Roll (around X), pitch angle Pitch (around Y), and yaw angle Yaw (around Z) in Euler angles).


Found that the cuboid is offset by 1 on X


Let the heading angle Yaw (rotation about Z) be 0.5 rad


Set the roll angle Roll (rotation about X) to 0.5 rad


Set the pitch angle Pitch (motion around Y) to 0.5 rad

sRGB is R,G,B,Alpha (floating-point model, so the range is 0-1.0)




If a color is to be used multiple times, it can be encapsulated as something similar to a global variable, and then when called in other links, directly use (This is a closed loop here, pay attention)

<robot name="link_demo">
<material name="yellow">
<color rgba="0.7 0.7 0 0.8" />
</material>
<link name="base_link">
<visual>
<geometry>
<box size="0.5 0.3 0.1" />
</geometry>
<origin xyz="0 0 0" rpy="0 0 0" />
<material name="yellow"/>
</visual>
</link>
</robot>
4.3.3.Use supplement

The mesh tag references a skin file, generally an STL file, which can be exported using SolidWorks; see SW2URDF later in the document.
If you don't know how to use SolidWorks, you can learn it; this thing can be learned in two and a half days, it's just a tool, and only being able to draw is nothing special. The most important thing is still mechanical design, which is relatively difficult. Those who want to learn can watch the videos on the WeChat official account of the brother club, the Mechanical Society, to study.
Of course, if you don't have time to learn, you can directly use robots that others have already drawn. Search for turtlebot3 on Github.



Here are the already exported models: in bases are the appearance models, sensors are the sensor models, and wheels are the wheel models.
Let's clone the repository, and note that we need to clone the branch that is the ros2 branch.

We can just reference one to see the effect; for real applications, we still need to use SolidWorks to export via the SW2URDF plugin.
Copy meshes/bases from the project to burger_base.stl in the meshes directory in our WS.


filename is for writing the skin file just mentioned; package:// is the protocol name, followed by the package name, which is cpp06_urdf. Then follows the file path under the package, which is meshes/burger_base.stl (actually the path under share).

Because it is a 3D model, when filling in the scale size for scaling, you need to fill in 3 ratios; we can just fill in 1.0 for all of them.

Why does the displayed model appear so large? Because RViz2 uses meters as its unit, while STL uses millimeters. Note this. In mechanical engineering, by default, if no unit is specified, it is millimeters. Do not change units arbitrarily; generally, millimeters should be used as the unit. We are working on robots, so we should be more professional.


4.4.joint tag
4.4.1.Introduction
The joint tag in urdf is used to describe the kinematic and dynamic properties of robot joints, and can also specify the safety limits of joint motion. Two parts of the robot (respectively called parent link and child link) are connected in the form of a "joint". Different joints have different motion forms: rotation, sliding, fixed, rotation speed, rotation angle limit..., for example: a wheel mounted on the base can rotate 360 degrees, while a camera may be completely fixed on the base.

-
name (required): Name the joint; the name must be unique.
-
type (required): Set the joint type. The available types are as follows:
-
continuous: A revolute joint that can rotate indefinitely about a single axis.
-
revolute: A revolute joint similar to continuous, but with limits on the rotation angle.
-
prismatic: A sliding joint that moves along an axis and has position limits.
-
planar: A planar joint that allows translation or rotation in orthogonal directions within a plane.
-
floating: A floating joint that allows translational and rotational motion.
-
fixed: A fixed joint, a special joint that does not allow motion.
-
The following are sub-level tags
-
(必填):指定父级link。
- link (必填):父级link的名字,是这个link在机器人结构树中的名字。
-
(必填):指定子级link。
- link (必填):子级link的名字,是这个link在机器人结构树中的名字。
-
(可选):这是从父link到子link的转换,关节位于子link的原点。
-
xyz :各轴线上的偏移量。
-
rpy :各轴线上的偏移弧度。
-
-
<axis> (可选):如不设置,默认值为(1,0,0)。
- xyz :用于设置围绕哪个关节轴运动。
-
(可选):关节的参考位置,用于校准关节的绝对位置。
-
rising (可选):当关节向正方向移动时,该参考位置将触发上升沿。
-
falling (可选):当关节向正方向移动时,该参考位置将触发下降沿。
-
-
(可选):指定接头物理特性的元素。这些值用于指定关节的建模属性,对仿真较为有用。
-
damping (可选):关节的物理阻尼值,默认为0。
-
friction (可选):关节的物理静摩擦值,默认为0。
-
-
(关节类型是revolute或prismatic时为必须的):
-
lower (可选):指定关节下限的属性(旋转关节以弧度为单位,棱柱关节以米为单位)。如果关节是连续的,则省略。
-
upper (可选):指定关节上限的属性(旋转关节以弧度为单位,棱柱关节以米为单位)。如果关节是连续的,则省略。
-
effort (必填):指定关节可受力的最大值。
-
velocity (必填):用于设置最大关节速度(旋转关节以弧度每秒 为单位,棱柱关节以米每秒 为单位)。
-
-
(可选):此标签用于指定定义的关节模仿另一个现有关节。该关节的值可以计算为value = multiplier * other_joint_value + offset。
-
joint (必填):指定要模拟的关节的名称。
-
multiplier (可选):指定上述公式中的乘法因子。
-
offset (可选):指定要在上述公式中添加的偏移量,默认为 0(旋转关节的单位是弧度,棱柱关节的单位是米)。
-
-
<safety_controller> (可选):安全控制器。
-
soft_lower_limit (可选):指定安全控制器开始限制关节位置的下关节边界,此限制需要大于joint下限。
-
soft_upper_limit (可选):指定安全控制器开始限制关节位置的关节上边界的属性,此限制需要小于joint上限。
-
k_position (可选):指定位置和速度限制之间的关系。
-
k_velocity (必填):指定力和速度限制之间的关系。
-

The joint name is required and must be unique.

The most commonly used are revolute joints with finite limits, continuous joints that can rotate infinitely, and fixed joints. This should be filled in according to the specific joint type.
A revolute joint is generally used for the joints of industrial robot manipulators; a continuous joint is, for example, the "joint" in a steering wheel structure; a fixed joint is a structural joint that is fixed and cannot move.

There are many subset labels, but we only use a few of the most common ones. Just remember the commonly used ones.

parent tag; the link attribute specifies the name of the parent link.

Similar to the child tag.


This axis defaults to 1, 0, 0, rotating about the X-axis, but we generally need to set it, which can be configured by setting a reference axis in SolidWorks.
The remaining tags are all related to joint types. For example, with limit, if the joint type is revolute and limit is not set, then in joint_state_publisher_gui it is impossible to adjust the joint angle.

Other tags will be introduced when they are used.
4.4.2.Practice

First implement the two joints separately, and then connect them through the joint.







If it is yellow, there should be more red and green.

This is red.

The chassis Link has now been created,

The camera Link has also been created,

Set the joint name to camera2base_link, which is the joint connecting the camera to the base, and set the type to continuous, allowing 360-degree rotation.

Fill in the child Link and parent Link

In this way, the two Links are connected together through this Joint.

We also need to set these two options, but let's not set them for now; first, let's see what the default state is.
colcon build --packages-select cpp06_urdf
source install/setup.bash
ros2 launch cpp06_urdf display.launch.py model:=`ros2 pkg prefix --share cpp06_urdf`/urdf/urdf/demo03_joint.urdf


可以打开TF看看坐标系,勾上Show Names,发现是重合的。所以显示效果不满足咱们的逻辑业务。(默认状态下),所以需要设置偏移量。

If we want to move the camera to the front of the vehicle, as shown in the figure, there is an offset in X, none in Y, but there is in Z. Then, there is none in roll, pitch, or yaw.



The height of Z is 1/2 of the chassis height + 1/2 of the camera height



We want to rotate around Yaw, which is rotation around the Z-axis, that is, 001. Note that it must be an integer, not a floating-point number.

To see whether it can rotate, open joint_state_publisher_gui; you can open it from launch or directly from the terminal.


ros2 run joint_state_publisher_gui joint_state_publisher_gui


Drag the scrollbar to change the camera's Yaw

Randomize refers to random numbers, and Center refers to returning to center (reset).
<robot name="joint_demo">
<material name="yellow">
<color rgba="0.7 0.7 0 0.8" />
</material>
<material name="red">
<color rgba="0.8 0.1 0.1 0.8" />
</material>
<link name="base_link">
<visual>
<geometry>
<box size="0.5 0.3 0.1" />
</geometry>
<origin xyz="0 0 0" rpy="0 0 0" />
<material name="yellow"/>
</visual>
</link>
<link name="camera">
<visual>
<geometry>
<box size="0.02 0.05 0.05" />
</geometry>
<origin xyz="0 0 0" rpy="0 0 0" />
<material name="red" />
</visual>
</link>
<joint name="camera2baselink" type="continuous">
<parent link="base_link"/>
<child link="camera" />
<origin xyz="0.2 0 0.075" rpy="0 0 0" />
<axis xyz="0 0 1" />
</joint>
</robot>
4.4.3.joint_state_publisher
bug:Yaw不稳定,会一直回中。



Actually, this is because the instance launched from launch conflicts with the one launched from the terminal.
Solution: start only one of them.

Solution: Directly launch the GUI version from Launch, and this will solve it. (However, this is not recommended.)
Suggested solution: Use the non-GUI version, because in the future, we will control the joints through programmatic control, not GUI control.

If you just want to demonstrate the model, use the GUI,
If you want to use program control, use the regular version.
4.4.4.base_footprint
bug: Robot chassis is partially sunk into the ground




<link name="base_footprint">
<visual>
<geometry>
<sphere radius="0.001"/>
</geometry>
</visual>
</link>
<joint name="baselink2basefootprint" type="fixed">
<parent link="base_footprint"/>
<child link="base_link"/>
<origin xyz="0.0 0.0 0.05"/>
</joint>
For Z's offset, fill in the distance of the lowered chassis, that is, half of the entire vehicle chassis.


Modify reference coordinate system
Actually, this optimization can be skipped; the impact is minimal. However, it is recommended to use the version with basefootprint.
<robot name="base_footprint_demo">
<material name="yellow">
<color rgba="0.7 0.7 0 0.8" />
</material>
<material name="red">
<color rgba="0.8 0.1 0.1 0.8" />
</material>
<link name="base_footprint">
<visual>
<geometry>
<sphere radius="0.001"/>
</geometry>
</visual>
</link>
<link name="base_link">
<visual>
<geometry>
<box size="0.5 0.3 0.1" />
</geometry>
<origin xyz="0 0 0" rpy="0 0 0" />
<material name="yellow"/>
</visual>
</link>
<joint name="baselink2basefootprint" type="fixed">
<parent link="base_footprint"/>
<child link="base_link"/>
<origin xyz="0.0 0.0 0.05"/>
</joint>
<link name="camera">
<visual>
<geometry>
<box size="0.02 0.05 0.05" />
</geometry>
<origin xyz="0 0 0" rpy="0 0 0" />
<material name="red" />
</visual>
</link>
<joint name="camera2baselink" type="fixed">
<parent link="base_link"/>
<child link="camera" />
<origin xyz="0.2 0 0.075" rpy="0 0 0" />
<axis xyz="0 0 1" />
</joint>
</robot>
4.5.Practice

There is no need to practice too deeply; we can directly use SolidWorks for modeling, which is more user-friendly.

<robot name="exercise_demo">
<material name="yellow">
<color rgba="0.7 0.7 0 0.8" />
</material>
<material name="red">
<color rgba="0.8 0.1 0.1 0.8" />
</material>
<material name="gray">
<color rgba="0.2 0.2 0.2 0.8" />
</material>
<link name="base_footprint">
<visual>
<geometry>
<sphere radius="0.001"/>
</geometry>
</visual>
</link>
<link name="base_link">
<visual>
<geometry>
<box size="0.2 0.12 0.07" />
</geometry>
<origin xyz="0 0 0" rpy="0 0 0" />
<material name="yellow"/>
</visual>
</link>
<joint name="baselink2basefootprint" type="fixed">
<parent link="base_footprint"/>
<child link="base_link"/>
<origin xyz="0.0 0.0 0.05"/>
</joint>
<link name="front_left_wheel">
<visual>
<geometry>
<cylinder radius="0.025" length="0.02"/>
</geometry>
<origin xyz="0 0 0" rpy="1.57 0 0" />
<material name="gray" />
</visual>
</link>
<joint name="frontleftwheel2baselink" type="continuous">
<parent link="base_link"/>
<child link="front_left_wheel" />
<origin xyz="0.075 0.06 -0.025" rpy="0 0 0" />
<axis xyz="0 1 0" />
</joint>
<link name="front_right_wheel">
<visual>
<geometry>
<cylinder radius="0.025" length="0.02"/>
</geometry>
<origin xyz="0 0 0" rpy="1.57 0 0" />
<material name="gray" />
</visual>
</link>
<joint name="frontrightwheel2baselink" type="continuous">
<parent link="base_link"/>
<child link="front_right_wheel" />
<origin xyz="0.075 -0.06 -0.025" rpy="0 0 0" />
<axis xyz="0 1 0" />
</joint>
<link name="back_left_wheel">
<visual>
<geometry>
<cylinder radius="0.025" length="0.02"/>
</geometry>
<origin xyz="0 0 0" rpy="1.57 0 0" />
<material name="gray" />
</visual>
</link>
<joint name="backleftwheel2baselink" type="continuous">
<parent link="base_link"/>
<child link="back_left_wheel" />
<origin xyz="-0.075 0.06 -0.025" rpy="0 0 0" />
<axis xyz="0 1 0" />
</joint>
<link name="back_right_wheel">
<visual>
<geometry>
<cylinder radius="0.025" length="0.02"/>
</geometry>
<origin xyz="0 0 0" rpy="1.57 0 0" />
<material name="gray" />
</visual>
</link>
<joint name="backrightwheel2baselink" type="continuous">
<parent link="base_link"/>
<child link="back_right_wheel" />
<origin xyz="-0.075 -0.06 -0.025" rpy="0 0 0" />
<axis xyz="0 1 0" />
</joint>
</robot>
Run in terminal
ros2 launch cpp06_urdf display.launch.py model:=`ros2 pkg prefix --share cpp06_urdf`/urdf/urdf/demo05_exercise.urdf

5.URDF tool
In ROS2, some tools related to URDF files are provided, such as:
-
check_urdfThe command can check whether a complex URDF file has syntax issues; -
urdf_to_graphvizThe command can inspect the URDF model structure and display the hierarchical relationships among different links.
Of course, before using the tool, please install it first. The installation command is:sudo apt install liburdfdom-tools.
5.1.check_urdf Syntax check
Enter the directory containing the URDF file and call:check_urdf urdf文件If no exception is thrown, the file is valid; otherwise, it is invalid.
示例,终端下进入功能包 cpp06_urdf 的 urdf/urdf 目录,执行如下命令:
check_urdf demo05_exercise.urdf
If the URDF file has no abnormalities, the hierarchical relationship of links in the URDF will be displayed as shown in the figure below:

Otherwise, an error message will be displayed.

Demonstration error,

5.2.urdf_to_graphviz Structure View
Enter the directory containing the URDF file and run:urdf_to_graphviz urdf文件A PDF file will be generated in the current directory.
示例,终端下进入功能包 cpp06_urdf 的 urdf/urdf 目录,执行如下命令:
urdf_to_graphviz demo05_exercise.urdf
In the current directory, .pdf and .gv files named after the robot name in the URDF will be generated. Opening the PDF file will display the content as shown in the following figure:

In the figure above, the relationship between links and joints is displayed in a tree structure.
Note: This tool was previously namedurdf_to_graphizIt is now recommended to useurdf_to_graphvizinstead.

urdf_to_graphizIt is a historical version that has been deprecated. It is recommended to use urdf_to_graphviz.




The black boxes represent Links, and the blue ones represent Joints. Information such as translation and rotation is also displayed.
6.SW2URDF
6.1.Introduction to SolidWorks
SolidWorks is a computer-aided design (CAD) and computer-aided manufacturing (CAM) software developed by Dassault Systèmes SolidWorks Corp. It is mainly used for engineering design and manufacturing, and can be used to create 3D models, perform assembly design, carry out engineering analysis and drafting, etc. SolidWorks has an intuitive user interface and powerful features, enabling engineers and designers to quickly and accurately design complex parts and assemblies. The software is widely used in machinery, aerospace, automotive, medical equipment, and other industries, and is one of the important tools in the field of engineering design.

6.2.Introduction to the SolidWorks plugin sw2urdf
sw2urdf插件维基百科:
https://wiki.ros.org/sw_urdf_exporter
github下载链接:
https://github.com/ros/solidworks_urdf_exporter/releases
虽然github链接上写着只支持到SW2021,但是目前发现最新版SW(2022、2024经测试)也是可以正常用的。

6.3.Install sw2urdf
- 去github上下载sw2urdf插件(这个SW版本不用管,实测在后续的SW版本依然可用)

- Check the SW installation path
For example, my path is "C:\\Program Files\\SOLIDWORKS Corp\\SOLIDWORKS\\"

- Open the plugin installer. By default, it will automatically locate your SW path for installation. If it does not automatically find the path for installation, you will need to manually select the SW installation path.

- Check whether the plugin is installed successfully


The installation is successful when it looks like this in the figure.
6.4.Export URDF and Meshes
- The figure shows a standard SW assembly drawing (you can also try to build one yourself; it does not need to contain a transmission mechanism).
这个过程看不太懂的话,可以参考一下古月居老师的视频,结合本教程一起学习。 【SolidWorks模型导出urdf(古月居老师)-哔哩哔哩】https://www.bilibili.com/video/BV1Tx411o7rH

- Create a datum axis for the joint.

Select the cylindrical surface of the joint or another surface to calibrate the rotation axis.

If the datum axis cannot be displayed properly after it is created, turn on this option.


After building all departments
- Open the Export as URDF option

- You need to select the base rigid body first. base_link

The Global Origin here may need to be selected manually; simply select Origin_Global, but generally selecting Automatically Generate is sufficient.
- Because his base_link is connected to a child_link through a joint, this option should be filled with 1.


Let's call the joint connected by base_link joint1, and the rigid body above that joint1 connects to link1,
Then, with reference to the datum axis, select the datum axis 1 that we just created at this joint, and then for the joint type, select revolute (a joint with limits; only in this way can the robot joint move normally).

For the reference coordinate system here, you can choose Automatically Generate, or you can choose Origin_Joint1.
Then add one more to link1 to create link2.
The steps for adding link3 and link4 later are the same and will not be shown in detail again.



- Click preview and export

- Set joint limits
coordinates is the coordinate system, axis is the axis. If the Axis coordinate display here is incorrect, it means the Coordinates or Axis selection is wrong. Please manually select the correct coordinate system or datum axis.

If the pivot generation is incorrect, for example, all zeros, you can fill it in yourself.

For example, here is the X-axis; you can fill the left wheel with -100 and the right wheel with 100 (this is for reference only, a solution provided by ChatGPT).

- View the matrix, and click Export URDF and meshes




This successfully generates a ROS1 WS; move the WS to the Linux system.
6.5.Convert ROS1 WS to ROS2 WS
- First, move the ROS1 workspace to the Linux system.

- Create a new WS for ROS2

mkdir -p ros2_4axisrobot_ws/src
cd ros2_4axisrobot_ws/
colcon build
cd src
ros2 pkg create cpp01_urdf --build-type ament_cmake
cd ..
code .
- Improve WS directories


Create folders such as launch, meshes, rviz, urdf, etc. under the path ./src/cpp01_urdf, and create urdf and xacro folders under the urdf folder.
- Copy the URDF and Meshes from the WS of ROS1 to the WS of ROS2



- Configure CMake and package
In CMake, please add
install(
DIRECTORY launch urdf rviz meshes
DESTINATION share/${PROJECT_NAME}
)
In Package.xml please add
<exec_depend>rviz2</exec_depend>
<exec_depend>xacro</exec_depend>
<exec_depend>robot_state_publisher</exec_depend>
<exec_depend>joint_state_publisher</exec_depend>
<exec_depend>ros2launch</exec_depend>
CMakeLists.txt
cmake_minimum_required(VERSION 3.8)
project(cpp01_urdf)
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wall -Wextra -Wpedantic)
endif()
# find dependencies
find_package(ament_cmake REQUIRED)
# uncomment the following section in order to fill in
# further dependencies manually.
# find_package(<dependency> REQUIRED)
install(
DIRECTORY launch urdf rviz meshes
DESTINATION share/${PROJECT_NAME}
)
if(BUILD_TESTING)
find_package(ament_lint_auto REQUIRED)
# the following line skips the linter which checks for copyrights
# comment the line when a copyright and license is added to all source files
set(ament_cmake_copyright_FOUND TRUE)
# the following line skips cpplint (only works in a git repo)
# comment the line when this package is in a git repo and when
# a copyright and license is added to all source files
set(ament_cmake_cpplint_FOUND TRUE)
ament_lint_auto_find_test_dependencies()
endif()
ament_package()
package.xml
<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
<name>cpp01_urdf</name>
<version>0.0.0</version>
<description>TODO: Package description</description>
<maintainer email="tungchiahui@gmail.com">tungchiahui</maintainer>
<license>TODO: License declaration</license>
<buildtool_depend>ament_cmake</buildtool_depend>
<exec_depend>rviz2</exec_depend>
<exec_depend>xacro</exec_depend>
<exec_depend>robot_state_publisher</exec_depend>
<exec_depend>joint_state_publisher</exec_depend>
<exec_depend>ros2launch</exec_depend>
<test_depend>ament_lint_auto</test_depend>
<test_depend>ament_lint_common</test_depend>
<export>
<build_type>ament_cmake</build_type>
</export>
</package>
- Write the launch file.
For the detailed process, see the section on core optimizations related to URDF Launch.

from launch import LaunchDescription
from launch_ros.actions import Node
# 封装终端指令相关类
# from launch.actions import ExecuteProcess
# from launch.substitutions import FindExecutable
# 参数声明与获取
from launch.actions import DeclareLaunchArgument
from launch.substitutions import LaunchConfiguration
# 文件包含相关
# from launch.actions import IncludeLaunchDescription
# from launch.launch_description_sources import PythonLaunchDescriptionSource
# 分组相关
# from launch_ros.actions import PushRosNamespace
# from launch.actions import GroupAction
# 事件相关
# from launch.event_handlers import OnProcessStart,OnProcessExit
# from launch.actions import ExecuteProcess,RegisterEventHandler,LogInfo
# 获取功能包下share目录或路径
from ament_index_python.packages import get_package_share_directory
from launch_ros.parameter_descriptions import ParameterValue
from launch.substitutions import Command
import os
cpp01_urdf_dir = get_package_share_directory("cpp01_urdf")
default_model_path = os.path.join(cpp01_urdf_dir,"urdf/urdf","4axisrobot.urdf")
default_rviz_path = os.path.join(cpp01_urdf_dir,"rviz","urdf.rviz")
model = DeclareLaunchArgument(name="model",default_value=default_model_path)
p_value = ParameterValue(Command(["xacro ",LaunchConfiguration("model")]))
robot_state_pub = Node(
package="robot_state_publisher",
executable="robot_state_publisher",
parameters=[{"robot_description":p_value}]
)
# 关节信息节点(建议)
# joint_state_pub = Node(
# package="joint_state_publisher",
# executable="joint_state_publisher"
# )
# 关节信息节点图形界面
joint_state_pub = Node(
package="joint_state_publisher_gui",
executable="joint_state_publisher_gui"
)
rviz2 = Node(
package="rviz2",
executable="rviz2",
# arguments=["-d",get_package_share_directory("cpp06_urdf") + "/rviz/urdf.rviz"]
arguments=["-d",default_rviz_path]
)
def generate_launch_description():
return LaunchDescription([model,robot_state_pub,joint_state_pub,rviz2])
- Modify the URDF file
The main changes consist of two items: one is the path of meshes, and the other is deleting comments that easily cause errors.
Mainly because the WS of ROS1 and the WS path of ROS2 are different, we only need to modify the content related to the path, such as the path of Meshes


package:// is usually the share directory, so we need to start writing from the cpp01_urdf level (the share directory will only appear after compilation).

Then press Ctrl+H


Then you also need to delete the comments at the beginning of the URDF file, otherwise it will also throw an error.

The following is the replaced URDF file:
<?xml version="1.0" encoding="utf-8"?>
<robot
name="4axisrobot">
<link
name="base_link">
<inertial>
<origin
xyz="-0.0819771145953119 -0.0394328123681598 -0.0605612328464761"
rpy="0 0 0" />
<mass
value="7.43807257011133" />
<inertia
ixx="0.33173827700719"
ixy="-1.91350798633049E-05"
ixz="-2.38586363043925E-05"
iyy="0.510937907009661"
iyz="1.14706407748075E-05"
izz="0.66107272632796" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="package://cpp01_urdf/meshes/base_link.STL" />
</geometry>
<material
name="">
<color
rgba="0.792156862745098 0.819607843137255 0.933333333333333 1" />
</material>
</visual>
<collision>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="package://cpp01_urdf/meshes/base_link.STL" />
</geometry>
</collision>
</link>
<link
name="link1">
<inertial>
<origin
xyz="0.19141 -0.050676 -0.030057"
rpy="0 0 0" />
<mass
value="21.822" />
<inertia
ixx="0.50464"
ixy="0.13295"
ixz="0.087353"
iyy="0.59136"
iyz="-0.066053"
izz="0.64081" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="package://cpp01_urdf/meshes/link1.STL" />
</geometry>
<material
name="">
<color
rgba="0.79216 0.81961 0.93333 1" />
</material>
</visual>
<collision>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="package://cpp01_urdf/meshes/link1.STL" />
</geometry>
</collision>
</link>
<joint
name="joint1"
type="revolute">
<origin
xyz="-0.081947 -0.039447 0.11191"
rpy="0.40394 -1.5708 0" />
<parent
link="base_link" />
<child
link="link1" />
<axis
xyz="1 0 0" />
<limit
lower="-3.14"
upper="3.14"
effort="100"
velocity="1" />
</joint>
<link
name="link2">
<inertial>
<origin
xyz="0.00711598521454873 0.430460941882332 -0.181074256126024"
rpy="0 0 0" />
<mass
value="30.6115349647897" />
<inertia
ixx="2.84056101478768"
ixy="0.00176700425270318"
ixz="-0.000605507690039326"
iyy="0.803420593303961"
iyz="0.881921487314161"
izz="2.25142021571439" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="package://cpp01_urdf/meshes/link2.STL" />
</geometry>
<material
name="">
<color
rgba="0.792156862745098 0.819607843137255 0.933333333333333 1" />
</material>
</visual>
<collision>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="package://cpp01_urdf/meshes/link2.STL" />
</geometry>
</collision>
</link>
<joint
name="joint2"
type="revolute">
<origin
xyz="0.35141 -0.28119 0.13918"
rpy="-2.0566 1.1982 1.5708" />
<parent
link="link1" />
<child
link="link2" />
<axis
xyz="-1 0 0" />
<limit
lower="-3.14"
upper="3.14"
effort="100"
velocity="1" />
</joint>
<link
name="link3">
<inertial>
<origin
xyz="0.00112887115421612 0.0366346915274407 0.31720330934553"
rpy="0 0 0" />
<mass
value="32.192726667578" />
<inertia
ixx="4.01515983126431"
ixy="0.0013193752174692"
ixz="0.0113529938102155"
iyy="3.86196311673823"
iyz="-0.336440022491883"
izz="0.399977905540088" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="package://cpp01_urdf/meshes/link3.STL" />
</geometry>
<material
name="">
<color
rgba="0.792156862745098 0.819607843137255 0.933333333333333 1" />
</material>
</visual>
<collision>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="package://cpp01_urdf/meshes/link3.STL" />
</geometry>
</collision>
</link>
<joint
name="joint3"
type="revolute">
<origin
xyz="0.3765 0.82481 -0.49591"
rpy="2.5802 0 0" />
<parent
link="link2" />
<child
link="link3" />
<axis
xyz="1 0 0" />
<limit
lower="-3.14"
upper="3.14"
effort="100"
velocity="1" />
</joint>
<link
name="link4">
<inertial>
<origin
xyz="0.162516620557178 -0.213844847423599 3.38133504529381E-07"
rpy="0 0 0" />
<mass
value="12.1646007734167" />
<inertia
ixx="0.288208127188405"
ixy="0.00080326659836716"
ixz="8.72489392803044E-07"
iyy="0.107000706575729"
iyz="-1.48935044230573E-07"
izz="0.295147863994059" />
</inertial>
<visual>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="package://cpp01_urdf/meshes/link4.STL" />
</geometry>
<material
name="">
<color
rgba="0.792156862745098 0.819607843137255 0.933333333333333 1" />
</material>
</visual>
<collision>
<origin
xyz="0 0 0"
rpy="0 0 0" />
<geometry>
<mesh
filename="package://cpp01_urdf/meshes/link4.STL" />
</geometry>
</collision>
</link>
<joint
name="joint4"
type="revolute">
<origin
xyz="0 0.070837 0.99728"
rpy="0.82315 -1.5708 0" />
<parent
link="link3" />
<child
link="link4" />
<axis
xyz="1 0 0" />
<limit
lower="-3.14"
upper="3.14"
effort="100"
velocity="1" />
</joint>
</robot>
- Compile
colcon build --packages-select cpp01_urdf


- Update Terminal Environment
source install/setup.bash

- Run Launch
ros2 launch cpp01_urdf display.launch.py


- Add plugins and perform basic configuration in Rviz2

- Use joint_state_publisher_gui to adjust the angle of the joint


- Save the RViz2 configuration to the rviz folder.
First, click File on the menu bar of rviz2, and then select Save Config as

You can watch the demo video in this section's introduction.
6.6.Note (for reference only, ChatGPT's solution)
6.6.1.Coordinate System 1
The wheels should use continuous joints, and you should choose the coordinate systems yourself as much as possible. The requirement is that when viewing the car from behind, left is Y, forward is X, and up is Z. All coordinate systems are required.
You can have it automatically generate one for you first, and then modify the coordinate system, which will be simpler.



6.6.2.The coordinate system makes the car lie down
If your car is lying on its side in rviz2, then:
Haha, exactly, this kind of "the whole vehicle lying on its side" situation 90% of the time it's because the STL coordinate axis directions are wrong . Many CAD tools export STL models by default as:
-
Z-axis forward (e.g., SolidWorks: Z forward, Y up, use the right-handed coordinate system to derive the X-axis position; when viewed from behind the vehicle, it points left.)
-
Or Y-axis up (e.g., Blender)
And the coordinate system of ROS/URDF is:
-
X forward (direction of travel)
-
Y left (translation direction)
-
Z up (gravity direction)
6.6.3.The vehicle changes from going straight to rotating, and from rotating to going straight
That indicates there is a problem with your rotating shaft.
If moving forward results in a left turn, then the rotation axis of the left wheel is wrong; a sign is missing, and adding it will fix the issue.
For example, this 010, you should change it to 0-10

7.xacro
7.1.Scenario, Role, and Concept
Scene
In the process of building the robot model with the previous URDF file, there were several problems.
Problem 1: When designing the positions of joints, calculations must be performed according to certain rules. The rules are fixed, but in URDF this relies on manual calculation, which is inconvenient and prone to calculation errors. Moreover, when some parameters change, recalculation is required.
Problem 2: Some content in URDF is highly repetitive, such as the design and implementation of wheels. Different wheels differ only in some parameters, while their shape, color, and flip amount are identical. In practical applications, when building complex robot models, highly repetitive designs are even more likely to occur. According to general programming principles, repeated code should be considered for encapsulation and reuse, but in previous URDF files there were no related operations for this.
......
If we encounter similar problems in general programming languages, we can solve them by combining variables with functions. Correspondingly, ROS also provides a similar programming optimization scheme, which is called: Xacro (can be understood as urdf2.0).
Concept
Xacro is short for XML Macros. Xacro is an XML macro language and is programmable XML.
Xacro can declare variables and solve them through mathematical operations; it can use flow control to manage execution order; it can also encapsulate and reuse functionality through macros (which can be thought of as functions), thereby improving code reuse and program safety.
Function
Compared with a pure URDF implementation, it is possible to write robot model files that are safer, more concise, and more readable, and writing efficiency can also be improved.
7.2.Quick Start
First install xacro
#humble版本
sudo apt install ros-humble-xacro
#jazzy版本
sudo apt install ros-jazzy-xacro
1. Requirements
Optimize the implementation of the car chassis in 6.4.4 URDF Exercise using xacro. You need to encapsulate the vehicle parameters with variables, and use xacro macros to encapsulate the repeated wheel code, then call the macro to create four wheels (Note: Here, the basic usage of xacro is demonstrated; it is not necessary to generate a valid URDF).
2. Implementation
功能包cpp06_urdf的urdf/xacro目录下,新建xacro文件demo01_helloworld.urdf.xacro,并编辑文件,输入如下内容:
<robot name="mycar" xmlns:xacro="http://wiki.ros.org/xacro">
<xacro:property name="wheel_radius" value="0.025" />
<xacro:property name="wheel_length" value="0.02" />
<xacro:property name="PI" value="3.1415927" />
<xacro:macro name="wheel_func" params="wheel_name" >
<link name="${wheel_name}_wheel">
<visual>
<geometry>
<cylinder radius="${wheel_radius}" length="${wheel_length}" />
</geometry>
<origin xyz="0 0 0" rpy="${PI / 2} 0 0" />
<material name="wheel_color">
<color rgba="0 0 0 0.3" />
</material>
</visual>
</link>
</xacro:macro>
<xacro:wheel_func wheel_name="left_front"/>
<xacro:wheel_func wheel_name="left_back"/>
<xacro:wheel_func wheel_name="right_front"/>
<xacro:wheel_func wheel_name="right_back"/>
</robot>

Macro-like function
params resembles entry parameters
A sub-tag is similar to a function body.
In the terminal, enter the directory described in the current file, and input the following command:
cd src/cpp06_urdf/urdf/xacro/
xacro demo01_helloworld.urdf.xacro
#或者
ros2 run xacro xacro demo01_helloworld.urdf.xacro
The terminal will output the following content (the following content is pure URDF):
<?xml version="1.0" ?>
<robot name="mycar">
<link name="left_front_wheel">
<visual>
<geometry>
<cylinder length="0.02" radius="0.025"/>
</geometry>
<origin rpy="1.57079635 0 0" xyz="0 0 0"/>
<material name="wheel_color">
<color rgba="0 0 0 0.3"/>
</material>
</visual>
</link>
<link name="left_back_wheel">
<visual>
<geometry>
<cylinder length="0.02" radius="0.025"/>
</geometry>
<origin rpy="1.57079635 0 0" xyz="0 0 0"/>
<material name="wheel_color">
<color rgba="0 0 0 0.3"/>
</material>
</visual>
</link>
<link name="right_front_wheel">
<visual>
<geometry>
<cylinder length="0.02" radius="0.025"/>
</geometry>
<origin rpy="1.57079635 0 0" xyz="0 0 0"/>
<material name="wheel_color">
<color rgba="0 0 0 0.3"/>
</material>
</visual>
</link>
<link name="right_back_wheel">
<visual>
<geometry>
<cylinder length="0.02" radius="0.025"/>
</geometry>
<origin rpy="1.57079635 0 0" xyz="0 0 0"/>
<material name="wheel_color">
<color rgba="0 0 0 0.3"/>
</material>
</visual>
</link>
</robot>
Obviously, through xacro we have conveniently achieved code reuse.

7.2.1.Syntax
1. 简介

xacro 提供了可编程接口,类似于计算机语言,包括变量声明调用、函数声明与调用等语法实现。在使用 xacro 生成 urdf 时,根标签`robot`中**必须**包含命名空间声明:`xmlns:xacro="``http://wiki.ros.org/xacro``"`。
**变量**
变量用于封装 URDF 中的一些字段,比如: PAI 值,小车的尺寸,轮子半径 ....,变量的基本使用语法包括变量定义、变量调用、变量运算等。
1.1变量定义
语法格式:
```xml
<xacro:property name="变量名" value="变量值" />
```
示例:
```xml
<xacro:property name="PI" value="3.1416"/>
<xacro:property name="wheel_radius" value="0.025"/>
<xacro:property name="wheel_length" value="0.02"/>
```
1.2变量调用
语法格式:
```xml
${变量名}
```
示例:
```xml
<geometry>
<cylinder radius="${wheel_radius}" length="${wheel_length}" />
</geometry>
```
1.3变量运算
语法格式:
```xml
${数学表达式}
```
示例:
```xml
<origin xyz="0 0 0" rpy="${PI / 2} 0 0" />
```
```xml
<robot xmlns:xacro="http://www.ros.org/wiki/xacro" name="demo2_pro">
<xacro:property name="num1" value="10"/>
<xacro:property name="num2" value="20"/>
<car length="${num1}" width="${num2}"/>
<sum value="${num1 + num2}"/>
</robot>
```

**宏**
类似于函数实现,提高代码复用率,优化代码结构,提高安全性。宏的基本使用语法包括宏的定义与调用。
2.1宏定义
语法格式:
```xml
<xacro:macro name="宏名称" params="参数列表(多参数之间使用空格分隔)">
.....
参数调用格式: ${参数名}
</xacro:macro>
```
示例:
```xml
<xacro:macro name="wheel_func" params="wheel_name" >
<link name="${wheel_name}_wheel">
<visual>
<geometry>
<cylinder radius="${wheel_radius}" length="${wheel_length}" />
</geometry>
<origin xyz="0 0 0" rpy="${PI / 2} 0 0" />
<material name="wheel_color">
<color rgba="0 0 0 0.3" />
</material>
</visual>
</link>
</xacro:macro>
```
2.2宏调用
语法格式:
```xml
<xacro:宏名称 参数1=xxx 参数2=xxx/>
```
示例:
```xml
<xacro:wheel_func wheel_name="left_front"/>
<xacro:wheel_func wheel_name="left_back"/>
<xacro:wheel_func wheel_name="right_front"/>
<xacro:wheel_func wheel_name="right_back"/>
```
```xml
<robot xmlns:xacro="http://www.ros.org/wiki/xacro" name="demo3_func">
<xacro:macro name="get_sum" params="num1 num2">
<sum value="${num1 + num2}"/>
</xacro:macro>
<xacro:get_sum num1="20" num2="30"/>
<xacro:get_sum num1="70" num2="30"/>
</robot>
```

**文件**
A robot is composed of multiple components, and different components may be encapsulated as separate xacro files. Finally, the different files are integrated and combined into a complete robot, which can be achieved using file inclusion.
Syntax format:
<xacro:include filename="其他xacro文件" />
Example:
<robot name="car" xmlns:xacro="http://wiki.ros.org/xacro">
<xacro:include filename="car_base.xacro" />
<xacro:include filename="car_camera.xacro" />
<xacro:include filename="car_laser.xacro" />
</robot>
<robot xmlns:xacro="http://www.ros.org/wiki/xacro" name="demo4_include">
<xacro:include filename="demo02_base_pro.urdf.xacro"/>
<xacro:include filename="demo03_base_func.urdf.xacro"/>
</robot>

However, this is not recommended; it is recommended that the parent xacro and child xacro use the same name.
7.3.Practice
7.3.1.Framework
1. Requirements
Use xacro to create a four-wheeled robot model. The chassis of this model can refer to the implementation in 6.4.4 URDF Exercise , and a camera and LiDAR are added on top of the chassis. The size parameters and installation positions of the camera and LiDAR can be customized.

2. Implementation analysis
The robot model in the requirements is composed of three parts: the chassis, camera, and radar. Therefore, each part can be encapsulated into an xacro file, and finally these xacro files can be included and organized into a complete robot model.
3. Implementation
功能包cpp06_urdf的urdf/xacro目录下,新建多个xacro文件,分别为:
-
car.urdf.xacro: Used to include the xacro files corresponding to different robot components;
-
car_base.urdf.xacro: The xacro file describing the robot chassis;
-
car_camera.urdf.xacro: The xacro file describing the camera;
-
car_laser.urdf.xacro: The xacro file describing the lidar.
Edit the car.urdf.xacro file and enter the following content:
<robot name="car" xmlns:xacro="http://wiki.ros.org/xacro">
<xacro:include filename="car_base.urdf.xacro"/>
<xacro:include filename="car_camera.urdf.xacro"/>
<xacro:include filename="car_laser.urdf.xacro"/>
</robot>
7.3.2.Car body
Edit the car_base.urdf.xacro file and enter the following content:
<robot xmlns:xacro="http://wiki.ros.org/xacro">
<xacro:property name="PI" value="3.1416"/>
<xacro:property name="base_link_x" value="0.2"/>
<xacro:property name="base_link_y" value="0.12"/>
<xacro:property name="base_link_z" value="0.07"/>
<xacro:property name="distance" value="0.015"/>
<xacro:property name="wheel_radius" value="0.025"/>
<xacro:property name="wheel_length" value="0.02"/>
<material name="yellow">
<color rgba="0.7 0.7 0 0.8" />
</material>
<material name="red">
<color rgba="0.8 0.1 0.1 0.8" />
</material>
<material name="gray">
<color rgba="0.2 0.2 0.2 0.95" />
</material>
<link name="base_footprint">
<visual>
<geometry>
<sphere radius="0.001"/>
</geometry>
</visual>
</link>
<link name="base_link">
<visual>
<geometry>
<box size="${base_link_x} ${base_link_y} ${base_link_z}" />
</geometry>
<origin xyz="0 0 0" rpy="0 0 0" />
<material name="yellow"/>
</visual>
</link>
<joint name="baselink2basefootprint" type="fixed">
<parent link="base_footprint"/>
<child link="base_link"/>
<origin xyz="0.0 0.0 ${distance + base_link_z / 2}"/>
</joint>
<xacro:macro name="wheel_func" params="wheel_name is_front is_left" >
<link name="${wheel_name}_wheel">
<visual>
<geometry>
<cylinder radius="${wheel_radius}" length="${wheel_length}" />
</geometry>
<origin xyz="0 0 0" rpy="${PI / 2} 0 0" />
<material name="gray"/>
</visual>
</link>
<joint name="${wheel_name}2baselink" type="continuous">
<parent link="base_link" />
<child link="${wheel_name}_wheel" />
<origin xyz="${(base_link_x / 2 - wheel_radius) * is_front} ${base_link_y / 2 * is_left} ${(base_link_z / 2 + distance - wheel_radius) * -1}" rpy="0 0 0" />
<axis xyz="0 1 0" />
</joint>
</xacro:macro>
<xacro:wheel_func wheel_name="left_front" is_front="1" is_left="1" />
<xacro:wheel_func wheel_name="left_back" is_front="-1" is_left="1" />
<xacro:wheel_func wheel_name="right_front" is_front="1" is_left="-1" />
<xacro:wheel_func wheel_name="right_back" is_front="-1" is_left="-1" />
</robot>
7.3.3.Add Camera
Edit the car_camera.urdf.xacro file and enter the following content:
<robot xmlns:xacro="http://wiki.ros.org/xacro">
<xacro:property name="camera_x" value="0.012" />
<xacro:property name="camera_y" value="0.05" />
<xacro:property name="camera_z" value="0.01" />
<xacro:property name="camera_joint_x" value="${base_link_x / 2 - camera_x / 2}" />
<xacro:property name="camera_joint_y" value="0.0" />
<xacro:property name="camera_joint_z" value="${base_link_z / 2 + camera_z / 2}" />
<link name="camera">
<visual>
<geometry>
<box size="${camera_x} ${camera_y} ${camera_z}" />
</geometry>
<origin xyz="0.0 0.0 0.0" rpy="0.0 0.0 0.0" />
<material name="red" />
</visual>
</link>
<joint name="camera2baselink" type="fixed">
<parent link="base_link" />
<child link="camera" />
<origin xyz="${camera_joint_x} ${camera_joint_y} ${camera_joint_z}" />
</joint>
</robot>
7.3.4.Add Radar
Edit the car_laser.urdf.xacro file, and enter the following content:
<robot xmlns:xacro="http://wiki.ros.org/xacro">
<material name="blue">
<color rgba="0.0 0.0 0.4 0.95" />
</material>
<xacro:property name="laser_length" value="0.03" />
<xacro:property name="laser_radius" value="0.03" />
<xacro:property name="laser_joint_x" value="0.0" />
<xacro:property name="laser_joint_y" value="0.0" />
<xacro:property name="laser_joint_z" value="${base_link_z / 2 + laser_length / 2}" />
<link name="laser">
<visual>
<geometry>
<cylinder radius="${laser_radius}" length="${laser_length}" />
</geometry>
<origin xyz="0.0 0.0 0.0" rpy="0.0 0.0 0.0" />
<material name="blue" />
</visual>
</link>
<joint name="laser2baselink" type="fixed">
<parent link="base_link" />
<child link="laser" />
<origin xyz="${laser_joint_x} ${laser_joint_y} ${laser_joint_z}" />
</joint>
</robot>
7.3.5.Execute
After compilation, run the following command in the workspace terminal:
# ROS Humble
ros2 launch cpp06_urdf display.launch.py model:=ros2 pkg prefix --share cpp06_urdf/urdf/xacro/car.urdf.xacro
#ROS Jazzy
colcon build
source install/setup.bash
ros2 run xacro xacro $(ros2 pkg prefix cpp06_urdf)/share/cpp06_urdf/urdf/xacro/car.urdf.xacro -o ./src/cpp06_urdf/urdf/urdf/car.urdf
ros2 launch cpp06_urdf display.launch.py model:=./src/cpp06_urdf/urdf/urdf/car.urdf
After the command is executed, a robot model similar to the requirement can be displayed in rviz2.

8.Summary

Currently, it is just an empty shell; the LiDAR, cameras, and wheels are all still empty shells. Only in the advanced exercises can they become functional.