ROS理論與實踐(以移動機器人爲例)連載(五) ——構建機器人仿真平臺

在這裏插入圖片描述
O(∩_∩)O哈哈哈~ 特想放張偉!我是什麼顏色???



參考功能包鏈接

https://github.com/XFFer/ROS_Resource

1. 優化物理仿真模型

使用xacro文件優化URDF模型

連載(四) 中我們使用URDF建模遇到了很多問題,比如多個輪子需要重複書寫代碼;不能進行參數計算,各個link的位置需要逐個輸入參數。
在這裏插入圖片描述

URDF模型的進化版本——xacro模型文件

官方Wiki地址跳轉

  • 精簡模型代碼
    • 創建宏定義
    • 文件包含
  • 提供可編程接口
    • 常量
    • 變量
    • 數學計算
    • 條件語句

① 常量的定義與使用

常量定義

<xacro:property name="M_PI" value="3.14159" />

舉例:

<!-- PROPERTY LIST -->
<xacro:property name="M_PI" value="3.1415926" />
<xacro:property name="base_radius" value="0.20" />
<xacro:property name="base_length" value="0.16" />

<xacro:property name="wheel_radius" value="0.06" />
<xacro:property name="wheel_length" value="0.025" />
<xacro:property name="wheel_joint_y" value="0.19" />
<xacro:property name="wheel_joint_z" value="0.05" />

<xacro:property name="caster_radius" value="0.015" />
<xacro:property name="caster_joint_x" value="0.18" />

常量使用

<origin xyz="0 0 0" rpy="${M_PI/2} 0 0"/>

舉例:

<joint name="base_footprint_joint" type="fixed">
	<origin xyz="0 0 ${base_length/2 + caster_radius*2}" rpy="0 0 0" />
	<parent link="base_footprint" />
	<child link="base_link" />
</joint>

<link name="base_link">
	<visual>
		<origin xyz="0 0 0" rpy="0 0 0" />
		<geometry>
			<cylinder length="${base_length}" radius="${base_radius}" />
		</geometry>
		<material name="yellow" />
	</visual>
</link>

② 數學計算

所有數學運算都會轉換成浮點數進行,以保證運算精度。

<origin xyz="0 ${(motor_length+wheel_length)/2} 0" rpy="0 0 0" />

舉例:

<joint name="base_footprint_joint" type="fixed">
	<origin xyz="0 0 ${base_length/2 + caster_radius*2}" rpy="0 0 0" />
	<parent link="base_footprint" />
	<child link="base_link" />
</joint>

③ 宏

宏定義

類似於函數,可以實現創建一個輪子的模型,通過再次調用修改參數,創建相同形狀,多個輪子模型。

<xacro:macro name="name" params="A B C">
......
</xacro:macro>

舉例:

<!--Macro for robot wheel-->
<xacro:macro name="wheel" param="prefix reflect">
	<joint name="${prefix}_wheel_joint" type="continuous">
		<origin xyz="0 ${reflect*wheel_joint_y} ${-wheel_joint_z}" rpy="0 0 0" />
		<parent link="base_link" />
		<child link="${prefix}_wheel_link" />
		<axis xyz="0 1 0" />
	</joint>

	<link name="${prefix}_wheel_link">
		<visual>
			<origin xyz="0 0 0" rpy="${M_PI/2} 0 0" />
			<geometry>
				<cylinder radius="${wheel_radius}" length="${wheel_length}" />
			</geometry>
			<material name="gray" />
		</visual>
	</link>
</xacro:macro>

宏調用

<name A="A_value" B="B_value" C="C_value" />

舉例:

<wheel prefix="left" reflect="1" />
<wheel prefix="right" reflect="-1" />

④ 文件包含

<xacro:include filename="$(find mbot_description)/urdf/mbot_base_gazebo.xacro" />

舉例:

<xacro:include filename="$(find mbot_description)/urdf/sensors/camera_gazebo.xacro" />
<xacro:include filename="$(find mbot_description)/urdf/mbot_base_gazebo.xacro" />

ros_control

  • ros_control是ROS爲開發者提供的機器人控制中間件
  • 包含一系列控制器接口、傳動裝置接口、硬件接口、控制器工具箱等
  • 可以幫助我們將上層功能包裏的指令發送給機器人

在這裏插入圖片描述
由於硬件包括各種有刷無刷電機、機械臂、激光雷達、視覺傳感器,驅動方式都有很大的差別。故硬件抽象層是對硬件進行包裝,使它更容易能夠和上層控制層綁定在一起。

  • 控制器管理器 Controller Manager
    • 提供一種通用的接口來管理不同的控制器。
  • 控制器 Controller
    • 讀取硬件狀態,發送控制命令,完成每個joint的控制。
  • 硬件資源 Hardware Resource Interface Layer
    • 爲上下兩層提供硬件資源的接口。
  • 機器人硬件抽象 DefaultRobotHWSim/hardware_interface::RobotHW
    • 機器人硬件抽象和硬件資源直接打交道,通過write和read方法完成硬件操作。
  • 真實機器人 Reality
    • 執行接收到的命令。

在這裏插入圖片描述

上圖中包含了上述的幾個模塊,Controller Manager可以幫助管理不同的控制器,如:底盤控制器,機械臂等的啓動、暫停、運行、停止;具體的是一個PID Loops,這個閉環PID更像在應用層面,會向下發指令會通過Hardware Resource Interface Layer這個硬件資源的接口,以不同的形式發佈;在仿真器中會有DefaultRobotHWSim默認硬件抽象,但對真實機器人,就需要自己去構建這個硬件抽象RobotHW,這裏也存在一個PID閉環控制,但偏向於控制板,如STM32對某個電機的控制,再通過解碼器Encoders得到關節的狀態信息反饋給Controller

前面MoveIt的插入課,我們用到過joint_position_controller,對機械臂進行MotionPlanning。這裏我們列舉控制器的所有類型,可參考 https://github.com/ros-controls/ros_control/wiki/controller_interface

  • joint_state_controller
  • joint_effort_controller
  • joint_position_controller
  • joint_velocity_controller

在這裏插入圖片描述

第一步:爲link添加慣性參數和碰撞屬性

轉動慣量是大學物理的內容研究的是2D平面的,公式爲:J=mr2J=\sum mr^2M=JωM=J\omegaω\omega是角速度,而這裏的慣性參數是三維的,用一個慣性矩陣來描述。推了一下圓柱體的公式,同樣長方體和球體也可以使用三次積分的方式求出。
在這裏插入圖片描述

<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>

<link name="base_link">
	<visual>
		<origin xyz="0 0 0" rpy="0 0 0" />
		<geometry>
			<cylinder length="${base_length}" radius="{base_radius}" />
		</geometry>
		<material name="yellow" />
	</visual>
	<collison>
		<origin xyz="0 0 0" rpy="0 0 0" />
		<geometry>
			<cylinder length="${base_length}" radius="{base_radius}" />
		</geometry>
	</collision>
	<cylinder_inertial_matrix m="${base_mass}" r="${base_radius}" h="${base_length}" />
</link>

第二步:爲link添加gazebo標籤

Gazebo中需要添加色彩標籤。由於rviz和gazebo的色彩空間不同,描述形式不一樣,不能通用,如果不標記標籤,Gazebo內的模型都爲灰色。

<gazebo reference="base_link">
	<material>Gazebo/Blue</material>
</gazebo>
<gazebo reference="${prefix}_wheel_link">
	<material>Gazebo/Gray</material>
</gazebo>
<gazebo reference="base_footprint">
	<turnGravityoff>false</turnGravityoff>
</gazebo>

“base_footprint”是對底盤在地面的一個映射,這裏需要把這個影子的重力設置爲無。

<gazebo reference="${prefix}_caster_link">
	<material>Gazebo/Black</material>
</gazebo>

第三步:爲joint添加傳動裝置

爲每一個joint添加虛擬電機(傳動裝置)。這裏使用了硬件接口——速度控制接口hardware_interface/VelocityJointInterface。

<!-- Transmission is important to link the joints and the controller -->
<transmission name="${prefix}_wheel_joint_trans">
	<type>transmission_interface/SimpleTransmission</type>
	<joint name="${prefix}_name_wheel_joint">
		<hardwareInterface>hardware_interface/VelocityJointInterface</hardwareInterface>
	</joint>
	<actuator name="${prefix}_wheel_joint_motor">
		<hardwareInterface>hardware_interface/VelocityJointInterface</hardwareInterface>
		<mechanicalReduction>1</mechanicalReduction>
	</actuator>
</transmission>

第四步:添加gazebo控制器插件

  • <robotNamespace>:機器人的命名空間。
  • <leftJoint>和<rightJoint>:左右輪轉動的關節joint。
  • <whellSeparation>和<wheelDiameter>:機器人模型的相關尺寸,在計算差速參數時需要用到。
  • <commandTopic>:控制器訂閱的速度控制指令,生成全局命名時需要結合<robotNamespace>中設置的命名空間。
  • <odometryFrame>:里程計數據的參考座標系,ROS中一般命名爲odom。
<!-- controller -->
<gazebo>
	<!--控制器插件,差速控制libgazebo_ros_diff_drive.so-->
	<plugin name="differential_drive_controller"
			filename="libgazebo_ros_diff_drive.so">
		<rosDebugLevel>Debug</rosDebugLevel>
		<publishWheelTF>true</publishWheelTF>
		<!-- /代表無命名空間,屬於全局命名空間 -->
		<robotNamespace>/</robotNamespace>
		<publishTf>1</publishTf>
		<publishWheelJointState>true</publishWheelJointState>
		<alwaysOn>true</alwaysOn>
		<updateRate>100.0</updateRate>
		<legacyMode>true</legacyMode>
		<leftJoint>left_wheel_joint</leftJoint>
		<rightJoint>right_wheel_joint</rightJoint>
		<!--兩個輪子的輪距-->
		<wheelSeparation>${wheel_joint_y*2}</wheelSeparation>
		<wheelDiameter>${wheel_radius*2}</wheelDiameter>
		<broadcastTF>1</broadcastTF>
		<!--轉矩Torque-->
		<wheelTorque>30</wheelTorque>
		<wheelAcceleration>1.8</wheelAcceleration>
		<!--需要發佈的速度信息-->
		<commandTopic>cmd_vel</commandTopic>
		<!--使用里程計,計算仿真中機器人的位置-->
		<odometryFrame>odom</odometryFrame>
		<odometryTopic>odom</odometryTopic>
		<robotBaseFrame>base_footprint</robotBaseFrame>
	</plugin>
</gazebo>	

全向移動使用libgazebo_ros_planar_move這個插件,具體gazebo插件可以參考官方網站

第五步:編輯.launch文件

在gazebo中加載機器人模型需要首先將.xacro文件寫入.launch文件中用來執行。

示例:

<launch>
	
	<!--設置launch文件的參數-->
	<arg name="paused" default="false" />
	<arg name="use_sim_time" default="true" />
	<arg name="gui" default="true" />	
	<arg name="headless" default="false" />
	<arg name="debug" default="false" />

	<!-- 運行gazebo仿真環境 -->
	<include file="$(find gazebo_ros)/launch/empty_world.launch">
		<arg name="debug" value="$(arg debug)" />
		<arg name="paused" value="$(arg paused)" />
		<arg name="use_sim_time" value="$(arg use_sim_time)" />
		<arg name="gui" value="$(arg gui)" />	
		<arg name="headless" value="$(arg headless)" />
	</include>

	<!-- 加載機器人模型描述參數 -->
	<!-- xacro --inorder解釋器作用於.xacro文件 -->
	<param name="robot_description" command="$(find xacro)/xacro --inorder '$(find mobot_description)/urdf/mbot_gazebo.xacro'"/>
	
	<!-- 運行joint_state_publisher節點,發佈機器人的關節狀態 -->
	<node name="joint_state_publisher" pkg="joint_state_publisher" type="joint_state_publisher" />

	<!-- 運行robot_state_publisher節點,發佈tf -->
	<node name="robot_state_publisher" pkg="robot_state_publisher" type="robot_state_publisher" output="screen" >
		<param name="publish_frequency" type="double" value="50.0" />
	</node>

	<!-- 在gazebo中加載機器人模型 -->
	<!-- spawn_model功能包,-urdf說明模型由URDF格式構建,-model後跟機器人名,-param參數爲上面加載的模型描述參數 -->
	<node name="urdf_spawner" pkg="gazebo_ros" type="spawn_model" respawn="false" output="screen"
		  args="-urdf -model mrobot -param robot_description" />
	
</launch>

運行

$ roslaunch mbot_gazebo view_mbot_gazebo_empty_world.launch

在這裏插入圖片描述
終端輸入

$ rostopic list

會發現/cmd_vel,那麼就可以通過

$ rostopic pub /cmd_vel geometry_msgs/Twist "linear:       
  x: 0.6
  y: 0.0
  z: 0.0
angular:
  x: 0.0
  y: 0.0
  z: 0.5"

發佈運動指令了。

2. 創建物理仿真環境

第一種方法

https://bitbucket.org/osrf/gazebo_models/downloads/下載,提前放到模型~/.gazebo/models下,.gazebo文件夾一般是隱藏的,點擊ctrl+h可以顯示隱藏文件夾。下載的模型包解壓後需要把文件內的所有文件散開放,每個文件夾都是一個模型。

在Gazebo仿真環境,點擊左上角的Insert插入,就可以放置物理環境了,通過左上角的Save World As可以保存當前設置的仿真環境。

第二種方法:使用Building Editor

首先需要打開一個Gazebo World。可以使用

$ roslaunch mbot_gazebo view_mbot_gazebo_empty_world.launch

但是要事先把功能包放置到工作空間下。(功能包已經在全文開頭給出了github地址)

點擊File中的Building Editor在這裏插入圖片描述
通過左邊的牆體、窗戶、門,顏色,質感等,構建一個虛擬的物理環境。

下面我們運行一個gazebo提供的仿真環境,只不過也就是放了幾個東西而已。

$ roslaunch mbot_gazebo view_mbot_gazebo_play_ground.launch

然後運行一個gazebo提供的鍵盤控制小車移動的腳本。

$ roslaunch mbot_teleop mbot_teleop.launch

你會發現使用i前進(紅色X軸),使用,後退,使用j向左轉,l右轉,q加速等等,kinetic中左右是相反的,你會發現小車是具有慣性的,這就是慣性矩陣的作用,同樣小車碰撞物體時,可能推動物體前進(在物體質量較輕時),小車對物體有力的作用。

3. 傳感器仿真及應用

①攝像頭仿真

  • <sensor>標籤:描述仿真器
    • type:傳感器類型,camera
    • name:攝像頭命名,自由設置
  • <camera>標籤:描述攝像頭參數
    • 分辨率,編碼格式,圖像範圍,噪音參數等
  • <plugin>標籤:加載攝像頭仿真插件
    • 設置插件的命名空間、發佈圖像的話題、參考座標系等

示例:(示例中僅包含傳感器設置,並不包含其他模型構建,不是完整的)

<gazebo reference="${prefix}_link">
	<sensor type="camera" name="camera_node">
		<!--每秒30幀-->
		<update_rate>30.0</update_rate>
		<camera name="head">
			<horizontal_fov>1.3962634</horizontal_fov>
			<image>
				<!--分辨率-->
				<width>1280</width>
				<height>720</height>
				<format>R8G8B8</format>
			</image>
			<clip>
				<!--最近和最遠(m)-->
				<near>0.02</near>
				<far>300</far>
			</clip>
			<noise>
				<type>gaussian</type>
				<mean>0.0</mean>
				<stddev>0.007</stddev>
			</noise>
		</camera>
		<plugin name="gazebo_camera" filename="libgazebo_ros_camera.so">
			<alwaysOn>true</alwayOn>
			<updateRate>0.0</updateRate>
			<!--命名空間-->
			<cameraName>/camera</cameraName>
			<!--對外發布話題名爲image_raw-->
			<imageTopicName>image_raw</imageTopicName>
			<cameraInfoTopicName>camera_info</cmaeraInfoTopicName>
			<frameName>camera_link</framName>
			<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>

可以運行一下給出功能包裏的程序

$ roslaunch mbot_gazebo view_mbot_with_camera_gazebo.launch

隨後運行

$ rostopic list

可以看到多出很多由camera開頭的話題,我們用rqt_image_view顯示話題仿真的圖像

$ rqt_image_view

②RGB-D攝像頭仿真(Kinect)

Kinect通過紅外感知周圍物體的深度信息。

示例:(示例中僅包含傳感器設置,並不包含其他模型構建,不是完整的)

<gazebo reference="${prefix}_link">
	<sensor type="depth" name="${prefix}">
		<always_on>true</always_on>
		<update_rate>20.0</update_rate>
		<camera>
			<!--俯仰角-->
			<horizontal_fov>${60.0*M_PI/180.0}</horizontal_fov>
			<image>
				<width>640</width>
				<height>480</height>
				<format>R8G8B8</format>
			</image>
			<clip>
				<!--最近和最遠(m)-->
				<near>0.05</near>
				<far>8.0</far>
			</clip>
		</camera>
		<plugin name="kinect_${prefix}_controller" filename="libgazebo_ros_openni_kinect.so">
			<alwaysOn>true</alwayOn>
			<updateRate>10</updateRate>
			<!--命名空間-->
			<cameraName>${prefix}</cameraName>
			<!--創建rgb彩色圖像話題-->
			<imageTopicName>rgb/image_raw</imageTopicName>
			<!--創建深度圖像話題-->
			<depthImageTopicName>depth/image_raw</depthImageTopicName>
			<!--創建點雲圖像話題-->
			<pointCloudTopicName>depth/points</pointCloudTopicName>			
			<depthImageCameraInfoTopicName>depth/camera_info</depthImageCameraInfoTopicName>
			<cameraInfoTopicName>rgb/camera_info</cmaeraInfoTopicName>
			<frameName>${prefix}_frame_optical</framName>
			<baseline>0.1</beseline>
			<distortionk1>0.0</distortionk1>
			<distortionk2>0.0</distortionk2>
			<distortionk3>0.0</distortionk3>
			<distortiont1>0.0</distortiont1>
			<distortiont2>0.0</distortiont2>
			<pointCloudCutoff>0.4</pointCloudCutoff>
		</plugin>
	</sensor>
</gazebo>

同樣可以執行給出功能包中的.launch文件

$ roslaunch mbot_gazebo view_mbot_with_kinect_gazebo.launch

使用rostopic list輸出的話題中由kinect開頭的都是該仿真攝像頭提供的話題。我們可以通過rviz進行顯示。

$ rosrun rviz rviz

添加PointCloud2和Image選擇Kinect發佈的話題,就可以在rviz中顯示出來。
在這裏插入圖片描述

③激光雷達仿真

示例:這裏也只列出仿真傳感器的設置,其他設置大差不差。

這裏是一個二維的雷達,可以得到二維的深度信息。

<gazebo reference="${prefix}_link">
	<sensor type="ray" name="rplidar">
		<pose>0 0 0 0 0 0</pose>
		<visualize>false</visualize>
		<update_rate>5.5</update_rate>
		<ray>
			<scan>
				<horizontal>
					<!--每一幀360個點-->
					<samples>360</samples>
					<!--每一度一個點-->
					<resolution>1</resolution>
					<min_angle>-3</min_angle>
					<max_angle>3</max_angle>
				</horizontal>
			</scan>
			<range>
				<!--最近0.10m,最遠6.0m-->
				<min>0.10</min>
				<max>6.0</max>
				<resolution>0.01</resolution>
			</range>
			<noise>
				<type>gaussian</type>
				<mean>0.0</mean>
				<stddev>0.01</stddev>
			</noise>
		</ray>
		<plugin name="gazebo_rplidar" filename="libgazebo_ros_laser.so">
			<topicName>/scan</topicName>
			<!--雷達信息都在laser_link下做描述-->
			<frameName>laser_link</frameName>
		</plugin>
	</sensor>
</gazebo>

在這裏插入圖片描述
推薦閱讀

  1. ROS xacro使用方法
  2. rviz和gazebo的區別
  3. Gazebo仿真官方教程
  4. 古月居——ros_control
  5. 古月居——gazebo插件plugin
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章