学习项目链接:https://so.csdn.net/so/search?q=%E4%BB%BF%E7%9C%9F%E6%9C%BA%E5%99%A8%E4%BA%BA&t=blog&u=Netceor&urw=
1.数值变量声明
<xacro:property name="PI" value="3.141" />
2.颜色设置
<material name="black">
<color rgba="0.0 0.0 0.0 1.0" />
</material>
3.宏(自定义语法,我理解为构造函数)
<xacro:macro name="getSum" params="num1 num2">
<result value="${num1 + num2}" />
</xacro:macro>
4.Link(用来描述刚体几何物理的,描述碰撞体积)
Visual是我们看到的,Collision是无形的、用于检测碰撞的
<link name="my_link">
<inertial> (惯性,暂时不知道怎么操作)
<origin xyz="0 0 0.5" rpy="0 0 0"/>
<mass value="1"/>
<inertia ixx="100" ixy="0" ixz="0" iyy="100" iyz="0" izz="100" />
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" /> (质心,这里理解为六轴,xyz方向平移,以及围绕坐标轴旋转用弧度制表示)
<geometry>
<box size="1 1 1" /> (刚体,长宽高)
</geometry>
<material name="Cyan"> (颜色)
<color rgba="0 1.0 1.0 1.0"/>
</material>
</visual>
<collision> (用于检测碰撞体积的)
<origin xyz="0 0 0" rpy="0 0 0"/>
<geometry>
<cylinder radius="1" length="0.5"/>(圆柱,底面半径、高)
</geometry>
</collision>
</link>
5.Joint(用于描述两个模块之间的连接装置)这个装置是无形的
<joint name="my_joint" type="floating"> (这里声明名字和连接关节类型,具体参数选择看官网)
<origin xyz="0 0 1" rpy="0 0 3.1416"/>
<parent link="link1"/>
<child link="link2"/>
<calibration rising="0.0"/>
<dynamics damping="0.0" friction="0.0"/>
<limit effort="30" velocity="1.0" lower="-2.2" upper="0.7" />
<safety_controller k_velocity="10" k_position="15" soft_lower_limit="-2.0" soft_upper_limit="0.5" />
</joint>
6.Gazebo中使用传感器(如摄像头,激光雷达,深度相机)
一、先设置刚体link(与rviz有三不同)
F:必须设置碰撞<collision>
S:使用inertial惯性标签(不同的刚体又不同的计算公式)
T:重新设置在Gazebo显示的颜色<gazebo reference="base_link">
二、将gazebo的传感器配置到刚体Link上
<gazebo reference="laser(刚体名字)">
个人理解:rviz和gazebo设置的刚体只是模型,真正赋予模型传感器属性需要经过第二步。因此可以把所有模型理解成积木,把<gazebo reference="laser(刚体名字)">理解成用真正的传感器替代积木。
7.设置关节移动(轮子前进)
设置轮子前进
一、添加轮子刚体,并且设置关节连接类型为continuous
<joint name="${name}_wheel2base_link" type="continuous">
<parent link="base_link" />
<child link="${name}_wheel" />
<origin xyz="0 ${flag * base_link_radius} ${-(earth_space + base_link_length / 2 - wheel_radius) }" />
<axis xyz="0 1 0" />
</joint>
二、设置传动实现(连接控制器与关节) 待完善
一定程度上与关节的选取有关,有固定的选取方案
<xacro:macro name="joint_trans" params="joint_name">
<!-- Transmission is important to link the joints and the controller -->
<transmission name="${joint_name}_trans">
<type>transmission_interface/SimpleTransmission</type>
<joint name="${joint_name}">
<hardwareInterface>hardware_interface/VelocityJointInterface</hardwareInterface>
</joint>
<actuator name="${joint_name}_motor">
<hardwareInterface>hardware_interface/VelocityJointInterface</hardwareInterface>
<mechanicalReduction>1</mechanicalReduction>
</actuator>
</transmission>
</xacro:macro>
<!-- 每一个驱动轮都需要配置传动装置 -->
<xacro:joint_trans joint_name="left_wheel2base_link" />
<xacro:joint_trans joint_name="right_wheel2base_link" />
三、设置gazebo控制器
这里是二轮差分
<gazebo>
<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_wheel2base_link</leftJoint> <!-- 左轮 -->
<rightJoint>right_wheel2base_link</rightJoint> <!-- 右轮 -->
<wheelSeparation>${base_link_radius * 2}</wheelSeparation> <!-- 车轮间距 -->
<wheelDiameter>${wheel_radius * 2}</wheelDiameter> <!-- 车轮直径 -->
<broadcastTF>1</broadcastTF>
<wheelTorque>30</wheelTorque>
<wheelAcceleration>1.8</wheelAcceleration>
<commandTopic>cmd_vel</commandTopic> <!-- 订阅的运动控制话题的名称 -->
<odometryFrame>odom</odometryFrame>
<odometryTopic>odom</odometryTopic> <!-- 发布的里程计话题,是本控制器发布的,供别的使用提取数据 -->
<robotBaseFrame>base_footprint</robotBaseFrame> <!-- 根坐标系 -->
</plugin>
</gazebo>
下面是四轮差分
参考链接:(1条消息) (三)ROS中控制机器人运动的实现(在gazebo中显示)_火星机器人life的博客-CSDN博客_控制机器人在gazebo中运动
<gazebo>
<plugin name="skid_steer_drive_controller" filename="libgazebo_ros_skid_steer_drive.so">
<updateRate>50.0</updateRate>
<robotNamespace></robotNamespace>
<leftFrontJoint>base_to_left_front_wheel</leftFrontJoint>
<leftRearJoint>base_to_left_back_wheel</leftRearJoint>
<rightFrontJoint>base_to_right_front_wheel</rightFrontJoint>
<rightRearJoint>base_to_right_back_wheel</rightRearJoint>
<wheelSeparation>4</wheelSeparation>
<wheelDiameter>0.1</wheelDiameter>
<commandTopic>cmd_vel</commandTopic>
<odometryTopic>odom</odometryTopic>
<robotBaseFrame>base_footprint</robotBaseFrame>
<odometryFrame>odom</odometryFrame>
<torque>1</torque>
<topicName>cmd_vel</topicName>
<broadcastTF>1</broadcastTF>
</plugin>
</gazebo>
8.Gazebo发布的传感器话题
<gazebo reference="support">
<sensor type="depth" name="camera">
<!-- 这里type:depth是表示深度相继。name :这里可以随意取名,到现在还不知道有使用它的地方-->
<always_on>true</always_on>
<update_rate>20.0</update_rate>
<camera>
<horizontal_fov>${60.0*PI/180.0}</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>camera</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>kinect link名称</frameName> -->
<!-- 在插件中为kinect设置坐标系,用support_depth替代support -->
<frameName>support_depth</frameName>
<!--这里用support_depth而不是用刚体名字support是因为好像要进行发布新的坐标系,具体还不知道什么意思 -->
<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>
他这里会把点云数据发布到/camera/depth/points里面,如下图
9.建图
一、启动gazeobo加载创建模型
<launch>
<!-- 将 Urdf 文件的内容加载到参数服务器 -->
<param name="robot_description" command="$(find xacro)/xacro $(find urdf_gazebo)/urdf/t7_12346.xacro" />
<!-- 启动 gazebo -->
<!-- 加载仿真环境 -->
<!-- <include file="$(find gazebo_ros)/launch/empty_world.launch" /> -->
<include file="$(find gazebo_ros)/launch/empty_world.launch">
<arg name="world_name" value="$(find urdf_gazebo)/worlds/box_house.world"></arg>
</include>
<!-- 在 gazebo 中显示机器人模型 -->
<node pkg="gazebo_ros" type="spawn_model" name="model" args="-urdf -model mycar -param robot_description" />
</launch>
二、Launch文件里添加gmapping
<launch>
<!-- 设置为true表示当前环境是仿真环境 -->
<param name="use_sim_time" value="true" />
<!-- gamping 节点 -->
<node pkg="gmapping" type="slam_gmapping" name="slam_gmapping" output="screen">
<!-- 设置雷达话题 -->
<remap from="scan" to="scan" />
<!-- 关键参数:坐标系 -->
<param name="base_frame" value="base_footprint" /> <!--底盘坐标系-->
<param name="odom_frame" value="odom" /> <!--里程计坐标系-->
<param name="map_frame" value="map" /> <!--地图坐标系-->
<!--这上面的坐标系是gmapping建图需要的,坐标系的意
思是分别以不同的刚体初始值为坐标原点看待世界-->
<param name="map_update_interval" value="5.0" />
<param name="maxUrange" value="16.0" />
<param name="sigma" value="0.05" />
<param name="kernelSize" value="1" />
<param name="lstep" value="0.05" />
<param name="astep" value="0.05" />
<param name="iterations" value="5" />
<param name="lsigma" value="0.075" />
<param name="ogain" value="3.0" />
<param name="lskip" value="0" />
<param name="srr" value="0.1" />
<param name="srt" value="0.2" />
<param name="str" value="0.1" />
<param name="stt" value="0.2" />
<param name="linearUpdate" value="1.0" />
<param name="angularUpdate" value="0.5" />
<param name="temporalUpdate" value="3.0" />
<param name="resampleThreshold" value="0.5" />
<param name="particles" value="30" />
<param name="xmin" value="-50.0" />
<param name="ymin" value="-50.0" />
<param name="xmax" value="50.0" />
<param name="ymax" value="50.0" />
<param name="delta" value="0.05" />
<param name="llsamplerange" value="0.01" />
<param name="llsamplestep" value="0.01" />
<param name="lasamplerange" value="0.005" />
<param name="lasamplestep" value="0.005" />
</node>
<node pkg="joint_state_publisher" name="joint_state_publisher" type="joint_state_publisher" />
<node pkg="robot_state_publisher" name="robot_state_publisher" type="robot_state_publisher" />
<!-- <node pkg="rviz" type="rviz" name="rviz" /> -->
<!-- 使用之前保存过的 rviz 配置-->
<node pkg="rviz" type="rviz" name="rviz" args="-d $(find urdf_gazebo)/config/t7_car.rviz" />
</launch>
三、保存yaml地图信息
gmapping会向话题“map”发送地图类型数据(MapGenerator)
而map_saver则会订阅map_sub_ = n.subscribe("map", 1, &MapGenerator::mapCallback, this);然后写成文件pgm和yaml。
<launch>
<arg name="filename" value="$(find urdf_gazebo)/map/nav" />
<node name="map_save" pkg="map_server" type="map_saver" args="-f $(arg filename)" />
<launch>
四、加载yaml地图信息
使用map_server由于其源码受限的缘故,只提供创建一个话题“map”,因此如果不修改包的代码无法达到多个地图同时发布的效果。
10.launch的node语法
<arg name="map" default="nav.yaml" />
<node name="map_server" pkg="map_server" type="map_server" args="$(find urdf_gazebo)/map/$(arg map)"/ >
这句话可以理解为: rosrun 包map_server 可执行文件map_server 后缀参数map:=路径/nav.yaml
或者可以写成:
<node name="map_server" pkg="map_server" type="map_server" >
<param name="map" value="nav.yaml">
</node>
11.acml定位
一、gazebo模型加载
二、地图发布
三、然后写acml的节点代码(设置三个坐标系)
<launch>
<node pkg="amcl" type="amcl" name="amcl" output="screen">
<!-- Publish scans from best pose at a max of 10 Hz -->
<param name="odom_model_type" value="diff" /> <!-- 里程计模式为差分 -->
<param name="odom_alpha5" value="0.1" />
<param name="transform_tolerance" value="0.2" />
<param name="gui_publish_rate" value="10.0" />
<param name="laser_max_beams" value="30" />
<param name="min_particles" value="500" />
<param name="max_particles" value="5000" />
<param name="kld_err" value="0.05" />
<param name="kld_z" value="0.99" />
<param name="odom_alpha1" value="0.2" />
<param name="odom_alpha2" value="0.2" />
<!-- translation std dev, m -->
<param name="odom_alpha3" value="0.8" />
<param name="odom_alpha4" value="0.2" />
<param name="laser_z_hit" value="0.5" />
<param name="laser_z_short" value="0.05" />
<param name="laser_z_max" value="0.05" />
<param name="laser_z_rand" value="0.5" />
<param name="laser_sigma_hit" value="0.2" />
<param name="laser_lambda_short" value="0.1" />
<param name="laser_lambda_short" value="0.1" />
<param name="laser_model_type" value="likelihood_field" />
<!-- <param name="laser_model_type" value="beam"/> -->
<param name="laser_likelihood_max_dist" value="2.0" />
<param name="update_min_d" value="0.2" />
<param name="update_min_a" value="0.5" />
<param name="odom_frame_id" value="odom" /> <!-- 里程计odom坐标系 -->
<param name="base_frame_id" value="base_footprint" /> <!-- 添加机器人基坐标系 -->
<param name="global_frame_id" value="map" /> <!-- 添加地图map坐标系 -->
<param name="resample_interval" value="1" />
<param name="transform_tolerance" value="0.1" />
<param name="recovery_alpha_slow" value="0.0" />
<param name="recovery_alpha_fast" value="0.0" />
</node>
</launch>
其他都是一些参数,暂时不知道怎么使用,保持默认值;重要的就是那三个坐标系。
acml节点自动发布话题particlecloud预测机器人位置
12.导航(导航用的是move_base包)
一、启动gazebo仿真
二、acml启动(其实第一第二步就是重复上面11)
三、最后加入move_base
move_base是一个路径规划包,但是是面向很多类型机器人的包,因此应用在特殊的个体上应该要向包说明清楚本地机器人的参数。
参数解读如下:
move_base配置参数解析_酸梅果茶的博客-CSDN博客_movebase参数配置
【移动机器人技术】move_base参数配置方法及含义_A MAN NAMED MAGIC的博客-CSDN博客_private_nh.param
一般会用到下面这些文件
<launch>
<node pkg="move_base" type="move_base" respawn="false" name="move_base" output="screen" clear_params="true">
<rosparam file="$(find urdf_gazebo)/param/costmap_common_params.yaml" command="load" ns="global_costmap" />
<rosparam file="$(find urdf_gazebo)/param/costmap_common_params.yaml" command="load" ns="local_costmap" />
<rosparam file="$(find urdf_gazebo)/param/local_costmap_params.yaml" command="load" />
<rosparam file="$(find urdf_gazebo)/param/global_costmap_params.yaml" command="load" />
<rosparam file="$(find urdf_gazebo)/param/base_local_planner_params.yaml" command="load" />
</node>
</launch>
四、moeve_base其他
- SLAM构建的地图是静态地图,而导航过程中,障碍物信息是可变的,可能障碍物被移走了,也可能添加了新的障碍物,导航中需要时时的获取障碍物信息;
- 在靠近障碍物边缘时,虽然此处是空闲区域,但是机器人在进入该区域后可能由于其他一些因素,比如:惯性、或者不规则形体的机器人转弯时可能会与障碍物产生碰撞,安全起见,最好在地图的障碍物边缘设置警戒区,尽量禁止机器人进入...
- 静态地图无法直接应用于导航,其基础之上需要添加一些辅助信息的地图,比如时时获取的障碍物数据,基于静态地图添加的膨胀区等数据。
- 代价地图有两张:global_costmap(全局代价地图) 和 local_costmap(本地代价地图),前者用于全局路径规划,后者用于本地路径规划。奥号
总结理解:
1.静态地图就是slam构建出来的图,然后经过膨胀暂时获得代价地图(全局),膨胀的意义在于防止碰撞,这个地图用于全局路径规划
2.代价地图(全局)会根据新扫描的障碍物更新地图
3.另外一个代价地图(本地)则是用于一个小区域内的路径规划
4.最终呈现的地图其实是由多个地图层级叠加而来的,可以理解为:最终表现=静态地图+膨胀+新扫描到的障碍物+新设置的地形
move_base自己会发布两个代价地图,因此可以在rviz上查看。
由于分为全局路径规划的局部路径规划,因此path有两条线表示路径,一条全局线,一条局部线。
问题:既然代价地图可以实时更新,那么直接用全局代价地图建图+导航不久可以了吗,为什么还要加上静态地图?
答:1.最初全局代价地图需要静态地图的帮助构建,这样才能实现简单的全局路径规划。
2.代价地图的意义在于弥补静态地图的实时性,他针对的工作在于更新加入地形的障碍物。那么随着时间的移动,障碍物也将会更新,如果用这种方法来建图+导航,那很有可能建成的地图有部分会随着激光雷达捕捉不到而被算法当作消失的障碍物丢弃,无法达到保存墙这类边界的目的。