ROS机器人开发实践->机器人导航
目录
(2).编写启动gmapping的launch文件gmapping_slam_start.launch
参考链接:7.2.3 导航实现03_定位 · Autolabor-ROS机器人入门课程《ROS理论与实践》零基础教程
前言:
通过我的上一篇文章ROS机器人开发实践->机器人建模与仿真-CSDN博客,我们已经可以在仿真环境中创建一个机器人了,并可以得到他的传感器信息。接下来我们就利用这个机器人进行导航。我们的目的是以下几个功能:
1.建图与保存地图
2.利用已经建好的图定位
3.利用已经建好的图导航
4.边建图边导航
一、建图与保存地图
1.先启动机器人仿真节点
roslaunch car_gazebo_simulation start_gazebo_all.launch
2.使用建图
(1).安装gmapping
sudo apt install ros-<ROS版本>-gmapping
(2).编写启动gmapping的launch文件gmapping_slam_start.launch
<launch>
<param name="use_sim_time" value="true"/>
<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>
解释:建图需要里程计和激光雷达的信息。对于这个launch文件,激光雷达数据通过话题scan获得,里程计的信息使用下面这两个坐标系的相对位置关系获得。
<param name="base_frame" value="base_footprint"/><!--底盘坐标系-->
<param name="odom_frame" value="odom"/> <!--里程计坐标系-->
输出上面配置文件里面没写,默认输出栅格地图的话题是/map,一会创建map的tf坐标,输出map → odom 的 tf

(3).启动gmapping
roslaunch car_gazebo_navigation gmapping_slam_start.launch
3.启动键盘键盘控制节点,用于控制机器人运动建图
rosrun teleop_twist_keyboard teleop_twist_keyboard.py

4.保存地图launch文件map_save.launch
(1)编写保存地图launch文件
finename的值就是地图保存路径
<launch>
<arg name="filename" value="$(find car_gazebo_navigation)/map/nav" />
<node name="map_save" pkg="map_server" type="map_saver" args="-f $(arg filename)" />
</launch>
默认将/map话题保存成地图,如果话题名不是/map,需要重映射
<launch>
<arg name="filename" value="$(find car_gazebo_navigation)/map/nav" />
<node name="map_save" pkg="map_server" type="map_saver" args="-f $(arg filename)">
<remap from="map" to="your_map_topic_name" />
</node>
</launch>
(2)启动保存地图launch文件
roslaunch car_gazebo_navigation map_save.launch


二、利用已经建好的图定位
1.加载地图
<node name="map_server" pkg="map_server" type="map_server" args="$(find car_gazebo_navigation)/map/$(arg map)"/>

2.启动acml
(1)下载acml
amcl已经被集成到了navigation包,navigation安装前面也有介绍,命令如下:
sudo apt install ros-<ROS版本>-navigation
(2)编写acml配置代码acml.launch
<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"/><!-- 里程计坐标系 -->
<param name="base_frame_id" value="base_footprint"/><!-- 添加机器人基坐标系 -->
<param name="global_frame_id" value="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>
(3)解释
amcl这个包输入base_frame和odom的tf。这样就可以知道Δx, Δy, Δθ,之后根据这个变化量更新粒子的位置。之后订阅了/scan和/map话题,用于确定每个例子的权重。之后订阅/initialpose用于确定初始位置。最后输出了map的tf。可以获得得到激光雷达修正之后,机器人的真实位置。
注意,上述文件中默认订阅/scan和/map,如果你的激光雷达数据和栅格地图数据不是这两个,那么需要你remap一下。
<node pkg="amcl" type="amcl" name="amcl" output="screen">
<remap from="scan" to="/rplidar/scan"/> <!-- 激光话题改名 -->
<remap from="map" to="/my_map_topic"/> <!-- 地图话题改名 -->
</node>
注意,为什么已经有了Δx, Δy, Δθ,还需要差分的模型呢?原因是acml在工作的时候,不仅考虑Δx, Δy, Δθ,还会考虑误差。所以不同的运动学模型,每次计算误差不同
① 差分驱动(odom_model_type=diff)
-
假设机器人靠左右轮差速来运动。
-
当有误差时,主要体现为:
-
直行误差:左右轮编码器不一致,导致直线走歪。
-
旋转误差:原地转动时,两轮速度不对称,导致角度偏差。
-
-
粒子采样时,AMCL 会在 Δθ 上加较多不确定性。
② 全向驱动(odom_model_type=omni)
-
假设机器人有麦克纳姆轮或全向轮,可以直接实现任意 Δx, Δy, Δθ。
-
误差分布更均匀,直行时也可能有侧滑。
-
粒子采样会在 Δx, Δy 上增加更多横向不确定性。

(4)如何使用
1.启动仿真环境
roslaunch car_gazebo_simulation start_gazebo_all.launch
2.启动遥控器
rosrun teleop_twist_keyboard teleop_twist_keyboard.py
3.启动定位节点
roslaunch car_gazebo_navigation dingwei.launch
######
dingwei.launch的代码如下
<launch>
<!-- 设置地图的配置文件 -->
<arg name="map" default="nav.yaml" />
<!-- 运行地图服务器,并且加载设置的地图-->
<node name="map_server" pkg="map_server" type="map_server" args="$(find car_gazebo_navigation)/map/$(arg map)"/>
<!-- 启动AMCL节点 -->
<include file="$(find car_gazebo_navigation)/launch/amcl.launch" />
<!-- 运行rviz -->
<!-- <node pkg="rviz" type="rviz" name="rviz"/> -->
</launch>

三、利用建立好的地图导航
1.初始准备
下载move_base导航包
sudo apt install ros-<ROS版本>-navigation
2.move_base导航介绍
基本架构就是订阅目标话题,输出速度控制话题

3.地图分层介绍
代价地图有两张:global_costmap(全局代价地图) 和 local_costmap(本地代价地图),前者用于全局路径规划,后者用于本地路径规划。
两张代价地图都可以多层叠加,一般有以下层级:
-
Static Map Layer:静态地图层,SLAM构建的静态地图。
-
Obstacle Map Layer:障碍地图层,传感器感知的障碍物信息。
-
Inflation Layer:膨胀层,在以上两层地图上进行膨胀(向外扩张),以避免机器人的外壳会撞上障碍物。
-
Other Layers:自定义costmap。
4.参数文件介绍
(1)costmap_common_params.yaml
sensor_frame: 激光雷达坐标系,要和tf中一致
data_type: 激光雷达数据类型
topic:激光雷达话题名
-
marking: true-
表示该传感器数据可以 在代价地图上添加障碍物。
-
举例:激光扫到墙 → 在 costmap 上标记为障碍。
-
-
clearing: true-
表示该传感器数据也可以 清除代价地图上的障碍物。
-
举例:原来 costmap 上有个障碍物,现在激光能穿过去了 → 把它删掉。
-
obstacle_range
-
作用:设定传感器数据能在代价地图里“标记障碍物”的最大有效距离。
-
举例:
-
你用的是 10m 激光雷达,但
obstacle_range = 3.0, -
那么只有 3m 内的障碍物会被认为是障碍,更新进局部代价地图。
-
-
好处:
-
只考虑近距离障碍,更符合局部规划需要(机器人一般不会管 10m 以外的障碍)。
-
减少计算量。
-
raytrace_range
-
作用:设定传感器的“自由空间射线”能清除障碍物的最远距离。
-
举例:
-
raytrace_range = 3.5,表示如果激光打到 3.5m 外,就会把射线路径上更近的旧障碍物“清空”。
-
-
这是为了防止“幽灵障碍物”:
-
比如一个人走开了,之前 2m 处的障碍点如果不清除,就会一直留在 costmap 里。
-
有了 raytrace,就能把消失的障碍物清掉。
-
#机器人几何参,如果机器人是圆形,设置 robot_radius,如果是其他形状设置 footprint
robot_radius: 0.12 #圆形
# footprint: [[-0.12, -0.12], [-0.12, 0.12], [0.12, 0.12], [0.12, -0.12]] #其他形状
obstacle_range: 3.0 # 用于障碍物探测,比如: 值为 3.0,意味着检测到距离小于 3 米的障碍物时,就会引入代价地图
raytrace_range: 3.5 # 用于清除障碍物,比如:值为 3.5,意味着清除代价地图中 3.5 米以外的障碍物
#膨胀半径,扩展在碰撞区域以外的代价区域,使得机器人规划路径避开障碍物
inflation_radius: 0.2
#代价比例系数,越大则代价值越小
cost_scaling_factor: 3.0
#地图类型
map_type: costmap
#导航包所需要的传感器
observation_sources: scan
#对传感器的坐标系和数据进行配置。这个也会用于代价地图添加和清除障碍物。例如,你可以用激光雷达传感器用于在代价地图添加障碍物,再添加kinect用于导航和清除障碍物。
scan: {sensor_frame: laser, data_type: LaserScan, topic: scan, marking: true, clearing: true}
(2)global_costmap_params.yaml
static_map: 就是订阅/map话题初始化全局地图,如果话题不是/map需要重映射一下。
global_costmap:
global_frame: map #地图坐标系
robot_base_frame: base_footprint #机器人坐标系
# 以此实现坐标变换
update_frequency: 1.0 #代价地图更新频率
publish_frequency: 1.0 #代价地图的发布频率
transform_tolerance: 0.5 #等待坐标变换发布信息的超时时间
static_map: true # 是否使用一个地图或者地图服务器来初始化全局代价地图,如果不使用静态地图,这个参数为false.
(3)local_costmap_params.yaml
rolling_window: 这个为true表示局部代价地图随着机器人移动而变化,否则就是固定的一个位置。
这里为啥要使用里程计坐标系和机器人坐标系,这样得到的机器人的位置不是真实值。那么得到的局部地图不是偏了吗?
但是它的解释是使用odom比较稳定,局部地图的位置不会跳变,其实我觉的使用map和odom应该区别不大。
local_costmap:
global_frame: odom #里程计坐标系
robot_base_frame: base_footprint #机器人坐标系
update_frequency: 10.0 #代价地图更新频率
publish_frequency: 10.0 #代价地图的发布频率
transform_tolerance: 0.5 #等待坐标变换发布信息的超时时间
static_map: false #不需要静态地图,可以提升导航效果
rolling_window: true #是否使用动态窗口,默认为false,在静态的全局地图中,地图不会变化
width: 3 # 局部地图宽度 单位是 m
height: 3 # 局部地图高度 单位是 m
resolution: 0.05 # 局部地图分辨率 单位是 m,一般与静态地图分辨率保持一致
(4)base_local_planner_params
使用DWA局部路径规划算法。
sim_time: 0.8 每步采样仿真时间,应该指的是总仿真时间。
vx_samples: 18 在vx的最大值和最小值之间随机采样18个速度
vtheta_samples: 20:在vtheta最大值和最小值之间随机采样20个速度
sim_granularity: 0.05 : 采样完成之后生成轨迹。之后进行碰撞检测,这个是碰撞检测的时间步长。
TrajectoryPlannerROS:
# Robot Configuration Parameters
max_vel_x: 0.5 # X 方向最大速度
min_vel_x: 0.1 # X 方向最小速速
max_vel_theta: 1.0 #
min_vel_theta: -1.0
min_in_place_vel_theta: 1.0
acc_lim_x: 1.0 # X 加速限制
acc_lim_y: 0.0 # Y 加速限制
acc_lim_theta: 0.6 # 角速度加速限制
# Goal Tolerance Parameters,目标公差
xy_goal_tolerance: 0.10
yaw_goal_tolerance: 0.05
# Differential-drive robot configuration
# 是否是全向移动机器人
holonomic_robot: false
# Forward Simulation Parameters,前进模拟参数
sim_time: 0.8
vx_samples: 18
vtheta_samples: 20
sim_granularity: 0.05
5.运行
my_move_base.launch
<launch>
<node pkg="move_base" type="move_base" respawn="false" name="move_base" output="screen" clear_params="true">
<rosparam file="$(find car_gazebo_navigation)/param/costmap_common_params.yaml" command="load" ns="global_costmap" />
<rosparam file="$(find car_gazebo_navigation)/param/costmap_common_params.yaml" command="load" ns="local_costmap" />
<rosparam file="$(find car_gazebo_navigation)/param/local_costmap_params.yaml" command="load" />
<rosparam file="$(find car_gazebo_navigation)/param/global_costmap_params.yaml" command="load" />
<rosparam file="$(find car_gazebo_navigation)/param/base_local_planner_params.yaml" command="load" />
</node>
</launch>
path_finding.launch
<launch>
<!-- 设置地图的配置文件 -->
<arg name="map" default="nav.yaml" />
<!-- 运行地图服务器,并且加载设置的地图-->
<node name="map_server" pkg="map_server" type="map_server" args="$(find car_gazebo_navigation)/map/$(arg map)"/>
<!-- 启动AMCL节点 -->
<include file="$(find car_gazebo_navigation)/launch/amcl.launch" />
<!-- 运行move_base节点 -->
<include file="$(find car_gazebo_navigation)/launch/my_move_base.launch" />
</launch>
直接启动仿真节点和path_finding.launch
roslaunch car_gazebo_simulation start_gazebo_all.launch
roslaunch car_gazebo_navigation path_finding.launch

四、导航和SLAM建图
其实直接启动gammaping节点和move_base节点,还有仿真环境节点就可以了。但是要注意gammaping和move_base要求的话题名和tf坐标名
<launch>
<!-- 启动SLAM节点 -->
<include file="$(find car_gazebo_navigation)/launch/gmapping_slam_start.launch" />
<!-- 运行move_base节点 -->
<include file="$(find car_gazebo_navigation)/launch/my_move_base.launch" />
<!-- 运行rviz -->
<!-- <node pkg="rviz" type="rviz" name="rviz" args="-d $(find mycar_nav)/rviz/nav.rviz" /> -->
</launch>

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐


所有评论(0)