This is an introductory chapter, explaining macros we are going to use.
In the URDF model that we are going to create, all wheels share shape and material properties. Links (URDF term for "objects") of the wheels have a lot in common, as well as joints (URDF term for things that connect objects together).
We can move repeating properties to a separate place (macro) to make our code better structured.
A XACRO file is an XML file. As its name implies, xacro is an Xml Macro. The xacro utility program runs all of the macros and outputs the result, producing a single file, in a way similar to what a preprocessor does to the include files and macros. Typical usage looks something like this:
$ xacro model.xacro > model.urdf
You can also automatically generate the urdf in a launch file.
path_to_urdf = get_package_share_path('pr2_description') / 'robots' / 'pr2.urdf.xacro' robot_state_publisher_node = launch_ros.actions.Node( package='robot_state_publisher', executable='robot_state_publisher', parameters=[{ 'robot_description': ParameterValue( Command(['xacro ', str(path_to_urdf)]), value_type=str ) }] )
At the top of the URDF file, you must specify a namespace in order for the file to parse properly. For example, these are the first two lines of a valid xacro file:
<?xml version="1.0"?> <robot xmlns:xacro="http://www.ros.org/wiki/xacro" name="bot">
A minimal XACRO file looks like this:
<?xml version="1.0"?> <robot xmlns:xacro="http://www.ros.org/wiki/xacro" name="bot"> <!-- .... --> </robot>
Let's define some variables (constants) in XACRO. These are
defined as
<?xml version="1.0"?> <robot xmlns:xacro="http://www.ros.org/wiki/xacro" name="bot"> <xacro:property name="wheel_radius" value="0.04" /> <xacro:property name="wheel_length" value="0.05" /> <xacro:property name="wheel_color_name" value="red" /> <xacro:property name="wheel_color_rgb" value="1 0 0 1" /> </robot>
In addition to the variables, we can introduce Macros (using these variables):
<xacro:macro name="wheel" params="wheel_name"> <link name="${wheel_name}"> <visual> <geometry> <cylinder radius="${wheel_radius}" length="${wheel_length}" /> </geometry> <material name="${wheel_color_name}"> <color rgba="${wheel_color_rgb}"/> </material> </visual> </link> </xacro:macro>
It is possible to use complex expressions using the four basic operations (+,-,*,/), the unary minus, and parenthesis. Examples:
<cylinder radius="${wheeldiam/2}" length="0.1"/> <origin xyz="${reflect*(width+.02)} 0 0.25" />
Macros are templates: to instantiate them, we use template name and necessary attributes:
<xacro:wheel name="right_wheel_frontside" />
XACRO files can be manually rendered via the command line.
$ xacro src/fwd_bot/urdf/bot.xacro
Here is a sample bot.xacro file:
<?xml version="1.0"?> <robot xmlns:xacro="http://www.ros.org/wiki/xacro" name="bot"> <xacro:property name="wheel_radius" value="0.04" /> <xacro:property name="wheel_length" value="0.05" /> <xacro:property name="wheel_color_name" value="red" /> <xacro:property name="wheel_color_rgb" value="1 0 0 1" /> <xacro:macro name="wheel" params="name"> <link name="${name}"> <visual> <geometry> <cylinder radius="${wheel_radius}" length="${wheel_length}" /> </geometry> <material name="${wheel_color_name}"> <color rgba="${wheel_color_rgb}"/> </material> </visual> </link> </xacro:macro> <xacro:macro name="box_link" params="name size color color_rgb" > <link name="${name}"> <visual> <geometry> <box size="${size}"/> </geometry> <material name="${color}"> <color rgba="${color_rgb}"/> </material> </visual> </link> </xacro:macro> <xacro:macro name="static_joint" params="name parent child"> <joint name="${name}" type="continuous"> <parent link="${parent}" /> <child link="${child}" /> <origin xyz="0 0 0" rpy="0 0 0"/> </joint> </xacro:macro> <xacro:macro name="wheel_joint" params="name parent child xyz "> <joint name="${name}" type="continuous"> <parent link="${parent}" /> <child link="${child}" /> <origin xyz="${xyz}" rpy="1.570796 0 0"/> </joint> </xacro:macro> <xacro:box_link name="base_link" size="0 0 0" color="red" color_rgb="1 0 0 1" /> <xacro:box_link name="torso" size="0.6 0.3 0.1" color="green" color_rgb="0 1 0 1" /> <xacro:wheel name="right_wheel_frontside" /> <xacro:wheel_joint name="base_link_right_wheel_frontside" parent="base_link" child="right_wheel_frontside" xyz="0.2 -0.2 -0.05" /> <xacro:wheel name="right_wheel_backside" /> <xacro:wheel_joint name="base_link_right_wheel_backside" parent="base_link" child="right_wheel_backside" xyz="-0.2 -0.2 -0.05" /> <xacro:wheel name="left_wheel_frontside" /> <xacro:wheel_joint name="base_link_left_wheel_frontside" parent="base_link" child="left_wheel_frontside" xyz="0.2 0.2 -0.05" /> <xacro:wheel name="left_wheel_backside" /> <xacro:wheel_joint name="base_link_left_wheel_backside" parent="base_link" child="left_wheel_backside" xyz="-0.2 0.2 -0.05" /> </robot>
Additionally, we can store macros in a separate bot.xacro file, and use bot.urdf.xacro as a main file:
bot.xacro
<?xml version="1.0"?> <robot xmlns:xacro="http://www.ros.org/wiki/xacro"> <xacro:property name="wheel_radius" value="0.04" /> <xacro:property name="wheel_length" value="0.05" /> <xacro:property name="wheel_color_name" value="red" /> <xacro:property name="wheel_color_rgb" value="1 0 0 1" /> <xacro:macro name="wheel" params="name"> <link name="${name}"> <visual> <geometry> <cylinder radius="${wheel_radius}" length="${wheel_length}" /> </geometry> <material name="${wheel_color_name}"> <color rgba="${wheel_color_rgb}"/> </material> </visual> </link> </xacro:macro> <xacro:macro name="box_link" params="name size color color_rgb" > <link name="${name}"> <visual> <geometry> <box size="${size}"/> </geometry> <material name="${color}"> <color rgba="${color_rgb}"/> </material> </visual> </link> </xacro:macro> <xacro:macro name="static_joint" params="name parent child"> <joint name="${name}" type="continuous"> <parent link="${parent}" /> <child link="${child}" /> <origin xyz="0 0 0" rpy="0 0 0"/> </joint> </xacro:macro> <xacro:macro name="wheel_joint" params="name parent child xyz "> <joint name="${name}" type="continuous"> <parent link="${parent}" /> <child link="${child}" /> <origin xyz="${xyz}" rpy="1.570796 0 0"/> </joint> </xacro:macro> </robot>
bot.urdf.xacro
<robot xmlns:xacro="http://www.ros.org/wiki/xacro" name="bot"> <xacro:include filename="bot.xacro" /> <xacro:box_link name="base_link" size="0 0 0" color="red" color_rgb="1 0 0 1" /> <xacro:box_link name="torso" size="0.6 0.3 0.1" color="green" color_rgb="0 1 0 1" /> <xacro:wheel name="right_wheel_frontside" /> <xacro:wheel_joint name="base_link_right_wheel_frontside" parent="base_link" child="right_wheel_frontside" xyz="0.2 -0.2 -0.05" /> <xacro:wheel name="right_wheel_backside" /> <xacro:wheel_joint name="base_link_right_wheel_backside" parent="base_link" child="right_wheel_backside" xyz="-0.2 -0.2 -0.05" /> <xacro:wheel name="left_wheel_frontside" /> <xacro:wheel_joint name="base_link_left_wheel_frontside" parent="base_link" child="left_wheel_frontside" xyz="0.2 0.2 -0.05" /> <xacro:wheel name="left_wheel_backside" /> <xacro:wheel_joint name="base_link_left_wheel_backside" parent="base_link" child="left_wheel_backside" xyz="-0.2 0.2 -0.05" /> </robot>
I am going to use the first approach (everything in one file) for now, as our robot is not that complex.