ROS 教程之 navigation :在 catkin 环境下创建costmap layer plugin

本文详细介绍如何在ROS环境中创建并使用自定义的Costmap Layer,包括在catkin工作空间中构建新Layer插件的步骤及如何将其整合到全局路径规划中。
在做机器人导航的时候,肯定见到过global_costmap和local_costmap。global_costmap是为了全局路径规划服务的,如从这个房间到那个房间该怎么走。local_costmap是为局部路径规划服务的,如机器人局部有没有遇到行人之类的障碍。costmap就是栅格图,每个栅格里存放着障碍物信息。但是hydro里的costmap不是单独一个栅格图,而是将一个栅格图分成了很多层,如最底下是静态地图,即墙壁,桌子等建地图时已经存在的障碍物;第二层是障碍物层;第三层是膨胀层,可以理解为把障碍物膨胀了一圈,最上面就是把前面各层的cost叠加起来形成最后的栅格图mast_grid,关于这种costmap的 简介见wiki介绍。这些分层的cost layer 是怎么叠加起来,他们是如何工作的?有何优点?可以参见costmap layer 的 发明人David V.Lu的IROS会议论文,关于costmap_2d package的细节主要来自 这个ros answer

       那么我们自己能用costmap layer 来干什么呢?首先来看一个问题,假设你想让你的机器人不能去厨房或者其他一些地方,可能你会想在全局地图中的这些地方人为的放置一些假障碍,那机器人就去不了了。可假障碍如何放到机器人已经生成的costmap里?有人用假激光或者假的点云数据来制造假障碍,但是,很麻烦。如果你能创建一个专门存放这些假障碍的costmap ,然后把这个costmap作为插件(plugin)放到ROS自带的地图里去,这个问题就解决了。

      ROS的官网wiki里有教你怎么新建costmap layer以及怎么插入到global_costmap 或者local_costmap里去,官方的tutorials请点击这里,教程里的例子是在你机器人前方1m处防止一个障碍点。可惜教程是建立在rosbulid编译环境下的,对于一路都是使用catkin的新手来说,怎么使得rosbuild 和catkin两个work space合起来工作,都成问题。所以,这里就教你如何在catkin环境下创建layer。

    本文分为两部分:

   1、如何在catkin 工作空间中创建New layer 插件

    2、如何在程序中调用新创建的New layer



一、创建 layer 插件

      在这部分,将创建一个simpler layer,这个layer的作用是在机器人前方1m处放置一个障碍物。

    1. 首先要保证安装了costmap-2d这个包

即使当初安装ros的时候采用-full版本安装,也是没有costmap_2d这个包的,使用命令:

~$ sudo apt-get install ros-kinetic-costmap-2d ,(不是"_2d")

安装成功后,会在/opt/ros/kenetic/share/目录下看到costmap_2d这个包

      2.首先像创建beginner_tutorials package一样,在catkin工作空间下创建simple_layers package。在终端中输入:

[python] view plain copy
  1. cd ~/catkin_ws/src  
  2. catkin_create_pkg simple_layers roscpp costmap_2d dynamic_reconfigure std_msgs rospy  
其中:catkin_ws/src 对应你自己的catkin工作空间。

     2.创建layer 所需的头文件。

     在创建好的simple_layers/include/simple_layers/ 文件夹下创建空白文档,命名为simple_layer.h,将下列程序复制进去并保存。

  1. #ifndef SIMPLE_LAYER_H_  
  2. #define SIMPLE_LAYER_H_  
  3. #include <ros/ros.h>  
  4. #include <costmap_2d/layer.h>  
  5. #include <costmap_2d/layered_costmap.h>  
  6. #include <costmap_2d/GenericPluginConfig.h>  
  7. #include <dynamic_reconfigure/server.h>  
  8.   
  9. namespace simple_layer_namespace  
  10. {  
  11.   
  12. class SimpleLayer : public costmap_2d::Layer  
  13. {  
  14. public:  
  15.   SimpleLayer();  
  16.   
  17.   virtual void onInitialize();  
  18.   virtual void updateBounds(double origin_x, double origin_y, double origin_yaw, double* min_x, double* min_y, double* max_x,  
  19.                              double* max_y);  
  20.   virtual void updateCosts(costmap_2d::Costmap2D& master_grid, int min_i, int min_j, int max_i, int max_j);
  21.  
  22. private:  
  23.   void reconfigureCB(costmap_2d::GenericPluginConfig &config, uint32_t level);  
  24.   
  25.   double mark_x_, mark_y_;  
  26.   dynamic_reconfigure::Server<costmap_2d::GenericPluginConfig> *dsrv_;  
  27. };  
  28. }  
  29. #endif 
 新建了一个SimpleLayer类,基类是costmap_2d::Layer, 可以点击这里查看costmap_2d类。 上面头文件里有两个主要的函数updateBounds 和 updateCosts。这个函数对应前面提到的论文中的更新costmap区域和更新costmap的值。updateBounds的参数origin_x,origin_y,origin_yaw是机器人的当前位姿,不需要我们人为的去设定,ROS会自动传递这几个参数。
    3.创建c++文件,在simpler_layers/src文件夹下创建空白文档,命名为simple_layer.cpp,输入下面程序,并保存。
  1. #include<simple_layers/simple_layer.h>  
  2. #include <pluginlib/class_list_macros.h>  
  3.   
  4. PLUGINLIB_EXPORT_CLASS(simple_layer_namespace::SimpleLayer, costmap_2d::Layer)  //注册插件
  5.   
  6. using costmap_2d::LETHAL_OBSTACLE;  
  7.   
  8. namespace simple_layer_namespace  
  9. {  
  10.   
  11. SimpleLayer::SimpleLayer() {}  
  12.   
  13. void SimpleLayer::onInitialize()  
  14. {  
  15.   ros::NodeHandle nh("~/" + name_);  
  16.   current_ = true;  
  17.   
  18.   dsrv_ = new dynamic_reconfigure::Server<costmap_2d::GenericPluginConfig>(nh);  
  19.   dynamic_reconfigure::Server<costmap_2d::GenericPluginConfig>::CallbackType cb = boost::bind(  
  20.       &SimpleLayer::reconfigureCB, this, _1, _2);  
  21.   dsrv_->setCallback(cb);  
  22. }  
  23.   
  24.   
  25. void SimpleLayer::reconfigureCB(costmap_2d::GenericPluginConfig &config, uint32_t level)  
  26. {  
  27.   enabled_ = config.enabled;  
  28. }  
  29.   
  30. void SimpleLayer::updateBounds(double origin_x, double origin_y, double origin_yaw, double* min_x,  
  31.                                            double* min_y, double* max_x, double* max_y)  
  32. {  
  33.   if (!enabled_)  
  34.     return;  
  35.   
  36.   mark_x_ = origin_x + cos(origin_yaw);  
  37.   mark_y_ = origin_y + sin(origin_yaw);  
  38.   
  39.   *min_x = std::min(*min_x, mark_x_);  
  40.   *min_y = std::min(*min_y, mark_y_);  
  41.   *max_x = std::max(*max_x, mark_x_);  
  42.   *max_y = std::max(*max_y, mark_y_);  
  43. }  
  44.   
  45. void SimpleLayer::updateCosts(costmap_2d::Costmap2D& master_grid, int min_i, int min_j, int max_i,  
  46.                                           int max_j)  
  47. {  
  48.   if (!enabled_)  
  49.     return;  
  50.   unsigned int mx;  
  51.   unsigned int my;  
  52.   if(master_grid.worldToMap(mark_x_, mark_y_, mx, my)){  
  53.     master_grid.setCost(mx, my, LETHAL_OBSTACLE);  
  54.   }  
  55. }  
  56.   
  57. // end namespace  
    这个程序的主要思路是,更新updateBounds中的mark_x_和mark_y_,这两个变量是用来存放障碍点位置的,他们是在世界坐标系定义的变量。然后在updateCosts里将这两个变量从世界坐标转换到地图坐标系,并在地图坐标系中设定障碍点,即在栅格地图master_grid设定这个点的cost。
    以上部分都和官方教程中没什么差别,下面开始是如何在ros中注册这个插件的关键步奏了,这部分可以参看 wiki pluginlib的说明 ,也可以看看 navigation里如何写自己的全局路径规划插件教程。如果,没有过写插件的基础,先照着Lz下面的教程执行一遍,再回过头去看上面的两个教程。

      4.修改simpler_layers package下的Cmakerlists.txt文件。

添加:

[python] view plain copy
  1. add_library(simple_layer src/simple_layer.cpp)  

然后修改在include_directories(...)中添加include,修改后如下: 

[python] view plain copy
  1. include_directories(include ${catkin_INCLUDE_DIRS})  
添加include的目的是,为了在编译这个package的时候,能够找到之前你存放在simple_layers/include/simple_layers这个文件下的头文件,没有这个步奏,编译将出错。

      5.创建一个空白的costmap_plugins.xml文件,输入一下内容,将其存放在/catkin_ws/src/simpler_layers 文件夹下,也就是和Cmakelists.txt以及package.xml存放在同一路径下:

[python] view plain copy
  1. <library path="lib/libsimple_layer">  
  2.   <class type="simple_layer_namespace::SimpleLayer" base_class_type="costmap_2d::Layer">  
  3.     <description>Demo Layer that adds a point 1 meter in front of the robot</description>  
  4.   </class>  
  5. </library>  
 这个文件是插件的描述文件,它的目的是让ROS系统发现这个插件和load这个插件到系统里去。    

       6.修改package.xml,将下面语句

[python] view plain copy
  1. <costmap_2d plugin="${prefix}/costmap_plugins.xml" />  
放入package.xml中两个<export>之间。效果如图:
[python] view plain copy
  1. <export>  
  2.     <costmap_2d plugin="${prefix}/costmap_plugins.xml" />  
  3.   </export>  
这一步的目的是注册这个插件,说白了就是告诉pluginlib,这个插件可用。

       上面步奏完成以后,就是编译了。
[python] view plain copy
  1. cd ~/catkin_ws  
  2. catkin_make  
编译完成以后,可以查看ROS系统里是否已经有了这个layer 插件。在终端中输入:

rospack plugins --attrib=plugin costmap_2d

应该会得到LZ这样的结果:


说明simple_layer 已经是一个可供ROS用的插件了。

二、如何在move_base中使用这个costmap layer插件

      上面已经创建好了layer插件,并不意味着ROS就会使用它,我们得显式的global_costmap 或者 local_costmap中声明它。 当然首先可以看看wiki 配置layer costmap的教程。看不懂没关系,至少心里先有个大概印象。

      在调用自己写的这个layer之前,先看看系统默认的global_costmap 和local_costmap使用了哪些layer。假设你已经安装了《ROS by example 1》中的rbx1 package。如果没有安装,可以按照下面的过程进行安装。

-------------------------------------------------------------------------------------------------------------------------------

安装rbx1包,参考https://www.cnblogs.com/TooyLee/p/7736732.html

1.首先安装一些依赖包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
sudo apt- get install ros-kinetic-turtlebot-bringup \
ros-kinetic-turtlebot-create ros-kinetic-openni-* \
ros-kinetic-openni2-* ros-kinetic-freenect-* ros-kinetic-usb-cam \
ros-kinetic-laser-* ros-kinetic-hokuyo-node \
ros-kinetic-audio-common gstreamer0.10-pocketsphinx \
ros-kinetic-pocketsphinx ros-kinetic-slam-gmapping \
ros-kinetic-joystick-drivers python-rosinstall \
ros-kinetic-orocos-kdl ros-kinetic-python-orocos-kdl \
python-setuptools ros-kinetic-dynamixel-motor \
libopencv-dev python-opencv ros-kinetic-vision-opencv \
ros-kinetic-depthimage-to-laserscan ros-kinetic-arbotix-* \
ros-kinetic-turtlebot-teleop ros-kinetic-move- base \
ros-kinetic-map-server ros-kinetic-fake-localization \
ros-kinetic-amcl git subversion mercurial

其中,ros-kinetic-hokuyo-node(北阳激光雷达)、gstreamer0.10-pocketsphinx和ros-kinetic-pocketsphinx(语音识别库)和ros-kinetic-arbotix-*(模拟器)在ros kinetic版本中没有,如果用到需要源码编译 

2.下载rbx1和arbotix源码并编译

1
2
3
4
5
mkdir -p ~/dev/catkin_ws/src
cd ~/dev/catkin_ws/src
catkin_init_workspace
git clone https: //github.com/pirobot/rbx1.git
git clone  https: //github.com/vanadiumlabs/arbotix_ros.git
cd ..
catkin_make
source devel/setup.bash

其他包没用到就没有用到就没有编译

-------------------------------------------------------------------------------------------------------------------------------------------

[python] view plain copy
  1. roslaunch rbx1_bringup fake_turtlebot.launch  
在新的终端中输入:
[python] view plain copy
  1. roslaunch rbx1_nav fake_move_base_blank_map.launch  
你将看到如下图所示的一些信息:


你会看到pre-hydro 字样。说明当前的costmap是默认的配置,也就是static_layer,obstacle_layer,inflation_layer这三层。

下面我们来把创建的simple_layer,放入全局global_costmap中。要想使得ROS接受你的插件,要在参数中用plugins指明,也就是主要修改move_base中涉及到costmap的yaml文件,下面给出我的修改:

1.costmap_common_params.yaml

[python] view plain copy
  1. obstacle_range: 2.5  
  2. raytrace_range: 3.0  
  3. robot_radius: 0.49  
  4. inflation_radius: 0.1  
  5. max_obstacle_height: 0.6  
  6. min_obstacle_height: 0.0  
  7. obstacles:  
  8.    observation_sources: scan  
  9.    scan: {data_type: LaserScan, topic: /scan, marking: true, clearing: true, expected_update_rate: 0}  

一定要注意,这里添加了一个obstales,说明障碍物层的数据来源scan,"obstacles:"的作用是强调命名空间。

2.global_costmap_params.yaml

[python] view plain copy
  1. global_costmap:  
  2.    global_frame: /map  
  3.    robot_base_frame: /base_footprint  
  4.    update_frequency: 1.0  
  5.    publish_frequency: 0  
  6.    static_map: true  
  7.    rolling_window: false  
  8.    resolution: 0.01  
  9.    transform_tolerance: 1.0  
  10.    map_type: costmap  
  11.    plugins:  
  12.       - {name: static_map,       type: "costmap_2d::StaticLayer"}  
  13.       - {name: obstacles,        type: "costmap_2d::VoxelLayer"}  
  14.       - {name: simplelayer,        type: "simple_layer_namespace::SimpleLayer"}       
  15.       - {name: inflation_layer,        type: "costmap_2d::InflationLayer"}  

3.local_costmap_params.yaml

[python] view plain copy
  1. local_costmap:  
  2.    global_frame: /map  
  3.    robot_base_frame: /base_footprint  
  4.    update_frequency: 3.0  
  5.    publish_frequency: 1.0  
  6.    static_map: false  
  7.    rolling_window: true  
  8.    width: 6.0  
  9.    height: 6.0  
  10.    resolution: 0.01  
  11.    transform_tolerance: 1.0  
  12.    map_type: costmap  

另外一个与路径规划相关的base_local_planner_params,yaml不用修改。

     这里由于只在全局层添加simple_layer,所以local_costmap还是使用的默认layer插件。然后我们运行这个新配置的move_base launch文件,你会发现simplerlayer已经添加进global_costmap了,而local_costmap还是默认的pre-hydro格式。

最后在rviz中看看global_costmap中有没有加入这个障碍物点。下面是我在实验室环境的地图,红色标记的地方是人为加入的障碍物。按照上面的程序,障碍物应该出现在机器人正前方1m出,下面这个效果是lz自己的程序。

障碍物的膨胀系数,可以用下面的命令进行动态调试:

[python] view plain copy
  1. rosrun rqt_reconfigure rqt_reconfigure  


     

     由于网上关于这部分的教程很少,lz也是自己慢慢摸索出来的,查看了很多ros answer,一一列出在下面。同时,这个地址有一个别人创建的行人layer,为你定制自己的移动障碍物层有很好的借鉴意义。

原文参考:http://blog.youkuaiyun.com/heyijia0327/article/details/42241831#insertcode
<think>好的,用户想知道如何在ROS上仿真移动机器人的自主巡航。这个问题需要分步骤来解答,我需要先回忆一下ROS仿真的基本流程和常用工具。 首先,应该从搭建仿真环境开始。ROS里常用的Gazebo是一个强大的物理仿真工具,用户可能需要先安装Gazebo和相关的ROS包。然后,移动机器人模型需要创建,可能用URDF或者更现代的xacro文件来描述机器人的结构和传感器,比如激光雷达或者摄像头,这些对自主巡航很重要。 接下来,运动控制部分。机器人需要能够接收速度指令,比如通过cmd_vel话题,所以需要配置控制器,比如diff_drive_controller,或者用已有的移动机器人包,比如TurtleBot3的仿真模型。这部分需要确保机器人在仿真中能正确响应控制指令。 然后是导航功能的实现。ROS的导航栈(move_base)是关键,它需要地图、定位(如AMCL)、路径规划等组件。用户可能需要先构建一个仿真环境的地图,或者使用已有的地图。在Gazebo中生成地图可能涉及SLAM工具,比如gmapping,但仿真中可能直接加载预设环境更方便。 传感器数据的处理也很重要,比如激光雷达数据用于避障和定位,可能需要配置相应的插件,确保Gazebo中的传感器数据能够发布到正确的ROS话题上。同时,可能需要调整噪声参数,使仿真更接近实际情况。 接着是集成和测试。将机器人的模型、控制器、导航栈和传感器集成到同一个启动文件中,方便一键启动。测试过程中需要检查各个节点是否正常通信,比如地图是否正确加载,路径规划是否生成,机器人是否能避开障碍物等。 用户可能还关心如何验证自主巡航的效果,比如在Gazebo中设置不同的场景,或者使用RViz来可视化导航过程。此外,可能还需要调试导航参数,如代价地图的膨胀半径、路径规划算法等,以优化机器人的移动效果。 需要注意的常见问题包括:URDF模型中的关节配置是否正确,传感器话题是否与导航栈所需的一致,坐标变换(TF)是否正确发布,以及仿真中的物理参数(如摩擦、质量)是否合理,避免机器人出现不自然的行为。 如果用户是刚接触ROS,可能需要先了解一些基础知识,比如ROS节点、话题、服务、参数服务器等概念,以及如何编写启动文件和使用roslaunch。此外,参考现有的仿真项目,比如TurtleBot3的仿真教程,可以快速上手。 最后,总结步骤时,要确保逻辑清晰,从环境搭建、机器人模型、传感器配置、导航设置到测试调试,每个环节都解释清楚,并提供可能的工具和包,帮助用户逐步实现目标。同时,提醒用户注意常见错误和调试方法,增强实用性。</think>在ROS中实现移动机器人自主巡航仿真的核心步骤及方法如下: --- ### 一、环境准备 1. **安装ROS及依赖** ```bash sudo apt-get install ros-<distro>-gazebo-ros-pkgs sudo apt-get install ros-<distro>-navigation sudo apt-get install ros-<distro>-slam-gmapping ``` 2. **创建工作空间** ```bash mkdir -p ~/catkin_ws/src cd ~/catkin_ws/ catkin_make ``` --- ### 二、机器人建模 1. **URDF/Xacro模型开发** ```xml <!-- 示例:差分驱动机器人底盘 --> <link name="base_link"> <inertial> <mass value="5"/> <inertia ixx="0.1" ixy="0" ixz="0" iyy="0.1" iyz="0" izz="0.1"/> </inertial> </link> <joint name="left_wheel_joint" type="continuous"> <parent link="base_link"/> <child link="left_wheel"/> <axis xyz="0 1 0"/> </joint> ``` 2. **传感器集成** - 激光雷达插件配置: ```xml <gazebo reference="laser_frame"> <sensor type="ray" name="hokuyo"> <plugin name="gazebo_ros_head_hokuyo_controller" filename="libgazebo_ros_laser.so"> <topicName>/scan</topicName> <frameName>laser_frame</frameName> </plugin> </sensor> </gazebo> ``` --- ### 三、仿真环境构建 1. **Gazebo世界文件创建** ```xml <!-- warehouse.world --> <sdf version="1.6"> <world name="default"> <include> <uri>model://sun</uri> </include> <include> <uri>model://obstacle_course</uri> </include> </world> </sdf> ``` 2. **启动机器人+环境** ```bash roslaunch my_robot_gazebo my_robot_world.launch ``` --- ### 四、导航系统配置 1. **MoveBase参数配置** ```yaml # costmap_common_params.yaml obstacle_layer: observation_sources: laser_scan laser_scan: {sensor_frame: laser_frame, data_type: LaserScan, topic: scan} # base_local_planner_params.yaml TrajectoryPlannerROS: max_vel_x: 0.5 acc_lim_theta: 3.0 ``` 2. **AMCL定位配置** ```yaml # amcl_params.yaml laser_min_range: 0.1 laser_max_range: 12.0 odom_frame_id: odom base_frame_id: base_link ``` --- ### 五、自主巡航实现流程 1. **启动导航栈** ```bash roslaunch my_robot_navigation move_base.launch ``` 2. **构建地图(可选)** ```bash rosrun gmapping slam_gmapping scan:=scan rosrun map_server map_saver -f ~/map ``` 3. **设置目标点** ```python # Python示例发送目标位姿 goal = MoveBaseGoal() goal.target_pose.header.frame_id = "map" goal.target_pose.pose.position.x = 3.0 goal.target_pose.pose.orientation.w = 1.0 ``` --- ### 六、调试优化技巧 1. **RViz可视化配置** ```bash rosrun rviz rviz -d `rospack find my_robot_navigation`/rviz/nav.rviz ``` 2. **关键调试工具** ```bash rostopic echo /move_base/status # 监控导航状态 rosrun rqt_reconfigure rqt_reconfigure # 动态参数调整 ``` --- ### 七、典型问题解决方案 1. **TF树异常** ```bash rosrun tf view_frames # 生成TF树PDF ``` 2. **路径规划失败** - 检查`/map`与`/odom`坐标系的正确关联 - 调整`inflation_radius`参数 --- ### 八、进阶方向 1. **多机器人协同仿真** 2. **动态障碍物测试** 3. **传感器融合(RGB-D+IMU)** 4. **Gazebo与真实世界数据联合仿真** 完整实现代码示例可参考:[TurtleBot3仿真包](https://github.com/ROBOTIS-GIT/turtlebot3_simulations) 推荐学习路径:Gazebo物理引擎→ROS控制→导航栈源码分析→强化学习集成
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值