**机器人系统仿真:**是通过计算机对实体机器人系统进行模拟的技术,在 ROS 中,仿真实现涉及的内容主要有三:对机器人建模(URDF)、创建仿真环境(Gazebo)以及感知环境(Rviz)等系统性实现。
仿真在机器人系统研发过程中占有举足轻重的地位,在研发与测试中较之于实体机器人实现,仿真有如下几点的显著优势:
1.**低成本:**当前机器人成本居高不下,动辄几十万,仿真可以大大降低成本,减小风险
2.**高效:**搭建的环境更为多样且灵活,可以提高测试效率以及测试覆盖率
3.**高安全性:**仿真环境下,无需考虑耗损问题
机器人在仿真环境与实际环境下的表现差异较大,换言之,仿真并不能完全做到模拟真实的物理世界,存在一些"失真"的情况,原因:
1.仿真器所使用的物理引擎目前还不能够完全精确模拟真实世界的物理情况
2.仿真器构建的是关节驱动器(电机&齿轮箱)、传感器与信号通信的绝对理想情况,目前不支持模拟实际硬件缺陷或者一些临界状态等情形
需求描述:
在 Rviz 中显示一个盒状机器人
实现流程:
创建一个新的功能包,名称自定义,导入依赖包:urdf
与xacro
在当前功能包下,再新建几个目录:
urdf
: 存储 urdf 文件的目录
meshes
:机器人模型渲染文件(暂不使用)
config
: 配置文件
launch
: 存储 launch 启动文件
新建一个子级文件夹:urdf
(可选),文件夹中添加一个.urdf
文件,复制如下内容:
<robot name="mycar">
<link name="base_link">
<visual>
<geometry>
<box size="0.5 0.2 0.1" />
geometry>
visual>
link>
robot>
Copy
在launch
目录下,新建一个 launch 文件,该 launch 文件需要启动 Rviz,并导入 urdf 文件,Rviz 启动后可以自动载入解析urdf
文件,并显示机器人模型,核心问题:如何导入 urdf 文件? 在 ROS 中,可以将 urdf 文件的路径设置到参数服务器,使用的参数名是:robot_description
,示例代码如下:
<launch>
<param name="robot_description" textfile="$(find 包名)/urdf/urdf/urdf01_HelloWorld.urdf" />
<node pkg="rviz" type="rviz" name="rviz" />
launch>
Copy
rviz 启动后,会发现并没有盒装的机器人模型,这是因为默认情况下没有添加机器人显示组件,需要手动添加,添加方式如下:
设置完毕后,可以正常显示了
重复启动launch
文件时,Rviz 之前的组件配置信息不会自动保存,需要重复执行步骤4的操作,为了方便使用,可以使用如下方式优化:
首先,将当前配置保存进config
目录
然后,launch
文件中 Rviz 的启动配置添加参数:args
,值设置为-d 配置文件路径
<launch>
<param name="robot_description" textfile="$(find 包名)/urdf/urdf/urdf01_HelloWorld.urdf" />
<node pkg="rviz" type="rviz" name="rviz" args="-d $(find 报名)/config/rviz/show_mycar.rviz" />
launch>
Copy
再启动时,就可以包含之前的组件配置了,使用更方便快捷。
urdf 中为了保证 xml 语法的完整性,使用了robot
标签作为根标签,所有的 link 和 joint 以及其他标签都必须包含在 robot 标签内,在该标签内可以通过 name 属性设置机器人模型的名称
name: 指定机器人模型的名称
其他标签都是子级标签
urdf 中的 link 标签用于描述机器人某个部件(也即刚体部分)的外观和物理属性,比如: 机器人底座、轮子、激光雷达、摄像头…每一个部件都对应一个 link, 在 link 标签内,可以设计该部件的形状、尺寸、颜色、惯性矩阵、碰撞参数等一系列属性
urdf 中的 joint 标签用于描述机器人关节的运动学和动力学属性,还可以指定关节运动的安全极限,机器人的两个部件(分别称之为 parent link 与 child link)以"关节"的形式相连接,不同的关节有不同的运动形式: 旋转、滑动、固定、旋转速度、旋转角度限制…,比如:安装在底座上的轮子可以360度旋转,而摄像头则可能是完全固定在底座上。
parent(必需的)
parent link的名字是一个强制的属性:
child(必需的)
child link的名字是一个强制的属性:
origin
axis
需求描述:
创建一个四轮圆柱状机器人模型,机器人参数如下,底盘为圆柱状,半径 10cm,高 8cm,四轮由两个驱动轮和两个万向支撑轮组成,两个驱动轮半径为 3.25cm,轮胎宽度1.5cm,两个万向轮为球状,半径 0.75cm,底盘离地间距为 1.5cm(与万向轮直径一致)
urdf 文件:基本实现
<robot name="mycar">
<link name="base_footprint">
<visual>
<geometry>
<sphere radius="0.001"/>
geometry>
visual>
link>
<link name="base_link">
<visual>
<geometry>
<cylinder radius="0.1" length="0.08"/>
geometry>
<origin xyz="0.0 0.0 0.0" rpy="0.0 0.0 0.0"/>
<material name="baselink_color">
<color rgba="0.7 0.3 0.2 0.8"/>
material>
visual>
link>
<joint name="baselink2basefootprint" type="fixed">
<origin xyz="0.0 0.0 0.055" rpy="0.0 0.0 0.0"/>
<parent link="base_footprint"/>
<child link="base_link"/>
joint>
<link name="left_Wheel">
<visual>
<geometry>
<cylinder radius="0.0325" length="0.015"/>
geometry>
<origin xyz="0.0 0.0 0.0" rpy="1.5708 0.0 0.0"/>
<material name="Whell03_color">
<color rgba="0.0 0.0 0.0 0.8"/>
material>
visual>
link>
<joint name="LeftWheel2baselink" type="continuous">
<origin xyz="0.0 0.1 -0.0225" rpy="0.0 0.0 0.0"/>
<parent link="base_link"/>
<child link="left_Wheel"/>
<axis xyz="0.0 1.0 0.0"/>
joint>
<link name="right_Wheel">
<visual>
<geometry>
<cylinder radius="0.0325" length="0.015"/>
geometry>
<origin xyz="0.0 0.0 0.0" rpy="1.5708 0.0 0.0"/>
<material name="Whell03_color">
<color rgba="0.0 0.0 0.0 0.8"/>
material>
visual>
link>
<joint name="RightWheel2baselink" type="continuous">
<origin xyz="0.0 -0.1 -0.0225" rpy="0.0 0.0 0.0"/>
<parent link="base_link"/>
<child link="right_Wheel"/>
<axis xyz="0.0 1.0 0.0"/>
joint>
<link name="front_Wheel">
<visual>
<geometry>
<sphere radius="0.0075"/>
geometry>
<origin xyz="0.0 0.0 0.0" rpy="0.0 0.0 0.0"/>
<material name="Whell01_color">
<color rgba="0.0 0.0 0.0 0.8"/>
material>
visual>
link>
<joint name="FrontWheel2baselink" type="continuous">
<origin xyz="0.08 0.0 -0.0475" rpy="0.0 0.0 0.0"/>
<parent link="base_link"/>
<child link="front_Wheel"/>
<axis xyz="1.0 1.0 0.0"/>
joint>
<link name="back_Wheel">
<visual>
<geometry>
<sphere radius="0.0075"/>
geometry>
<origin xyz="0.0 0.0 0.0" rpy="0.0 0.0 0.0"/>
<material name="Whell02_color">
<color rgba="0.0 0.0 0.0 0.8"/>
material>
visual>
link>
<joint name="BackWheel2baselink" type="continuous">
<origin xyz="-0.08 0.0 -0.0475" rpy="0.0 0.0 0.0"/>
<parent link="base_link"/>
<child link="back_Wheel"/>
<axis xyz="1.0 1.0 0.0"/>
joint>
robot>
launch 文件:
<launch>
<param name="robot_description" textfile="$(find urdf01_rviz)/urdf/urdf/demo05_test.urdf" />
<node pkg="rviz" type="rviz" name="rviz" args="-d $(find urdf01_rviz)/config/show_mycar.rviz" />
<node pkg="joint_state_publisher_gui" type="joint_state_publisher_gui" name="joint_state_publisher_gui" />
<node pkg="robot_state_publisher" type="robot_state_publisher" name="robot_state_publisher" />
launch>
在 ROS 中,提供了一些工具来方便 URDF 文件的编写,比如:
check_urdf
命令可以检查复杂的 urdf 文件是否存在语法问题urdf_to_graphiz
命令可以查看 urdf 模型结构,显示不同 link 的层级关系当然,要使用工具之前,首先需要安装,安装命令:sudo apt install liburdfdom-tools
进入urdf文件所属目录,调用:check_urdf urdf文件
,如果不抛出异常,说明文件合法,否则非法
实验:
注意右键进入第一个urdf文件夹里才可以tab补全
进入urdf文件所属目录,调用:urdf_to_graphiz urdf文件
,当前目录下会生成 pdf 文件
概念
Xacro 是 XML Macros 的缩写,Xacro 是一种 XML 宏语言,是可编程的 XML。
原理
Xacro 可以声明变量,可以通过数学运算求解,使用流程控制控制执行顺序,还可以通过类似函数的实现,封装固定的逻辑,将逻辑中需要的可变的数据以参数的方式暴露出去,从而提高代码复用率以及程序的安全性。
作用
较之于纯粹的 URDF 实现,可以编写更安全、精简、易读性更强的机器人模型文件,且可以提高编写效率。
需求描述:
使用xacro优化上一节案例中驱动轮实现,需要使用变量封装底盘的半径、高度,使用数学公式动态计算底盘的关节点坐标,使用 Xacro 宏封装轮子重复的代码并调用宏创建两个轮子(注意: 在此,演示 Xacro 的基本使用,不必要生成合法的 URDF )。
准备:
创建功能包,导入 urdf 与 xacro。
编写 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>
<joint name="${wheel_name}2link" type="continuous">
<parent link="base_link" />
<child link="${wheel_name}_wheel" />
<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>
命令行进入 xacro文件 所属目录,执行:rosrun xacro xacro xxx.xacro > xxx.urdf
, 会将 xacro 文件解析为 urdf 文件。
注意: 该案例编写生成的是非法的 URDF 文件,目的在于演示 Xacro 的极简使用以及优点。
xacro 提供了可编程接口,类似于计算机语言,包括变量声明调用、函数声明与调用等语法实现。在使用 xacro 生成 urdf 时,根标签robot
中必须包含命名空间声明:xmlns:xacro="http://wiki.ros.org/xacro"
用于封装 URDF 中的一些字段,比如: PAI 值,小车的尺寸,轮子半径 …
属性定义
<xacro:property name="xxxx" value="yyyy" />
属性调用
${属性名称}
算数运算
${数学表达式}
类似于函数实现,提高代码复用率,优化代码结构,提高安全性
宏定义
<xacro:macro name="宏名称" params="参数列表(多参数之间使用空格分隔)">
.....
参数调用格式: ${参数名}
xacro:macro>
宏调用
<xacro:宏名称 参数1=xxx 参数2=xxx/>
机器人由多部件组成,不同部件可能封装为单独的 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 优化 URDF 版的小车底盘模型实现
预期结果演示:
<robot xmlns:xacro="http://www.ros.org/wiki/xacro" name="mycar">
<xacro:property name="footprint_radius" value="0.001"/>
<link name="base_footprint">
<visual>
<geometry>
<sphere radius="${footprint_radius}"/>
geometry>
visual>
link>
<xacro:property name="base_radius" value="0.1"/>
<xacro:property name="base_length" value="0.08"/>
<xacro:property name="lidi" value="0.015"/>
<xacro:property name="base_joint_z" value="${base_length / 2 + lidi}"/>
<link name="base_link">
<visual>
<geometry>
<cylinder radius="${base_radius}" length="${base_length}"/>
geometry>
<origin xyz="0.0 0.0 0.0" rpy="0.0 0.0 0.0"/>
<material name="baselink_color">
<color rgba="0.7 0.3 0.2 0.5"/>
material>
visual>
link>
<joint name="baselink2basefootprint" type="fixed">
<origin xyz="0.0 0.0 ${base_joint_z}" rpy="0.0 0.0 0.0"/>
<parent link="base_footprint"/>
<child link="base_link"/>
joint>
<xacro:property name="wheel_radius" value="0.0325"/>
<xacro:property name="wheel_length" value="0.015"/>
<xacro:property name="PI" value="3.1415927"/>
<xacro:property name="wheel_joint_z" value="${(base_length / 2 + lidi - wheel_radius) * -1}"/>
<xacro:macro name="wheel_func" params="wheel_name flag">
<link name="${wheel_name}">
<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="whell03_color">
<color rgba="0.0 0.0 0.0 0.8"/>
material>
visual>
link>
<joint name="${wheel_name}2baselink" type="continuous">
<origin xyz="0.0 ${flag * 0.1} ${wheel_joint_z}" rpy="0.0 0.0 0.0"/>
<parent link="base_link"/>
<child link="${wheel_name}"/>
<axis xyz="0.0 1.0 0.0"/>
joint>
xacro:macro>
<xacro:wheel_func wheel_name="base_l_wheel_joint" flag="1"/>
<xacro:wheel_func wheel_name="base_r_wheel_joint" flag="-1"/>
<xacro:property name="sup_wheel_radius" value="0.0075"/>
<xacro:property name="sup_wheel_joint_x" value="${base_radius - sup_wheel_radius }"/>
<xacro:property name="sup_wheel_joint_z" value="${(base_length / 2 + lidi - sup_wheel_radius ) * -1}"/>
<xacro:macro name="sup_wheel_func" params="name flag">
<link name="${name}_wheel">
<visual>
<geometry>
<sphere radius="${sup_wheel_radius}"/>
geometry>
<origin xyz="0.0 0.0 0.0" rpy="0.0 0.0 0.0"/>
<material name="whell01_color">
<color rgba="0.0 0.0 0.0 0.8"/>
material>
visual>
link>
<joint name="${name}2baselink" type="continuous">
<origin xyz="${sup_wheel_joint_x * flag } 0.0 ${sup_wheel_joint_z}" rpy="0.0 0.0 0.0"/>
<parent link="base_link"/>
<child link="${name}_wheel"/>
<axis xyz="1.0 1.0 1.0"/>
joint>
xacro:macro>
<xacro:sup_wheel_func name="front" flag="1"/>
<xacro:sup_wheel_func name="back" flag="-1"/>
robot>
**方式1:**先将 xacro 文件转换出 urdf 文件,然后集成
先将 xacro 文件解析成 urdf 文件:rosrun xacro xacro xxx.xacro > xxx.urdf
然后再按照之前的集成方式直接整合 launch 文件;
**方式2:**在 launch 文件中直接加载 xacro(建议使用)
launch 内容示例:
<launch>
<param name="robot_description" command="$(find xacro)/xacro $(find urdf01_rviz)/urdf/xacro/demo05_car_base.xacro" />
<node pkg="rviz" type="rviz" name="rviz" args="-d $(find urdf01_rviz)/config/show_mycar.rviz" />
<node pkg="joint_state_publisher_gui" type="joint_state_publisher_gui" name="joint_state_publisher_gui" />
<node pkg="robot_state_publisher" type="robot_state_publisher" name="robot_state_publisher" />
launch>
核心代码:
<param name="robot_description" command="$(find xacro)/xacro $(find urdf01_rviz)/urdf/xacro/demo05_car_base.xacro" />
加载robot_description
时使用command
属性,属性值就是调用 xacro 功能包的 xacro 程序直接解析 xacro 文件。
需求描述:
在前面小车底盘基础之上,添加摄像头和雷达传感器。
结果演示:
实现分析:
机器人模型由多部件组成,可以将不同组件设置进单独文件,最终通过文件包含实现组件的拼装。
实现流程:
摄像头 xacro 文件:
<robot xmlns:xacro="http://www.ros.org/wiki/xacro" name="mycar">
<xacro:property name="camera_length" value="0.02"/>
<xacro:property name="camera_width" value="0.05"/>
<xacro:property name="camera_hight" value="0.05"/>
<xacro:property name="joint_camera_x" value="0.08"/>
<xacro:property name="joint_camera_y" value="0.0"/>
<xacro:property name="joint_camera_z" value="${base_length / 2 + camera_length / 2 + lidi }"/>
<link name="camera">
<visual>
<geometry>
<box size="${camera_length} ${camera_width} ${camera_hight}"/>
geometry>
<origin xyz="0.0 0.0 0.0" rpy="0.0 0.0 0.0"/>
<material name="camera_black">
<color rgba="0.0 0.0 0.0 1.0"/>
material>
visual>
link>
<joint name="camera2base" type="fixed">
<parent link="base_link"/>
<child link="camera"/>
<origin xyz="${joint_camera_x} ${joint_camera_y} ${joint_camera_z}" rpy="0.0 0.0 0.0"/>
joint>
robot>
雷达 xacro 文件:
<robot xmlns:xacro="http://www.ros.org/wiki/xacro" name="mycar">
<xacro:property name="support_radius" value="0.01"/>
<xacro:property name="support_length" value="0.15"/>
<xacro:property name="joint_support_x" value="0"/>
<xacro:property name="joint_support_y" value="0"/>
<xacro:property name="joint_support_z" value="${support_length / 2+ base_length / 2 }"/>
<xacro:property name="laser_radius" value="0.03"/>
<xacro:property name="laser_length" value="0.05"/>
<xacro:property name="joint_laser_x" value="0"/>
<xacro:property name="joint_laser_y" value="0"/>
<xacro:property name="joint_laser_z" value="${support_length / 2+ laser_length / 2}"/>
<link name="support">
<visual>
<origin xyz="0.0 0.0 0.0" rpy="0.0 0.0 0.0"/>
<geometry>
<cylinder radius="${support_radius}" length="${support_length}"/>
geometry>
<material name="yellow">
<color rgba="0.8 0.5 0.0 0.5"/>
material>
visual>
link>
<joint name="support2baselink" type="fixed">
<parent link="base_link"/>
<child link="support"/>
<origin xyz="${joint_support_x} ${joint_support_y} ${joint_support_z}" rpy="0.0 0.0 0.0"/>
joint>
<link name="laser">
<visual>
<origin xyz="0.0 0.0 0.0" rpy="0.0 0.0 0.0"/>
<geometry>
<cylinder radius="${laser_radius}" length="${laser_length}"/>
geometry>
<material name="laser">
<color rgba="0.0 0.0 0.0 0.5"/>
material>
visual>
link>
<joint name="laser2supportlink" type="fixed">
<parent link="support"/>
<child link="laser"/>
<origin xyz="${joint_laser_x} ${joint_laser_y} ${joint_laser_z}" rpy="0 0 0"/>
joint>
robot>
<robot xmlns:xacro="http://www.ros.org/wiki/xacro" name="mycar">
<xacro:include filename="demo05_car_base.urdf.xacro"/>
<xacro:include filename="demo06_car_camera.urdf.xacro"/>
<xacro:include filename="demo07_car_laser.urdf.xacro"/>
robot>
<launch>
<param name="robot_description" command="$(find xacro)/xacro $(find urdf01_rviz)/urdf/xacro/car.urdf.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" />
<node pkg="robot_state_publisher" type="robot_state_publisher" name="robot_state_publisher" />
launch>
**Arbotix:**Arbotix 是一款控制电机、舵机的控制板,并提供相应的 ros 功能包,这个功能包的功能不仅可以驱动真实的 Arbotix 控制板,它还提供一个差速控制器,通过接受速度控制指令更新机器人的 joint 状态,从而帮助我们实现机器人在 rviz 中的运动。
这个差速控制器在 arbotix_python 程序包中,完整的 arbotix 程序包还包括多种控制器,分别对应 dynamixel 电机、多关节机械臂以及不同形状的夹持器。
接下来,通过一个案例演示 arbotix 的使用。
需求描述:
控制机器人模型在 rviz 中做圆周运动
结果演示:
实现流程:
**方式1:**命令行调用
sudo apt-get install ros-<<VersionName()>>-arbotix
将 <
**方式2:**源码安装
先从 github 下载源码,然后调用 catkin_make 编译
git clone https://github.com/vanadiumlabs/arbotix_ros.git
urdf 和 xacro 调用上一讲实现即可
添加 arbotix 所需配置文件
# 该文件是控制器配置,一个机器人模型可能有多个控制器,比如: 底盘、机械臂、夹持器(机械手)....
# 因此,根 name 是 controller
controllers: {
# 单控制器设置
base_controller: {
#类型: 差速控制器
type: diff_controller,
#参考坐标
base_frame_id: base_footprint,
#两个轮子之间的间距
base_width: 0.2,
#控制频率
ticks_meter: 2000,
#PID控制参数,使机器人车轮快速达到预期速度
Kp: 12,
Kd: 12,
Ki: 0,
Ko: 50,
#加速限制
accel_limit: 1.0
}
}
另请参考: http://wiki.ros.org/arbotix_python/diff_controller
launch 示例代码
<launch>
<param name="robot_description" command="$(find xacro)/xacro $(find urdf01_rviz)/urdf/xacro/car.urdf.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" />
<node pkg="robot_state_publisher" type="robot_state_publisher" name="robot_state_publisher" />
<node pkg="arbotix_python" type="arbotix_driver" name="driver" output="screen">
<rosparam command="load" file="$(find urdf01_rviz)/config/control.yaml" />
<param name="sim" value="true"/>
node>
launch>
代码解释:
调用了 arbotix_python 功能包下的 arbotix_driver 节点
arbotix 驱动机器人运行时,需要获取机器人信息,可以通过 file 加载配置文件
在仿真环境下,需要配置 sim 为 true**启动launch:**roslaunch xxxx …launch
启动报错:
Joint state with name: "base_l_wheel_joint" was received but not found in URDF
原因:与arbotix配置的名字不同
demo05_car_base.urdf将左右轮的joint节点的name改成"base_l_wheel_joint"和"base_r_wheel_joint"
将xacro变换成urdf:rosrun xacro xacro demo05_car_base.urdf.xacro > demo05.urdf
转换成pdf查看: urdf_to_graphiz urdf/demo05.urdf
配置 rviz:
控制小车运动:
此时调用 rostopic list 会发现一个熟悉的话题: /cmd_vel
rostopic pub -r 10 /cmd_vel geometry_msgs/Twist '{linear: {x: 0.2, y: 0, z: 0}, angular: {x: 0, y: 0, z: 0.5}}'
另请参考:
URDF 需要集成进 Rviz 或 Gazebo 才能显示可视化的机器人模型
URDF 与 Gazebo 集成流程与 Rviz 实现类似,主要步骤如下:
创建新功能包,导入依赖包: urdf、xacro、gazebo_ros、gazebo_ros_control、gazebo_plugins
注意, 当 URDF 需要与 Gazebo 集成时,和 Rviz 有明显区别:
1.必须使用 collision 标签,因为既然是仿真环境,那么必然涉及到碰撞检测,collision 提供碰撞检测的依据。
2.必须使用 inertial 标签,此标签标注了当前机器人某个刚体部分的惯性矩阵,用于一些力学相关的仿真计算。
3.颜色设置,也需要重新使用 gazebo 标签标注,因为之前的颜色设置为了方便调试包含透明度,仿真环境下没有此选项
代码解释:
<include file="$(find gazebo_ros)/launch/empty_world.launch" />
<node pkg="gazebo_ros" type="spawn_model" name="model" args="-urdf -model mycar -param robot_description" />
较之于 rviz,gazebo在集成 URDF 时,需要做些许修改,比如:必须添加 collision 碰撞属性相关参数、必须添加 inertial 惯性矩阵相关参数,另外,如果直接移植 Rviz 中机器人的颜色设置是没有显示的,颜色设置也必须做相应的变更。
如果机器人link是标准的几何体形状,和link的 visual 属性设置一致即可。
惯性矩阵的设置需要结合link的质量与外形参数动态生成, 原则上,除了 base_footprint 外,机器人的每个刚体部分都需要设置惯性矩阵,且惯性矩阵必须经计算得出,如果随意定义刚体部分的惯性矩阵,那么可能会导致机器人在 Gazebo 中出现抖动,移动等现象。
在 gazebo 中显示 link 的颜色,必须要使用指定的标签:
<gazebo reference="link节点名称">
<material>Gazebo/Bluematerial>
gazebo>
**PS:**material 标签中,设置的值区分大小写,颜色可以设置为 Red Blue Green Black …
将之前的机器人模型(xacro版)显示在 gazebo 中
实现流程:
<robot name="base" xmlns:xacro="http://wiki.ros.org/xacro">
<xacro:macro name="sphere_inertial_matrix" params="m r">
<inertial>
<mass value="${m}" />
<inertia ixx="${2*m*r*r/5}" ixy="0" ixz="0"
iyy="${2*m*r*r/5}" iyz="0"
izz="${2*m*r*r/5}" />
inertial>
xacro:macro>
<xacro:macro name="cylinder_inertial_matrix" params="m r h">
<inertial>
<mass value="${m}" />
<inertia ixx="${m*(3*r*r+h*h)/12}" ixy = "0" ixz = "0"
iyy="${m*(3*r*r+h*h)/12}" iyz = "0"
izz="${m*r*r/2}" />
inertial>
xacro:macro>
<xacro:macro name="Box_inertial_matrix" params="m l w h">
<inertial>
<mass value="${m}" />
<inertia ixx="${m*(h*h + l*l)/12}" ixy = "0" ixz = "0"
iyy="${m*(w*w + l*l)/12}" iyz= "0"
izz="${m*(w*w + h*h)/12}" />
inertial>
xacro:macro>
robot>
<robot name="my_base"
xmlns:xacro="http://www.ros.org/wiki/xacro">
<material name="black">
<color rgba="0.0 0.0 0.0 1.0" />
material>
<xacro:property name="footprint_radius" value="0.001" />
<xacro:property name="PI" value="3.1415926"/>
<xacro:property name="base_radius" value="0.1" />
<xacro:property name="base_length" value="0.08" />
<xacro:property name="lidi" value="0.015" />
<xacro:property name="base_mass" value="0.5" />
<xacro:property name="wheel_radius" value="0.0325" />
<xacro:property name="wheel_length" value="0.015" />
<xacro:property name="wheel_mass" value="0.05" />
<xacro:property name="sup_wheel_radius" value="0.0075" />
<xacro:property name="sup_wheel_mass" value="0.03" />
<link name="base_footprint">
<visual>
<geometry>
<sphere radius="${footprint_radius}" />
geometry>
visual>
link>
<link name="base_link">
<visual>
<geometry>
<cylinder radius="${base_radius}" length="${base_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>
<collision>
<geometry>
<cylinder radius="${base_radius}" length="${base_length}" />
geometry>
<origin xyz="0 0 0" rpy="0 0 0" />
collision>
<xacro:cylinder_inertial_matrix m="${base_mass}" r="${base_radius}" h="${base_length}" />
link>
<joint name="base_link2base_footprint" type="fixed">
<parent link="base_footprint" />
<child link="base_link" />
<origin xyz="0 0 ${lidi + base_length / 2 }" />
joint>
<gazebo reference="base_link">
<material>Gazebo/Yellowmaterial>
gazebo>
<xacro:macro name="wheel_func" 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>
<collision>
<geometry>
<cylinder radius="${wheel_radius}" length="${wheel_length}" />
geometry>
<origin xyz="0.0 0.0 0.0" rpy="${PI / 2} 0.0 0.0" />
collision>
<xacro:cylinder_inertial_matrix m="${wheel_mass}" r="${wheel_radius}" h="${wheel_length}" />
link>
<joint name="${name}_wheel2base_link" type="continuous">
<parent link="base_link" />
<child link="${name}_wheel" />
<origin xyz="0 ${flag * base_radius} ${-(lidi + base_length / 2 - wheel_radius) }" />
<axis xyz="0 1 0" />
joint>
<gazebo reference="${name}_wheel">
<material>Gazebo/Redmaterial>
gazebo>
xacro:macro>
<xacro:wheel_func name="left" flag="1" />
<xacro:wheel_func name="right" flag="-1" />
<xacro:macro name="sup_wheel_func" params="name flag">
<link name="${name}_wheel">
<visual>
<geometry>
<sphere radius="${sup_wheel_radius}" />
geometry>
<origin xyz="0 0 0" rpy="0 0 0" />
<material name="black" />
visual>
<collision>
<geometry>
<sphere radius="${sup_wheel_radius}" />
geometry>
<origin xyz="0 0 0" rpy="0 0 0" />
collision>
<xacro:sphere_inertial_matrix m="${sup_wheel_mass}" r="${sup_wheel_radius}" />
link>
<joint name="${name}_wheel2base_link" type="continuous">
<parent link="base_link" />
<child link="${name}_wheel" />
<origin xyz="${flag * (base_radius - sup_wheel_radius)} 0 ${-(base_length / 2 + lidi / 2)}" />
<axis xyz="1 1 1" />
joint>
<gazebo reference="${name}_wheel">
<material>Gazebo/Redmaterial>
gazebo>
xacro:macro>
<xacro:sup_wheel_func name="front" flag="1" />
<xacro:sup_wheel_func name="back" flag="-1" />
robot>
注意: 如果机器人模型在 Gazebo 中产生了抖动,滑动,缓慢位移 … 诸如此类情况,请查看
<robot xmlns:xacro="http://www.ros.org/wiki/xacro" name="mycar">
<xacro:property name="camera_length" value="0.02"/>
<xacro:property name="camera_width" value="0.05"/>
<xacro:property name="camera_hight" value="0.05"/>
<xacro:property name="camera_mass" value="0.01"/>
<xacro:property name="joint_camera_x" value="0.08"/>
<xacro:property name="joint_camera_y" value="0.0"/>
<xacro:property name="joint_camera_z" value="${base_length / 2 + camera_length / 2 + lidi }"/>
<link name="camera">
<visual>
<geometry>
<box size="${camera_length} ${camera_width} ${camera_hight}"/>
geometry>
<origin xyz="0.0 0.0 0.0" rpy="0.0 0.0 0.0"/>
<material name="camera_black">
<color rgba="0.0 0.0 0.0 1.0"/>
material>
visual>
<collision>
<geometry>
<box size="${camera_length} ${camera_width} ${camera_hight}"/>
geometry>
<origin xyz="0.0 0.0 0.0" rpy="0.0 0.0 0.0"/>
collision>
<xacro:Box_inertial_matrix m="${camera_mass}" l="${camera_length}" w="${camera_width}" h="${camera_hight}" />
link>
<joint name="camera2base" type="fixed">
<parent link="base_link"/>
<child link="camera"/>
<origin xyz="${joint_camera_x} ${joint_camera_y} ${joint_camera_z}" rpy="0.0 0.0 0.0"/>
joint>
<gazebo reference="camera">
<material>
Gazebo/Blue
material>
gazebo>
robot>
<robot xmlns:xacro="http://www.ros.org/wiki/xacro" name="mycar">
<xacro:property name="suport_radius" value="0.01"/>
<xacro:property name="suport_length" value="0.15"/>
<xacro:property name="suport_mass" value="0.10"/>
<xacro:property name="joint_suport_x" value="0"/>
<xacro:property name="joint_suport_y" value="0"/>
<xacro:property name="joint_suport_z" value="${suport_length / 2+ base_length / 2 }"/>
<xacro:property name="laser_radius" value="0.03"/>
<xacro:property name="laser_length" value="0.05"/>
<xacro:property name="laser_mass" value="0.15"/>
<xacro:property name="joint_laser_x" value="0"/>
<xacro:property name="joint_laser_y" value="0"/>
<xacro:property name="joint_laser_z" value="${suport_length / 2+ laser_length / 2}"/>
<link name="suport_link">
<visual>
<origin xyz="0.0 0.0 0.0" rpy="0.0 0.0 0.0"/>
<geometry>
<cylinder radius="${suport_radius}" length="${suport_length}"/>
geometry>
<material name="suport_link">
<color rgba="1.0 0.0 1.0 0.6"/>
material>
visual>
<collision>
<geometry>
<cylinder radius="${suport_radius}" length="${suport_length}"/>
geometry>
<origin xyz="0.0 0.0 0.0" rpy="0.0 0.0 0.0"/>
collision>
<xacro:cylinder_inertial_matrix m="${suport_mass}" r="${suport_radius}" h="${suport_length}"/>
link>
<joint name="suport2baselink" type="fixed">
<parent link="base_link"/>
<child link="suport_link"/>
<origin xyz="${joint_suport_x} ${joint_suport_y} ${joint_suport_z}" rpy="0.0 0.0 0.0"/>
joint>
<gazebo reference="suport_link">
<material>
Gazebo/Grey
material>
gazebo>
<link name="laser">
<visual>
<origin xyz="0.0 0.0 0.0" rpy="0.0 0.0 0.0"/>
<geometry>
<cylinder radius="${laser_radius}" length="${laser_length}"/>
geometry>
<material name="laser">
<color rgba="0.5 0.7 1.0 0.8"/>
material>
visual>
<collision>
<geometry>
<cylinder radius="${laser_radius}" length="${laser_length}"/>
geometry>
<origin xyz="0.0 0.0 0.0" rpy="0.0 0.0 0.0"/>
collision>
<xacro:cylinder_inertial_matrix m="${laser_mass}" r="${laser_radius}" h="${laser_length}"/>
link>
<joint name="laser2suportlink" type="fixed">
<parent link="suport_link"/>
<child link="laser"/>
<origin xyz="${joint_laser_x} ${joint_laser_y} ${joint_laser_z}" rpy="0 0 0"/>
joint>
<gazebo reference="laser">
<material>
Gazebo/Black
material>
gazebo>
robot>
<robot xmlns:xacro="http://www.ros.org/wiki/xacro" name="mycar">
<xacro:include filename="inertia.xacro"/>
<xacro:include filename="demo05_car_base.urdf.xacro"/>
<xacro:include filename="demo06_car_camera.urdf.xacro"/>
<xacro:include filename="demo07_car_laser.urdf.xacro"/>
robot>
launch 文件:
<launch>
<param name="robot_description" command="$(find xacro)/xacro $(find urdf02_gazebo)/urdf/car.urdf.xacro" />
<include file="$(find gazebo_ros)/launch/empty_world.launch">
<arg name="world_name" value="$(find urdf02_gazebo)/worlds/box_house.world" />
include>
<node pkg="gazebo_ros" type="spawn_model" name="model" args="-urdf -model mycar -param robot_description" />
launch>
Gazebo 中创建仿真实现方式有两种:
也还可以直接下载使用官方或第三方提高的仿真环境插件。
添加完毕后,选择 file —> Save World as 选择保存路径(功能包下: worlds 目录),文件名自定义,后缀名设置为 .world
<launch>
<param name="robot_description" command="$(find xacro)/xacro $(find demo02_urdf_gazebo)/urdf/xacro/my_base_camera_laser.urdf.xacro" />
<include file="$(find gazebo_ros)/launch/empty_world.launch">
<arg name="world_name" value="$(find demo02_urdf_gazebo)/worlds/hello.world" />
include>
<node pkg="gazebo_ros" type="spawn_model" name="model" args="-urdf -model mycar -param robot_description" />
launch>
Copy
核心代码: 启动 empty_world 后,再根据arg
加载自定义的仿真环境
<include file="$(find gazebo_ros)/launch/empty_world.launch">
<arg name="world_name" value="$(find demo02_urdf_gazebo)/worlds/hello.world" />
include>
Copy
点击: 左上角 file —> Save (保存路径功能包下的: models)
然后 file —> Exit Building Editor
可以像方式1一样再添加一些插件,然后保存为 world 文件(保存路径功能包下的: worlds)
同方式1
当前 Gazebo 提供的仿真道具有限,还可以下载官方支持,可以提供更为丰富的仿真实现,具体实现如下:
git clone https://github.com/osrf/gazebo_models
之前是:hg clone https://bitbucket.org/osrf/gazebo_models
但是已经不可用
注意: 此过程可能比较耗时
将得到的gazebo_models文件夹内容复制到 /usr/share/gazebo-*/models
重启 Gazebo,选择左侧菜单栏的 insert 可以选择并插入相关道具了
通过 Gazebo 模拟机器人的传感器,然后在 Rviz 中显示这些传感器感知到的数据。主要内容包括:
gazebo 中已经可以正常显示机器人模型了,那么如何像在 rviz 中一样控制机器人运动呢?在此,需要涉及到ros中的组件: ros_control。
**场景:**同一套 ROS 程序,如何部署在不同的机器人系统上,比如:开发阶段为了提高效率是在仿真平台上测试的,部署时又有不同的实体机器人平台,不同平台的实现是有差异的,如何保证 ROS 程序的可移植性?ROS 内置的解决方式是 ros_control。
**ros_control:**是一组软件包,它包含了控制器接口,控制器管理器,传输和硬件接口。ros_control 是一套机器人控制的中间件,是一套规范,不同的机器人平台只要按照这套规范实现,那么就可以保证 与ROS 程序兼容,通过这套规范 ,实现了一种可插拔的架构设计,大大提高了程序设计的效率与灵活性。
gazebo 已经实现了 ros_control 的相关接口,如果需要在 gazebo 中控制机器人运动,直接调用相关接口即可
承上,运动控制基本流程:
两轮差速配置
<robot name="my_car_move"
xmlns:xacro="http://wiki.ros.org/xacro">
<xacro:macro name="joint_trans" params="joint_name">
<transmission name="${joint_name}_trans">
<type>transmission_interface/SimpleTransmissiontype>
<joint name="${joint_name}">
<hardwareInterface>hardware_interface/VelocityJointInterfacehardwareInterface>
joint>
<actuator name="${joint_name}_motor">
<hardwareInterface>hardware_interface/VelocityJointInterfacehardwareInterface>
<mechanicalReduction>1mechanicalReduction>
actuator>
transmission>
xacro:macro>
<xacro:joint_trans joint_name="left2link" />
<xacro:joint_trans joint_name="right2link" />
<gazebo>
<plugin name="differential_drive_controller" filename="libgazebo_ros_diff_drive.so">
<rosDebugLevel>DebugrosDebugLevel>
<publishWheelTF>truepublishWheelTF>
<robotNamespace>/robotNamespace>
<publishTf>1publishTf>
<publishWheelJointState>truepublishWheelJointState>
<alwaysOn>truealwaysOn>
<updateRate>100.0updateRate>
<legacyMode>truelegacyMode>
<leftJoint>left2linkleftJoint>
<rightJoint>right2linkrightJoint>
<wheelSeparation>${base_radius * 2}wheelSeparation>
<wheelDiameter>${wheel_radius * 2}wheelDiameter>
<broadcastTF>1broadcastTF>
<wheelTorque>30wheelTorque>
<wheelAcceleration>1.8wheelAcceleration>
<commandTopic>cmd_velcommandTopic>
<odometryFrame>odomodometryFrame>
<odometryTopic>odomodometryTopic>
<robotBaseFrame>base_footprintrobotBaseFrame>
plugin>
gazebo>
robot>
<robot name="my_sensors" xmlns:xacro="http://wiki.ros.org/xacro">
<gazebo reference="camera">
<sensor type="camera" name="camera_node">
<update_rate>30.0update_rate>
<camera name="head">
<horizontal_fov>1.3962634horizontal_fov>
<image>
<width>1280width>
<height>720height>
<format>R8G8B8format>
image>
<clip>
<near>0.02near>
<far>300far>
clip>
<noise>
<type>gaussiantype>
<mean>0.0mean>
<stddev>0.007stddev>
noise>
camera>
<plugin name="gazebo_camera" filename="libgazebo_ros_camera.so">
<alwaysOn>truealwaysOn>
<updateRate>0.0updateRate>
<cameraName>/cameracameraName>
<imageTopicName>image_rawimageTopicName>
<cameraInfoTopicName>camera_infocameraInfoTopicName>
<frameName>cameraframeName>
<hackBaseline>0.07hackBaseline>
<distortionK1>0.0distortionK1>
<distortionK2>0.0distortionK2>
<distortionK3>0.0distortionK3>
<distortionT1>0.0distortionT1>
<distortionT2>0.0distortionT2>
plugin>
sensor>
gazebo>
robot>
<robot name="my_sensors" xmlns:xacro="http://wiki.ros.org/xacro">
<gazebo reference="suport_link">
<sensor type="depth" name="camera">
<always_on>truealways_on>
<update_rate>20.0update_rate>
<camera>
<horizontal_fov>${60.0*PI/180.0}horizontal_fov>
<image>
<format>R8G8B8format>
<width>640width>
<height>480height>
image>
<clip>
<near>0.05near>
<far>8.0far>
clip>
camera>
<plugin name="kinect_camera_controller" filename="libgazebo_ros_openni_kinect.so">
<cameraName>cameracameraName>
<alwaysOn>truealwaysOn>
<updateRate>10updateRate>
<imageTopicName>rgb/image_rawimageTopicName>
<depthImageTopicName>depth/image_rawdepthImageTopicName>
<pointCloudTopicName>depth/pointspointCloudTopicName>
<cameraInfoTopicName>rgb/camera_infocameraInfoTopicName>
<depthImageCameraInfoTopicName>depth/camera_infodepthImageCameraInfoTopicName>
<frameName>support_depthframeName>
<baseline>0.1baseline>
<distortion_k1>0.0distortion_k1>
<distortion_k2>0.0distortion_k2>
<distortion_k3>0.0distortion_k3>
<distortion_t1>0.0distortion_t1>
<distortion_t2>0.0distortion_t2>
<pointCloudCutoff>0.4pointCloudCutoff>
plugin>
sensor>
gazebo>
robot>
<robot name="my_sensors" xmlns:xacro="http://wiki.ros.org/xacro">
<gazebo reference="laser">
<sensor type="ray" name="rplidar">
<pose>0 0 0 0 0 0pose>
<visualize>truevisualize>
<update_rate>5.5update_rate>
<ray>
<scan>
<horizontal>
<samples>360samples>
<resolution>1resolution>
<min_angle>-3min_angle>
<max_angle>3max_angle>
horizontal>
scan>
<range>
<min>0.10min>
<max>30.0max>
<resolution>0.01resolution>
range>
<noise>
<type>gaussiantype>
<mean>0.0mean>
<stddev>0.01stddev>
noise>
ray>
<plugin name="gazebo_rplidar" filename="libgazebo_ros_laser.so">
<topicName>/scantopicName>
<frameName>laserframeName>
plugin>
sensor>
gazebo>
robot>
最后还需要将上述 xacro 文件集成进总的机器人模型文件,代码示例如下:
<robot xmlns:xacro="http://www.ros.org/wiki/xacro" name="mycar">
<xacro:include filename="inertia.xacro"/>
<xacro:include filename="demo05_car_base.urdf.xacro"/>
<xacro:include filename="demo06_car_camera.urdf.xacro"/>
<xacro:include filename="demo07_car_laser.urdf.xacro"/>
<xacro:include filename="gazebo/move.xacro"/>
<xacro:include filename="gazebo/laser.xacro"/>
<xacro:include filename="gazebo/camera.xacro"/>
<xacro:include filename="gazebo/kinect.xacro"/>
robot>
当前核心: 包含 控制器以及传动配置的 xacro 文件
<xacro:include filename="move.xacro" />
launch文件:
<launch>
<param name="robot_description" command="$(find xacro)/xacro $(find urdf02_gazebo)/urdf/car.urdf.xacro" />
<include file="$(find gazebo_ros)/launch/empty_world.launch">
<arg name="world_name" value="$(find urdf02_gazebo)/worlds/box_house.world" />
include>
<node pkg="gazebo_ros" type="spawn_model" name="model" args="-urdf -model mycar -param robot_description" />
launch>
启动 launch 文件,使用 topic list 查看话题列表,会发现多了 /cmd_vel 然后发布 vmd_vel 消息控制即可
使用命令控制(或者可以编写单独的节点控制)
rostopic pub -r 10 /cmd_vel geometry_msgs/Twist '{linear: {x: 0.2, y: 0, z: 0}, angular: {x: 0, y: 0, z: 0.5}}'
小车在 Gazebo 中已经正常运行起来了
在 Gazebo 的仿真环境中,机器人的里程计信息以及运动朝向等信息是无法获取的,可以通过 Rviz 显示机器人的里程计信息以及运动朝向
里程计: 机器人相对出发点坐标系的位姿状态(X 坐标 Y 坐标 Z坐标以及朝向)。
launch 文件
<launch>
<node pkg="tf2_ros" type="static_transform_publisher" name="static_transform_publisher" args="0 0 0 -1.57 0 -1.57 /suport_link /support_depth" />
<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" />
launch>
执行 launch 文件后,在 Rviz 中添加图示组件:
运行:
roslaunch urdf02_gazebo demo03_env.launch
roslaunch urdf02_gazebo demo04_sensor.launch
rosrun teleop_twist_keyboard teleop_twist_keyboard.py _speed:=0.3 _turn:=0.5
rostopic pub -r 10 /cmd_vel geometry_msgs/Twist "linear:
x: 0.0
y: 0.0
z: 0.0
angular:
x: 0.0
y: 0.0
z: 0.3"
注意:名字一定要打对,否则后续启动gazebo不会出现cmd_vel
URDF 是用于描述机器人模型的 xml 文件,可以使用不同的标签具代表不同含义,URDF 编写机器人模型代码冗余,xacro 可以优化 URDF 实现,代码实现更为精简、高效、易读。容易混淆的是Rviz与Gazebo,在此我们着重比较以下二者的区别:
rviz是三维可视化工具,强调把已有的数据可视化显示;
gazebo是三维物理仿真平台,强调的是创建一个虚拟的仿真环境。
rviz需要已有数据。
rviz提供了很多插件,这些插件可以显示图像、模型、路径等信息,但是前提都是这些数据已经以话题、参数的形式发布,rviz做的事情就是订阅这些数据,并完成可视化的渲染,让开发者更容易理解数据的意义。
gazebo不是显示工具,强调的是仿真,它不需要数据,而是创造数据。
我们可以在gazebo中免费创建一个机器人世界,不仅可以仿真机器人的运动功能,还可以仿真机器人的传感器数据。而这些数据就可以放到rviz中显示,所以使用gazebo的时候,经常也会和rviz配合使用。当我们手上没有机器人硬件或实验环境难以搭建时,仿真往往是非常有用的利器。
综上,如果你手上已经有机器人硬件平台,并且在上边可以完成需要的功能,用rviz应该就可以满足开发需求。
如果你手上没有机器人硬件,或者想在仿真环境中做一些算法、应用的测试,gazebo+rviz应该是你需要的。
另外,rviz配合其他功能包也可以建立一个简单的仿真环境,比如rviz+ArbotiX。