最近闲来无事,重温了一下ROS方面的相关知识。先前的学习都是一带而过,发现差不多都忘了,学习的不够深入。因此,在重温的同时,于先前写下了2篇ROS架构与通信机制的博客:
对应的博客的链接为:
ROS架构的学习【Noetic】-CSDN博客
ROS实操:通信机制的实现-CSDN博客
以上的这些博客可以算是一些基础入门心得与学习记录,下面我们开始新的历程:ROS机器人系统仿真
~~~路漫漫其修远兮,吾将上下而求索~~~
1. 概述:
机器人系统仿真:是通过计算机对实体机器人系统进行模拟的技术,在 ROS 中,仿真实现涉及的内容主要有三:对机器人建模(URDF)、创建仿真环境(Gazebo)以及感知环境(Rviz)等系统性实现。
URDF是 Unified Robot Description Format 的首字母缩写,直译为统一(标准化)机器人描述格式,可以以一种 XML 的方式描述机器人的部分结构,比如底盘、摄像头、激光雷达、机械臂以及不同关节的自由度, 该文件可以被 C++ 内置的解释器转换成可视化的机器人模型,是 ROS 中实现机器人仿真的重要组件。
RViz 是 ROS Visualization Tool 的首字母缩写,直译为ROS的三维可视化工具。它的主要目的是以三维方式显示ROS消息,可以将数据进行可视化表达。例如:可以显示机器人模型,可以无需编程就能表达激光测距仪(LRF)传感器中的传感 器到障碍物的距离,RealSense、Kinect或Xtion等三维距离传感器的点云数据(PCD, Point Cloud Data),从相机获取的图像值等。
运行使用命令rviz
或rosrun rviz rviz
Gazebo是一款3D动态模拟器,用于显示机器人模型并创建仿真环境,能够在复杂的室内和室外环境中准确有效地模拟机器人。与游戏引擎提供高保真度的视觉模拟类似,Gazebo提供高保真度的物理模拟,其提供一整套传感器模型,以及对用户和程序非常友好的交互方式。
运行使用命令gazebo
或rosrun gazebo_ros gazebo
-----------------------------------------------------------------------
机器人的系统仿真是一种集成实现,主要包含三部分:
-
URDF 用于创建机器人模型
-
Gzebo 用于搭建仿真环境
-
Rviz 图形化的显示机器人各种传感器感知到的环境信息
三者应用中,只是创建 URDF 意义不大,一般需要结合 Gazebo 或 Rviz 使用,在 Gazebo 或 Rviz 中可以将 URDF 文件解析为图形化的机器人模型,一般的使用组合为:
-
如果非仿真环境,那么使用 URDF 结合 Rviz 直接显示感知的真实环境信息
-
如果是仿真环境,那么需要使用 URDF 结合 Gazebo 搭建仿真环境,并结合 Rviz 显示感知的虚拟环境信息
2. URDF集成Rviz基本流程
补充:这一部分我先前学习过【古月居】的相关课程,具体学习和心得的博客记录补充如下:
ROS建模:一起从零手写URDF模型:https://blog.csdn.net/qq_54900679/article/details/135726348?spm=1001.2014.3001.5501
ROS建模:从零手写机械臂的URDF模型:https://blog.csdn.net/qq_54900679/article/details/136872351?spm=1001.2014.3001.5501
一起来从Solidworks中导出URDF模型:https://blog.csdn.net/qq_54900679/article/details/137279115?spm=1001.2014.3001.5501
补充:一起来从Solidworks中导出机械臂的URDF模型:https://blog.csdn.net/qq_54900679/article/details/137342680?spm=1001.2014.3001.5501
关于在Ubuntu20.04(ROS1 noetic)中使用catkin_make编译时发生的与pyhton版本不兼容的问题解决办法:https://blog.csdn.net/qq_54900679/article/details/135780142?spm=1001.2014.3001.5501
实现流程:
-
准备:新建功能包,导入依赖
-
核心:编写 urdf 文件
-
核心:在 launch 文件集成 URDF 与 Rviz
-
在 Rviz 中显示机器人模型
1.创建功能包,导入依赖
创建一个新的功能包,名称自定义,导入依赖包:urdf
与xacro
在当前功能包下,再新建几个目录:
urdf
: 存储 urdf 文件的目录
meshes
:机器人模型渲染文件(暂不使用)
config
: 配置文件
launch
: 存储 launch 启动文件
下面进行实操演示,首先在自己的工作空间的src中新建一个功能包:
输入包名:urdf01_rviz ;导入依赖:urdf xacro 后回车,出现如下显示:
继续在urdf01_rviz文件夹当中创建一些文件夹:config、launch、urdf、meshes
在urdf文件夹当中再创建2个文件夹:urdf、xacro
其中urdf是用来存放urdf文件,xacro是存放urdf升级文件
2.编写 URDF 文件
新建一个子级文件夹:urdf
(可选),文件夹中添加一个.urdf
文件(domo01_helloworld.urdf),复制如下内容:
<robot name="mycar">
<link name="base_link">
<visual>
<geometry>
<box size="0.5 0.2 0.1" />
</geometry>
</visual>
</link>
</robot>
3.在 launch 文件中集成 URDF 与 Rviz
在launch
目录下,新建一个 launch 文件(demo01_helloworld.launch),该 launch 文件需要启动 Rviz,并导入 urdf 文件,Rviz 启动后可以自动载入解析urdf
文件,并显示机器人模型,核心问题:如何导入 urdf 文件? 在 ROS 中,可以将 urdf 文件的路径设置到参数服务器,使用的参数名是:robot_description
,示例代码如下:
<launch>
<!-- 设置参数 -->
<param name="robot_description" textfile="$(find urdf01_rviz)/urdf/urdf/demo01_helloworld.urdf" />
<!-- 启动 rviz -->
<node pkg="rviz" type="rviz" name="rviz" />
</launch>
具体的界面截图如下:
保存文件后,在VScode编辑器内可以source一下:
source ./devel/setup.bash
然后再执行:
roslaunch urdf01_rviz demo01_helloworld.launch
可以发现rviz启动成功:
但是没有模型显示,需要做一下如下的配置:
可以发现在rviz中显示模型成功!
如果想将这个模型保存,可以保存到config配置文件夹中(点击File-->Save Config As)
保存到工作空间中的当前项目中去,命名自定义即可:
可以发现多出了一个文件(我这里命名为show_mycar)
这样下次再启动launch文件时,只需要将这个配置读进来即可
然后,launch
文件中 Rviz 的启动配置添加参数:args
,值设置为-d 配置文件路径
优化后的launch文件如下图所示,红色方框为后续添加内容:
再次启动launch文件时,就可以包含之前的组件配置了,使用更方便快捷。
3. URDF语法详解
URDF 文件是一个标准的 XML 文件,在 ROS 中预定义了一系列的标签用于描述机器人模型,机器人模型可能较为复杂,但是 ROS 的 URDF 中机器人的组成却是较为简单,可以主要简化为两部分:连杆(link标签) 与 关节(joint标签),接下来我们就通过案例了解一下 URDF 中的不同标签:
- robot 根标签,类似于 launch文件中的launch标签
- link 连杆标签
- joint 关节标签
- gazebo 集成gazebo需要使用的标签
关于gazebo标签,后期在使用 gazebo 仿真时,才需要使用到,用于配置仿真环境所需参数,比如: 机器人材料属性、gazebo插件等,但是该标签不是机器人模型必须的,只有在仿真时才需设置。
robot:urdf 中为了保证 xml 语法的完整性,使用了robot
标签作为根标签,所有的 link 和 joint 以及其他标签都必须包含在 robot 标签内,在该标签内可以通过 name 属性设置机器人模型的名称。
1.属性
name: 指定机器人模型的名称
2.子标签
其他标签都是子级标签
link:urdf 中的 link 标签用于描述机器人某个部件(也即刚体部分)的外观和物理属性,比如: 机器人底座、轮子、激光雷达、摄像头...每一个部件都对应一个 link, 在 link 标签内,可以设计该部件的形状、尺寸、颜色、惯性矩阵、碰撞参数等一系列属性。
1.属性
- name ---> 为连杆命名
2.子标签
-
visual ---> 描述外观(对应的数据是可视的)
-
geometry 设置连杆的形状
-
标签1: box(盒状)
- 属性:size=长(x) 宽(y) 高(z)
-
标签2: cylinder(圆柱)
- 属性:radius=半径 length=高度
-
标签3: sphere(球体)
- 属性:radius=半径
-
标签4: mesh(为连杆添加皮肤)
- 属性: filename=资源路径(格式:package://<packagename>/<path>/文件)
-
-
origin 设置偏移量与倾斜弧度
-
属性1: xyz=x偏移 y便宜 z偏移
-
属性2: rpy=x翻滚 y俯仰 z偏航 (单位是弧度)
-
-
metrial 设置材料属性(颜色)
-
属性: name
-
标签: color
- 属性: rgba=红绿蓝权重值与透明度 (每个权重值以及透明度取值[0,1])
-
-
-
collision ---> 连杆的碰撞属性
-
Inertial ---> 连杆的惯性矩阵
在此,只演示visual
使用。
3.案例
分别生成长方体、圆柱与球体的机器人部件。给出以下的urdf代码:
<!-- 需求:设置不同形状的机器人部件 -->
<robot name="mycar">
<link name="base_link">
<visual>
<!-- 形状 -->
<geometry>
<!-- 长方体的长宽高 -->
<!-- <box size="0.5 0.3 0.1" /> -->
<!-- 圆柱,半径和长度 -->
<cylinder radius="0.5" length="0.1" />
<!-- 球体,半径-->
<!-- <sphere radius="0.3" /> -->
</geometry>
<!-- xyz坐标 rpy翻滚俯仰与偏航角度(3.14=180度 1.57=90度) -->
<origin xyz="0 0 0" rpy="0 0 0" />
<!-- 颜色: r=red g=green b=blue a=alpha -->
<material name="black">
<color rgba="0.7 0.5 0 0.5" />
</material>
</visual>
</link>
</robot>
下面进行案例演示,将上面的urdf代码复制到demo02_link.urdf文件当中,然后再新建一个launch文件(demo02_link.launch):
<launch>
<!-- 设置参数 -->
<param name="robot_description" textfile="$(find urdf01_rviz)/urdf/urdf/demo02_link.urdf" />
<!-- 启动 rviz -->
<node pkg="rviz" type="rviz" name="rviz" args="-d $(find urdf01_rviz)/config/show_mycar.rviz" />
</launch>
保存文件后,在VScode编辑器内可以source一下:
source ./devel/setup.bash
然后再执行:
roslaunch urdf01_rviz demo02_link.launch
可以发现圆柱成功地显示了出来:
下面演示如何加载meshes,需要下载素材链接,找一个文件目录在终端执行:
git clone https://github.com/zx595306686/sim_demo.git
将其中的2个STL文件复制到meshes文件夹当中,并在urdf文件的代码中添加如下一行:
<mesh filename="package://urdf01_rviz/meshes/autolabor_mini.stl"/>
保存文件后,然后再执行launch指令:
roslaunch urdf01_rviz demo02_link.launch
可以发现,我们自己所添加的模型显示了出来:
但是,这个机器人模型的显示姿态好像不对,我们如何将它的显示摆正呢?下面我们给出解决方法:观察xyz坐标系,默认车头沿着x轴的正方向,只需绕x轴逆时针转90°即可:
修改urdf代码中的origin部分,先将 r 设置为1.57:
<origin xyz="0 0 0" rpy="1.57 0 0" />
保存文件后,然后再执行launch指令,可以发现,小车摆正了,但车头还没朝向x轴正方向。紧接着,我们进行第二步,绕z轴转动90°:
修改urdf代码中的origin部分,将rpy中的 y 设置为1.57:
<origin xyz="0 0 0" rpy="1.57 0 1.57" />
保存文件后,然后再执行launch指令,可以发现小车摆正了姿态:
于是问题得到了解决!
下面继续来介绍关节:
joint:urdf 中的 joint 标签用于描述机器人关节的运动学和动力学属性,还可以指定关节运动的安全极限,机器人的两个部件(分别称之为 parent link 与 child link)以"关节"的形式相连接,不同的关节有不同的运动形式: 旋转、滑动、固定、旋转速度、旋转角度限制....,比如:安装在底座上的轮子可以360度旋转,而摄像头则可能是完全固定在底座上。
注意:joint标签对应的数据在模型中是不可见的
1.属性
-
name ---> 为关节命名
-
type ---> 关节运动形式
-
continuous: 旋转关节,可以绕单轴无限旋转
-
revolute: 旋转关节,类似于 continues,但是有旋转角度限制
-
prismatic: 滑动关节,沿某一轴线移动的关节,有位置极限
-
planer: 平面关节,允许在平面正交方向上平移或旋转
-
floating: 浮动关节,允许进行平移、旋转运动
-
fixed: 固定关节,不允许运动的特殊关节
-
2.子标签
-
parent(必需的)
parent link的名字是一个强制的属性:
- link:父级连杆的名字,是这个link在机器人结构树中的名字。
-
child(必需的)
child link的名字是一个强制的属性:
- link:子级连杆的名字,是这个link在机器人结构树中的名字。
-
origin
- 属性: xyz=各轴线上的偏移量 rpy=各轴线上的偏移弧度。
-
axis
- 属性: xyz用于设置围绕哪个关节轴运动。
3.案例
创建机器人模型,底盘为长方体,在长方体的前面添加一摄像头,摄像头可以沿着 Z 轴 360 度旋转。新建一个urdf文件:demo03_joint.urdf,并给出相关的urdf文件代码:
<!--
需求: 创建机器人模型,底盘为长方体,
在长方体的前面添加一摄像头,
摄像头可以沿着 Z 轴 360 度旋转
-->
<robot name="mycar">
<!-- 底盘 -->
<link name="base_link">
<visual>
<geometry>
<box size="0.5 0.2 0.1" />
</geometry>
<origin xyz="0 0 0" rpy="0 0 0" />
<material name="blue">
<color rgba="0 0 1.0 0.5" />
</material>
</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">
<color rgba="1 0 0 0.5" />
</material>
</visual>
</link>
<!-- 关节 -->
<joint name="camera2baselink" type="continuous">
<parent link="base_link"/>
<child link="camera" />
<!-- 需要计算两个 link 的物理中心之间的偏移量 -->
<origin xyz="0.2 0 0.075" rpy="0 0 0" />
<axis xyz="0 0 1" />
</joint>
</robot>
接着给出相关的launch文件(demo03_joint.launch)代码:
<launch>
<param name="robot_description" textfile="$(find urdf01_rviz)/urdf/urdf/demo03_joint.urdf" />
<node pkg="rviz" type="rviz" name="rviz" args="-d $(find urdf01_rviz)/config/show_mycar.rviz" />
<!-- 添加关节状态发布节点 -->
<node pkg="joint_state_publisher" type="joint_state_publisher" name="joint_state_publisher" />
<!-- 添加机器人状态发布节点 -->
<node pkg="robot_state_publisher" type="robot_state_publisher" name="robot_state_publisher" />
<!-- 可选:用于控制关节运动的节点 -->
<node pkg="joint_state_publisher_gui" type="joint_state_publisher_gui" name="joint_state_publisher_gui" />
</launch>
保存文件后,然后再执行launch指令:
roslaunch urdf01_rviz demo03_joint.launch
可以发现我们的模型显示成功,拖动关节滑动条可以观察红色小方块产生了旋转:
base_footprint优化urdf
前面实现的机器人模型是半沉到地下的,因为默认情况下: 底盘的中心点位于地图原点上,所以会导致这种情况产生,可以使用的优化策略,将初始 link 设置为一个尺寸极小的 link(比如半径为 0.001m 的球体,或边长为 0.001m 的立方体),然后再在初始 link 上添加底盘等刚体,这样实现,虽然仍然存在初始link半沉的现象,但是基本可以忽略了。这个初始 link 一般称之为 base_footprint
下面给出优化后的urdf代码(demo04_base_footprint.urdf):
<!--
使用 base_footprint 优化
-->
<robot name="mycar">
<!-- 设置一个原点(机器人中心点的投影) -->
<link name="base_footprint">
<visual>
<geometry>
<sphere radius="0.001" />
</geometry>
</visual>
</link>
<!-- 添加底盘 -->
<link name="base_link">
<visual>
<geometry>
<box size="0.5 0.2 0.1" />
</geometry>
<origin xyz="0 0 0" rpy="0 0 0" />
<material name="blue">
<color rgba="0 0 1.0 0.5" />
</material>
</visual>
</link>
<!-- 底盘与原点连接的关节 -->
<joint name="base_link2base_footprint" type="fixed">
<parent link="base_footprint" />
<child link="base_link" />
<origin xyz="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">
<color rgba="1 0 0 0.5" />
</material>
</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>
接着给出相关的launch文件(demo04_base_footprint.launch)代码:
<launch>
<param name="robot_description" textfile="$(find urdf01_rviz)/urdf/urdf/demo04_base_footprint.urdf" />
<node pkg="rviz" type="rviz" name="rviz" args="-d $(find urdf01_rviz)/config/show_mycar.rviz" />
<!-- 添加关节状态发布节点 -->
<node pkg="joint_state_publisher" type="joint_state_publisher" name="joint_state_publisher" />
<!-- 添加机器人状态发布节点 -->
<node pkg="robot_state_publisher" type="robot_state_publisher" name="robot_state_publisher" />
<!-- 可选:用于控制关节运动的节点 -->
<node pkg="joint_state_publisher_gui" type="joint_state_publisher_gui" name="joint_state_publisher_gui" />
</launch>
保存文件后,然后再执行launch指令:
roslaunch urdf01_rviz demo04_base_footprint.launch
再将 Fixed Frame 修改为 base_footprint 后,可以发现机器人模型的半沉问题得到了解决:
URDF练习
在上述的URDF语法详解学完之后,我们可以来次练习:创建一个四轮圆柱状机器人模型,机器人参数如下,底盘为圆柱状,半径 10cm,高 8cm,四轮由两个驱动轮和两个万向支撑轮组成,两个驱动轮半径为 3.25cm, 轮胎宽度1.5cm,两个万向轮为球状,半径 0.75cm,底盘离地间距为 1.5cm(与万向轮直径一致)
实现流程:
创建机器人模型可以分步骤实现
-
新建 urdf 文件,并与 launch 文件集成
-
搭建底盘
-
在底盘上添加两个驱动轮
-
在底盘上添加两个万向轮
下面给出对应的urdf 文件如下(demo05_test.urdf):
<robot name="mycar">
<!-- 设置 base_footprint -->
<link name="base_footprint">
<visual>
<geometry>
<sphere radius="0.001" />
</geometry>
</visual>
</link>
<!-- 添加底盘 -->
<!--
参数
形状:圆柱
半径:10 cm
高度:8 cm
离地:1.5 cm
-->
<link name="base_link">
<visual>
<geometry>
<cylinder radius="0.1" length="0.08" />
</geometry>
<origin xyz="0 0 0" rpy="0 0 0" />
<material name="yellow">
<color rgba="0.8 0.3 0.1 0.5" />
</material>
</visual>
</link>
<joint name="base_link2base_footprint" type="fixed">
<parent link="base_footprint" />
<child link="base_link"/>
<origin xyz="0 0 0.055" />
</joint>
<!-- 添加驱动轮 -->
<!--
驱动轮是侧翻的圆柱
参数
半径: 3.25 cm
宽度: 1.5 cm
颜色: 黑色
关节设置:
x = 0
y = 底盘的半径 + 轮胎宽度 / 2
z = 离地间距 + 底盘长度 / 2 - 轮胎半径 = 1.5 + 4 - 3.25 = 2.25(cm)
axis = 0 1 0
-->
<link name="left_wheel">
<visual>
<geometry>
<cylinder radius="0.0325" length="0.015" />
</geometry>
<origin xyz="0 0 0" rpy="1.5705 0 0" />
<material name="black">
<color rgba="0.0 0.0 0.0 1.0" />
</material>
</visual>
</link>
<joint name="left_wheel2base_link" type="continuous">
<parent link="base_link" />
<child link="left_wheel" />
<origin xyz="0 0.1 -0.0225" />
<axis xyz="0 1 0" />
</joint>
<link name="right_wheel">
<visual>
<geometry>
<cylinder radius="0.0325" length="0.015" />
</geometry>
<origin xyz="0 0 0" rpy="1.5705 0 0" />
<material name="black">
<color rgba="0.0 0.0 0.0 1.0" />
</material>
</visual>
</link>
<joint name="right_wheel2base_link" type="continuous">
<parent link="base_link" />
<child link="right_wheel" />
<origin xyz="0 -0.1 -0.0225" />
<axis xyz="0 1 0" />
</joint>
<!-- 添加万向轮(支撑轮) -->
<!--
参数
形状: 球体
半径: 0.75 cm
颜色: 黑色
关节设置:
x = 自定义(底盘半径 - 万向轮半径) = 0.1 - 0.0075 = 0.0925(cm)
y = 0
z = 底盘长度 / 2 + 离地间距 / 2 = 0.08 / 2 + 0.015 / 2 = 0.0475
axis= 1 1 1
-->
<link name="front_wheel">
<visual>
<geometry>
<sphere radius="0.0075" />
</geometry>
<origin xyz="0 0 0" rpy="0 0 0" />
<material name="black">
<color rgba="0.0 0.0 0.0 1.0" />
</material>
</visual>
</link>
<joint name="front_wheel2base_link" type="continuous">
<parent link="base_link" />
<child link="front_wheel" />
<origin xyz="0.0925 0 -0.0475" />
<axis xyz="1 1 1" />
</joint>
<link name="back_wheel">
<visual>
<geometry>
<sphere radius="0.0075" />
</geometry>
<origin xyz="0 0 0" rpy="0 0 0" />
<material name="black">
<color rgba="0.0 0.0 0.0 1.0" />
</material>
</visual>
</link>
<joint name="back_wheel2base_link" type="continuous">
<parent link="base_link" />
<child link="back_wheel" />
<origin xyz="-0.0925 0 -0.0475" />
<axis xyz="1 1 1" />
</joint>
</robot>
给出对应的launch文件如下(demo05_test.launch):
<launch>
<!-- 将 urdf 文件内容设置进参数服务器 -->
<param name="robot_description" textfile="$(find urdf01_rviz)/urdf/urdf/demo05_test.urdf" />
<!-- 启动 rivz -->
<node pkg="rviz" type="rviz" name="rviz" args="-d $(find urdf01_rviz)/config/show_mycar.rviz" />
<!-- 启动机器人状态和关节状态发布节点 -->
<node pkg="robot_state_publisher" type="robot_state_publisher" name="robot_state_publisher" />
<node pkg="joint_state_publisher" type="joint_state_publisher" name="joint_state_publisher" />
<!-- 启动图形化的控制关节运动节点 -->
<node pkg="joint_state_publisher_gui" type="joint_state_publisher_gui" name="joint_state_publisher_gui" />
</launch>
保存文件后,然后再执行launch指令:
roslaunch urdf01_rviz demo05_test.launch
可以发现四轮圆柱状机器人模型显示成功:
URDF工具
这一节的最后,我们来看一下URDF工具:
在 ROS 中,提供了一些工具来方便 URDF 文件的编写,比如:
-
check_urdf
命令可以检查复杂的 urdf 文件是否存在语法问题 -
urdf_to_graphiz
命令可以查看 urdf 模型结构,显示不同 link 的层级关系
当然,要使用工具之前,首先需要安装,安装命令:sudo apt install liburdfdom-tools
1.check_urdf 语法检查
进入urdf文件所属目录,调用:check_urdf urdf文件
,如果不抛出异常,说明文件合法,否则非法
以demo05_test.urdf文件为例子,在urdf的文件夹中开启一个终端并执行:
check_urdf demo05_test.urdf
可以发现终端显示如下输出,说明文件合法:
2.urdf_to_graphiz 结构查看
进入urdf文件所属目录,调用:urdf_to_graphiz urdf文件
,当前目录下会生成 pdf 文件
以demo05_test.urdf文件为例子,在urdf的文件夹中开启一个终端并执行:
urdf_to_graphiz demo05_test.urdf
可以发现在urdf的文件夹中新生成了2个文件:
下面来打开mycar.pdf文件进行查看:
这个被解析出的PDF文件描述了urdf模型的层级关系,以树形结构显示。
下面进行新内容的学习与记录:
4. URDF优化_xacro
在前面的URDF文件学习后,仅仅对URDF模型进行可视化是单一的,需要对URDF文件进行升级(优化)。在 ROS 中,已经给出了类似编程的优化方案,称之为:Xacro
Xacro 是 XML Macros 的缩写,Xacro 是一种 XML 宏语言,是可编程的 XML
原理:Xacro 可以声明变量,可以通过数学运算求解,使用流程控制控制执行顺序,还可以通过类似函数的实现,封装固定的逻辑,将逻辑中需要的可变的数据以参数的方式暴露出去,从而提高代码复用率以及程序的安全性。
所以,较之于先前纯粹的 URDF 实现,可以编写更安全、精简、易读性更强的机器人模型文件(Xacro),且可以提高编写效率。
下面开始进行 Xacro 的快速体验:
首先需要编写 Xacro 文件,以变量的方式封装属性(常量半径、高度、车轮半径...),以函数的方式封装重复实现(车轮的添加)。
使用xacro优化上一节案例中驱动轮实现,需要使用变量封装底盘的半径、高度,使用数学公式动态计算底盘的关节点坐标,使用 Xacro 宏封装轮子重复的代码并调用宏创建两个轮子(注意: 在此,演示 Xacro 的基本使用,不必要生成合法的 URDF )。
需要创建功能包,导入 urdf 与 xacro。这个之间沿用先前的功能即可,无需重新创建。
下面给出xacro文件(demo01_helloworld.xacro)的相关代码:
<robot name="mycar" xmlns:xacro="http://wiki.ros.org/xacro">
<!-- 属性封装 -->
<xacro:property name="wheel_radius" value="0.0325" />
<xacro:property name="wheel_length" value="0.0015" />
<xacro:property name="PI" value="3.1415927" />
<xacro:property name="base_link_length" value="0.08" />
<xacro:property name="lidi_space" value="0.015" />
<!-- 宏 -->
<xacro:macro name="wheel_func" params="wheel_name flag" >
<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>
<!-- 3-2.joint -->
<joint name="${wheel_name}2link" type="continuous">
<parent link="base_link" />
<child link="${wheel_name}_wheel" />
<!--
x 无偏移
y 车体半径
z z= 车体高度 / 2 + 离地间距 - 车轮半径
-->
<origin xyz="0 ${0.1 * flag} ${(base_link_length / 2 + lidi_space - wheel_radius) * -1}" rpy="0 0 0" />
<axis xyz="0 1 0" />
</joint>
</xacro:macro>
<xacro:wheel_func wheel_name="left" flag="1" />
<xacro:wheel_func wheel_name="right" flag="-1" />
</robot>
注意:在ROS中,Xacro文件是不能被直接识别的。需要先将Xacro文件转化为URDF文件,转换的指令如下(先启动 roscore 节点,再进入到Xacro文件夹内):
rosrun xacro xacro demo01_helloworld.xacro
终端打印结果如下,生成了一大段文本(其实就是生成了urdf文件的内容):
更常见的作法是将上面的解析结果保存到urdf文件当中,使用如下指令:
rosrun xacro xacro demo01_helloworld.xacro > demo01_helloworld.urdf
可以发现,在Xacro文件夹内新生成了一个urdf文件:
这个生成的urdf结果与之前终端打印的结构是一样的,给出生成的urdf代码:
<?xml version="1.0" ?>
<!-- =================================================================================== -->
<!-- | This document was autogenerated by xacro from demo01_helloworld.xacro | -->
<!-- | EDITING THIS FILE BY HAND IS NOT RECOMMENDED | -->
<!-- =================================================================================== -->
<robot name="mycar">
<link name="left_wheel">
<visual>
<geometry>
<cylinder length="0.0015" radius="0.0325"/>
</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>
<!-- 3-2.joint -->
<joint name="left2link" type="continuous">
<parent link="base_link"/>
<child link="left_wheel"/>
<!--
x 无偏移
y 车体半径
z z= 车体高度 / 2 + 离地间距 - 车轮半径
-->
<origin rpy="0 0 0" xyz="0 0.1 -0.0225"/>
<axis xyz="0 1 0"/>
</joint>
<link name="right_wheel">
<visual>
<geometry>
<cylinder length="0.0015" radius="0.0325"/>
</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>
<!-- 3-2.joint -->
<joint name="right2link" type="continuous">
<parent link="base_link"/>
<child link="right_wheel"/>
<!--
x 无偏移
y 车体半径
z z= 车体高度 / 2 + 离地间距 - 车轮半径
-->
<origin rpy="0 0 0" xyz="0 -0.1 -0.0225"/>
<axis xyz="0 1 0"/>
</joint>
</robot>
注意: 该案例编写生成的是非法的 URDF 文件,目的在于演示 Xacro 的极简使用以及优点。
Xacro的语法详解
xacro 提供了可编程接口,类似于计算机语言,包括变量声明调用、函数声明与调用等语法实现。
在使用 xacro 生成 urdf 时,根标签robot
中必须包含命名空间声明: xmlns:xacro="http://wiki.ros.org/xacro"
1.属性与算数运算
用于封装 URDF 中的一些字段,比如: PAI 值,小车的尺寸,轮子半径 ....
属性定义:
<xacro:property name="xxxx" value="yyyy" />
属性调用:
${属性名称}
算数运算:
${数学表达式}
2.宏
类似于函数实现,提高代码复用率,优化代码结构,提高安全性
宏定义
<xacro:macro name="宏名称" params="参数列表(多参数之间使用空格分隔)">
.....
参数调用格式: ${参数名}
</xacro:macro>
宏调用
<xacro:宏名称 参数1=xxx 参数2=xxx/>
3.文件包含
机器人由多部件组成,不同部件可能封装为单独的 xacro 文件,最后再将不同的文件集成,组合为完整机器人,可以使用文件包含实现
文件包含
<robot name="xxx" xmlns:xacro="http://wiki.ros.org/xacro">
<xacro:include filename="my_base.xacro" />
<xacro:include filename="my_camera.xacro" />
<xacro:include filename="my_laser.xacro" />
....
</robot>
Xacro的完整使用流程示例
这一部分才是实操的精华所在,使用 Xacro 优化 URDF 版的小车底盘模型实现
1.编写 Xacro 文件
下面进入到xacro文件夹中新建一个文件(demo_car_base.xacro):
<!--
使用 xacro 优化 URDF 版的小车底盘实现:
实现思路:
1.将一些常量、变量封装为 xacro:property
比如:PI 值、小车底盘半径、离地间距、车轮半径、宽度 ....
2.使用 宏 封装驱动轮以及支撑轮实现,调用相关宏生成驱动轮与支撑轮
-->
<!-- 根标签,必须声明 xmlns:xacro -->
<robot name="my_base" xmlns:xacro="http://www.ros.org/wiki/xacro">
<!-- 封装变量、常量 -->
<xacro:property name="PI" value="3.141"/>
<!-- 宏:黑色设置 -->
<material name="black">
<color rgba="0.0 0.0 0.0 1.0" />
</material>
<!-- 底盘属性 -->
<xacro:property name="base_footprint_radius" value="0.001" /> <!-- base_footprint 半径 -->
<xacro:property name="base_link_radius" value="0.1" /> <!-- base_link 半径 -->
<xacro:property name="base_link_length" value="0.08" /> <!-- base_link 长 -->
<xacro:property name="earth_space" value="0.015" /> <!-- 离地间距 -->
<!-- 底盘 -->
<link name="base_footprint">
<visual>
<geometry>
<sphere radius="${base_footprint_radius}" />
</geometry>
</visual>
</link>
<link name="base_link">
<visual>
<geometry>
<cylinder radius="${base_link_radius}" length="${base_link_length}" />
</geometry>
<origin xyz="0 0 0" rpy="0 0 0" />
<material name="yellow">
<color rgba="0.5 0.3 0.0 0.5" />
</material>
</visual>
</link>
<joint name="base_link2base_footprint" type="fixed">
<parent link="base_footprint" />
<child link="base_link" />
<origin xyz="0 0 ${earth_space + base_link_length / 2 }" />
</joint>
<!-- 驱动轮 -->
<!-- 驱动轮属性 -->
<xacro:property name="wheel_radius" value="0.0325" /><!-- 半径 -->
<xacro:property name="wheel_length" value="0.015" /><!-- 宽度 -->
<!-- 驱动轮宏实现 -->
<xacro:macro name="add_wheels" params="name flag">
<link name="${name}_wheel">
<visual>
<geometry>
<cylinder radius="${wheel_radius}" length="${wheel_length}" />
</geometry>
<origin xyz="0.0 0.0 0.0" rpy="${PI / 2} 0.0 0.0" />
<material name="black" />
</visual>
</link>
<joint name="${name}_wheel2base_link" type="continuous">
<parent link="base_link" />
<child link="${name}_wheel" />
<origin xyz="0 ${flag * base_link_radius} ${-(earth_space + base_link_length / 2 - wheel_radius) }" />
<axis xyz="0 1 0" />
</joint>
</xacro:macro>
<xacro:add_wheels name="left" flag="1" />
<xacro:add_wheels name="right" flag="-1" />
<!-- 支撑轮 -->
<!-- 支撑轮属性 -->
<xacro:property name="support_wheel_radius" value="0.0075" /> <!-- 支撑轮半径 -->
<!-- 支撑轮宏 -->
<xacro:macro name="add_support_wheel" params="name flag" >
<link name="${name}_wheel">
<visual>
<geometry>
<sphere radius="${support_wheel_radius}" />
</geometry>
<origin xyz="0 0 0" rpy="0 0 0" />
<material name="black" />
</visual>
</link>
<joint name="${name}_wheel2base_link" type="continuous">
<parent link="base_link" />
<child link="${name}_wheel" />
<origin xyz="${flag * (base_link_radius - support_wheel_radius)} 0 ${-(base_link_length / 2 + earth_space / 2)}" />
<axis xyz="1 1 1" />
</joint>
</xacro:macro>
<xacro:add_support_wheel name="front" flag="1" />
<xacro:add_support_wheel name="back" flag="-1" />
</robot>
注意:demo_car_base.xacro文件是参考demo05_test.urdf文件编写出来的
下面进入到xacro文件夹下开启一个终端,运行:
rosrun xacro xacro demo_car_base.xacro > demo_car_base.urdf
可以发现成功生成了demo_car_base.urdf文件:
为了验证这个urdf的生成是正确的,可以运行如下指令:
check_urdf demo_car_base.urdf
发现没有抛异常,说明这个urdf文件是没有问题的
2.集成launch文件
方式1:先将 xacro 文件转换出 urdf 文件,然后集成
先将 xacro 文件解析成 urdf 文件:rosrun xacro xacro xxx.xacro > xxx.urdf
然后再按照之前的集成方式直接整合 launch 文件,内容示例(新建一个launch文件:demo06_car_base.launch):
<launch>
<param name="robot_description" textfile="$(find urdf01_rviz)/urdf/xacro/demo_car_base.urdf" />
<node pkg="rviz" type="rviz" name="rviz" args="-d $(find urdf01_rviz)/config/show_mycar.rviz" />
<node pkg="joint_state_publisher" type="joint_state_publisher" name="joint_state_publisher" output="screen" />
<node pkg="robot_state_publisher" type="robot_state_publisher" name="robot_state_publisher" output="screen" />
<node pkg="joint_state_publisher_gui" type="joint_state_publisher_gui" name="joint_state_publisher_gui" output="screen" />
</launch>
保存文件后,进入当前的工作空间source一下,然后再执行launch指令:
conda activate hjx
roslaunch urdf01_rviz demo06_car_base.launch
可以发现rviz中显示正常:
方式2: 无需中间文件urdf,在 launch 文件中直接加载 xacro(建议使用)
注意:加载robot_description
时使用command
属性,属性值就是调用 xacro 功能包的 xacro 程序直接解析 xacro 文件。
launch 内容示例(新建一个launch文件:demo07_car_base.launch):
<launch>
<param name="robot_description" command="$(find xacro)/xacro $(find urdf01_rviz)/urdf/xacro/demo_car_base.xacro" />
<node pkg="rviz" type="rviz" name="rviz" args="-d $(find urdf01_rviz)/config/show_mycar.rviz" />
<node pkg="joint_state_publisher" type="joint_state_publisher" name="joint_state_publisher" output="screen" />
<node pkg="robot_state_publisher" type="robot_state_publisher" name="robot_state_publisher" output="screen" />
<node pkg="joint_state_publisher_gui" type="joint_state_publisher_gui" name="joint_state_publisher_gui" output="screen" />
</launch>
保存文件后,进入当前的工作空间source一下,然后再执行launch指令:
conda activate hjx
roslaunch urdf01_rviz demo07_car_base.launch
同样可以发现rviz中显示正常:
Xacro的实操
在前面小车底盘基础之上,添加摄像头和雷达传感器。机器人模型由多部件组成,可以将不同组件设置进单独文件,最终通过文件包含实现组件的拼装。
实现流程:
-
首先编写摄像头和雷达的 xacro 文件
-
然后再编写一个组合文件,组合底盘、摄像头与雷达
-
最后,通过 launch 文件启动 Rviz 并显示模型
1.摄像头和雷达 Xacro 文件实现
摄像头 xacro 文件(demo_car_camera.xacro):
<!-- 摄像头相关的 xacro 文件 -->
<robot name="my_camera" xmlns:xacro="http://wiki.ros.org/xacro">
<!-- 摄像头属性 -->
<xacro:property name="camera_length" value="0.01" /> <!-- 摄像头长度(x) -->
<xacro:property name="camera_width" value="0.025" /> <!-- 摄像头宽度(y) -->
<xacro:property name="camera_height" value="0.025" /> <!-- 摄像头高度(z) -->
<xacro:property name="camera_x" value="0.08" /> <!-- 摄像头安装的x坐标 -->
<xacro:property name="camera_y" value="0.0" /> <!-- 摄像头安装的y坐标 -->
<xacro:property name="camera_z" value="${base_link_length / 2 + camera_height / 2}" /> <!-- 摄像头安装的z坐标:底盘高度 / 2 + 摄像头高度 / 2 -->
<!-- 摄像头关节以及link -->
<link name="camera">
<visual>
<geometry>
<box size="${camera_length} ${camera_width} ${camera_height}" />
</geometry>
<origin xyz="0.0 0.0 0.0" rpy="0.0 0.0 0.0" />
<material name="black" />
</visual>
</link>
<joint name="camera2base_link" type="fixed">
<parent link="base_link" />
<child link="camera" />
<origin xyz="${camera_x} ${camera_y} ${camera_z}" />
</joint>
</robot>
雷达 xacro 文件(demo_car_laser.xacro):
<!--
小车底盘添加雷达
-->
<robot name="my_laser" xmlns:xacro="http://wiki.ros.org/xacro">
<!-- 雷达支架 -->
<xacro:property name="support_length" value="0.15" /> <!-- 支架长度 -->
<xacro:property name="support_radius" value="0.01" /> <!-- 支架半径 -->
<xacro:property name="support_x" value="0.0" /> <!-- 支架安装的x坐标 -->
<xacro:property name="support_y" value="0.0" /> <!-- 支架安装的y坐标 -->
<xacro:property name="support_z" value="${base_link_length / 2 + support_length / 2}" /> <!-- 支架安装的z坐标:底盘高度 / 2 + 支架高度 / 2 -->
<link name="support">
<visual>
<geometry>
<cylinder radius="${support_radius}" length="${support_length}" />
</geometry>
<origin xyz="0.0 0.0 0.0" rpy="0.0 0.0 0.0" />
<material name="red">
<color rgba="0.8 0.2 0.0 0.8" />
</material>
</visual>
</link>
<joint name="support2base_link" type="fixed">
<parent link="base_link" />
<child link="support" />
<origin xyz="${support_x} ${support_y} ${support_z}" />
</joint>
<!-- 雷达属性 -->
<xacro:property name="laser_length" value="0.05" /> <!-- 雷达长度 -->
<xacro:property name="laser_radius" value="0.03" /> <!-- 雷达半径 -->
<xacro:property name="laser_x" value="0.0" /> <!-- 雷达安装的x坐标 -->
<xacro:property name="laser_y" value="0.0" /> <!-- 雷达安装的y坐标 -->
<xacro:property name="laser_z" value="${support_length / 2 + laser_length / 2}" /> <!-- 雷达安装的z坐标:支架高度 / 2 + 雷达高度 / 2 -->
<!-- 雷达关节以及link -->
<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="black" />
</visual>
</link>
<joint name="laser2support" type="fixed">
<parent link="support" />
<child link="laser" />
<origin xyz="${laser_x} ${laser_y} ${laser_z}" />
</joint>
</robot>
2.组合底盘摄像头与雷达的 xacro 文件
需要再新建一个xacro的集成文件(car.xacro):
<!-- 组合小车底盘与摄像头与雷达 -->
<robot name="mycar" xmlns:xacro="http://wiki.ros.org/xacro">
<xacro:include filename="demo_car_base.xacro" />
<xacro:include filename="demo_car_camera.xacro" />
<xacro:include filename="demo_car_laser.xacro" />
</robot>
3.launch 文件
最后编写launch文件(car.launch):
<launch>
<param name="robot_description" command="$(find xacro)/xacro $(find urdf01_rviz)/urdf/xacro/car.xacro" />
<node pkg="rviz" type="rviz" name="rviz" args="-d $(find urdf01_rviz)/config/show_mycar.rviz" />
<node pkg="joint_state_publisher" type="joint_state_publisher" name="joint_state_publisher" output="screen" />
<node pkg="robot_state_publisher" type="robot_state_publisher" name="robot_state_publisher" output="screen" />
<node pkg="joint_state_publisher_gui" type="joint_state_publisher_gui" name="joint_state_publisher_gui" output="screen" />
</launch>
保存文件后,进入当前的工作空间source一下,然后再执行launch指令:
conda activate hjx
roslaunch urdf01_rviz car.launch
最后可以发现我们的car在rviz中显示正常:
好啦 ~ 由于篇幅原因,目前有关URDF与Rviz的博客到此结束,但是,【ROS机器人入门:机器人系统仿真 】这个系列还没有结束。
后续的博客链接请见:
ROS机器人入门:机器人系统仿真【学习记录】——2-CSDN博客
~~~加油~~~
这篇博客的学习网址为:227引言_机器人系统仿真_哔哩哔哩_bilibili
所对应的图文教程为:6.1 概述 · Autolabor-ROS机器人入门课程《ROS理论与实践》零基础教程
参考资料:
第 6 章 机器人系统仿真 · Autolabor-ROS机器人入门课程《ROS理论与实践》零基础教程