仿真机器人实例已上传至本网站,点击此处下载
原写于2023秋,2024年夏稍有修改
这是笔记本,不是教程。
几位耶jiweiyecau@163.com
机械、物理、数学--仿真软件前瞻
这些建模与仿真平台有很多相似的概念。
solidworks:机械设计,达索公司
CATIA:汽车飞机船舶,曲面设计,达索公司
PTC proe(Creo):CAD、CAM、CAE,模具设计,参数化设计,PTC公司
UG-NX:汽车、模具,西门子公司
AotuCAD:CAD二维,Autodesk公司
Edgecam:数控编程
Adams:动力学和运动学仿真
Ansys系列:有限元
Abaqus(达索系列)
comsol:有限元,偏微分方程
matlab:数值分析、数学建模等,几乎无所不能
机器人仿真平台:gazebo, webots, unity等
Gazebo软件安装及简介
# 安装gazebo仿真软件
sudo apt install gazebo
# 安装gazebo-ros功能包(所有)
sudo apt install ros-humble-gazebo-*
# 安装gazebo_ros插件
sudo apt install ros-humble-gazebo-ros
# 测试是否安装成功
gazebo --verbose -s libgazebo_ros_init.so -s libgazebo_ros_factory.so
ubuntu系统中,可以按Alt-F2,键入Gazebo,然后按Enter键。
常规操作有:“shift+鼠标左键”转换视角,“鼠标左键”平移视角,“滚轮”缩放大小。
Gazebo was started in 2002. After over 15 years of development it was time for a significant upgrade and modernization. This upgrade also provided the opportunity to move away from a monolithic architecture to a collection of loosely coupled libraries. We branded the new development effort Igntion in order to differentiate it from Gazebo that had come before, which is now referred to as Gazebo Classic. Those changes have gone very well, thanks in no small part to contributions and support from our worldwide community of users and other stakeholders.
In 2022 we encountered a trademark obstacle regarding the use of the name "Ignition". We used this as an opportunity to switch back the widely known name "Gazebo". Going forward, the modern robotics software collection formerly known as Ignition, is now branded Gazebo.
Gazebo成立于2002年。经过15年的发展,现在是进行重大升级和现代化的时候了。这种升级还提供了一个机会,可以从单一的体系结构转移到松散耦合的库集合。 我们将新的开发工作命名为Ignation,以将其与之前的Gazebo区别开来,后者现在被称为Gazebo Classic。这些变化进展顺利,这在很大程度上要归功于我们全球用户社区和其他利益相关者的贡献和支持。
2022年,我们在使用“Ignition”这一名称时遇到了商标障碍。我们以此为契机,重新使用了广为人知的名称“Gazebo”。展望未来,以前被称为Ignition的现代机器人软件系列现在被命名为Gazebo.
现在的名称:Gazebo
参阅:
官网下载及安装指南
鱼香ROS【安装Gazebo】
建模
Gazebo创建简单模型
参考:
Gazebo 11新手课程指南——初级教程(三) - 深圳季连AIGRAPHX的文章 - 知乎
Gazebo官方教程入门篇(二)模型编辑器 - Bruce的文章 - 知乎
构建和修改机器人,包括构建轮式机器人、安装传感器、安装执行器和启动模型动画。
SDF模型的范围可以从简单的形状到复杂的机器人。它指的是<model>
SDF标签,本质上是链接(links)、关节(joints)、碰撞对象(collision objects)、视觉(visuals)和插件(plugins)的集合。步骤:
- 添加一个链接。
- 设置碰撞元素。
- 设置视觉元素。
- 设置惯性属性。
- 回到#1,直到添加了所有链接。
- 添加所有关节(如果有的话)。
- 添加所有插件(如果有的话)
URDF模型设计
参考:
Building a visual robot model from scratch
XML Specifications wiki.ros.org
单个link机器人模型
<?xml version="1.0"?>
<robot name="myfirst">
<link name="base_link">
<visual>
<geometry>
<cylinder length="0.6" radius="0.2"/>
</geometry>
</visual>
</link>
</robot>
多link间通过joint连接
The joint is defined in terms of a parent and a child. URDF is ultimately a tree structure with one root link. This means that the leg’s position is dependent on the base_link’s position.
<?xml version="1.0"?>
<robot name="multipleshapes">
<link name="base_link">
<visual>
<geometry>
<cylinder length="0.6" radius="0.2"/>
</geometry>
</visual>
</link>
<link name="right_leg">
<visual>
<geometry>
<box size="0.6 0.1 0.2"/>
</geometry>
</visual>
</link>
<joint name="base_to_right_leg" type="fixed">
<parent link="base_link"/>
<child link="right_leg"/>
</joint>
</robot>
origins参数
<?xml version="1.0"?>
<robot name="origins">
<link name="base_link">
<visual>
<geometry>
<cylinder length="0.6" radius="0.2"/>
</geometry>
</visual>
</link>
<link name="right_leg">
<visual>
<geometry>
<box size="0.6 0.1 0.2"/>
</geometry>
<origin rpy="0 1.57075 0" xyz="0 0 -0.3"/>
</visual>
</link>
<joint name="base_to_right_leg" type="fixed">
<parent link="base_link"/>
<child link="right_leg"/>
<origin xyz="0 -0.22 0.25"/>
</joint>
</robot>
material color参数
<material name="blue">
<color rgba="0 0 0.8 1"/>
</material>
<material name="white">
<color rgba="1 1 1 1"/>
</material>
<link name="base_link">
<visual>
<geometry>
<cylinder length="0.6" radius="0.2"/>
</geometry>
<material name="blue"/>
</visual>
</link>
导入外部模型文件
The meshes here were borrowed from the PR2. They are separate files which you have to specify the path for. You should use the package://NAME_OF_PACKAGE/path notation. The meshes for this tutorial are located within the urdf_tutorial package, in a folder called meshes.
<link name="left_gripper">
<visual>
<origin rpy="0.0 0 0" xyz="0 0 0"/>
<geometry>
<mesh filename="package://urdf_tutorial/meshes/l_finger.dae"/>
</geometry>
</visual>
</link>
The meshes can be imported in a number of different formats. STL is fairly common, but the engine also supports DAE, which can have its own color data, meaning you don’t have to specify the color/material. Often these are in separate files. These meshes reference the .tif files also in the meshes folder.
Meshes can also be sized using relative scaling parameters or a bounding box size.
We could have also referred to meshes in a completely different package.
机器人关节设计
连续旋转关节
连续旋转关节可以呈现从负无穷大到正无穷大的任何角度。车轮也是这样建模的,因此它们可以永远向两个方向滚动。
我们必须添加的唯一附加信息是旋转轴,此处由 xyz 三元组指定,它指定头部将围绕其旋转的向量。 由于我们希望它绕 z 轴移动,因此我们指定向量 “0 0 1”。
<joint name="head_swivel" type="continuous">
<parent link="base_link"/>
<child link="head"/>
<axis xyz="0 0 1"/>
<origin xyz="0 0 0.3"/>
</joint>
有限旋转关节
revolute joints 旋转方式与连续旋转关节相同,但必须指定关节上限和下限(以弧度为单位)的极限标签。我们还必须为此关节指定最大速度和关节力。
<joint name="left_gripper_joint" type="revolute">
<axis xyz="0 0 1"/>
<limit effort="1000.0" lower="0.0" upper="0.548" velocity="0.5"/>
<origin rpy="0 0 0" xyz="0.2 0.01 0"/>
<parent link="gripper_pole"/>
<child link="left_gripper"/>
</joint>
直线关节
The gripper arm is a different kind of joint, namely a prismatic joint. This means that it moves along an axis, not around it. This translational movement is what allows our robot model to extend and retract its gripper arm.
The limits of the prismatic arm are specified in the same way as a revolute joint, except that the units are meters, not radians.
<joint name="gripper_extension" type="prismatic">
<parent link="base_link"/>
<child link="gripper_pole"/>
<limit effort="1000.0" lower="-0.38" upper="0" velocity="0.5"/>
<origin rpy="0 0 0" xyz="0.19 0 0.2"/>
</joint>
平面关节和浮动关节
There are two other kinds of joints that move around in space. Whereas(鉴于) the prismatic joint can only move along one dimension, a planar joint can move around in a plane, or two dimensions. Furthermore, a floating joint is unconstrained, and can move around in any of the three dimensions. These joints cannot be specified by just one number, and therefore aren’t included in this tutorial.
物理和碰撞属性
参阅:Adding physical and collision properties
collision碰撞属性
这里的碰撞属性概念与SW里面的接触属性类似,与gazebo建模时设置的碰撞属性概念一样。
The collision element is a direct subelement of the link object, at the same level as the visual tag.
The collision element defines its shape the same way the visual element does, with a geometry tag. The format for the geometry tag is exactly the same here as with the visual.
You can also specify an origin in the same way as a subelement of the collision tag (as with the visual).
<link name="base_link">
<visual>
<geometry>
<cylinder length="0.6" radius="0.2"/>
</geometry>
<material name="blue">
<color rgba="0 0 .8 1"/>
</material>
</visual>
<collision>
<geometry>
<cylinder length="0.6" radius="0.2"/>
</geometry>
</collision>
</link>
无论是什么仿真/有限元软件,都应该考虑模型简化问题,这是常识,本文略过不表。
在碰撞属性中可以设置安全距离,这与碰撞属性的概念有关,本文略过不表。
inertia惯性
Every link element being simulated needs an inertial tag. Here is a simple one.
<link name="base_link">
<visual>
<geometry>
<cylinder length="0.6" radius="0.2"/>
</geometry>
<material name="blue">
<color rgba="0 0 .8 1"/>
</material>
</visual>
<collision>
<geometry>
<cylinder length="0.6" radius="0.2"/>
</geometry>
</collision>
<inertial>
<mass value="10"/>
<inertia ixx="1e-3" ixy="0.0" ixz="0.0" iyy="1e-3" iyz="0.0" izz="1e-3"/>
</inertial>
</link>
- This element is also a subelement of the link object.
- The mass is defined in kilograms.
- The 3x3 rotational inertia matrix is specified with the inertia element. Since this is symmetrical(对称), it can be represented by only 6 elements, as such.
转动惯量 | ||
---|---|---|
ixx | ixy | ixz |
ixy | iyy | iyz |
ixz | iyz | izz |
- 这些信息可以通过建模程序(如MeshLab)提供给您。几何图元(圆柱体、长方体、球体)的惯性可以使用维基百科的惯性矩张量列表来计算(在上面的例子中使用了该列表)。
- 惯性张量取决于物体的质量和质量分布。一个好的第一近似是假设物体体积中的质量分布相等,并根据物体的形状计算惯性张量,如上所述。
- 如果不确定该放什么,ixx/iyy/izz=1e-3或更小的矩阵通常是中等尺寸链路的合理默认值(它对应于边长为0.1 m、质量为0.6 kg的盒子)。单位矩阵是一个特别糟糕的选择,因为它通常太高了(它对应于一个边长0.1米、质量600公斤的盒子!)。
- 也可以指定原点标记来指定重心和惯性参照系(相对于链接的参照系)。 当使用实时控制器时,零(或几乎为零)的惯性元素可能会导致机器人模型在没有警告的情况下崩溃,并且所有链接都会出现其原点与世界原点重合的情况。
接触系数
You can also define how the links behave when they are in contact with one another. This is done with a subelement of the collision tag called contact_coefficients. There are three attributes to specify:
Friction coefficient-摩擦系数-mu
Stiffness coefficient-刚度系数kp
Dampening coefficient-阻尼系数-kd
关节动力学Joint Dynamics
默认是0
- friction(摩擦力) - The physical static friction. For prismatic joints, the units are Newtons. For revolving joints, the units are Newton meters.
- damping(阻尼) - The physical damping value. For prismatic joints, the units are Newton seconds per meter. For revolving joints, Newton meter seconds per radian.
代码简化xacro
修改launch文件
path_to_urdf = get_package_share_path('pr2_description') / 'robots' / 'pr2.urdf.xacro'
robot_state_publisher_node = launch_ros.actions.Node(
package='robot_state_publisher',
executable='robot_state_publisher',
parameters=[{
'robot_description': ParameterValue(
Command(['xacro ', str(path_to_urdf)]), value_type=str
)
}]
)
xacro文件前两行:
<?xml version="1.0"?>
<robot xmlns:xacro="http://www.ros.org/wiki/xacro" name="firefighter">
常量代替
<xacro:property name="width" value="0.2" />
<xacro:property name="bodylen" value="0.6" />
<link name="base_link">
<visual>
<geometry>
<cylinder radius="${width}" length="${bodylen}"/>
</geometry>
<material name="blue"/>
</visual>
<collision>
<geometry>
<cylinder radius="${width}" length="${bodylen}"/>
</geometry>
</collision>
</link>
<xacro:property name=”robotname” value=”marvin” />
<link name=”${robotname}s_leg” />
数学计算,可以使用sin,cos
<cylinder radius="${wheeldiam/2}" length="0.1"/>
<origin xyz="${reflect*(width+.02)} 0 0.25" />
宏示例一:
<xacro:macro name="default_inertial" params="mass">
<inertial>
<mass value="${mass}" />
<inertia ixx="1e-3" ixy="0.0" ixz="0.0"
iyy="1e-3" iyz="0.0"
izz="1e-3" />
</inertial>
</xacro:macro>
<xacro:default_inertial mass="10"/>
宏示例二:
<xacro:macro name="leg" params="prefix reflect">
<link name="${prefix}_leg">
<visual>
<geometry>
<box size="${leglen} 0.1 0.2"/>
</geometry>
<origin xyz="0 0 -${leglen/2}" rpy="0 ${pi/2} 0"/>
<material name="white"/>
</visual>
<collision>
<geometry>
<box size="${leglen} 0.1 0.2"/>
</geometry>
<origin xyz="0 0 -${leglen/2}" rpy="0 ${pi/2} 0"/>
</collision>
<xacro:default_inertial mass="10"/>
</link>
<joint name="base_to_${prefix}_leg" type="fixed">
<parent link="base_link"/>
<child link="${prefix}_leg"/>
<origin xyz="0 ${reflect*(width+.02)} 0.25" />
</joint>
<!-- A bunch of stuff cut -->
</xacro:macro>
<xacro:leg prefix="right" reflect="1" />
<xacro:leg prefix="left" reflect="-1" />
另:
向社区贡献自己的原创机器人
本学习笔记的重点是学习导航算法,故不对xacro作进一步学习。
插件详解Gazebo Plugins
初步认识Gazebo Plugins
参阅:
- Tutorial: Using Gazebo plugins with ROS(包含所有可在gazebo_plugins中使用的插件)
- https://classic.gazebosim.org/tutorials?tut=plugins_hello_world&cat=write_plugin
- 官方文档namespacegazebo
在gazebo中可以通过插入一些插件,来仿真机器人传感器、执行器的特性,这些插件通过<gazebo>
元素中的<plugin>
标签描述。
当前有6种类型的插件:1. 仿真世界插件2. 模型插件3. 传感器插件4. 系统插件5. 视觉插件6. 图形用户界面插件。(World,Model,Sensor,System,Visual,GUI)
其中,能直接在urdf中添加的有:
- 模型插件ModelPlugins, to provide access to the physics::Model API
- 传感器插件SensorPlugins, to provide access to the sensors::Sensor API
- 视觉插件VisualPlugins, to provide access to the rendering::Visual API
插件列表 | |||
---|---|---|---|
Camera | Multicamera | Depth Camera | Openni Kinect |
GPU Laser | Laser | Block Laser | F3D |
Force | IMU | IMU sensor | Joint Pose Trajectory |
P3D | Projector | Prosilica Camera | Bumper |
Differential Drive | Skid Steering Drive | Video Plugin | Planar Move Plugin |
添加模型插件示例:
<robot>
... robot description ...
<gazebo>
<plugin name="differential_drive_controller" filename="libdiffdrive_plugin.so">
... plugin parameters ...
</plugin>
</gazebo>
... robot description ...
</robot>
添加传感器插件示例:
gazebo中的传感器必须连接到连杆(link),因此描述该传感器的
Specifying sensor plugins is slightly different. Sensors in Gazebo are meant to be attached to links, so the
element describing that sensor must be given a reference to that link.
<robot>
... robot description ...
<link name="sensor_link">
... link description ...
</link>
<gazebo reference="sensor_link">
<sensor type="camera" name="camera1">
... sensor parameters ...
<plugin name="camera_controller" filename="libgazebo_ros_camera.so">
... plugin parameters ..
</plugin>
</sensor>
</gazebo>
</robot>
单相机(Camera)与多相机(Multicamera)模块
建模
<joint name="camera_joint" type="fixed">
<axis xyz="0 1 0" />
<origin xyz="${camera_link} 0 ${height3 - axel_offset*2}" rpy="0 0 0"/>
<parent link="link3"/>
<child link="camera_link"/>
</joint>
<!-- Camera -->
<link name="camera_link">
<collision>
<origin xyz="0 0 0" rpy="0 0 0"/>
<geometry>
<box size="${camera_link} ${camera_link} ${camera_link}"/>
</geometry>
</collision>
<visual>
<origin xyz="0 0 0" rpy="0 0 0"/>
<geometry>
<box size="${camera_link} ${camera_link} ${camera_link}"/>
</geometry>
<material name="red"/>
</visual>
<inertial>
<mass value="1e-5" />
<origin xyz="0 0 0" rpy="0 0 0"/>
<inertia ixx="1e-6" ixy="0" ixz="0" iyy="1e-6" iyz="0" izz="1e-6" />
</inertial>
</link>
A Xacro property is also defined:
<xacro:property name="camera_link" value="0.05" /> <!-- Size of square 'camera' box -->
You should be able to launch the RRBot and see a red box attached to the end of the arm.
Next we will review the Gazebo plugin that gives us the camera functionality and publishes the image to a ROS message.
<!-- camera -->
<!-- The link name "camera_link" must match the name of the link we added to the Xacro URDF. -->
<gazebo reference="camera_link">
<!-- The sensor name "camera1" must be unique from all other sensor names. The name is not used many places except for within Gazebo plugins you can access. -->
<sensor type="camera" name="camera1">
<!-- Gazebo内每秒拍摄新相机图像的次数。这是传感器在模拟过程中尝试的最大更新速率,但如果物理模拟运行速度快于传感器生成速度,则可能会落后于此目标速率。 -->
<update_rate>30.0</update_rate>
<!-- 填写这些值以匹配制造商对物理相机硬件的规格。需要注意的一点是,像素被假定为正方形。 -->
<!-- 此外,the near and far clips是模拟特定的参数,它们为摄影机在模拟中看到对象的距离提供上限和下限。这是在相机的验光框架中指定的。 -->
<camera name="head">
<horizontal_fov>1.3962634</horizontal_fov>
<image>
<width>800</width>
<height>800</height>
<format>R8G8B8</format>
</image>
<clip>
<near>0.02</near>
<far>300</far>
</clip>
<noise>
<type>gaussian</type>
<!-- Noise is sampled independently per pixel on each frame.
That pixel's noise value is added to each of its color
channels, which at that point lie in the range [0,1]. -->
<mean>0.0</mean>
<stddev>0.007</stddev>
</noise>
</camera>
<!-- This is where the actual file is linked to, as a shared object. -->
<plugin name="camera_controller" filename="libgazebo_ros_camera.so">
<alwaysOn>true</alwaysOn>
<updateRate>0.0</updateRate>
<!-- Here we define the rostopic the camera will be publishing to, for both the image topic and the camera info topic. -->
<cameraName>rrbot/camera1</cameraName>
<imageTopicName>image_raw</imageTopicName>
<cameraInfoTopicName>camera_info</cameraInfoTopicName>
<!-- 在tf树中发布图像的坐标系。 -->
<frameName>camera_link</frameName>
<hackBaseline>0.07</hackBaseline>
<distortionK1>0.0</distortionK1>
<distortionK2>0.0</distortionK2>
<distortionK3>0.0</distortionK3>
<distortionT1>0.0</distortionT1>
<distortionT2>0.0</distortionT2>
</plugin>
</sensor>
</gazebo>
多相机Multicamera
同步多个相机的快门,以便它们一起发布图像。
Note: currently only supports stereo cameras.(目前仅支持立体相机)
In this code example there is both a left and right camera:
<gazebo reference="left_camera_frame">
<sensor type="multicamera" name="stereo_camera">
<update_rate>30.0</update_rate>
<camera name="left">
<horizontal_fov>1.3962634</horizontal_fov>
<image>
<width>800</width>
<height>800</height>
<format>R8G8B8</format>
</image>
<clip>
<near>0.02</near>
<far>300</far>
</clip>
<noise>
<type>gaussian</type>
<mean>0.0</mean>
<stddev>0.007</stddev>
</noise>
</camera>
<camera name="right">
<pose>0 -0.07 0 0 0 0</pose>
<horizontal_fov>1.3962634</horizontal_fov>
<image>
<width>800</width>
<height>800</height>
<format>R8G8B8</format>
</image>
<clip>
<near>0.02</near>
<far>300</far>
</clip>
<noise>
<type>gaussian</type>
<mean>0.0</mean>
<stddev>0.007</stddev>
</noise>
</camera>
<plugin name="stereo_camera_controller" filename="libgazebo_ros_multicamera.so">
<alwaysOn>true</alwaysOn>
<updateRate>0.0</updateRate>
<cameraName>multisense_sl/camera</cameraName>
<imageTopicName>image_raw</imageTopicName>
<cameraInfoTopicName>camera_info</cameraInfoTopicName>
<frameName>left_camera_optical_frame</frameName>
<!--<rightFrameName>right_camera_optical_frame</rightFrameName>-->
<hackBaseline>0.07</hackBaseline>
<distortionK1>0.0</distortionK1>
<distortionK2>0.0</distortionK2>
<distortionK3>0.0</distortionK3>
<distortionT1>0.0</distortionT1>
<distortionT2>0.0</distortionT2>
</plugin>
</sensor>
</gazebo>
两类雷达模块
GPU Laser
如sensor_msgs中所述,通过广播LaserScan消息模拟激光测距传感器。
示例:
<joint name="hokuyo_joint" type="fixed">
<axis xyz="0 1 0" />
<origin xyz="0 0 ${height3 - axel_offset/2}" rpy="0 0 0"/>
<parent link="link3"/>
<child link="hokuyo_link"/>
</joint>
<!-- Hokuyo Laser -->
<link name="hokuyo_link">
<collision>
<origin xyz="0 0 0" rpy="0 0 0"/>
<geometry>
<box size="0.1 0.1 0.1"/>
</geometry>
</collision>
<visual>
<origin xyz="0 0 0" rpy="0 0 0"/>
<geometry>
<mesh filename="package://rrbot_description/meshes/hokuyo.dae"/>
</geometry>
</visual>
<inertial>
<mass value="1e-5" />
<origin xyz="0 0 0" rpy="0 0 0"/>
<inertia ixx="1e-6" ixy="0" ixz="0" iyy="1e-6" iyz="0" izz="1e-6" />
</inertial>
</link>
Most of the properties are self-explanatory.(大多数属性都是不言自明的)
<!-- hokuyo -->
<gazebo reference="hokuyo_link">
<sensor type="gpu_ray" name="head_hokuyo_sensor">
<pose>0 0 0 0 0 0</pose>
<!-- 当为true时,在gpu激光器的扫描区域内可见半透明激光射线。这可能是一个有用的信息可视化,也可能是一种麻烦。 -->
<visualize>false</visualize>
<update_rate>40</update_rate>
<ray>
<scan>
<horizontal>
<samples>720</samples>
<resolution>1</resolution>
<min_angle>-1.570796</min_angle>
<max_angle>1.570796</max_angle>
</horizontal>
</scan>
<range>
<min>0.10</min>
<max>30.0</max>
<resolution>0.01</resolution>
</range>
<noise>
<type>gaussian</type>
<!-- Noise parameters based on published spec for Hokuyo laser
achieving "+-30mm" accuracy at range < 10m. A mean of 0.0m and
stddev of 0.01m will put 99.7% of samples within 0.03m of the true
reading. -->
<mean>0.0</mean>
<stddev>0.01</stddev>
</noise>
</ray>
<plugin name="gazebo_ros_head_hokuyo_controller" filename="libgazebo_ros_gpu_laser.so">
<!-- 将这些设置为您希望发布激光扫描的ROS主题名称,以及您希望TF使用的转换帧。 -->
<topicName>/rrbot/laser/scan</topicName>
<frameName>hokuyo_link</frameName>
</plugin>
</sensor>
</gazebo>
Laser
the non-GPU version of , but essentially uses the same code. See GPU Laser for documentation
两类IMU模块
IMU (GazeboRosImu)
Description: simulates IMU sensor. Measurements are computed by the ROS plugin, not by Gazebo. See usage snippet sample below for implementation.
描述:模拟IMU传感器。测量是由ROS插件计算的,而不是由Gazebo计算的。
<robot>
:
<gazebo>
<plugin name="imu_plugin" filename="libgazebo_ros_imu.so">
<alwaysOn>true</alwaysOn>
<bodyName>base_footprint</bodyName>
<topicName>imu</topicName>
<serviceName>imu_service</serviceName>
<gaussianNoise>0.0</gaussianNoise>
<updateRate>20.0</updateRate>
</plugin>
</gazebo>
</robot>
IMU sensor (GazeboRosImuSensor)
模拟惯性运动单元传感器,与IMU(GazeboRosIMU)的主要区别是:-继承SensorPlugin而不是ModelPlugin,-测量由gazebo ImuSensor给出而不是由ros插件计算,-重力包含在惯性测量中。
<gazebo reference="imu_link">
<gravity>true</gravity>
<sensor name="imu_sensor" type="imu">
<always_on>true</always_on>
<update_rate>100</update_rate>
<visualize>true</visualize>
<topic>__default_topic__</topic>
<plugin filename="libgazebo_ros_imu_sensor.so" name="imu_plugin">
<topicName>imu</topicName>
<bodyName>imu_link</bodyName>
<updateRateHZ>10.0</updateRateHZ>
<gaussianNoise>0.0</gaussianNoise>
<xyzOffset>0 0 0</xyzOffset>
<rpyOffset>0 0 0</rpyOffset>
<frameName>imu_link</frameName>
<initialOrientationAsReference>false</initialOrientationAsReference>
</plugin>
<pose>0 0 0 0 0 0</pose>
</sensor>
</gazebo>
Differential Drive(差速驱动器)
Description: model plugin that provides a basic controller for differential drive robots in Gazebo. You need a well defined differential drive robot to use this plugin.
描述:模型插件,为Gazebo的差速驱动机器人提供基本控制器。你需要一个定义良好的差速驱动机器人来使用这个插件。
配置列表模板:
<gazebo>
<plugin name="differential_drive_controller" filename="libgazebo_ros_diff_drive.so">
<!-- Plugin update rate in Hz -->
<updateRate>${update_rate}</updateRate>
<!-- Name of left joint, defaults to `left_joint` -->
<leftJoint>base_link_left_wheel_joint</leftJoint>
<!-- Name of right joint, defaults to `right_joint` -->
<rightJoint>base_link_right_wheel_joint</rightJoint>
<!-- The distance from the center of one wheel to the other, in meters, defaults to 0.34 m -->
<wheelSeparation>0.5380</wheelSeparation>
<!-- Diameter of the wheels, in meters, defaults to 0.15 m -->
<wheelDiameter>0.2410</wheelDiameter>
<!-- Wheel acceleration, in rad/s^2, defaults to 0.0 rad/s^2 -->
<wheelAcceleration>1.0</wheelAcceleration>
<!-- Maximum torque which the wheels can produce, in Nm, defaults to 5 Nm -->
<wheelTorque>20</wheelTorque>
<!-- Topic to receive geometry_msgs/Twist message commands, defaults to `cmd_vel` -->
<commandTopic>cmd_vel</commandTopic>
<!-- Topic to publish nav_msgs/Odometry messages, defaults to `odom` -->
<odometryTopic>odom</odometryTopic>
<!-- Odometry frame, defaults to `odom` -->
<odometryFrame>odom</odometryFrame>
<!-- Robot frame to calculate odometry from, defaults to `base_footprint` -->
<robotBaseFrame>base_footprint</robotBaseFrame>
<!-- Odometry source, 0 for ENCODER, 1 for WORLD, defaults to WORLD -->
<odometrySource>1</odometrySource>
<!-- Set to true to publish transforms for the wheel links, defaults to false -->
<publishWheelTF>true</publishWheelTF>
<!-- Set to true to publish transforms for the odometry, defaults to true -->
<publishOdom>true</publishOdom>
<!-- Set to true to publish sensor_msgs/JointState on /joint_states for the wheel joints, defaults to false -->
<publishWheelJointState>true</publishWheelJointState>
<!-- Set to true to swap right and left wheels, defaults to true -->
<legacyMode>false</legacyMode>
</plugin>
</gazebo>
其他插件模块
详情请移步文档:https://classic.gazebosim.org/tutorials?tut=ros_gzplugins#Tutorial:UsingGazebopluginswithROS
Joint Pose Trajectory
Description: listens to a jointtrajectoryaction and plays back the set of joint positions. Sets the set of joints to exact positions without regards to simulated physics and forces.
描述:收听一个联合目标动作并回放一组联合位置。将关节集设置为精确的位置,而不考虑模拟的物理和力
P3D (3D Position Interface for Ground Truth)(地面实况的3D位置接口)
Description: broadcasts the inertial pose of any body in simulation via Odometry message as described in nav_msgs via ROS topic.
描述:通过ROS主题通过nav_msgs中描述的Odometry消息广播模拟中任何物体的惯性姿态。
Projector
Description: projects a static texture from a source outwards, such as used with the PR2's original head camera sensor. See API documentation for more information.
描述:从源向外投射静态纹理,例如与PR2的原始头部摄像头传感器一起使用。有关更多信息,请参阅API文档。
Prosilica Camera
Description: simulates interfaces exposed by a ROS Prosilica Camera. Here's an example URDF Xacro macro.
描述:模拟ROS Prosilica相机暴露的界面。这是一个URDF Xacro宏示例。
Bumper
Description: provides contact feedback via ContactsState message.
描述:通过ContactsState message提供联系人反馈。
另有:
- Skid Steering Drive
- Video Plugin
- Planar Move Plugin
- Template
- Depth Camera:模拟Kinect插件中复制的传感器。将来可能会与Openni Kinect合并。
- Openni Kinect:模拟Microsoft Kinect,发布与Fuerte文档中所述的Microsoft Kinect的相应ROS驱动程序相同的主题。(注:Microsoft Kinect是微软发布的一款产品,可以理解为3D体感摄影机。)
- Block Laser,描述:提供网格式激光测距扫描仪模拟(例如Velodyne)。
- F3D (Force Feedback Ground Truth)描述:如geometry_msgs中所述,通过WrenchStamped消息在模拟中广播物体上的外力。
- Force,Description: ROS interface for applying Wrench (geometry_msgs) on a body in simulation.描述:ROS接口,用于在模拟中在物体上应用扳手(geometry_msgs)
进阶:创建自定义插件
另可参阅:
- ROS入门(七):ROS与gazebo plugins - Melody的文章 - 知乎
- 官方文档【gazebo_plugins】
- ROS技术点滴 —— gazebo中的常用插件 - 古月的文章 - 知乎
- /opt/ros/noetic/lib文件夹下的gazebo相关插件及其用处 - Zox7的文章 - 知乎
仿真实例
实验设计
这个实例的源文件已上传至本网站,点击此处下载
设计一个利用超声波避障模块的机器小车,在一个世界环境中自主行走。
为简便起见,轮式机器人源文件参考了鱼香ros教程,本篇笔记对其中部分代码作进一步解释。
考虑将来的实验,采用C++方案,编译方式:ament_cmake
避障节点发布机器人运动的cmd_vel话题,话题接口类型geometry_msgs/msg/twist,订阅超声波发布的话题,话题接口类型为sensor_msgs/msg/range。
因此需要的依赖有:rclcpp sensor_msgs geometry_msgs
创建工作包
#在src目录下
ros2 pkg create --build-type ament_cmake --node-name avoid_node johnbot2 --dependencies rclcpp sensor_msgs geometry_msgs
参阅:
# 阅读帮助文档
ros2 pkg create -h
创建机器人模型
- 创建文件夹
- 创建机器人物理模型
- 添加机器人仿真插件
在pro_ws/src/package_name/下创建models文件夹,用于放置模型文件
(文件夹目录层级参阅笔记1第3.3.2章节)
cd pro_ws/src/package_name
mkdir models
cd models
别忘了修改CMakeList.txt
install(DIRECTORY
models
DESTINATION share/${PROJECT_NAME}/
)
机器人的物理模型包括一个虚拟的Footprint连接到地面,一个base_link,一个left_wheel_link,一个right_wheel_link,一个前轮caster_link,一个超声波模块ultrasonic_sensor_link,各Link均与base_link连接。物理模型不多解释,文件见src文件夹。下面详细介绍超声波模块插件的设计。
根据第4.3章节中雷达模块编写超声波模块插件
Lidar is an acronym for "light detection and ranging". This sensor can help us detect obstacles around the robot.
原雷达插件模板
<!-- 原雷达(Laser)插件模板-->
<gazebo reference="hokuyo_link">
<sensor type="ray" name="head_hokuyo_sensor">
<pose>0 0 0 0 0 0</pose>
<visualize>false</visualize>
<update_rate>40</update_rate>
<ray>
<scan>
<horizontal>
<samples>720</samples>
<resolution>1</resolution>
<min_angle>-1.570796</min_angle>
<max_angle>1.570796</max_angle>
</horizontal>
</scan>
<range>
<min>0.10</min>
<max>30.0</max>
<resolution>0.01</resolution>
</range>
<noise>
<type>gaussian</type>
<!-- Noise parameters based on published spec for Hokuyo laser
achieving "+-30mm" accuracy at range < 10m. A mean of 0.0m and
stddev of 0.01m will put 99.7% of samples within 0.03m of the true
reading. -->
<mean>0.0</mean>
<stddev>0.01</stddev>
</noise>
</ray>
<plugin name="gazebo_ros_head_hokuyo_controller" filename="libgazebo_ros_laser.so">
<topicName>/rrbot/laser/scan</topicName>
<frameName>hokuyo_link</frameName>
</plugin>
</sensor>
</gazebo>
修改为我们需要的超声波插件:
<!--超声波模块-->
<!-- Link名称更换为模型中的名称 -->
<gazebo reference="ultrasonic_sensor_link">
<!-- 传感器类型是ray,插件名称自定义,不能重复 -->
<sensor type="ray" name="ultrasonic_sensor">
<pose>0 0 0 0 0 0</pose>
<visualize>true</visualize>
<update_rate>40</update_rate>
<ray>
<scan>
<horizontal>
<!-- 水平方向 -->
<samples>50</samples>
<!-- 每圈产生的激光数量num = samples * resolution -->
<resolution>1</resolution>
<!-- 0.8弧度约为45度角度,这里根据实际自定义即可 -->
<min_angle>-0.4</min_angle>
<max_angle>0.4</max_angle>
</horizontal>
<vertical>
<!-- 竖直方向,不需要扫描 -->
<samples>1</samples>
<resolution>1</resolution>
<min_angle>0</min_angle>
<max_angle>0</max_angle>
</vertical>
</scan>
<!-- 超声波检测的范围和数据分辨率单位m,考虑实际情况修改测量范围 -->
<range>
<min>0.02</min>
<max>4.0</max>
<resolution>0.01</resolution>
</range>
<!-- 数据噪声采用高斯噪声 -->
<noise>
<type>gaussian</type>
<!-- Noise parameters based on published spec for Hokuyo laser
achieving "+-30mm" accuracy at range < 10m. A mean of 0.0m and
stddev of 0.01m will put 99.7% of samples within 0.03m of the true
reading.此处可以考虑修改,在实际工程应用中参考实际模块参数即可 -->
<mean>0.0</mean>
<stddev>0.01</stddev>
</noise>
</ray>
<plugin name="ultrasonic_sensor_controller" filename="libgazebo_ros_ray_sensor.so">
<ros>
<!-- 重映射输出话题 注意这里的话题名称,在后面设计节点时需要用到-->
<remapping>~/out:=ultrasonic_sensor_range</remapping>
</ros>
<!-- 输出消息的类型,参阅/opt/ros/humble/include/gazebo_plugins/gazebo_ros_ray_sensor.hpp文件 -->
<output_type>sensor_msgs/Range</output_type>
<!-- 射线类型有两种,参阅opt/ros/humble/include/sensor_msgs/msg/detail/range_struct.h文件-->
<radiation_type>ultrasound</radiation_type>
<!-- 坐标系名称,要与reference名称一致 -->
<frame_name>ultrasonic_sensor_link</frame_name>
</plugin>
</sensor>
</gazebo>
引自/opt/ros/humble/include/gazebo_plugins/gazebo_ros_ray_sensor.hpp
文件,此文件中解释了上面代码中的一些参数设置:
/// Plugin to attach to a gazebo ray or gpu_ray sensor and publish its data to ROS
/**
\details
SDF parameters:
\verbatim
<output_type>: Optional. The message type of the plugin's output. Can be any of the following:
* sensor_msgs/PointCloud2: 3D cloud of points, Default
* sensor_msgs/PointCloud: 3D cloud of points
* sensor_msgs/LaserScan: 2D scan, uses center vertical ray if there are multiple
* sensor_msgs/Range: Single distance value, minimum of all ray ranges of parent sensor
\endverbatim
\verbatim
<frame_name>: TF Frame id of output header.
If not set, frame id will be name of sensor's parent link
\endverbatim
\verbatim
<min_intensity>: Clip intensity values for output to this value.
Default: 0.0
\endverbatim
\verbatim
<radiation_type>: The radiation type to publish when the output type is sensor_msgs/Range.
Can be either "infrared"(红外线) or "ultrasonic"(超声波).
Default: "infrared".
\endverbatim
Example SDF:
\code{.xml}
<plugin name="my_ray_sensor_plugin" filename="libgazebo_ros_ray_sensor.so">
<ros>
<!-- Configure namespace and remap to publish to /ray/pointcloud2 -->
<namespace>/ray</namespace>
<remapping>~/out:=pointcloud2</remapping>
</ros>
<!-- Output as a PointCloud2, see above for other types -->
<output_type>sensor_msgs/PointCloud2</output_type>
<!-- Clip intensity values so all are above 100, optional -->
<min_intensity>100.0</min_intensity>
<!-- Frame id for header of output, defaults to sensor's parent link name -->
<frame_name>ray_link</frame_name>
</plugin>
\endcode
*/
默认输出的话题是
/ultrasonic_sensor_controller/out
可以按照上面的方式重定向。
以下内容引自:opt/ros/humble/include/sensor_msgs/msg/detail/range_struct.h,指出了radiation_type的可取值。
/// Constant 'ULTRASOUND'.
/**
* Radiation type enums
* If you want a value added to this list, send an email to the ros-users list
*/
enum
{
sensor_msgs__msg__Range__ULTRASOUND = 0
};
/// Constant 'INFRARED'.
enum
{
sensor_msgs__msg__Range__INFRARED = 1
};
另可参阅:https://github.com/gazebosim/docs/blob/master/harmonic/sensors.md#lidar-sensor
至此,机器人模型设计完毕。下面先通过launch文件打开看看。
Launch文件设计
同样的,在pro_ws/src/package_name/下创建launch文件夹,在launch文件夹中创建avoid.launch.py文件
同时修改CMakeList.txt
install(DIRECTORY
launch
DESTINATION share/${PROJECT_NAME}/
)
参阅笔记1第四章
from launch_ros.actions import Node # 节点启动的描述类
import os
from ament_index_python.packages import get_package_share_directory # 查询功能包路径的方法
from launch import LaunchDescription # launch文件的描述类
from launch.actions import ExecuteProcess
def generate_launch_description(): # 自动生成launch文件的函数
ld=LaunchDescription()
package_name = 'johnbot2'
pkg_path = os.path.join(get_package_share_directory(package_name))
world_file_path = 'models/wall01.world'
world_path = os.path.join(pkg_path, world_file_path)
robot_name_in_model = 'johnbot2'
urdf_file_path = 'models/avoid_bot2.urdf'
urdf_model_path = os.path.join(pkg_path,urdf_file_path)
# Start Gazebo server
start_gazebo_cmd = ExecuteProcess(
cmd=['gazebo', '--verbose','-s', 'libgazebo_ros_init.so', '-s', 'libgazebo_ros_factory.so', world_path],
output='screen'
)
ld.add_action(start_gazebo_cmd)
# # Launch the robot
# Pose where we want to spawn the robot,这一组参数指定起始位置和姿态,否则默认机器人出生在原点,有点类似MC游戏《我的世界》中“出生点”的概念
spawn_x_val = '0.0'
spawn_y_val = '-2.0'
spawn_z_val = '0.0'
spawn_yaw_val = '0.00'
spawn_entity_cmd = Node(
package='gazebo_ros',
executable='spawn_entity.py',
arguments=['-entity', robot_name_in_model, '-file', urdf_model_path ,'-x', spawn_x_val,
'-y', spawn_y_val,
'-z', spawn_z_val,
'-Y', spawn_yaw_val], output='screen')
ld.add_action(spawn_entity_cmd)
# Start Robot State publisher
start_robot_state_publisher_cmd = Node(
package='robot_state_publisher',
executable='robot_state_publisher',
arguments=[urdf_model_path]
)
ld.add_action(start_robot_state_publisher_cmd)
# Launch RViz
start_rviz_cmd = Node(
package='rviz2',
executable='rviz2',
name='rviz2',
output='screen',
# arguments=['-d', default_rviz_config_path]
)
ld.add_action(start_rviz_cmd)
# start_avoid_node = Node(
# package='johnbot2',
# executable= 'avoid_node',
# # arguments=[output = 'screen']
# )
# ld.add_action(start_avoid_node)
# Launch them all!
return ld
尝试运行:
# 在proj_ws文件夹下
colcon build
source install/setup.bash
ros2 launch johnbot2 avoid.launch.py
运行完毕,一切正常,下面设计避障节点。
避障节点设计
参阅:API文档 rclcpp: ROS Client Library for C++
避障节点发布机器人运动的cmd_vel话题,话题接口类型geometry_msgs/msg/twist,订阅超声波发布的ultrasonic_sensor_range话题,话题接口类型为sensor_msgs/msg/range。
可以先查看两种消息接口的详细信息
ros2 interface show geometry_msgs/msg/Twist
参阅:geometry_msgs/Twist Message
linear[3]:对应3维线性速度,angular[3]对应3轴旋转速度
简单示例如下:
#include <cstdio>
#include <chrono>
#include <memory>
#include "rclcpp/rclcpp.hpp"
#include "geometry_msgs/msg/twist.hpp"
#include "sensor_msgs/msg/range.hpp"
class Node_avoid : public rclcpp::Node
{
public:
Node_avoid(std::string name) : Node(name)
{
RCLCPP_INFO(this -> get_logger(), "Node_avoid init here,name: %s",name.c_str());
//创建发布者
command_publisher_ = this->create_publisher<geometry_msgs::msg::Twist>("cmd_vel",10);
// timer_ = this->create_wall_timer(std::chrono::milliseconds(500), std::bind(&Node_avoid::timer_callback, this));
distance_monitor_ = this->create_subscription<sensor_msgs::msg::Range>("ultrasonic_sensor_range",10,
std::bind(&Node_avoid::distance_callback, this, std::placeholders::_1));
}
private:
rclcpp::Publisher<geometry_msgs::msg::Twist>::SharedPtr command_publisher_;
rclcpp::TimerBase::SharedPtr timer_;
//超声波,距离监视器
rclcpp::Subscription<sensor_msgs::msg::Range>::SharedPtr distance_monitor_;
void distance_callback(const sensor_msgs::msg::Range distance_real)
{
geometry_msgs::msg::Twist run_command;
float divide = 0.3;
if(distance_real.range >= divide)
{
run_command = direct_run();
}
else{
run_command = turn_left();
}
command_publisher_->publish(run_command);
}
//直行
geometry_msgs::msg::Twist direct_run()
{
geometry_msgs::msg::Twist loc;
loc.linear.x = 0.2;
loc.linear.y = 0.0;
loc.linear.z = 0.0;
loc.angular.x = 0.0;
loc.angular.y = 0.0;
loc.angular.z = 0.0;
return loc;
}
//转弯
geometry_msgs::msg::Twist turn_left()
{
geometry_msgs::msg::Twist loc;
loc.linear.x = 0.0;
loc.linear.y = 0.0;
loc.linear.z = 0.0;
loc.angular.x = 0.0;
loc.angular.y = 0.0;
loc.angular.z = 0.8;
return loc;
}
};
/*
@brief: 避障节点,没有障碍就直行,有则转弯。
@note:订阅超声波ultrasonic_sensor_range话题,话题接口类型为sensor_msgs/msg/range,
发布机器人运动的cmd_vel话题,话题接口类型geometry_msgs/msg/twist
*/
int main(int argc, char ** argv)
{
rclcpp::init(argc,argv);
auto node = std :: make_shared<Node_avoid>("avoid_node");
rclcpp::spin(node);
rclcpp::shutdown();
return 0;
}
节点设计完毕,修改launch文件。
文件在project_examples/avoid_bot/下。
设计完毕,运行,查看。
colcon build
source install/setup.bash
ros2 launch johnbot2 avoid.launch.py
没什么问题,准备进入下一步——建图与导航。