Using xacro to simplify URDF
xacro is a language defined in ROS to be used to:
- simplify URDF; make URDF cleaner
- make it easier to change the URDF’s parameters
- create a modular URDF
xacro:property
Use case 1:
Using xacro:property, we can use constants as variables so that it is easier to change them or to avoid redundancy (repetition) in their use.
Step 1: Declare the xacro:property. The syntax of the declaration is the following:
1 |
<xacro:property name="variable_name" value="variable_value" /> |
Step 2: Use it. We can use the defined “xacro:property” by using ${ } construct.
Example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<xacro:property name="width" value="0.2" /> <xacro:property name="bodylen" value="0.6" /> <link name="base_link"> <visual> <geometry> <cylinder radius="${width}" length="${bodylen}"/> </geometry> <material name="blue"/> </visual> <collision> <geometry> <cylinder radius="${width}" length="${bodylen}"/> </geometry> </collision> </link> |
Use case 2:
We can also use xacro:property for naming. First we declare the property and afterwards we can use it by using ${ } construct.
Example:
1 2 |
<xacro:property name=”robotname” value=”marvin” /> <link name=”${robotname}_base_link” /> |
This will generate:
1 |
<link name=”marvin_base_link” /> |
Use case 3:
We can also use xacro:property to perform some basic mathematics operations such as +, -, *, /, sin, cos, and parentheses.
Example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
<xacro:property name="width" value="0.2" /> <xacro:property name="bodylen" value="0.6" /> <xacro:property name="reflect" value="1" /> <link name="base_link"> <visual> <geometry> <cylinder radius="${bodylen/2}" length="${bodylen}"/> </geometry> <material name="blue"/> <origin xyz="${reflect*(width+.02)} 0 0.25" /> </visual> <collision> <geometry> <cylinder radius="${bodylen/2}" length="${bodylen}"/> </geometry> </collision> </link> |
In the example above, we perform two maths:
1 2 |
${bodylen/2} ${reflect*(width+.02)} |
xacro:macro
Using xacro:macro, we can provide modularity in the robot description.
Simple xacro:macro
Step 1: Declare the xacro:macro
1 2 3 4 5 |
<xacro:macro name="macro_name"> ... [macro content] ... </xacro:macro> |
Step 2: Use it, by calling the xacro:macro.
1 |
<xacro:macro_name /> |
For example, first we declare the xacro:macro:
1 2 3 4 5 |
<xacro:macro name="default_color"> <material name="blue"> <color rgba="0 0 0.8 1"/> </material> </xacro:macro> |
Subsequently, we can call the xacro:macro by writing:
1 |
<xacro:default_color /> |
Parametrized xacro:macro
Step 1: Declare the xacro:macro
1 2 3 4 5 |
<xacro:macro name="macro_name" params="my_parameter_1 ... my_parameter_n"> ... [macro content] ... </xacro:macro> |
Step 2: Use it, by calling the xacro:macro and specifying the parameter value:
1 |
<xacro:macro_name my_parameter_1="value" ... my_parameter_n="value"/> |
For example, first we declare we declare a xacro:macro with mass being parametrized:
1 2 3 4 5 6 7 8 |
<xacro:macro name="default_inertial" params="mass"> <inertial> <mass value="${mass}" /> <inertia ixx="1.0" ixy="0.0" ixz="0.0" iyy="1.0" iyz="0.0" izz="1.0" /> </inertial> </xacro:macro> |
Subsequently, we can use the xacro:macro by calling it and specifying a certain value of the mass:
1 |
<xacro:default_inertial mass="10"/> |
Using xacro:macro to construct a modular robot description
We need to have the main part (which basically uses/integrates the xacro:macro(s)) and one or more child parts. The child parts may consist of URDF and xacro files.
For example, we have a URDF file called “base_link”. We want to connect another module to the “base_link”. The module, in this case, can be conveniently written as a xacro file.
Let’s say we have the URDF file namely “my_urdf_file.urdf” which looks like this:
1 2 3 4 5 6 |
<?xml version="1.0"?> <robot name="my_urdf"> <link name="base_link"> ... </link> </robot> |
The xacro child part is written in a file called “my_xacro_child_file.xacro” which looks like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<?xml version="1.0"?> <robot xmlns:xacro="http://ros.org/wiki/xacro" name="name_of_child_part"> <xacro:macro name="my_xacro_child_part" params="connected_to"> <link name="additional_link"> ... </link> <joint name="connection_joint" type="revolute"> <parent link="${connected_to}"/> <child link="additional_link"/> ... </joint> ... </xacro:macro> </robot> |
We can see that the connection between the base_link and the additional_link is performed in the xacro_child part above.
The main part is written in a file called “main_file.xacro” which looks like this:
1 2 3 4 5 6 7 8 |
<?xml version="1.0"?> <robot xmlns:xacro="http://ros.org/wiki/xacro" name="name_of_main_part"> <!-- URDF child_part --> <xacro:include filename="$(find my_robot_description)/urdf/my_urdf_file.urdf" /> <!-- xacro child part --> <xacro:include filename="$(find my_robot_description)/urdf/my_xacro_child_file.xacro" /> <xacro:my_xacro_child_part connected_to="base_link"/> </robot> |
We can see that both the main and child xacro files should declare the xacro inside the <robot> tags.
For the main xacro, we can see that it consists of two parts:
- xacro:include –> this is to include all the files used.
- xacro:name_of_child_xacro –> this is to call the xacro_child_part.
In building such modularity, it is important to track the order of the links and joints, i.e. to pay attention to which one is the base part and which one is the child part connected to the base part, etc.
Running xacro:macro
To run the “main_file.xacro” in the terminal, use the following command:
1 |
rosrun xacro xacro ./src/my_robot_description/urdf/main_file.xacro > ./src/my_robot_description/urdf/main_file.urdf |
To run the “main_file.xacro” inside a launch file (which also runs “robot_state_publisher”, “joint_state_publisher”, and “rviz” nodes), we write a launch file that looks like this:
1 2 3 4 5 6 7 8 9 |
<launch> <arg name="gui" default="true"/> <param name="robot_description" command="$(find xacro)/xacro --inorder '$(find my_robot_description)/urdf/main_file.xacro'" /> <node name="robot_state_publisher" pkg="robot_state_publisher" type="robot_state_publisher"/> <node name="joint_state_publisher" pkg="joint_state_publisher" type="joint_state_publisher"> <param name="use_gui" value="$(arg gui)"/> </node> <node name="rviz" pkg="rviz" type="rviz" if="$(arg gui)"/> </launch> |
Notice: It is quite common to name a xacro file as “my_xacro.urdf.xacro”. This does not matter since only what comes after the last dot is considered as the extension of the file. This quite common practice is probably to indicate that the xacro file contains URDF.