ROS URDF机器人建模
创建URDF模型
(1)创建机器人描述功能包
catkin_create_pkg mrobot_description urdf xacro
(2)mrobot_description下创建urdf, launch文件夹,在urdf文件夹内放置机器人模型的
URDF或xacro文件。launch文件夹内放置相关启动文件。
URDF例子mrobot_chassis.urdf:
<?xml version="1.0" ?>
<robot name="mrobot_chassis">
<link name="base_link">
<visual>
<origin xyz=" 0 0 0" rpy="0 0 0" />
<geometry>
<cylinder length="0.005" radius="0.13"/>
</geometry>
<material name="yellow">
<color rgba="1 0.4 0 1"/>
</material>
</visual>
</link>
<joint name="base_left_motor_joint" type="fixed">
<origin xyz="-0.055 0.075 0" rpy="0 0 0" />
<parent link="base_link"/>
<child link="left_motor" />
</joint>
<link name="left_motor">
<visual>
<origin xyz="0 0 0" rpy="1.5707 0 0" />
<geometry>
<cylinder radius="0.02" length = "0.08"/>
</geometry>
<material name="gray">
<color rgba="0.75 0.75 0.75 1"/>
</material>
</visual>
</link>
<joint name="left_wheel_joint" type="continuous">
<origin xyz="0 0.0485 0" rpy="0 0 0"/>
<parent link="left_motor"/>
<child link="left_wheel_link"/>
<axis xyz="0 1 0"/>
</joint>
<link name="left_wheel_link">
<visual>
<origin xyz="0 0 0" rpy="1.5707 0 0" />
<geometry>
<cylinder radius="0.033" length = "0.017"/>
</geometry>
<material name="white">
<color rgba="1 1 1 0.9"/>
</material>
</visual>
</link>
<joint name="base_right_motor_joint" type="fixed">
<origin xyz="-0.055 -0.075 0" rpy="0 0 0" />
<parent link="base_link"/>
<child link="right_motor" />
</joint>
<link name="right_motor">
<visual>
<origin xyz="0 0 0" rpy="1.5707 0 0" />
<geometry>
<cylinder radius="0.02" length = "0.08" />
</geometry>
<material name="gray">
<color rgba="0.75 0.75 0.75 1"/>
</material>
</visual>
</link>
<joint name="right_wheel_joint" type="continuous">
<origin xyz="0 -0.0485 0" rpy="0 0 0"/>
<parent link="right_motor"/>
<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.033" length = "0.017"/>
</geometry>
<material name="white">
<color rgba="1 1 1 0.9"/>
</material>
</visual>
</link>
<joint name="front_caster_joint" type="fixed">
<origin xyz="0.1135 0 -0.0165" rpy="0 0 0"/>
<parent link="base_link"/>
<child link="front_caster_link"/>
</joint>
<link name="front_caster_link">
<visual>
<origin xyz="0 0 0" rpy="1.5707 0 0"/>
<geometry>
<sphere radius="0.0165" />
</geometry>
<material name="black">
<color rgba="0 0 0 0.95"/>
</material>
</visual>
</link>
</robot>
(3)urdf提供一些命令行工具,用于检测模型文件。安装命令
sudo apt-get install liburdfdom-tools
使用check_urdf命令对mrobot_chassis.urdf文件进行检查, 终端会显示如下所示信息。
$ check_urdf mrobot_chassis.urdf
robot name is: mrobot_chassis
---------- Successfully Parsed XML ---------------
root Link: base_link has 3 child(ren)
child(1): left_motor
child(1): left_wheel_link
child(2): right_motor
child(1): right_wheel_link
child(3): front_caster_link
使用urdf_to_graphiz命令查看urdf模型整体结构,会生成如下一个pdf文件。
$ urdf_to_graphiz mrobot_chassis.urdf
Created file mrobot_chassis.gv
Created file mrobot_chassis.pdf
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eofvUUhi-1605342281577)(/mnt/hgfs/ROSNote/docs/image/mrobot-chassis-urdf整体结构.png)]
在rviz中显示URDF模型
(1)在mrobot_description功能包的launch文件夹中创建用于显示mrobot_chassis模型的launch文件display_mrobot_chassis_urdf.launch,内容如下
<launch>
<param name="robot_description" textfile="$(find mrobot_description)/urdf/mrobot_chassis.urdf" />
<!-- 设置GUI参数,显示关节控制插件 -->
<param name="use_gui" value="true"/>
<!-- 运行joint_state_publisher节点,发布机器人的关节状态 -->
<node name="joint_state_publisher" pkg="joint_state_publisher" type="joint_state_publisher" />
<!-- 运行robot_state_publisher节点,发布tf -->
<node name="robot_state_publisher" pkg="robot_state_publisher" type="state_publisher" />
<!-- 运行rviz可视化界面 -->
<node name="rviz" pkg="rviz" type="rviz" args="-d $(find mrobot_description)/config/mrobot_urdf.rviz" required="true" />
</launch>
执行启动命令,启动如下窗口,但是显示界面了出现Global Status:Error, Fixed Frame错误。
需要进行一些设置才能正常显示模型,把FixedFrame下拉选项中选择base_link, 点击左下角add按钮,增加一个RobotModel。
roslaunch mrobot_description display_mrobot_chassis_urdf.launch
最后显示的模型如下图所示
改进URDF模型
添加物理和碰撞属性
在base_link中加入和标签,描述机器人的物理属性和碰撞属性。
<?xml version="1.0" ?>
<robot name="mrobot_chassis">
<link name="base_link">
<inertial>
<mass value="2" />
<origin xyz="0 0 0" />
<inertia ixx="0.01" ixy="0.0" ixz="0.0"
iyy="0.01" iyz="0.0" izz="0.5" />
</inertial>
<visual>
<origin xyz=" 0 0 0" rpy="0 0 0" />
<geometry>
<cylinder length="0.005" radius="0.13"/>
</geometry>
<material name="yellow">
<color rgba="1 0.4 0 1"/>
</material>
</visual>
<collision>
<origin xyz=" 0 0 0" rpy="0 0 0" />
<geometry>
<cylinder length="0.005" radius="0.13"/>
</geometry>
</collision>
</link>
使用xacro优化URDF
URDF文件不支持代码复用的特性。xacro是URDF的升级版,后缀名为xacro。具有以下几点突出优势
(1)精简模型代码。可以通过宏创建的方式定义常量或复用代码。
(2)提供可编程接口。支持一些可编程接口,如常量、变量、数学公式、条件语句等
在模型标签中需要加入xacro声明:
<robot xmlns:xacro="http://www.ros.org/wiki/xacro">
常量定义及调用
定义常量方便改动机器人模型的参数。
定义
<xacro:property name="M_PI" value="3.14159" />
调用
<origin xyz="0 0 0" rpy="${M_PI/2} 0 0" />
调用数学公式
在"${}"语句中,不仅可以调用常量,也可以使用一些常用的数学公式,如加,减,除,负号,括号等,例如:
<origin xyz="0 ${(motor_length+wheel_length)/2} 0" rpy="0 0 0" />
使用宏定义
xacro文件可以使用宏定义来声明重复使用的代码模块,而且可以包含输入参数,类似编程中的函数概念。
<!-- 定义MRobot本体的宏 -->
<xacro:macro name="mrobot_standoff_2in" params="parent number x_loc y_loc z_loc">
<joint name="standoff_2in_${number}_joint" type="fixed">
<origin xyz="${x_loc} ${y_loc} ${z_loc}" rpy="0 0 0" />
<parent link="${parent}"/>
<child link="standoff_2in_${number}_link" />
</joint>
<link name="standoff_2in_${number}_link">
<inertial>
<mass value="0.001" />
<origin xyz="0 0 0" />
<inertia ixx="0.0001" ixy="0.0" ixz="0.0"
iyy="0.0001" iyz="0.0"
izz="0.0001" />
</inertial>
<visual>
<origin xyz=" 0 0 0 " rpy="0 0 0" />
<geometry>
<box size="0.01 0.01 0.07" />
</geometry>
<material name="black">
<color rgba="0.16 0.17 0.15 0.9"/>
</material>
</visual>
<collision>
<origin xyz="0.0 0.0 0.0" rpy="0 0 0" />
<geometry>
<box size="0.01 0.01 0.07" />
</geometry>
</collision>
</link>
</xacro:macro>
如上定义了一个宏mrobot_standoff_2in,包含五个输入参数。调用该宏模块时,使用如下语句调用,设置输入参数即可:
<mrobot_standoff_2in parent="base_link" number="4" x_loc="${standoff_x/2}" y_loc="${standoff_y}" z_loc="${plate_height/2}"/>
xacro文件引用
改进后的机器人模型文件是mrobot.urdf.xacro, 详细内容如下
<?xml version="1.0"?>
<robot name="mrobot" xmlns:xacro="http://www.ros.org/wiki/xacro">
<xacro:include filename="$(find mrobot_description)/urdf/mrobot_body.urdf.xacro" />
<!-- MRobot机器人平台 -->
<mrobot_body/>
</robot>
标签之间只有两行代码。
<xacro:include filename="$(find mrobot_description)/urdf/mrobot_body.urdf.xacro" />
第一行代码描述了该xacro文件所包含的其他xacro文件。声明关系后就可以调用被包含文件中的模块了。
<mrobot_body/>
第二行代码就调用了被包含文件mrobot_body.urdf.xacro中的机器人模型的一个宏定义mrbot_body,该宏定义了整个机器人本体模型。
mrobot_body.urdf.xacro的内容如下
<?xml version="1.0"?>
<robot xmlns:xacro="http://www.ros.org/wiki/xacro">
<xacro:property name="M_PI" value="3.14159"/>
<xacro:property name="wheel_radius" value="0.033"/>
<xacro:property name="wheel_length" value="0.017"/>
<xacro:property name="base_link_radius" value="0.13"/>
<xacro:property name="base_link_length" value="0.005"/>
<xacro:property name="motor_radius" value="0.02"/>
<xacro:property name="motor_length" value="0.08"/>
<xacro:property name="motor_x" value="-0.055"/>
<xacro:property name="motor_y" value="0.075"/>
<xacro:property name="plate_height" value="0.07"/>
<xacro:property name="standoff_x" value="0.12"/>
<xacro:property name="standoff_y" value="0.10"/>
<!-- 定义MRobot本体的宏 -->
<xacro:macro name="mrobot_standoff_2in" params="parent number x_loc y_loc z_loc">
<joint name="standoff_2in_${number}_joint" type="fixed">
<origin xyz="${x_loc} ${y_loc} ${z_loc}" rpy="0 0 0" />
<parent link="${parent}"/>
<child link="standoff_2in_${number}_link" />
</joint>
<link name="standoff_2in_${number}_link">
<inertial>
<mass value="0.001" />
<origin xyz="0 0 0" />
<inertia ixx="0.0001" ixy="0.0" ixz="0.0"
iyy="0.0001" iyz="0.0"
izz="0.0001" />
</inertial>
<visual>
<origin xyz=" 0 0 0 " rpy="0 0 0" />
<geometry>
<box size="0.01 0.01 0.07" />
</geometry>
<material name="black">
<color rgba="0.16 0.17 0.15 0.9"/>
</material>
</visual>
<collision>
<origin xyz="0.0 0.0 0.0" rpy="0 0 0" />
<geometry>
<box size="0.01 0.01 0.07" />
</geometry>
</collision>
</link>
</xacro:macro>
<xacro:macro name="mrobot_body">
<material name="Green">
<color rgba="0.0 0.8 0.0 1.0"/>
</material>
<material name="yellow">
<color rgba="1 0.4 0 1"/>
</material>
<material name="black">
<color rgba="0 0 0 0.95"/>
</material>
<material name="gray">
<color rgba="0.75 0.75 0.75 1"/>
</material>
<link name="base_footprint">
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<box size="0.001 0.001 0.001" />
</geometry>
</visual>
</link>
<link name="base_link">
<inertial>
<mass value="2" />
<origin xyz="0 0 0.0" />
<inertia ixx="0.01" ixy="0.0" ixz="0.0"
iyy="0.01" iyz="0.0" izz="0.5" />
</inertial>
<visual>
<origin xyz=" 0 0 0" rpy="0 0 0" />
<geometry>
<cylinder length="${base_link_length}" radius="${base_link_radius}"/>
</geometry>
<material name="yellow" />
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<cylinder length="${base_link_length}" radius="${base_link_radius}"/>
</geometry>
</collision>
</link>
<joint name="base_footprint_joint" type="fixed">
<origin xyz="0 0 ${wheel_radius}" rpy="0 0 0" />
<parent link="base_footprint"/>
<child link="base_link" />
</joint>
<link name="left_motor">
<inertial>
<origin xyz="0.0 0 0"/>
<mass value="0.1" />
<inertia ixx="0.001" ixy="0.0" ixz="0.0"
iyy="0.001" iyz="0.0" izz="0.001" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="${M_PI/2} 0 0" />
<geometry>
<cylinder radius="${motor_radius}" length = "${motor_length}"/>
</geometry>
<material name="gray" />
</visual>
<collision>
<origin xyz="0 0 0" rpy="${M_PI/2} 0 0" />
<geometry>
<cylinder radius="${motor_radius}" length = "${motor_length}"/>
</geometry>
</collision>
</link>
<joint name="base_left_motor_joint" type="fixed">
<origin xyz="${motor_x} ${motor_y} 0" rpy="0 0 0" />
<parent link="base_link"/>
<child link="left_motor" />
</joint>
<link name="left_wheel_link">
<inertial>
<origin xyz="0 0 0"/>
<mass value="0.01" />
<inertia ixx="0.001" ixy="0.0" ixz="0.0"
iyy="0.001" iyz="0.0" izz="0.001" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="${M_PI/2} 0 0" />
<geometry>
<cylinder radius="${wheel_radius}" length = "${wheel_length}"/>
</geometry>
<material name="white">
<color rgba="1 1 1 0.9"/>
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="${M_PI/2} 0 0" />
<geometry>
<cylinder radius="${wheel_radius}" length = "${wheel_length}"/>
</geometry>
</collision>
</link>
<joint name="left_wheel_joint" type="continuous">
<origin xyz="0 ${(motor_length+wheel_length)/2} 0" rpy="0 0 0"/>
<parent link="left_motor"/>
<child link="left_wheel_link"/>
<axis xyz="0 1 0"/>
</joint>
<link name="right_motor">
<inertial>
<origin xyz="0.0 0 0"/>
<mass value="0.1" />
<inertia ixx="0.001" ixy="0.0" ixz="0.0"
iyy="0.001" iyz="0.0" izz="0.001" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="${M_PI/2} 0 0" />
<geometry>
<cylinder radius="${motor_radius}" length = "${motor_length}" />
</geometry>
<material name="gray" />
</visual>
<collision>
<origin xyz="0 0 0" rpy="${M_PI/2} 0 0" />
<geometry>
<cylinder radius="${motor_radius}" length = "${motor_length}"/>
</geometry>
</collision>
</link>
<joint name="base_right_motor_joint" type="fixed">
<origin xyz="${motor_x} -${motor_y} 0" rpy="0 0 0" />
<parent link="base_link"/>
<child link="right_motor" />
</joint>
<link name="right_wheel_link">
<inertial>
<origin xyz="0 0 0"/>
<mass value="0.01" />
<inertia ixx="0.001" ixy="0.0" ixz="0.0"
iyy="0.001" iyz="0.0" izz="0.001" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="${M_PI/2} 0 0" />
<geometry>
<cylinder radius="${wheel_radius}" length = "${wheel_length}"/>
</geometry>
<material name="white">
<color rgba="1 1 1 0.9"/>
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="${M_PI/2} 0 0" />
<geometry>
<cylinder radius="${wheel_radius}" length = "${wheel_length}"/>
</geometry>
</collision>
</link>
<joint name="right_wheel_joint" type="continuous">
<origin xyz="0 -${(motor_length+wheel_length)/2} 0" rpy="0 0 0"/>
<parent link="right_motor"/>
<child link="right_wheel_link"/>
<axis xyz="0 1 0"/>
</joint>
<link name="front_caster_link">
<inertial>
<origin xyz="0 0 0"/>
<mass value="0.001" />
<inertia ixx="0.0001" ixy="0.0" ixz="0.0"
iyy="0.0001" iyz="0.0" izz="0.0001" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="${M_PI/2} 0 0"/>
<geometry>
<sphere radius="${wheel_radius/2}" />
</geometry>
<material name="black" />
</visual>
<collision>
<origin xyz="0 0 0.01" rpy="${M_PI/2} 0 0" />
<geometry>
<sphere radius="${wheel_radius/2}" />
</geometry>
</collision>
</link>
<joint name="front_caster_joint" type="fixed">
<origin xyz="${base_link_radius-wheel_radius/2} 0 -${wheel_radius/2}" rpy="0 0 0"/>
<parent link="base_link"/>
<child link="front_caster_link"/>
</joint>
<mrobot_standoff_2in parent="base_link" number="1" x_loc="-${standoff_x/2 + 0.03}" y_loc="-${standoff_y - 0.03}" z_loc="${plate_height/2}"/>
<mrobot_standoff_2in parent="base_link" number="2" x_loc="-${standoff_x/2 + 0.03}" y_loc="${standoff_y - 0.03}" z_loc="${plate_height/2}"/>
<mrobot_standoff_2in parent="base_link" number="3" x_loc="${standoff_x/2}" y_loc="-${standoff_y}" z_loc="${plate_height/2}"/>
<mrobot_standoff_2in parent="base_link" number="4" x_loc="${standoff_x/2}" y_loc="${standoff_y}" z_loc="${plate_height/2}"/>
<joint name="plate_1_joint" type="fixed">
<origin xyz="0 0 ${plate_height}" rpy="0 0 0" />
<parent link="base_link"/>
<child link="plate_1_link" />
</joint>
<link name="plate_1_link">
<inertial>
<mass value="0.1" />
<origin xyz="0 0 0" />
<inertia ixx="0.01" ixy="0.0" ixz="0.0"
iyy="0.01" iyz="0.0" izz="0.01" />
</inertial>
<visual>
<origin xyz=" 0 0 0 " rpy="0 0 0" />
<geometry>
<cylinder length="${base_link_length}" radius="${base_link_radius}"/>
</geometry>
<material name="yellow"/>
</visual>
<collision>
<origin xyz="0.0 0.0 0.0" rpy="0 0 0" />
<geometry>
<cylinder length="${base_link_length}" radius="${base_link_radius}"/>
</geometry>
</collision>
</link>
<mrobot_standoff_2in parent="standoff_2in_1_link" number="5" x_loc="0" y_loc="0" z_loc="${plate_height}"/>
<mrobot_standoff_2in parent="standoff_2in_2_link" number="6" x_loc="0" y_loc="0" z_loc="${plate_height}"/>
<mrobot_standoff_2in parent="standoff_2in_3_link" number="7" x_loc="0" y_loc="0" z_loc="${plate_height}"/>
<mrobot_standoff_2in parent="standoff_2in_4_link" number="8" x_loc="0" y_loc="0" z_loc="${plate_height}"/>
<joint name="plate_2_joint" type="fixed">
<origin xyz="0 0 ${plate_height}" rpy="0 0 0" />
<parent link="plate_1_link"/>
<child link="plate_2_link" />
</joint>
<link name="plate_2_link">
<inertial>
<mass value="0.01" />
<origin xyz="0 0 0" />
<inertia ixx="0.001" ixy="0.0" ixz="0.0"
iyy="0.001" iyz="0.0" izz="0.001" />
</inertial>
<visual>
<origin xyz=" 0 0 0 " rpy="0 0 0" />
<geometry>
<cylinder length="${base_link_length}" radius="${base_link_radius}"/>
</geometry>
<material name="yellow" />
</visual>
<collision>
<origin xyz="0.0 0.0 0.0" rpy="0 0 0" />
<geometry>
<cylinder length="${base_link_length}" radius="${base_link_radius}"/>
</geometry>
</collision>
</link>
</xacro:macro>
</robot>
显示优化后的模型
xacro文件设计完成后,有两种方式在rviz中显示模型。
1.将xacro文件转换成URDF文件, 命令如下
rosrun xacro xacro.py mrobot.urdf.xacro >mrobot.urdf
这时候会生成一个新的URDF文件mrobot.urdf,使用上面launch文件可将URDF模型显示在rviz中。注意所使用了URDF文件名在launch文件中做相应修改。
launch文件内容如下
<launch>
<param name="robot_description" textfile="$(find mrobot_description)/urdf/mrobot.urdf" />
<!-- 设置GUI参数,显示关节控制插件 -->
<param name="use_gui" value="true"/>
<!-- 运行joint_state_publisher节点,发布机器人的关节状态 -->
<node name="joint_state_publisher" pkg="joint_state_publisher" type="joint_state_publisher" />
<!-- 运行robot_state_publisher节点,发布tf -->
<node name="robot_state_publisher" pkg="robot_state_publisher" type="state_publisher" />
<!-- 运行rviz可视化界面 -->
<node name="rviz" pkg="rviz" type="rviz" args="-d $(find mrobot_description)/config/mrobot_urdf.rviz" required="true" />
</launch>
-
直接调用xacro文件解析器
在launch文件中做如下配置,再运动启动文件即可。
<arg name="model" default="$(find xacro)/xacro --inorder '$(find mrobot_description)/urdf/mrobot.urdf.xacro'" />
<param name="robot_description" command="$(arg model)" />
launch文件的内容如下
<launch>
<arg name="model" default="$(find xacro)/xacro --inorder '$(find mrobot_description)/urdf/mrobot.urdf.xacro'" />
<param name="robot_description" command="$(arg model)" />
<!-- 设置GUI参数,显示关节控制插件 -->
<param name="use_gui" value="true"/>
<!-- 运行joint_state_publisher节点,发布机器人的关节状态 -->
<node name="joint_state_publisher" pkg="joint_state_publisher" type="joint_state_publisher" />
<!-- 运行robot_state_publisher节点,发布tf -->
<node name="robot_state_publisher" pkg="robot_state_publisher" type="robot_state_publisher" />
<!-- 运行rviz可视化界面 -->
<node name="rviz" pkg="rviz" type="rviz" args="-d $(find mrobot_description)/config/mrobot.rviz" required="true" />
</launch>
显示的模型如下图所示
到此,使用URDF建模的过程就走一遍了。
参考文献
[1]胡春旭. ROS机器人开发实践[M]. 机械工业出版社, 北京, 2018.5