本节主要记录如何使用urdf和xacro编写小车模型。
目录
1、使用urdf编写自己的ros小车模型
1.1 常见的ROS小车结构有哪些?
下面就是常见ROS小车,下面是几种小车的样子:
这里对urdf进行简单介绍,然后使用urdf最基础的标签,建立机器人的urdf机器人模型。关于更多细节,请参考官网:urdf/Tutorials - ROS Wiki
1.2 什么是urdf?建模的原因
1.2.1 什么是urdf?
URDF(Unified Robot Description Format),统一机器人描述格式。在ROS中,大家通常使用urdf对机器人模型进行描述,主要的作用就是维护机器人关节的tf树以及使用机器人模型3D可视化的作用,关于其他的作用,这里并没有使用到。
1.2.2 建模的原因
关于机器TF树,这里要对它进行详细说明,为的是让大家清楚建立机器人模型的一个主要原因。
ROS中的很多软件包都需要机器人发布TF变换树,那么什么是TF变换树呢?
抽象的来讲,一颗TF变换树定义了不同坐标系之间的平移与旋转变换关系。
具体来说,我们假设有一个机器人,包括一个机器人移动平台和一个安装在平台之上的激光雷达,以这两个机器人为例,定义两个坐标系,一个坐标系以机器人移动平台的中心为原点,称为base_link参考系,另一个坐标系以激光雷达的中心为原点,称为base_laser参考系。
为了定义这个变换关系,假设我们已知激光雷达安装的位置在机器人的中心点上方20cm,前方10cm处。这就根据这些数据,就足以定义这两个参考系之间的变换关系;当我们获取激光数据后,采用(x:0.1m,y:0.0m,z:0.2m)的坐标变换,就可以将数据从base_laser参考系变换到base_link参考系了。当然,如果方向变化,采用(x:-0.1m,y:0.0m,z:-0.50m)即可。
从上边的示例来看,参考系之间的坐标变换好像并不复杂,但是在复杂的系统中,存在的参考系可能远远大于两个,如果我们都使用这种手动的方式进行变换,估计很快你就会被繁杂的坐标关系搞懵了。ROS提供的TF变换就是为解决这个问题的,TF功能包提供了存储、计算不同数据的在不同參考系之的变换公式即可,这棵TF树就可以用树的数据结构管理我们所需要的参考系变换。
如上图所示,每两个相邻的坐标系之间都有变换关系,也就是TF变换。我们需要通过机器人的urdf模型文件,向TF树发布机器人相关TF和关节状态,这也是建立机器人urdf模型的主要原因之一。
1.2.3 介绍urdf部分细节
大家都知道,几乎所有机器人都可以用连杆和关节组成;对于不同形状的机器人,连杆大小和关节的分布不同。urdf就是采用这种方式,使用连杆link和关节joint共同组建机器人结构。
robot
link
描述机器人某个刚体部分的外观和物理属性。
包括大小(size)、颜色(color)、形状(shape)、惯性参数(inertial matrix)、碰撞参数(collision properties)等。
如下面所示,描述一个原点origin设置为零,坐标系无任何旋转,几何形状geometry为半径=0.075m,长度为0.104m的圆柱,物理属性material颜色为黄色,可设置三基色rgb和透明度a。
<link name="base_link"> <!-- 连杆的名字,自定义,也可以理解为一个三维坐标系 -->
<visual> <!-- 可视化标签 -->
<origin xyz=" 0 0 0" rpy="0 0 0" /> <!-- 原点设置标签:原点为集合中心上,欧拉角为零,无任何方向旋转 -->
<geometry> <!-- 几何形状标签 -->
<cylinder radius="0.075" length = "0.104"/> <!-- 此形状圆柱,若为方形 <box size="0.03 0.157 0.03" /> 参数长宽高、若为球体 <sphere radius="0.0125" /> 参数半径 -->
</geometry>
<material name="yellow"> <!-- 物理属性标签,此name的颜色并不起作用,用作示意 -->
<color rgba="1 0.4 0 1"/> <!-- 可设置连杆rgb三基色和a透明度 -->
</material>
</visual>
</link>
joint
描述机器人关节的运动学和动力学属性;
包括关节运动的位置和速度限制;根据具体的关节运动形式分为 6 种类型。
如下面所示,描述了一个连接父link和子link,绕y轴可持续旋转的关节joint。
<joint name="left_wheel_joint" type="continuous"> <!-- name关节名字、type关节类型-->
<origin xyz="0.0 0.0785 -0.085" rpy="0 0 0"/> <!-- 关节无尺寸大小 ,origin原点必须参照parent link设置。这里表示该关节以父link原点为准,设置在向y轴正方向0.0785m,向z轴负方向0.085m的地方 -->
<parent link="base_link"/> <!-- 父link名字 -->
<child link="left_wheel_link"/> <!-- 子link名字 -->
<axis xyz="0 1 0"/> <!-- 此节点绕y轴旋转 -->
</joint>
关于origin xyz=“0 0 0”,rpy=“0 0 0”的参数还是有点说法的,一般连杆的原点坐标xyz都设置为零,但是rpy(欧拉角:弧度制)有时候会变化。一般关节的原点都有坐标,rpy都设置为零。
欧拉角RPY分别代表Roll(翻滚角)Pitch(俯仰角)、Yaw(偏航角),分别对应绕XYZ轴旋转。旋转的正方向是,从XYZ轴的箭头方向看过去,顺时针为正,逆时针为负。
比如这里origin xyz=“0 0 0” rpy=“1.5707 0 0”,就表示连杆模型绕X轴顺时针旋转1.5707rad=90度。
1.2.4 介绍urdf部分细节
在自己的工作空间下建立机器人模型描述功能包:
cd ~/catkin_test_ws/src/
catkin_create_pkg robot_description urdf xacro
创建urdf、launch、config、meshes文件夹:
cd robot_description/
mkdir urdf launch config meshes
urdf存放机器人模型文件。
launch存放相关启动文件。
config保存rviz的配置文件,可用可不用,使用rviz保存配置的时候用来存放的位置。
meshes存放urdf引用的模型渲染文件,可用可不用,如果有渲染的模型文件就用,没有也没关系。
编写ROS小车urdf模型文件:
cd urdf/
vim robot_with_laser.urdf
urdf文件内容如下:
<?xml version="1.0" ?>
<robot name="robot">
<link name="base_link">
<visual>
<origin xyz=" 0 0 0" rpy="0 0 0" />
<geometry>
<cylinder length="0.16" radius="0.20"/>
</geometry>
<material name="yellow">
<color rgba="1 0.4 0 1"/>
</material>
</visual>
</link>
<joint name="left_wheel_joint" type="continuous">
<origin xyz="0 0.19 -0.05" rpy="0 0 0"/>
<parent link="base_link"/>
<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.06" length = "0.025"/>
</geometry>
<material name="white">
<color rgba="1 1 1 0.9"/>
</material>
</visual>
</link>
<joint name="right_wheel_joint" type="continuous">
<origin xyz="0 -0.19 -0.05" rpy="0 0 0"/>
<parent link="base_link"/>
<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.06" length = "0.025"/>
</geometry>
<material name="white">
<color rgba="1 1 1 0.9"/>
</material>
</visual>
</link>
<joint name="front_caster_joint" type="continuous">
<origin xyz="0.18 0 -0.095" rpy="0 0 0"/>
<parent link="base_link"/>
<child link="front_caster_link"/>
<axis xyz="0 1 0"/>
</joint>
<link name="front_caster_link">
<visual>
<origin xyz="0 0 0" rpy="0 0 0"/>
<geometry>
<sphere radius="0.015" />
</geometry>
<material name="black">
<color rgba="0 0 0 0.95"/>
</material>
</visual>
</link>
<joint name="back_caster_joint" type="continuous">
<origin xyz="-0.18 0 -0.095" rpy="0 0 0"/>
<parent link="base_link"/>
<child link="back_caster_link"/>
<axis xyz="0 1 0"/>
</joint>
<link name="back_caster_link">
<visual>
<origin xyz="0 0 0" rpy="0 0 0"/>
<geometry>
<sphere radius="0.015" />
</geometry>
<material name="black">
<color rgba="0 0 0 0.95"/>
</material>
</visual>
</link>
<link name="laser_link">
<visual>
<origin xyz=" 0 0 0 " rpy="0 0 0" />
<geometry>
<cylinder length="0.05" radius="0.05"/>
</geometry>
<material name="black"/>
</visual>
</link>
<joint name="laser_joint" type="fixed">
<origin xyz="0 0 0.105" rpy="0 0 0"/>
<parent link="base_link"/>
<child link="laser_link"/>
</joint>
</robot>
检查urdf模型整体结构:
cd ~/catkin_test_ws/src/robot_description/urdf
urdf_to_graphiz robot_with_laser.urdf
这里生成两个文件,查看pdf如下图:
编写显示模型的launch文件:
cd ..
cd launch
vim dispaly_robot_with_laser.launch
launch的内容如下:
<launch>
<param name="robot_description" textfile="$(find robot_description)/urdf/robot_with_laser.urdf" />
<!-- 运行joint_state_publisher_gui节点,发布机器人的关节状态 -->
<node name="joint_state_publisher_gui" pkg="joint_state_publisher_gui" type="joint_state_publisher_gui" />
<!-- 运行robot_state_publisher节点,发布tf,将机器人的连杆关节之间的关系,通过TF的形式,整理成三维姿态发布出去 -->
<node name="robot_state_publisher" pkg="robot_state_publisher" type="state_publisher" />
</launch>
运行launch文件:
cd ~/catkin_test_ws
catkin_make
source devel/setup.bash
rospack profile
roslaunch robot_description dispaly_robot_with_laser.launch
将出现下面这样的错误,原因是,joint_state_publisher_gui功能包默认没有安装:
解决方法:
sudo apt-get install ros-melodic-joint-state-publisher
sudo apt-get install ros-melodic-joint-state-publisher-gui
将出现下面一个这样的gui调试窗口:
使用rviz查看自己的机器人模型:
rviz
按照图中序号添加机器人模型:
然后将Fixed Frame选择base_link,就出现你想要的小车模型了。
通过joint_state_publisher_gui可以控制模型,自行尝试。按照上面的方法,还可以将TF添加的rviz中显示,如下图这个样子:
1.2.5 总结
这里只是给出了,一个最简单的ROS小车urdf文件功能包编写流程,对于新手来说这就是一个参考的流程。过程中需要注意的地方,都在文章中重点说明了。如果需要更加细化地了解urdf,可以参考官网。但是,这里描述的内容,对小车已经绰绰有余了。
1.2.6 urdf的缺点
- 模型冗长
- 参数修改麻烦
- 没有参数计算能力等等
2、使用xacro编写自己的ros小车模型
和上一节一样,首先奉上xacro的官网地址xacro - ROS Wiki。
本文章只基于上一篇针对urdf文件的缺点进行优化开发,不做更多的xacro介绍。当然对于ROS机器人平台搭建,这里对xacro的介绍已经足够了。
2.1 urdf建模存在的问题
urdf的缺点:
- 模型冗长,重复内容过大,相似的关节都要重复编写
- 参数修改麻烦,不便于二次开发
- 没有参数计算能力等等
2.2 使用xacro建模的优势
首先可以理解xacro是urdf的进化版本。
xacro里面的模型仍然是urdf模型,但是从整个模型的管理上有很大的改观。
- 提供编程接口
- 常量
- 变量
- 数学计算
- 条件语句
- 精简模型代码
- 宏定义
- 文件包含
2.3 xacro模型文件常用语法介绍
1. 常量定义:
<xacro:property name="M_PI" value="3.14159"/>
xacro: property 常量定义标签
name=常量名
value=常量值
2. 常量使用
<origin xyz="0 0 0" rpy="${M_PI/2} 0 0"/>
通常使用${常量名}使用常量 ,并在{}内还可以进行加减乘除等数学运算。
3. 数学计算
<origin xyz="0 ${(motor_length+wheel_length)/2} 0" rpy="0 0 0"/>
在括号里使用数学运算,所有数学运算都会转化为浮点数进行计算,保证运算精度。
4. 宏定义
<xacro:macro name="name" params="A B C">
......具体模型定义(类似函数内容)
</xacro:macro>
xacro: macro定义标签
name=宏定义名字,类似C语言函数名字
params=类似C语言函数参数,可以是字符串
5. 宏调用
<name A="A_value" B="B_value" C="C_value" />
name=宏定义的名字,即函数名
A = 参数A ...
6. 文件包含
<xacro:include filename="$(find robot_descripiton)/xacro/robot_base.xacro" />
xacro:include文件包含定义标签
filename存放xacro文件绝对地址,含文件名字
使用${find+功能包}=功能包的绝对路径
7. 模型显示
方法一:将xacro文件通过命令转化为urdf文件,使用上一章的方式显示(不常用)
rosrun xacro xacro.py robot.xacro>robot.urdf
方法二:直接使用xacro文件解析器
<arg name="model" default="$(find xacro)/xacro --inorder '$(find robot_description)/xacro/robot.xacro'" />
<arg name="gui" default="true" />
<param name="robot_description" command="$(arg model)" />
根据自己xacro的存放路径,按照上述格式自行更改。
2.4 xacro建模实例
本次仍然和上一章一样,只构建一个最基础的带激光雷达的机器人模型。该实例在上一章创建的功能包基础之上进行继续开发。
- 新建存放xacro文件的文件夹
cd ~/catkin_test_ws/src/robot_description/
mkdir xacro
cd xacro
- 新建robot_base.xacro文件:主要是做出各种基础宏定义函数
vim robot_base.xacro
下面是robot_base.xacro文件全部内容,可以对比和urdf的不同之处。
使用robot name="robot" xmlns:xacro="http://www.ros.org/wiki/xacro"表明文件是xacro文件。
使用xacro: property name="M_PI" value="3.1415926" 定义常量放在一起,便于修改与运算,其他类比。
使用xacro: name="wheel" params="prefix reflect" 宏定义轮子,以备后面使用。
使用xacro: name="caster" params="prefix reflect" 宏定义万向轮,以备后面使用。
使用xacro: name="robot_base" 宏定义机器人主体,以备其他文件使用。
<?xml version="1.0"?>
<robot name="robot" xmlns:xacro="http://www.ros.org/wiki/xacro">
<!-- PROPERTY LIST -->
<xacro:property name="M_PI" value="3.1415926"/>
<xacro:property name="base_radius" value="0.20"/>
<xacro:property name="base_length" value="0.16"/>
<xacro:property name="wheel_radius" value="0.06"/>
<xacro:property name="wheel_length" value="0.025"/>
<xacro:property name="wheel_joint_y" value="0.19"/>
<xacro:property name="wheel_joint_z" value="0.05"/>
<xacro:property name="caster_radius" value="0.015"/> <!-- wheel_radius - ( base_length/2 - wheel_joint_z) -->
<xacro:property name="caster_joint_x" value="0.18"/>
<!-- Defining the colors used in this robot -->
<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>
<!-- Macro for robot wheel -->
<xacro:macro name="wheel" params="prefix reflect">
<joint name="${prefix}_wheel_joint" type="continuous">
<origin xyz="0 ${reflect*wheel_joint_y} ${-wheel_joint_z}" rpy="0 0 0"/>
<parent link="base_link"/>
<child link="${prefix}_wheel_link"/>
<axis xyz="0 1 0"/>
</joint>
<link name="${prefix}_wheel_link">
<visual>
<origin xyz="0 0 0" rpy="${M_PI/2} 0 0" />
<geometry>
<cylinder radius="${wheel_radius}" length = "${wheel_length}"/>
</geometry>
<material name="gray" />
</visual>
</link>
</xacro:macro>
<!-- Macro for robot caster -->
<xacro:macro name="caster" params="prefix reflect">
<joint name="${prefix}_caster_joint" type="continuous">
<origin xyz="${reflect*caster_joint_x} 0 ${-(base_length/2 + caster_radius)}" rpy="0 0 0"/>
<parent link="base_link"/>
<child link="${prefix}_caster_link"/>
<axis xyz="0 1 0"/>
</joint>
<link name="${prefix}_caster_link">
<visual>
<origin xyz="0 0 0" rpy="0 0 0"/>
<geometry>
<sphere radius="${caster_radius}" />
</geometry>
<material name="black" />
</visual>
</link>
</xacro:macro>
<xacro:macro name="robot_base">
<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>
<joint name="base_footprint_joint" type="fixed">
<origin xyz="0 0 ${base_length/2 + caster_radius*2}" rpy="0 0 0" />
<parent link="base_footprint"/>
<child link="base_link" />
</joint>
<link name="base_link">
<visual>
<origin xyz=" 0 0 0" rpy="0 0 0" />
<geometry>
<cylinder length="${base_length}" radius="${base_radius}"/>
</geometry>
<material name="yellow" />
</visual>
</link>
<wheel prefix="left" reflect="-1"/>
<wheel prefix="right" reflect="1"/>
<caster prefix="front" reflect="-1"/>
<caster prefix="back" reflect="1"/>
</xacro:macro>
</robot>
- 新建robot.xacro文件
cd xacro
vim robot.xacro
robot.xacro文件内容如下,主要是引用robot_base.xacro文件,宏调用定义机器人主体模型。
<?xml version="1.0"?>
<robot name="robot" xmlns:xacro="http://www.ros.org/wiki/xacro">
<xacro:include filename="$(find robot_description)/xacro/robot_base.xacro" />
<robot_base/>
</robot>
- 新建显示xacro模型的launch文件
cd ~/catkin_test_ws/src/robot_description/launch
cp dispaly_robot_with_laser.launch dispaly_robot_base_xacro.launch #上一章新建过,所以这里直接复制其他文件修改即可
修改display_robot_base_xacro.launch文件内容如下:
<launch>
<arg name="model" default="$(find xacro)/xacro --inorder '$(find robot_description)/xacro/robot.xacro'" />
<param name="robot_description" command="$(arg model)" />
<!-- 运行joint_state_publisher_gui节点,发布机器人的关节状态 -->
<node name="joint_state_publisher_gui" pkg="joint_state_publisher_gui" type="joint_state_publisher_gui" />
<!-- 运行robot_state_publisher节点,发布tf,将机器人的连杆关节之间的关系,通过TF的形式,整理成三维姿态发布出去 -->
<node name="robot_state_publisher" pkg="robot_state_publisher" type="state_publisher" />
</launch>
- 运行display_robot_base_xacro.launch文件
cd ~/catkin_test_ws
catkin_make
source devel/setup.bash
rospack profile
roslaunch robot_description dispaly_robot_base_xacro.launch
将出现下面这样的错误,原因是,joint_state_publisher_gui功能包默认没有安装。如果上一节错误出现过并且解决了,将不会再出现。
解决方法:
sudo apt-get install ros-melodic-joint-state-publisher
sudo apt-get install ros-melodic-joint-state-publisher-gui
将出现下面一个这样的gui调试窗口。
- 使用rviz查看自己的机器人模型
rviz
然后将Fixed Frame选择base_link,就出现你要小车模型了。
- 新建添加激光雷达的机器人模型的xacro文件
cd ~/catkin_test_ws/src/robot_description/xacro
mkdir sensors #存放传感器的xacro文件,以后添加其他传感器就方便了
cd sensors
vim lidar.xacro
lidar.xacro的内容如下:
<?xml version="1.0"?>
<robot name="laser" xmlns:xacro="http://www.ros.org/wiki/xacro" >
<xacro:macro name="rplidar" params="prefix:=laser">
<link name="${prefix}_link">
<visual>
<origin xyz=" 0 0 0 " rpy="0 0 0" />
<geometry>
<cylinder length="0.06" radius="0.035"/>
</geometry>
<material name="black"/>
</visual>
</link>
</xacro:macro>
</robot>
- 新建robot_with_laser.xacro文件
cd ~/catkin_test_ws/src/robot_description/xacro
vim robot_with_laser.xacro
robot_with_laser.xacro文件内容如下:
<?xml version="1.0"?>
<robot name="robot" xmlns:xacro="http://www.ros.org/wiki/xacro">
<xacro:include filename="$(find robot_description)/xacro/robot_base.xacro" />
<xacro:include filename="$(find robot_description)/xacro/sensors/lidar.xacro" />
<xacro:property name="laser_offset_x" value="0.0" />
<xacro:property name="laser_offset_y" value="0.0" />
<xacro:property name="laser_offset_z" value="0.082" />
<robot_base/>
<!-- laser -->
<joint name="laser_joint" type="fixed">
<origin xyz="${laser_offset_x} ${laser_offset_y} ${laser_offset_z}" rpy="0 0 0" />
<parent link="base_link"/>
<child link="laser_link"/>
</joint>
<xacro:rplidar prefix="laser"/>
</robot>
关于新建显示xacro模型的launch文件,以及如何显示模型和上面内容一致,对launch文件的model参数引入的文件名稍作修改即可,效果如下。
2.5 总结
通过文章内容,我们清楚认识到,xacro文件对于机器人建模的优点,特别是针对复杂模型。易读、容易修改、有编程接口等等,好处多多。
但是,我们需要思考一个问题,我们的小车复杂吗?如果不复杂,我们可以接受urdf的直白繁琐吗?如果可以接受,本章的内容将毫无意义。因为urdf和xacro模型文件,实现的是同一种功能,既然对于简单的小车使用urdf就足够了,又没有很繁琐,这时使用xacro意义不大。
下面,我们就要正式进入ros小车ros层的重要部分了。下一章,我们对差分轮式机器人的里程计模型进行说明。
文件关系:
display_robot_base_xacro.launch
<arg name="model" default="$(find xacro)/xacro --inorder '$(find robot_description)/xacro/robot.xacro'" />
robot.xacro ==> robot_base.xacro
display_robot_base_xacro.launch
<arg name="model" default="$(find xacro)/xacro --inorder '$(find robot_description)/xacro/robot_with_laser.xacro'" />
robot_with_laser.xacro ==> robot_base.xacro + sensors/lidar.xacro