前言
gazebo 中能仿真真實(shí)世界,包括很多物理屬性,比如慣性,碰撞等。對(duì)于沒有真實(shí)機(jī)器人和場(chǎng)地條件的情況下,作用十分強(qiáng)大。
gazebo 世界中的機(jī)器人
先上個(gè)成品圖:

在gazebo世界中,要完成仿真,首先要有機(jī)器人模型。
先貼上機(jī)器人模型的代碼:
<?xml version="1.0"?>
<robot xmlns:xacro="http://www.ros.org/wiki/xacro"
xmlns:sensor="http://playerstage.sourceforge.net/gazebo/xmlschema/#sensor"
xmlns:controller="http://playerstage.sourceforge.net/gazebo/xmlschema/#controller"
xmlns:interface="http://playerstage.sourceforge.net/gazebo/xmlschema/#interface"
name="robot1_xacro">
<xacro:property name="length_wheel" value="0.05" />
<xacro:property name="radius_wheel" value="0.05" />
<xacro:property name="camera_link" value="0.05" />
<xacro:macro name="default_inertial" params="mass">
<inertial>
<mass value="${mass}" />
<inertia ixx="1.0" ixy="0.0" ixz="0.0"
iyy="1.0" iyz="0.0"
izz="1.0" />
</inertial>
</xacro:macro>
<!-- <link name="base_footprint">
<visual>
<geometry>
<box size="0.001 0.001 0.001"/>
</geometry>
<origin rpy="0 0 0" xyz="0 0 0"/>
</visual>
<xacro:default_inertial mass="0.0001"/>
</link> -->
<xacro:include filename="$(find robot1_description)/urdf/robot_depthCam_laser.gazebo" />
<gazebo reference="base_footprint">
<material>Gazebo/Green</material>
<turnGravityOff>false</turnGravityOff>
</gazebo>
<!-- <joint name="base_footprint_joint" type="fixed">
<origin xyz="0 0 0" />
<parent link="base_footprint" />
<child link="base_link" />
</joint> -->
<link name="base_link">
<visual>
<geometry>
<box size="0.2 .3 1"/>
</geometry>
<origin rpy="0 0 1.54" xyz="0 0 0.05"/>
<material name="white">
<color rgba="1 1 1 1"/>
</material>
</visual>
<collision>
<geometry>
<box size="0.2 .3 1"/>
</geometry>
</collision>
<xacro:default_inertial mass="10"/>
</link>
<link name="wheel_1">
<visual>
<geometry>
<cylinder length="${length_wheel}" radius="${radius_wheel}"/>
</geometry>
<!-- <origin rpy="0 1.5 0" xyz="0.1 0.1 0"/> -->
<origin rpy="0 0 0" xyz="0 0 0"/>
<material name="black">
<color rgba="0 0 0 1"/>
</material>
</visual>
<collision>
<geometry>
<cylinder length="${length_wheel}" radius="${radius_wheel}"/>
</geometry>
</collision>
<xacro:default_inertial mass="1"/>
</link>
<link name="wheel_2">
<visual>
<geometry>
<cylinder length="${length_wheel}" radius="${radius_wheel}"/>
</geometry>
<!-- <origin rpy="0 1.5 0" xyz="-0.1 0.1 0"/> -->
<origin rpy="0 0 0" xyz="0 0 0"/>
<material name="black"/>
</visual>
<collision>
<geometry>
<cylinder length="${length_wheel}" radius="${radius_wheel}"/>
</geometry>
</collision>
<xacro:default_inertial mass="1"/>
</link>
<link name="wheel_3">
<visual>
<geometry>
<cylinder length="${length_wheel}" radius="${radius_wheel}"/>
</geometry>
<!-- <origin rpy="0 1.5 0" xyz="0.1 -0.1 0"/> -->
<origin rpy="0 0 0" xyz="0 0 0"/>
<material name="black"/>
</visual>
<collision>
<geometry>
<cylinder length="${length_wheel}" radius="${radius_wheel}"/>
</geometry>
</collision>
<xacro:default_inertial mass="1"/>
</link>
<link name="wheel_4">
<visual>
<geometry>
<cylinder length="${length_wheel}" radius="${radius_wheel}"/>
</geometry>
<!-- <origin rpy="0 1.5 0" xyz="-0.1 -0.1 0"/> -->
<origin rpy="0 0 0" xyz="0 0 0" />
<material name="black"/>
</visual>
<collision>
<geometry>
<cylinder length="${length_wheel}" radius="${radius_wheel}"/>
</geometry>
</collision>
<xacro:default_inertial mass="1"/>
</link>
<joint name="base_to_wheel1" type="continuous">
<parent link="base_link"/>
<child link="wheel_1"/>
<origin rpy="-1.5707 0 0" xyz="0.1 0.125 -0.45"/>
<axis xyz="0 0 1" />
</joint>
<joint name="base_to_wheel2" type="continuous">
<axis xyz="0 0 1" />
<anchor xyz="0 0 0" />
<limit effort="100" velocity="100" />
<parent link="base_link"/>
<child link="wheel_2"/>
<origin rpy="-1.5707 0 0" xyz="-0.1 0.125 -0.45"/>
</joint>
<joint name="base_to_wheel3" type="continuous">
<parent link="base_link"/>
<axis xyz="0 0 1" />
<child link="wheel_3"/>
<origin rpy="-1.5707 0 0" xyz="0.1 -0.125 -0.45"/>
</joint>
<joint name="base_to_wheel4" type="continuous">
<parent link="base_link"/>
<axis xyz="0 0 1" />
<child link="wheel_4"/>
<origin rpy="-1.5707 0 0" xyz="-0.1 -0.125 -0.45"/>
</joint>
<!-- Camera joint -->
<joint name="camera_joint" type="fixed">
<axis xyz="0 1 0" />
<origin xyz="0.125 0 0.575" rpy="0 0 0"/>
<parent link="base_link"/>
<child link="camera_link"/>
</joint>
<!-- Camera -->
<link name="camera_link">
<collision>
<origin xyz="0 0 0" rpy="0 0 0"/>
<geometry>
<box size="0.05 0.05 0.05"/>
</geometry>
</collision>
<visual>
<origin xyz="0 0 0" rpy="0 0 0"/>
<geometry>
<box size="0.05 0.05 0.05"/>
</geometry>
<material name="red">
<color rgba="1 0 0 1"/>
</material>
</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>
<!-- Hokuyo joint -->
<joint name="hokuyo_joint" type="fixed">
<origin xyz="0.125 0.075 0.575" rpy="0 0 0"/>
<parent link="base_link"/>
<axis xyz="0 1 0" />
<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.05 0.05 0.05"/>
</geometry>
</collision>
<visual>
<origin xyz="0 0 0" rpy="0 0 0"/>
<geometry>
<box size="0.05 0.05 0.05"/>
</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>
<!-- Drive controller -->
<gazebo>
<plugin name="skid_steer_drive_controller" filename="libgazebo_ros_skid_steer_drive.so">
<updateRate>100.0</updateRate>
<robotNamespace>/</robotNamespace>
<leftFrontJoint>base_to_wheel1</leftFrontJoint>
<rightFrontJoint>base_to_wheel3</rightFrontJoint>
<leftRearJoint>base_to_wheel2</leftRearJoint>
<rightRearJoint>base_to_wheel4</rightRearJoint>
<wheelSeparation>4</wheelSeparation>
<wheelDiameter>0.1</wheelDiameter>
<robotBaseFrame>base_link</robotBaseFrame>
<torque>1</torque>
<topicName>cmd_vel</topicName>
<broadcastTF>0</broadcastTF>
</plugin>
</gazebo>
</robot>
這是一個(gè)帶有傳感器Hokuyo雷達(dá),相機(jī)的機(jī)器人模型文件:robot_depthCam_laser.xacro,
<!-- Camera -->
<link name="camera_link">
<collision>
<origin xyz="0 0 0" rpy="0 0 0"/>
<geometry>
<box size="0.05 0.05 0.05"/>
</geometry>
</collision>
<visual>
<origin xyz="0 0 0" rpy="0 0 0"/>
<geometry>
<box size="0.05 0.05 0.05"/>
</geometry>
<material name="red">
<color rgba="1 0 0 1"/>
</material>
</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>
這是相機(jī)模塊的節(jié)點(diǎn),基本語法在這里就不多說了,很多資料有教程。
<!-- Hokuyo Laser -->
<link name="hokuyo_link">
<collision>
<origin xyz="0 0 0" rpy="0 0 0"/>
<geometry>
<box size="0.05 0.05 0.05"/>
</geometry>
</collision>
<visual>
<origin xyz="0 0 0" rpy="0 0 0"/>
<geometry>
<box size="0.05 0.05 0.05"/>
</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>
這是Hokuyo雷達(dá)的模塊。
<!-- Drive controller -->
<gazebo>
<plugin name="skid_steer_drive_controller" filename="libgazebo_ros_skid_steer_drive.so">
<updateRate>100.0</updateRate>
<robotNamespace>/</robotNamespace>
<leftFrontJoint>base_to_wheel1</leftFrontJoint>
<rightFrontJoint>base_to_wheel3</rightFrontJoint>
<leftRearJoint>base_to_wheel2</leftRearJoint>
<rightRearJoint>base_to_wheel4</rightRearJoint>
<wheelSeparation>4</wheelSeparation>
<wheelDiameter>0.1</wheelDiameter>
<robotBaseFrame>base_link</robotBaseFrame>
<torque>1</torque>
<topicName>cmd_vel</topicName>
<broadcastTF>0</broadcastTF>
</plugin>
</gazebo>
這一個(gè)節(jié)點(diǎn)有點(diǎn)不同,這個(gè)是一個(gè)gazebo插件,是用于使機(jī)器人在gazebo世界中運(yùn)動(dòng)的模塊,正常來說,如果不需要在gazebo中進(jìn)行仿真運(yùn)動(dòng),在一般的xacro文件或者urdf文件中,定義機(jī)器人是不需要這種模塊的,去掉這個(gè)模塊也可以在rviz中顯示出機(jī)器人模型。gazebo插件有很多種,這里要補(bǔ)充說明的是,上文中已經(jīng)添加了雷達(dá),相機(jī)等模塊,但是也只是一個(gè)安放在機(jī)器人身上的一個(gè)模型,在gazebo中是不會(huì)起到仿真作用,因此必須要在另外一個(gè)文件(或者在同一個(gè)文件)中進(jìn)行插件定義。
定義gazebo插件:gazebo文件
在robot_depthCam_laser.gazebo文件中:
<?xml version="1.0"?>
<robot>
<!-- materials -->
<gazebo reference="base_link">
<material>Gazebo/Orange</material>
</gazebo>
<gazebo reference="wheel_1">
<material>Gazebo/Black</material>
</gazebo>
<gazebo reference="wheel_2">
<material>Gazebo/Black</material>
</gazebo>
<gazebo reference="wheel_3">
<material>Gazebo/Black</material>
</gazebo>
<gazebo reference="wheel_4">
<material>Gazebo/Black</material>
</gazebo>
<!-- ros_control plugin -->
<gazebo>
<plugin name="gazebo_ros_control" filename="libgazebo_ros_control.so">
<robotNamespace>/robot</robotNamespace>
</plugin>
</gazebo>
<!--
<gazebo reference="link1">
<material>Gazebo/Orange</material>
</gazebo>
<gazebo reference="link2">
<mu1>0.2</mu1>
<mu2>0.2</mu2>
<material>Gazebo/Black</material>
</gazebo>
<gazebo reference="link3">
<mu1>0.2</mu1>
<mu2>0.2</mu2>
<material>Gazebo/Orange</material>
</gazebo>-->
<!-- camera_link -->
<gazebo reference="camera_link">
<mu1>0.2</mu1>
<mu2>0.2</mu2>
<material>Gazebo/Red</material>
</gazebo>
<!-- hokuyo -->
<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>/robot/laser/scan</topicName>
<frameName>hokuyo_link</frameName>
</plugin>
</sensor>
</gazebo>
<gazebo reference="camera_link">
<sensor type="depth" name="camera">
<always_on>true</always_on>
<update_rate>20.0</update_rate>
<camera>
<horizontal_fov>${1.3962634}</horizontal_fov>
<image>
<format>R8G8B8</format>
<width>640</width>
<height>480</height>
</image>
<clip>
<near>0.05</near>
<far>8.0</far>
</clip>
</camera>
<plugin name="kinect_camera_controller" filename="libgazebo_ros_openni_kinect.so">
<cameraName></cameraName>
<alwaysOn>true</alwaysOn>
<updateRate>10</updateRate>
<imageTopicName>rgb/image_raw</imageTopicName>
<depthImageTopicName>depth/image_raw</depthImageTopicName>
<pointCloudTopicName>depth/points</pointCloudTopicName>
<cameraInfoTopicName>rgb/camera_info</cameraInfoTopicName>
<depthImageCameraInfoTopicName>depth/camera_info</depthImageCameraInfoTopicName>
<frameName>camera_link</frameName>
<baseline>0.1</baseline>
<distortion_k1>0.0</distortion_k1>
<distortion_k2>0.0</distortion_k2>
<distortion_k3>0.0</distortion_k3>
<distortion_t1>0.0</distortion_t1>
<distortion_t2>0.0</distortion_t2>
<pointCloudCutoff>0.4</pointCloudCutoff>
</plugin>
</sensor>
</gazebo>
</robot>
這個(gè)文件主要定義很多機(jī)器人在gazebo世界中各種表現(xiàn),和屬性。這里主要看一下如何定義傳感器等插件的定義:
<!-- hokuyo -->
<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>
<mean>0.0</mean>
<stddev>0.01</stddev>
</noise>
</ray>
<plugin name="gazebo_ros_head_hokuyo_controller" filename="libgazebo_ros_laser.so">
<topicName>/robot/laser/scan</topicName>
<frameName>hokuyo_link</frameName>
</plugin>
</sensor>
</gazebo>
這個(gè)是定義雷達(dá)模塊的,一般屬性定義成默認(rèn)值即可使用。
<gazebo reference="camera_link">
<sensor type="depth" name="camera">
<always_on>true</always_on>
<update_rate>20.0</update_rate>
<camera>
<horizontal_fov>${1.3962634}</horizontal_fov>
<image>
<format>R8G8B8</format>
<width>640</width>
<height>480</height>
</image>
<clip>
<near>0.05</near>
<far>8.0</far>
</clip>
</camera>
<plugin name="kinect_camera_controller" filename="libgazebo_ros_openni_kinect.so">
<cameraName></cameraName>
<alwaysOn>true</alwaysOn>
<updateRate>10</updateRate>
<imageTopicName>rgb/image_raw</imageTopicName>
<depthImageTopicName>depth/image_raw</depthImageTopicName>
<pointCloudTopicName>depth/points</pointCloudTopicName>
<cameraInfoTopicName>rgb/camera_info</cameraInfoTopicName>
<depthImageCameraInfoTopicName>depth/camera_info</depthImageCameraInfoTopicName>
<frameName>camera_link</frameName>
<baseline>0.1</baseline>
<distortion_k1>0.0</distortion_k1>
<distortion_k2>0.0</distortion_k2>
<distortion_k3>0.0</distortion_k3>
<distortion_t1>0.0</distortion_t1>
<distortion_t2>0.0</distortion_t2>
<pointCloudCutoff>0.4</pointCloudCutoff>
</plugin>
</sensor>
</gazebo>
這個(gè)是定義了深度相機(jī)模塊的屬性。
值得注意的是:

這里很重要,因?yàn)檫@里的reference作用是直接與xacro文件中的模塊名字進(jìn)行對(duì)應(yīng),從而將相機(jī)屬性賦予xacro中camera_link模塊。

這里在與gazebo世界中進(jìn)行仿真算法的時(shí)候十分重要,在這里定義的topic名稱必須與算法中所要求的topic名稱要一致。
啟動(dòng)gazebo世界和機(jī)器人模型
現(xiàn)在已經(jīng)完成了機(jī)器人模型的創(chuàng)建了,下一步就是把機(jī)器人在gazebo世紀(jì)中啟動(dòng)。在ros系統(tǒng)中,打開多個(gè)節(jié)點(diǎn)一般需要編寫launch文件:
<?xml version="1.0"?>
<launch>
<arg name="world" />
<arg name="model"/>
<include file="$(find gazebo_ros)/launch/$(arg world)">
</include>
<!-- Load the URDF into the ROS Parameter Server -->
<param name="robot_description" command="$(find xacro)/xacro.py --inorder $(arg model)"/>
<node name="robot_state_publisher" pkg="robot_state_publisher" type="robot_state_publisher" />
<!-- joint state publisher -->
<node name="joint_state_publisher" pkg="joint_state_publisher" type="joint_state_publisher"/>
<!-- Run a python script to the send a service call to gazebo_ros to spawn a URDF robot -->
<node name="urdf_spawner" pkg="gazebo_ros" type="spawn_model" respawn="false" output="screen"
args="-urdf -model robot1 -param robot_description -z 0.05"/>
<!-- tf transform -->
<arg name="pi/2" value="1.5707963267948966" />
<arg name="optical_rotate" value="0 0 0 -$(arg pi/2) 0 -$(arg pi/2)" />
<node pkg="tf" type="static_transform_publisher" name="camera_base_link"
args="$(arg optical_rotate) base_link camera_link 100" />
</launch>
launch文件的語法在這里也不多說,主要說以下使用這個(gè)launch 文件所需要的參數(shù):

在啟動(dòng)的時(shí)候需要傳入兩個(gè)參數(shù),一個(gè)是所用的gazebo環(huán)境的啟動(dòng)launch文件,一般會(huì)放在./opt/ros/melodic/share/gazebo_ros/launch中,包括自定義的gazebo世界的launch,最好也要放在同處地方。另一個(gè)參數(shù)則是所用的模型文件。這里傳入?yún)?shù)后會(huì)用到一個(gè)命令,完整命令是這樣的:
rosrun xacro xacro.py model.xacro > model_processed.urdf
這句命令很簡單,主要是用作將xacro文件導(dǎo)出成urdf文件。在launch文件中則是這樣使用:

這里實(shí)際上是將xacro文件轉(zhuǎn)化為urdf后,將其放進(jìn)param參數(shù)服務(wù)器中,便于其他節(jié)點(diǎn)的調(diào)用,也就是launch文件中那個(gè)urdf_spawner節(jié)點(diǎn)的調(diào)用。

在這里需要啟用一個(gè)節(jié)點(diǎn)是tf轉(zhuǎn)換,原因是在rtabmap算法中,世界坐標(biāo)系是x向前,y向左,z向上,但是在相機(jī)坐標(biāo)系中,三軸的方向是不同的,因此需要轉(zhuǎn)換。
啟動(dòng)gazebo世界和機(jī)器人模型:
roslaunch robot1_gazebo gazebo_wg.launch model:="`rospack find robot1_description`/urdf/robot_depthCam_laser.xacro" world:="willowgarage_world.launch"
啟動(dòng)后的結(jié)果:

機(jī)器人已經(jīng)成功地在gazebo世界中運(yùn)行了,接下來就是運(yùn)行rtabmap算法進(jìn)行建圖了。