资讯详情

资讯详情

基于ROS的轮式机器人建模与路径规划仿真实践指南

简介基于ROS的轮式机器人建模与路径规划仿真是哈尔滨师范大学自然科学学报2020年刊发的一篇学术论文PDF格式聚焦移动机器人路径规划、自主导航与避障技术。文章以ROS作为机器人开发平台通过Rviz建立双轮差速机器人仿真模型并结合Gazebo三维仿真平台设计运动环境系统介绍了坐标系建立、里程计运动模型、粒子滤波状态估计以及A*路径规划算法的完整实现过程。仿真实验表明所设计的机器人能够在未知环境中完成二维栅格地图构建并实现自主导航与避障功能是机器人、机器学习、深度学习方向研究者与工程师值得参考的专业文献。资源包为单份PDF文件共5.55MB内容包含全文图文信息涵盖SLAM框架架构、gmapping功能包应用、导航算法等关键知识点。目前已有1509人次浏览学习适合从事自主移动机器人、ROS系统开发及课题研究的读者参考。1. 基于ROS的轮式机器人建模与路径规划仿真先回答三个问题再动手轮式机器人开发团队在样机到位之前遇到的第一道坎基本都是同一个导航算法拿什么验证。基于ROS的轮式机器人建模与路径规划仿真就是先用URDF把底盘描述成虚拟模型放进Gazebo里接受重力、摩擦和碰撞再挂上导航栈完成自主移动验证。这套流程最常见也最稳的组合是Ubuntu 20.04、ROS Noetic和Gazebo 11配合move_base、amcl和gmapping一台普通电脑就能跑完整条链路。它能解决的核心痛点是样机还没装好时导航代码不空等真机调试成本太高时先用仿真把算法和参数暴露出来。读这篇笔记你只要带着三个问题来就行这台虚拟小车靠什么运动模型动起来全局规划和局部规划分别解决什么问题仿真里调好的参数到了真机有多少参考价值。后面内容从环境搭建、URDF建模、传感器配置一路讲到建图与路径规划最后是一组高频踩坑记录。适合刚接触ROS的开发者也适合已经有真机项目、想补一套仿真回归流程的工程师。先说明白这篇不谈底层原理大全只讲落地路径。2. 选型与最小环境搭建为什么是Ubuntu 20.04 ROS Noetic Gazebo 112.1 ROS版本和仿真器怎么选先看生态再看性能常见做法是先用ROS Noetic把仿真链路跑通再考虑ROS2迁移。原因很直接Noetic是ROS1最后一个LTS版本navigation栈里move_base、amcl、gmapping在这个版本上已经迭代多年参数资料和踩坑记录最全。Gazebo 11作为Noetic默认配套的仿真器差速驱动插件、激光雷达插件都直接用二进制包安装不需要碰源码编译。对团队开发来说这意味着你遇到的大部分问题在已有社区记录里都能搜到解法不用自己从头黑匣子里摸。ROS2和Nav2在架构上更现代但Gazebo Fortress和Nav2的参数配置方式与ROS1差异不小。如果团队里没有一个人完整走过Nav2迁移路径我一般建议第一版仿真项目还是留在Noetic上。尤其当你只关心轮式底盘和路径规划效果而不是分布式通信、多机协同这类特性时Noetic的生产力优势非常明显。硬件方面8G内存、4核CPU能跑通基础场景但建议16G内存因为同时开Gazebo、Rviz和导航栈内存峰值轻松超过6G。2.2 用命令行快速搭建ROS工作空间环境安装不是这篇的重点但工作空间初始化是后续所有包的基础。按下面命令创建一个干净的catkin工作空间mkdir -p ~/robot_ws/src cd ~/robot_ws catkin_make source devel/setup.bash echo $ROS_PACKAGE_PATH第一次执行catkin_make会创建build和devel目录。source之后如果ROS_PACKAGE_PATH里包含robot_ws/src说明工作空间生效。这里有个细节如果你电脑上还有别的工作空间_bashrc里source顺序决定谁的包优先后source的工作空间会覆盖同名包。出现莫名其妙的包版本不对问题时先看这里别急着重装。再验证rosdep依赖是否可用rosdep check --rosdistro noetic正常会输出所有已安装包的依赖状态。如果提示缺依赖执行rosdep install加上--from-paths src参数补齐。注意rosdep需要提前初始化没初始化会出现找不到rosdep命令的情况。这块属于环境搭建最常见的小坎花十分钟处理掉后面建模阶段会顺畅很多。2.3 验证Gazebo和Rviz能正常通信工作空间就绪后先跑一个最小的仿真实例。创建一个空launch文件把Gazebo和Rviz同时拉起来launch include file$(find gazebo_ros)/launch/empty_world.launch arg namepaused valuefalse/ arg nameuse_sim_time valuetrue/ /include node namerviz pkgrviz typerviz outputscreen/ /launchuse_sim_time设为true是关键它让所有节点使用仿真时钟而不是系统时钟分布式调试时时间不同步的问题从这里就能避开。启动后Gazebo窗口出现一个带地面的空世界Rviz可能显示默认视角先不管等到第4章再配TF和地图显示。如果Gazebo窗口打开很久还是灰屏优先检查显卡驱动和OpenGL支持别在ROS层面折腾。3. 轮式机器人建模URDF四要素与差速驱动配置3.1 URDF里决定物理正确性的四个要素URDF不只是画一个好看的3D模型对仿真来说真正影响行为的是link的visual、collision、inertial和joint这四样。visual只影响显示collision决定碰撞检测体积inertial决定质量和转动惯量。很多新手模型加载后瘫在地上就是collision或inertial没写全。我用一台常见的两轮差速底盘做例子底盘主体用box几何体描述link namebase_link visual geometry box size0.4 0.3 0.08/ /geometry origin xyz0 0 0.04/ /visual collision geometry box size0.4 0.3 0.08/ /geometry origin xyz0 0 0.04/ /collision inertial mass value5.0/ origin xyz0 0 0.04/ inertia ixx0.05 ixy0.0 ixz0.0 iyy0.08 iyz0.0 izz0.08/ /inertial /link注意collision和visual的几何尺寸必须一致否则会出现模型看起来躲开了障碍物、碰撞体积却撞上去的翻车现场。inertia的数值别随便填它是物体转动惯量近似值底盘这类近似长方体ixx和iyy、izz按照质量分布估算。填零会导致Gazebo物理引擎计算异常模型会抖动甚至直接崩飞。3.2 轮子与底盘的连接joint类型和TF树的关系底盘和轮子之间用continuous类型的joint连接表示轮子绕Y轴连续旋转。每个驱动轮必须对应一个joint差速驱动插件从joint获取轮速。还要加一个支撑轮否则车会在重力作用下前后倾倒。支撑轮用fixed joint固定到底盘位置在底盘前后两端。joint nameleft_wheel_joint typecontinuous parent linkbase_link/ child linkleft_wheel/ origin xyz0.15 0.2 0/ axis xyz0 1 0/ /joint joint namefront_caster_joint typefixed parent linkbase_link/ child linkfront_caster/ origin xyz0.2 0 -0.04/ /jointTF树里base_link的位姿由joint计算得到而base_footprint通常定义为base_link在水平面上的投影点避免导航栈里出现高度分量干扰。常见做法是额外加一个base_footprint link作为root把odom到base_footprint的变换交给差速驱动插件去发布。这里如果你偷懒直接让odom连base_link后面AMCL配置时坐标系对不上代价地图会莫名其妙转起来。3.3 差速驱动与激光雷达的Gazebo插件配置Gazebo里让车跑起来核心是加载libgazebo_ros_diff_drive这个插件。插件参数包括左右轮joint名、轮距、轮径、命令话题和里程计话题。下面这份配置实践验证过双轮轴距0.4m、轮径0.1m的底盘可以按这个改gazebo plugin namediff_drive_plugin filenamelibgazebo_ros_diff_drive.so left_jointleft_wheel_joint/left_joint right_jointright_wheel_joint/right_joint wheel_separation0.4/wheel_separation wheel_diameter0.1/wheel_diameter max_wheel_torque20/max_wheel_torque max_wheel_acceleration1.0/max_wheel_acceleration command_topiccmd_vel/command_topic odometry_frameodom/odometry_frame robot_base_framebase_footprint/robot_base_frame publish_odomtrue/publish_odom publish_wheel_tftrue/publish_wheel_tf /plugin /gazebowheel_separation和wheel_diameter必须与URDF里轮子joint的origin和轮子半径一致这是里程计推算是否准确的基础。max_wheel_torque和max_wheel_acceleration控制加速能力设置过大会导致仿真里起步打滑过小则跑起来像疲劳驾驶。publish_wheel_tf设为true插件会自动发布odom到base_footprint的变换省去手工维护里程计的烦恼。激光雷达同样需要插件支持。轮式机器人导航仿真一般配单线激光雷达挂在底盘上方获取2D信息。在URDF里添加雷达link、joint和一个sensor blockgazebo referencelaser_link sensor namelaser typeray pose0 0 0.1 0 0 0/pose ray scan horizontal samples360/samples resolution1/resolution min_angle-3.14159/min_angle max_angle3.14159/max_angle /horizontal /scan range min0.1/min max10.0/max /range /ray plugin namelaser_plugin filenamelibgazebo_ros_ray_sensor.so ros remapping~/output:/scan/remapping /ros output_typesensor_msgs/LaserScan/output_type /plugin always_ontrue/always_on update_rate20/update_rate /sensor /gazeboupdate_rate设20Hz对应真实雷达扫描频率。samples设为360角度范围正负180度这是2D导航最常见的配置。扫描频率和采样密度决定建图质量但也会影响CPU负荷不需要一味追求高参数。4. 路径规划仿真从建图到move_base自主导航4.1 用gmapping和map_server把仿真环境变成2D栅格地图路径规划离不开地图。Gazebo世界模型建好后第一步是遥控小车在环境里转一圈用gmapping生成2D栅格地图。启动gmapping的launch文件一般包含激光雷达、里程计、tf和slam_gmapping节点。可以直接复用turtlebot3导航包里的建图launch结构但把机器人模型换成我们的。我习惯单独写一个launch核心部分是slam_gmapping节点node nameslam_gmapping pkggmapping typeslam_gmapping param namemap_frame valuemap/ param nameodom_frame valueodom/ param namebase_frame valuebase_footprint/ param namelaser_frame valuelaser_link/ remap fromscan to/scan/ /node建图时要遥控小车覆盖整个环境速度控制在0.2m/s以内转弯尽量平缓。这个阶段最怕大力出奇迹车速太快会导致里程计累计误差大、激光扫描匹配不稳定建出来的图会重影。建完图后用map_saver保存rosrun map_server map_saver -f ~/robot_ws/src/my_robot_map会生成map.pgm和map.yaml两个文件。map.pgm是栅格图像map.yaml包含分辨率、原点、占用阈值等参数。navi导航时直接加载这个yaml文件作为全局代价地图的静态层。4.2 配置move_base全局规划器与局部规划器分工move_base是导航栈的总管内部包含costmap_2d的全局和局部两层代价地图。全局代价地图负责静态地图加膨胀层供全局路径规划器搜索从起点到目标点的路径局部代价地图负责实时感知周围障碍物供局部规划器做安全跟踪和避障。全局规划器我用的默认NavfnROS它在Noetic里稳定高效适合室内结构化环境。局部规划器在DWA和TEB之间选DWA参数少、适合同步差速底盘TEB支持时间最优和动态避障但参数多调起来费时间。对于仿真验证阶段先用DWA跑通流程再按需切换TEB。move_base的launch核心如下node namemove_base pkgmove_base typemove_base remap fromcmd_vel tocmd_vel/ param namebase_global_planner valuenavfn/NavfnROS/ param namebase_local_planner valuedwa_local_planner/DWAPlannerROS/ rosparam file$(find my_robot_nav)/config/costmap_common_params.yaml commandload nsglobal_costmap/ rosparam file$(find my_robot_nav)/config/costmap_common_params.yaml commandload nslocal_costmap/ rosparam file$(find my_robot_nav)/config/local_costmap_params.yaml commandload/ rosparam file$(find my_robot_nav)/config/global_costmap_params.yaml commandload/ /nodecostmap_common_params里最常调的是膨胀半径和障碍物范围。下面这份适合0.4m长、0.3m宽的小型底盘robot_base_frame: base_footprint update_frequency: 5.0 publish_frequency: 2.0 obstacle_range: 3.0 raytrace_range: 3.5 footprint: [[0.2, 0.15], [0.2, -0.15], [-0.2, -0.15], [-0.2, 0.15]] inflation_radius: 0.25 observation_sources: laser laser: {sensor_frame: laser_link, data_type: LaserScan, topic: /scan, marking: true, clearing: true}footprint按实际车体外轮廓写别用圆形代替否则窄通道通行能力判断会失真。inflation_radius设0.25m让路径和障碍物保持25cm以上距离。局部代价地图的参数决定实时反应速度local_costmap: global_frame: odom robot_base_frame: base_footprint rolling_window: true width: 3.0 height: 3.0 resolution: 0.05 plugins: - {name: obstacle_layer, type: costmap_2d::ObstacleLayer} - {name: inflation_layer, type: costmap_2d::InflationLayer}local_costmap跟随机器人移动width和height设3m分辨率0.05m适合室内环境。宽度设太大障碍物检测滞后明显设太小路径规划看不到远方弯道容易冲进死角。4.3 用Rviz下发导航目标验证从A点到B点的完整闭环导航链路启动后在Rviz里添加Map、RobotModel、Path和LaserScan显示确认地图正常加载。然后点击2D Nav Goal按钮在地图上点一个目标点并拖出朝向箭头小车的DWA局部规划器会开始计算速度指令驱动差速插件让轮子转起来。这里如果只看到路径规划结果但车不动检查cmd_vel话题有没有被move_base发布以及差速插件是否订阅了同一个cmd_vel话题。rostopic echo /cmd_vel能持续输出线速度和角速度数据说明控制链路是通的。再开一个终端看局部规划状态rosparam get /move_base/local_costmap/inflation_radius输出0.25说明参数已加载。这时候在Rviz里能看到绿色全局路径和红色局部路径正常情况下局部路径跟随全局路径遇到临时障碍物时会自动绕开。仿真里整车跑完一轮如果路径平滑、无明显抖动说明URDF和导航参数基本匹配。5. 避坑五个高频翻车现场与排查路径5.1 现象模型加载进Gazebo就瘫倒或者乱弹这是URDF建模阶段最高频的问题。现象是模型一出现就塌向一侧或者轮子陷进地里乱弹。原因通常是三个一是collision体积缺失Gazebo默认给link生成的碰撞体积和visual不一致二是inertial参数没写或innertia矩阵不对三是支撑轮位置不当底盘重心不在支撑多边形内。解决方式是逐个link检查collision和inertial确认支撑轮在底盘前后端都有布置。把底盘主体的惯性矩阵按长方体近似计算填入模型就不会再倒。5.2 现象导航栈启动后报错找不到从odom到base_footprint的变换启动move_base时终端刷屏tf错误最常见是差速驱动插件没有正确发布odom到base_footprint的变换。检查模型xacro文件里robot_base_frame参数是否写成base_link而URDF里是否真的存在base_footprint这个link。差速插件发布odom变换需要robot_base_frame在TF树里存在。补上base_footprint并确保插件publish_wheel_tf设为true问题一般能解决。5.3 现象建图过程重影严重走廊错位、门洞重合建图是路径规划的地基建图崩了后面全崩。重影多因激光雷达帧率与里程计频率不匹配或者车速过快导致扫描匹配失败。查看雷达topic频率rostopic hz /scan如果远低于20Hz说明Gazebo仿真里update_rate没生效或者电脑负载太高。车速降到0.2m/s以下慢慢转一圈重影通常会明显减轻。还有一种隐藏因素是地面摩擦系数太小仿真轮子持续打滑里程计失真在URDF加摩擦系数能改善轮子的附着力。5.4 现象move_base规划的路径贴着墙或者直接穿墙而过path贴合墙边通常不是全局规划器的问题而是膨胀半径没生效或代价地图没更新。检查costmap_common_params里inflation_radius是否大于0同时确认obstacle_layer有订阅scan。costmap在Rviz里显示为红黄渐变层如果障碍物边缘没有渐变色说明动态障碍物没有进入代价地图。另外global_costmap的track_unknown_space和static_layer参数不能同时为true否则静态地图层会覆盖障碍物层路径就会无视已知障碍。这里属于典型的配置参数互相打架的问题排查时逐层检查代价地图的plugins。5.5 现象仿真跑得顺真机却表现完全不同仿真和真机的差异主要集中在轮径、轮距、电机响应和摩擦系数。真机轮径与URDF不一致会导致里程计距离比例偏大或偏小直接表现在地图缩放和旋转上。常见做法是做一个轮径标定让真车直线走一段已知距离对比cmd_vel累计位移与实测位移调整URDF或者里程计矫正系数。控制器响应差异则反映在路径跟踪的震荡上真机的最大速度、加速度一般要低于仿真参数move_base里max_vel_x和max_vel_trans这组参数落地时先保守再放开。6. 把仿真参数沉淀成真机标定依据仿真做到这个深度下一步就是让Gazebo里的数据为真机服务。我会在launch启动后固定用bag记录里程计和激光数据回放时对比不同规划器参数对路径平滑度的影响。每次调参数前先记录一组baseline修改后对比横向偏差和停车精度而不是靠肉眼判断。除了改参数Gazebo还能用来模拟动态场景。常见做法是往世界里随机生成圆柱形障碍物让小车在相同地图多次执行导航观察局部分数规划器能不能在移动障碍物逼近时及时绕行。这类方案在真实场地里布置成本高仿真里用几行Python脚本就能建完适合作为导航系统回归前的自动化测试场景我现在的习惯是保留三套仿真场景窄通道、多障碍物房间、长走廊转弯每套场景对应一份参数基线。等真机调试遇到问题时先回到仿真里复现再针对性改真机参数这套工作流帮我省掉了大量反复拆装车辆的时间。希望这次整理的内容能帮到你遇到具体问题欢迎按章节里的排查路径逐条对照。本文还有配套的精品资源点击获取
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →