O(∩_∩)O哈哈哈~ 特想放张伟!我是什么颜色???
文章目录
参考功能包链接
https://github.com/XFFer/ROS_Resource
1. 优化物理仿真模型
使用xacro文件优化URDF模型
连载(四) 中我们使用URDF建模遇到了很多问题,比如多个轮子需要重复书写代码;不能进行参数计算,各个link的位置需要逐个输入参数。
URDF模型的进化版本——xacro模型文件
- 精简模型代码
- 创建宏定义
- 文件包含
- 提供可编程接口
- 常量
- 变量
- 数学计算
- 条件语句
- …
① 常量的定义与使用
常量定义
<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平面的,公式为:
,
,
是角速度,而这里的惯性参数是三维的,用一个惯性矩阵来描述。推了一下圆柱体的公式,同样长方体和球体也可以使用三次积分的方式求出。
<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>
推荐阅读