文章目录
- 前言
- 1. 概述
- 1.1 概念
- 1.2 作用
- 1.3 相关组件
- 1.3.1 URDF
- 1.3.2 RViz
- 1.3.3 gazebo
- 1.4 准备
- 2. URDF集成Rviz基本流程
- 2.1 创建功能包,导入依赖
- 2.2 编写 URDF 文件
- 2.3 在 launch 文件中集成 URDF 与 Rviz
- 3. URDF语法详解
- 3.1 robot
- 3.2 link
- 3.3 joint
- 3.4 URDF练习
- 3.5 URDF工具
- 3.5.1 check_urdf 语法检查
- 3.5.2 urdf_to_graphiz 结构查看
前言
📢本系列将依托赵虚左老师的ROS课程,写下自己的一些心得与笔记。
📢课程链接:https://www.bilibili.com/video/BV1Ci4y1L7ZZ
📢讲义链接:http://www.autolabor.com.cn/book/ROSTutorials/index.html
📢 文章可能存在疏漏的地方,恳请大家指出。
1. 概述
1.1 概念
机器人系统仿真:是通过计算机对实体机器人系统进行模拟的技术,在 ROS 中,仿真实现涉及的内容主要有三:对机器人建模(URDF)、创建仿真环境(Gazebo) 以及 感知环境(Rviz) 等系统性实现。
1.2 作用
仿真优势 | 仿真缺陷 |
---|---|
低成本:当前机器人成本居高不下,动辄几十万,仿真可以大大降低成本,减小风险 | 仿真器所使用的物理引擎目前还不能够完全精确模拟真实世界的物理情况 |
高效:搭建的环境更为多样且灵活,可以提高测试效率以及测试覆盖率 | 仿真器构建的是关节驱动器(电机&齿轮箱)、传感器与信号通信的绝对理想情况,目前不支持模拟实际硬件缺陷或者一些临界状态等情形 |
高安全性:仿真环境下,无需考虑耗损问题 |
1.3 相关组件
1.3.1 URDF
URDF是 Unified Robot Description Format 的首字母缩写,直译为统一(标准化)机器人描述格式,可以以一种 XML 的方式描述机器人的部分结构,比如底盘、摄像头、激光雷达、机械臂以及不同关节的自由度…,该文件可以被 C++ 内置的解释器转换成可视化的机器人模型,是 ROS 中实现机器人仿真的重要组件
1.3.2 RViz
RViz 是 ROS Visualization Tool 的首字母缩写,直译为ROS的三维可视化工具。它的主要目的是以三维方式显示ROS消息,可以将 数据进行可视化表达。例如:可以显示机器人模型,可以无需编程就能表达激光测距仪(LRF)传感器中的传感 器到障碍物的距离,RealSense、Kinect或Xtion等三维距离 传感器的点云数据(PCD, Point Cloud Data),从相机获取的图像值等
1.3.3 gazebo
Gazebo是一款3D动态模拟器,用于显示机器人模型并创建仿真环境,能够在复杂的室内和室外环境中准确有效地模拟机器人。与游戏引擎提供高保真度的视觉模拟类似,Gazebo提供高保真度的物理模拟,其提供一整套传感器模型,以及对用户和程序非常友好的交互方式。
1.4 准备
将素材下载至文件夹内
git clone https://github.com/zx595306686/sim_demo.git
包含3个文件
2. URDF集成Rviz基本流程
需求描述:
在 Rviz 中显示一个盒状机器人
实现流程:
-
准备:新建功能包,导入依赖
-
核心:编写 urdf 文件
-
核心:在 launch 文件集成 URDF 与 Rviz
-
在 Rviz 中显示机器人模型
2.1 创建功能包,导入依赖
创建一个新的功能包,名称自定义,导入依赖包:urdf
与xacro
在当前功能包下,再新建几个目录:
- urdf: 存储 urdf 文件的目录
- meshes:机器人模型渲染文件,机器人的皮肤(暂不使用)
- config: 配置文件
- launch: 存储 launch 启动文件
2.2 编写 URDF 文件
如图所示创建文件
demo01_helloworld.urdf
<robot name="mycar">
<link name="base_link">
<visual>
<geometry>
<box size="0.5 0.2 0.1" />
</geometry>
</visual>
</link>
</robot>
2.3 在 launch 文件中集成 URDF 与 Rviz
demo01_helloworld.launch
<launch>
<!--在参数服务器载入URDF文件 -->
<param name = "robot_description" textfile = "$(find urdf01_rviz)/urdf/urdf/demo01_helloworld.urdf"/>
<!--启动rviz -->
<node pkg = "rviz" type = "rviz" name="rviz"/>
</launch>
robot_description
是固定的
Fixed Frame具体设置参考URDF文件具体实现
重复启动launch文件时,Rviz 之前的组件配置信息不会自动保存,需要重复执行步骤4的操作,为了方便使用,可以使用如下方式优化:
1.首先,将当前配置保存进config目录
2.然后,launch文件中 Rviz 的启动配置添加参数:args,值设置为-d 配置文件路径
<launch>
<!--在参数服务器载入URDF文件 -->
<param name = "robot_description" textfile = "$(find urdf01_rviz)/urdf/urdf/demo01_helloworld.urdf"/>
<!--启动rviz -->
<node pkg = "rviz" type = "rviz" name="rviz" args="-d $(find urdf01_rviz)/config/demo01.rviz"/>
</launch>
3. URDF语法详解
URDF 文件是一个标准的 XML 文件,在 ROS 中预定义了一系列的标签用于描述机器人模型,机器人模型可能较为复杂,但是 ROS 的 URDF 中机器人的组成却是较为简单,可以主要简化为两部分:连杆(link标签) 与 关节(joint标签),接下来我们就通过案例了解一下 URDF 中的不同标签:
- robot 根标签,类似于 launch文件中的launch标签
- link 连杆标签
- joint 关节标签
- gazebo 集成gazebo需要使用的标签
关于gazebo标签,后期在使用 gazebo 仿真时,才需要使用到,用于配置仿真环境所需参数,比如: 机器人材料属性、gazebo插件等,但是该标签不是机器人模型必须的,只有在仿真时才需设置
3.1 robot
urdf 中为了保证 xml 语法的完整性,使用了robot标签作为根标签,所有的 link 和 joint 以及其他标签都必须包含在 robot 标签内,在该标签内可以通过 name 属性设置机器人模型的名称
1.属性
name: 指定机器人模型的名称
2.子标签
其他标签都是子级标签
3.2 link
urdf 中的 link 标签用于描述机器人某个部件(也即刚体部分)的外观和物理属性,比如: 机器人底座、轮子、激光雷达、摄像头…每一个部件都对应一个 link, 在 link 标签内,可以设计该部件的形状、尺寸、颜色、惯性矩阵、碰撞参数等一系列属性
1.属性
name —> 为连杆命名
2.子标签
- - visual —> 描述外观(对应的数据是可视的)
标签名称 | 说明 | 子标签 | 属性 |
---|---|---|---|
geometry | 设置连杆的形状 | box(盒状) | size=长(x) 宽(y) 高(z) |
cylinder(圆柱) | radius=半径 length=高度 | ||
sphere(球体) | radius=半径 | ||
mesh(为连杆添加皮肤) | filename=资源路径(格式:package:///
| ||
origin | 设置偏移量与倾斜弧度 | xyz | x偏移 y便宜 z偏移 |
rpy | x翻滚 y俯仰 z偏航 (单位是弧度) | ||
metrial | 设置材料属性(颜色) | color | rgba=红绿蓝权重值与透明度 (每个权重值以及透明度取值[0,1]) |
- collision —> 连杆的碰撞属性
- Inertial —> 连杆的惯性矩阵
需求:分别生成长方体、圆柱与球体的机器人部件
demo02_link.urdf
<!-- 设置不同形状的机器人部件-->
<robot name = "my_robot">
<link name = "base_link">
<!-- 可视化标签-->
<visual>
<!-- 形状-->
<geometry>
<!-- 1.立方体-->
<!-- <box size="0.3 0.4 0.5"/> -->
<!-- 2.圆柱-->
<!-- <cylinder radius="0.5" length="1.0"/> -->
<!-- 3.球体-->
<!-- <sphere radius="0.5"/> -->
<!-- 皮肤-->
<mesh filename="package://urdf01_rviz/meshes/autolabor_mini.stl"/>
</geometry>
<!-- 偏移量与倾斜弧度-->
<!--
xyz 设置偏移量
rpy设置倾斜弧度
-->
<origin xyz = "0 0 0" rpy="1.57 0 1.57"/>
<!-- 颜色-->
<material name="robot_color">
<color rgba="1 0 1 1"/>
</material>
</visual>
</link>
</robot>
demo02_link.launch
<launch>
<!--在参数服务器载入URDF文件 -->
<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/demo01.rviz"/>
</launch>
长方体
圆柱
球体
皮肤
偏移
旋转
颜色
3.3 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用于设置围绕哪个关节轴运动。
需求:创建机器人模型,底盘为长方体,在长方体的前面添加一摄像头,摄像头可以沿着 Z 轴 360 度旋转。
demo03_joint.urdf
<!-- 设置机器人底盘 并添加摄像头-->
<robot name = "my_robot">
<!-- 底盘link-->
<link name = "base_link">
<visual>
<geometry>
<box size="0.3 0.2 0.1"/>
</geometry>
<origin xyz = "0 0 0" rpy="0 0 0"/>
<material name="camera_color">
<color rgba="1 0 0 1"/>
</material>
</visual>
</link>
<!--摄像头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="robot_color">
<color rgba="0 0 1 1"/>
</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>
demo03_joint.launch
<launch>
<!--在参数服务器载入URDF文件 -->
<param name = "robot_description" textfile = "$(find urdf01_rviz)/urdf/urdf/demo03_joint.urdf"/>
<!--启动rviz -->
<node pkg = "rviz" type = "rviz" name="rviz" args="-d $(find urdf01_rviz)/config/demo01.rviz"/>
</launch>
运行出现了这样的问题,但改正之后未能复现,可能是urdf文件一些细节的地方出了问题.
[ERROR] [1673507658.550707801]: Failed to build tree: Joint [camera2baselink] is missing a parent and/or child link specification.
roslauch 运行后,出现以下问题No transform from [camera] to [base_link]
launch文件改为:
<launch>
<!--在参数服务器载入URDF文件 -->
<param name = "robot_description" textfile = "$(find urdf01_rviz)/urdf/urdf/demo03_joint.urdf"/>
<!--启动rviz -->
<node pkg = "rviz" type = "rviz" name="rviz" args="-d $(find urdf01_rviz)/config/demo01.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" />
</launch>
若还报错,去掉URDF文件里的中文注释
<robot name = "my_robot">
<link name = "base_link">
<visual>
<geometry>
<box size="0.3 0.2 0.1"/>
</geometry>
<origin xyz = "0 0 0" rpy="0 0 0"/>
<material name="camera_color">
<color rgba="1 0 0 1"/>
</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="robot_color">
<color rgba="0 0 1 1"/>
</material>
</visual>
</link>
<joint name="camera2baselink" type="continuous">
<parent link = "base_link"/>
<child link = "camera" />
<origin xyz="0.12 0 0.075" rpy="0 0 0" />
<axis xyz="0 0 1" />
</joint>
</robot>
注意: joint的位置是以摄像头中心点为标准的.
接下来控制摄像头的旋转
sudo apt-get install ros-melodic-joint-state-publisher-gui
<launch>
<!--在参数服务器载入URDF文件 -->
<param name = "robot_description" textfile = "$(find urdf01_rviz)/urdf/urdf/demo03_joint.urdf"/>
<!--启动rviz -->
<node pkg = "rviz" type = "rviz" name="rviz" args="-d $(find urdf01_rviz)/config/demo01.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>
若产生抖动,注释掉joint_state_publisher
base_footprint优化urdf
前面实现的机器人模型是半沉到地下的,因为默认情况下: 底盘的中心点位于地图原点上,所以会导致这种情况产生,可以使用的优化策略,将初始 link 设置为一个尺寸极小的 link(比如半径为 0.001m 的球体,或边长为 0.001m 的立方体),然后再在初始 link 上添加底盘等刚体,这样实现,虽然仍然存在初始link半沉的现象,但是基本可以忽略了。这个初始 link 一般称之为 base_footprint
<robot name = "my_robot">
<link name = "base_footprint">
<visual>
<geometry>
<box size="0.001 0.001 0.001"/>
</geometry>
<origin xyz = "0 0 0" rpy="0 0 0"/>
<material name="color1">
<color rgba="0 1 1 1"/>
</material>
</visual>
</link>
<link name = "base_link">
<visual>
<geometry>
<box size="0.3 0.2 0.1"/>
</geometry>
<origin xyz = "0 0 0" rpy="0 0 0"/>
<material name="robot_color">
<color rgba="1 0 0 1"/>
</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="camera_color">
<color rgba="0 0 1 1"/>
</material>
</visual>
</link>
<joint name="baselink2basefootprint" type="fixed">
<parent link = "base_footprint"/>
<child link = "base_link" />
<origin xyz="0 0 0.05" rpy="0 0 0" />
<axis xyz="0 0 0" />
</joint>
<joint name="camera2baselink" type="continuous">
<parent link = "base_link"/>
<child link = "camera" />
<origin xyz="0.12 0 0.075" rpy="0 0 0" />
<axis xyz="0 0 1" />
</joint>
</robot>
base_link
改为 base_footprint
3.4 URDF练习
需求描述:
创建一个四轮圆柱状机器人模型,机器人参数如下,底盘为圆柱状,半径 10cm,高 8cm,四轮由两个驱动轮和两个万向支撑轮组成,两个驱动轮半径为 3.25cm,轮胎宽度1.5cm,两个万向轮为球状,半径 0.75cm,底盘离地间距为 1.5cm(与万向轮直径一致)
PS:连杆之间的偏移最好在joint上进行.
demo05_test.urdf
<robot name = "my_robot">
<link name = "base_footprint">
<visual>
<geometry>
<box size="0.001 0.001 0.001"/>
</geometry>
<origin xyz = "0 0 0" rpy="0 0 0"/>
<material name="color1">
<color rgba="0 1 1 1"/>
</material>
</visual>
</link>
<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="robot_color">
<color rgba="0.2 0.2 0.2 1"/>
</material>
</visual>
</link>
<joint name="baselink2basefootprint" type="fixed">
<parent link = "base_footprint"/>
<child link = "base_link" />
<origin xyz="0 0 0.055" rpy="0 0 0" />
<axis xyz="0 0 0" />
</joint>
<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="wheel_color">
<color rgba="0 0 0 1"/>
</material>
</visual>
</link>
<joint name="leftwheel2baselink" 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="wheel_color">
<color rgba="0 0 0 1"/>
</material>
</visual>
</link>
<joint name="rightwheel2baselink" type="continuous">
<parent link = "base_link"/>
<child link = "right_wheel" />
<origin xyz="0 -0.1 -0.0225" />
<axis xyz="0 1 0" />
</joint>
<link name = "front_wheel">
<visual>
<geometry>
<sphere radius="0.0075" />
</geometry>
<origin xyz = "0 0 0" rpy="0 0 0"/>
<material name="wheel_color">
<color rgba="0 0 0 1"/>
</material>
</visual>
</link>
<joint name="frontwheel2baselink" 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="wheel_color">
<color rgba="0 0 0 1"/>
</material>
</visual>
</link>
<joint name="backwheel2baselink" type="continuous">
<parent link = "base_link"/>
<child link = "back_wheel" />
<origin xyz="-0.0925 0 -0.0475" />
<axis xyz="1 1 1" />
</joint>
</robot>
demo05_test.launch
<launch>
<!--在参数服务器载入URDF文件 -->
<param name = "robot_description" textfile = "$(find urdf01_rviz)/urdf/urdf/demo05_test.urdf"/>
<!--启动rviz -->
<node pkg = "rviz" type = "rviz" name="rviz" args="-d $(find urdf01_rviz)/config/demo01.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>
3.5 URDF工具
在 ROS 中,提供了一些工具来方便 URDF 文件的编写,比如:
- check_urdf命令可以检查复杂的 urdf 文件是否存在语法问题
- urdf_to_graphiz命令可以查看 urdf 模型结构,显示不同 link 的层级关系
当然,要使用工具之前,首先需要安装,安装命令:sudo apt install liburdfdom-tools
3.5.1 check_urdf 语法检查
进入urdf文件所属目录,调用:check_urdf urdf文件,如果不抛出异常,说明文件合法,否则非法
合法
yuan@yuan-Legion-Y9000P-IAH7H:~/catkin_ws/src/urdf01_rviz/urdf/urdf$ check_urdf demo05_test.urdf
robot name is: my_robot
---------- Successfully Parsed XML ---------------
root Link: base_footprint has 1 child(ren)
child(1): base_link
child(1): back_wheel
child(2): front_wheel
child(3): left_wheel
child(4): right_wheel
3.5.2 urdf_to_graphiz 结构查看
进入urdf文件所属目录,调用:urdf_to_graphiz urdf文件,当前目录下会生成 pdf 文件
yuan@yuan-Legion-Y9000P-IAH7H:~/catkin_ws/src/urdf01_rviz/urdf/urdf$ urdf_to_graphiz demo05_test.urdf
Created file my_robot.gv
Created file my_robot.pdf