0 写在前面

在本系列博客中将主要介绍:

  1. ROS中的控制器插件
  2. 让之前博客中设计的移动机器人跑起来
  3. 使用博客中设计的移动机器人对我们搭建的gazebo环境进行建图
  4. 在已知地图上使用AMCL实现机器人定位,为之后的自主导航做准备工作
  5. 在已知地图上使用move_base实现机器人自主导航
  6. 实现机器人自主建图

在本博客中将先介绍1-3

1 ROS中的控制器插件

为什么要用控制器插件

  • Reason 1(原理角度). 我们一定希望我们操纵的机构能够快速,稳定,无误差的到达我们期望的状态,以智能车为例,当给定智能车的期望角速度和线速度时,我们希望智能车能够又快又准确的到达我们的速度目标,这又强调我们需要给定真正驱动小车行走的机构(电机,舵机)合适的信号,如何根据输入量(期望的角速度和线速度)获得执行机构(电机,舵机)的控制量呢,这就需要控制器串联其中,值得一提的是,我们绝大多数使用PID控制器。

  • Reason 2(应用角度). ROS中的控制器插件不仅仅为了我们提供了单纯的控制器,在官方文档中有着这样的描述:
    为了成功发送速度指令给到机器人底盘,需要以下几个部分:

  1. 直接控制电机的基本速度控制器(the base velocity controller that directly commands the motors)
  2. 基本里程计控制器(the base odometry controller)
  3. 发送速度命令的高级程序(a higher-level program to send the velocity commands)

让我们来逐一分析这三个部分,对于第一个速度控制器可以由PID控制器充当,第三个高级程序可以由键盘控制指令,rostopic pub指令等等充当,那么第二个由谁提供呢?答案是简单的,我们的差速控制器插件集成了里程计控制器,因此为了成功让我们的小车动起来必须包含一个控制器插件。

两个差速控制器插件

在之前的系列中,大家可以看到的是,我们的小车拥有两个主动轮,两个万向从动轮,因此,我们的小车应当使用差速驱动。同时,心细的小伙伴可以发现在之前博客launch文件(URDF与Rviz集成)中,我们包含了arbotix节点,这正是我们介绍的第一种差速控制器;同样是这个博客中,在Xacro文件夹的move.xacro文件中,大家可以看到我们包含了differential_drive_controller插件,这也正是我们要介绍的第二种差速控制器

Rviz中的arbotix

它是在Rviz(机器人的感知世界)中进行使用,它跟Gazebo软件比较,它不能模拟物理现象和没有传感器反馈,对于本系列中的移动机器人,它使用的是差速控制模式,需要调整的参数包含在control.yaml中,有如下:

controllers: {
   # 单控制器设置
   base_controller: {
          #类型: 差速控制器
       type: diff_controller,
       #参考坐标
       base_frame_id: base_footprint, 
       #两个轮子之间的间距
       base_width: 0.2,
       #控制频率
       ticks_meter: 2000, 
       #PID控制参数,使机器人车轮快速达到预期速度
       Kp: 12, 
       Kd: 12, 
       Ki: 0, 
       Ko: 50, 
       #加速限制
       accel_limit: 1.0 
    }
}
  • 通过查看yaml文件,我们可以从另外的一个角度验证设计base_footprint的重要性,而在本机器人中base_width的取值则是base_link(长方体)的长(0.2)
  • 笔者的博客中并没有只针对arbotix控制器的实验,感兴趣的伙伴可以查看:
    https://blog.csdn.net/qq_43481884/article/details/105433575
  • 总的来说,arbotix订阅/cmd_vel,并会做出相应操作,驱动小车做运动,再根据 Tf 变换,将小车的运动姿态,实时在 rviz 中显示出来,其中值得一提的是,提供TF变换的功能主要由robot_state_publisher功能包提供,这里也简单的对robot_state_publisher的应用加以介绍一下:

    1. 在前面关于机器人建模的系列博客中,其实我们主要对两个系统进行了仿真,一是机器人感知世界Rviz,二是机器人仿真物理世界Gazebo,细心的伙伴在看我们编写的launch文件时不难发现,跟gazebo有关的launch文件并不需要包含此功能包,然而包含Rviz的launch的文件则必须提供此功能包,这就是我们要介绍的第一个功能——提供机器人系统的各坐标系间状态(TF树),用于Rviz显示
    2. 在之后将要介绍关于机器人的一些上层应用层(AMCL,move_base,slam等)博客中,这些功能包的实现都要求获取传感器间坐标系关系,关节间坐标系关系,进而得以实现数据的坐标间转换等功能,robot_state_publish就负责提供机器人系统的各坐标系关系(TF树),作为这些应用的其中一个输入
    3. joint_state_publisher经常与robot_state_publisher一同出现,他的作用和与robot_state_publisher的关系大家可以查看这篇博客
  • 更详细的内容可以查看官方文档:http://wiki.ros.org/arbotix

gazebo中的differential_drive_controller

gazebo本身就提供差动控制器插件,控制器作用同arbotix一样,订阅/cmd_vel,驱动小车做运动,区别在于在设计gazebo中差速控制器插件之前,需要实现指定哪些关节是驱动关节,以及是什么类型的驱动器,具体详情可以看下面提供的文档:

  1. 官方文档写的已经相当详尽,就不再造轮子了,官方的文档地址:
    http://gazebosim.org/tutorials?tut=ros_gzplugins#PlanarMovePlugin
    值得一提的是,此官方文档详细介绍了之前博客所说的各个传感器插件,感兴趣的伙伴可以再看一看
  2. 或者大家可以看这篇博客写的也很是详细

2 让机器人动起来

当搭载运动控制器之后,我们向/cmd_vel话题发布速度信息即可实现机器人电机驱动,即机器人移动,这里我们可以使用ROS提供的键盘控制节点
rosrun teleop_twist_keyboard teleop_twist_keyboard.py
同样的,我们也可以使用rostopic指令向/cmd_vel发送速度信息致使机器人移动
rostopic pub -r 10 /cmd_vel geometry_msgs/Twist '{linear: {x: 0.2, y: 0, z: 0}, angular: {x: 0, y: 0, z: 0.5}}'

3 建图(SLAM)

我们在此过程中选择的是官方提供的gmapping节点,同时微调一些参数。

  • 第一步需要做的是使用在博客中xacro与gazebo与rviz的集成roslaunch指令
    roslaunch hw_car_gazebo.launch所在绝对路径
  • 第二步启动gmapping节点,这里采用我们自己编写的launch文件,取名叫slam_my.launch文件位置不重要,有如下:

    <launch>
    <param name="use_sim_time" value="true"/>
      <!--gmapping包-->
      <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_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 my_nav_sum)/rviz/gmapping.rviz"/>
      -->
    </launch>
    

    指令为:
    roslaunch slam_my.launch所在绝对路径

  • 第三步启动键盘控制节点指令:
    rosrun teleop_twist_keyboard teleop_twist_keyboard.py
  • 第四步当建图完成后,调用map_save.launch:
    roslaunch map_save.launch所在绝对路径
    文件位置不重要,有如下:
    <launch>
      <arg name="filename" value="$(find myNav)/map/map03" />
      <node name="map_save" pkg="map_server" type="map_saver" args="-f $(arg filename)" />
    </launch>
    
    其中filename参数对应建好地图的存放位置,地图读取需要输入此参数
  • 可以利用指令rosrun rqt_gragh rqt_gragh观察slam过程节点状态
    slam节点
    最后生成的地图如下:
    slam建成地图

    4 写在最后

    SLAM的全称是simultaneous localization and mapping,即同步定位与建图,可见其本身就带有定位里程计的功能,而在下一篇博客中,我将首先单独介绍AMCL这一定位节点,接着使用move_base节点订阅AMCL的tf消息进行机器人自主导航,最后将amcl、move_base节点和本博客介绍的gmapping节点进行串联,实现机器人的自主环境建图