最近使用了一下ROS中非常经典的导航包navigation。并通过自己的激光雷达以及相机里程计驱动了自己的小车在室内进行简单的定位以及导航。在此记录一下以免后期忘记。

1、导航包安装

ROS中navigation导航包可以通过GitHub上下载:

https://github.com/ros-planning/navigation
https://github.com/ros-planning/navigation_msgs

这里要下载两个文件,前面一个是导航包后面一个是move_base中使用的一些消息类型。该文件下载后放入ROS路径下编译即可。
需要注意的一点是navigation包对应最新更新为noetic版本,kinetic版本在branches中查找:

安装过程会提示缺少一些环境,对应安装一下即可。

2、仿真环境调试

仿真环境调试时使用的是古月居的mbot_navigation下的launch文件以及对应参数。launch文件以及对应参数如下:
nav_cloister_demo.launch

<launch><!-- 设置地图的配置文件 --><arg name="map" default="map38.yaml" /><!-- 运行地图服务器,并且加载设置的地图--><node name="map_server" pkg="map_server" type="map_server" args="$(find mbot_navigation)/maps/$(arg map)"/><!-- 运行move_base节点 --><include file="$(find mbot_navigation)/launch/move_base.launch"/><!-- 启动AMCL节点 --><include file="$(find mbot_navigation)/launch/amcl.launch" /><!-- 对于虚拟定位,需要设置一个/odom与/map之间的静态坐标变换 --><node pkg="tf" type="static_transform_publisher" name="map_odom_broadcaster" args="0 0 0 0 0 0 /map /odom 100" /><!-- 运行rviz --><node pkg="rviz" type="rviz" name="rviz" args="-d $(find mbot_navigation)/rviz/nav.rviz"/></launch>

base_local_planner_params.yaml

controller_frequency: 1.0
recovery_behavior_enabled: false
clearing_rotation_allowed: falseTrajectoryPlannerROS:max_vel_x: 0.5min_vel_x: 0.1max_vel_y: 0.0  # zero for a differential drive robotmin_vel_y: 0.0max_vel_theta: 0.5min_vel_theta: -0.5min_in_place_vel_theta: 0.5escape_vel: -0.1acc_lim_x: 1.5acc_lim_y: 0.0 # zero for a differential drive robotacc_lim_theta: 1.2holonomic_robot: falseyaw_goal_tolerance: 0.1 # about 6 degreesxy_goal_tolerance: 0.1  # 10 cmlatch_xy_goal_tolerance: falsepdist_scale: 12.0gdist_scale: 0.8meter_scoring: trueheading_lookahead: 0.325heading_scoring: falseheading_scoring_timestep: 0.8occdist_scale: 0.1oscillation_reset_dist: 0.05publish_cost_grid_pc: falseprune_plan: truesim_time: 3.0sim_granularity: 0.025angular_sim_granularity: 0.05vx_samples: 6vy_samples: 0 # zero for a differential drive robotvtheta_samples: 15dwa: truesimple_attractor: false

costmap_common_params.yaml

obstacle_range: 2.5
raytrace_range: 3.0
footprint: [[0.175, 0.175], [0.175, -0.175], [-0.175, -0.175], [-0.175, 0.175]]
footprint_inflation: 0.01
robot_radius: 0.875
inflation_radius: 0.35
max_obstacle_height: 0.6
min_obstacle_height: 0.0
observation_sources: scan
scan: {data_type: LaserScan, topic: /scan, marking: true, clearing: true, expected_update_rate: 0}

global_costmap_params.yaml

global_costmap:global_frame: maprobot_base_frame: base_footprintupdate_frequency: 10.0publish_frequency: 10.0static_map: truerolling_window: falseresolution: 0.01transform_tolerance: 1.0map_type: costmap

local_costmap_params.yaml

local_costmap:global_frame: odomrobot_base_frame: base_footprintupdate_frequency: 10.0publish_frequency: 10.0static_map: falserolling_window: truewidth: 6.0height: 6.0resolution: 0.01transform_tolerance: 1.0

机器人为自己搭建的一个安装了laser传感器的小机器人以及一个20x20大小的仿真环境。通过下述命令打开自己的仿真环境:

roslaunch mbot_gazebo mbot_stereo_with_laser.launch

这里根据自己设置的路径打开,这里的仿真环境还有机器人是我自己搭建的,所以可能跟每个人自己的会有所不同,需要根据自己的实际情况修改。需要这个仿真的也可以在最后的下载链接下载。然后打开move_base导航包:

roslaunch mbot_navigation nav_cloister_demo.launch

此时可以看到rviz界面下存在一个机器人以及一张地图,这地图是我之前自己建好的,然后点击rviz界面下的nav_goal给机器人设置一个目标点,此时就可以看到机器人沿着一条规划好的路线朝着终点走过去了。

这里注意一下机器人在真实运动的过程中并不是完全按照全局路径规划所规划的路径朝着终点走过去的,是因为除了全局路径规划外,move_base还存在一个局部路径规划器。机器人真正的速度不是由全局路径规划器发布的而是由局部路径规划器发布的。

全局路径规划器在规划路径时,只是考虑机器人的位置以及要到达的终点,然后使用dijkstra算法或者A*算法查找是否存在一条从起点到终点的路径。

在得到全局路径后,机器人要考虑如何运动到该目标点。因为机器人是一个物体不是一个点,它运动需要考虑一些自身的特性。比如说我现在有一个差速运动的机器人,它在坐标原点朝向Y轴方向,但是我给的目标点为X方向2m处。全局路径规划出来是一条水平长2m的直线,但是我机器人不可能水平沿着这条线运动过来。所以此时就需要在运动的过程中让机器人一边向前走一边向右边转弯,或者先旋转90°再向前运动2m。而这个策略就是来自于局部路径规划器:滑动窗口(DWA)

3、真实机器人调试

仿真的意义在于看效果,这里仿真的效果还是可以的。但是最终效果还是得在自己的机器人上试一下才知道。这里还遇到了几个问题:

首先第一个在于里程计问题,在仿真中机器人是存在一个里程计的,这个里程计不是我们通常理解的编码器信息,而是ROS中定义的一个数据消息。它包含了机器人当前的位置以及角度信息,还有机器人的运动速度和角速度信息。

通过轮子的编码器信息其实也应该能得到这些信息,但是由于之前正好搞过ORB_SLAM所以这里就打算用视觉作为算法的里程计信息来配合使用了。

3.1使用ORB_SLAM作为odom

相机的输出为三维坐标以及四元数角度表示。由于相机坐标系通常与我们使用的机器人坐标系并不相同,所以存在坐标转换的问题。

另外转换后的坐标需要以odom的形式发布出来,这里的发布有两个,一个是给到tf变换的,一个是给到订阅的。所以这里使用了两个文件来实现这两件事情,但其实这里有点问题,一个文件下应该也能完成:
tf变换:

#include <ros/ros.h>
#include <tf/transform_broadcaster.h>
#include <turtlesim/Pose.h>std::string turtle_name;void poseCallback(const geometry_msgs::PoseStamped& msg)
{//TF广播器 static tf::TransformBroadcaster br;//根据乌龟当前的位姿。设置相对于世界坐标系的坐标变换tf::Transform transform;transform.setOrigin(tf::Vector3(msg.pose.position.z,-msg.pose.position.y,0.0));//设置平移变换tf::Quaternion q;q.setRPY(0,0,0);transform.setRotation(tf::Quaternion(msg.pose.orientation.x,msg.pose.orientation.y,msg.pose.orientation.z,msg.pose.orientation.w));//设置角度变换//发布坐标变换br.sendTransform(tf::StampedTransform(transform,ros::Time::now(),"odom",turtle_name));
}int main(int argc,char** argv)
{ros::init(argc, argv, "my_tf_broadcaster");if(argc!=2){ROS_ERROR("need turtle name as argument");return -1;}turtle_name=argv[1];ros::NodeHandle node;ros::Subscriber sub=node.subscribe("/ORB_SLAM/pose",10,&poseCallback);//ros::Subscriber sub=node.subscribe("/slam_out_pose",10,&poseCallback);ros::spin();return 0;
}

话题发布:

#include <ros/ros.h>
#include <iostream>
#include <fstream>
#include <tf/transform_broadcaster.h>
#include <nav_msgs/Odometry.h>
#include <nav_msgs/Path.h>
#include <geometry_msgs/PointStamped.h>
#include "sensor_msgs/LaserScan.h"
#define RAD2DEG(x) ((x)*180./M_PI)
ros::Publisher odom_pub;
//using namespace std;
void scanCallback(const geometry_msgs::PoseStamped& slam_out_pose)
//void scanCallback(const sensor_msgs::LaserScan::ConstPtr& scan2)
{ros::Time current_time = ros::Time::now();float th=0.1;geometry_msgs::Quaternion odom_quat = tf::createQuaternionMsgFromYaw(th);tf::TransformBroadcaster odom_broadcaster;geometry_msgs::TransformStamped odom_trans;odom_trans.header.stamp = current_time;odom_trans.header.frame_id = "odom";odom_trans.child_frame_id = "base_footprint";odom_trans.transform.translation.x = slam_out_pose.pose.position.z;odom_trans.transform.translation.y = slam_out_pose.pose.position.x;odom_trans.transform.translation.z = 0.0;odom_trans.transform.rotation = odom_quat;//std::cout<<"pose"<<odom_trans.transform.translation.x<<std::endl;//odom.pose.pose.orientation.x = 0.0;//odom.pose.pose.orientation.y = 0.0;//odom.pose.pose.orientation.z = 0.0;//odom.pose.pose.orientation.w = 1.0;//send the transformodom_broadcaster.sendTransform(odom_trans);nav_msgs::Odometry odom;odom.header.stamp = ros::Time::now();odom.header.frame_id = "odom";odom.pose.pose.position.x = slam_out_pose.pose.position.z;odom.pose.pose.position.y = -slam_out_pose.pose.position.x;odom.pose.pose.position.z = 0.0;std::cout<<"pose"<<odom.pose.pose.position.x<<std::endl;odom.pose.pose.orientation.x = slam_out_pose.pose.orientation.x;odom.pose.pose.orientation.y = slam_out_pose.pose.orientation.y;odom.pose.pose.orientation.z = slam_out_pose.pose.orientation.z;odom.pose.pose.orientation.w = slam_out_pose.pose.orientation.w;odom.child_frame_id = "base_footprint";odom.twist.twist.linear.x = 0.05;odom.twist.twist.linear.y = 0.05;odom.twist.twist.angular.z = 0;odom_pub.publish(odom);
}int main(int argc, char **argv)
{ros::init(argc, argv, "odometery");ros::NodeHandle n;//ros::Subscriber sub = n.subscribe("slam_out_pose", 10, scanCallback);ros::Subscriber sub = n.subscribe("/ORB_SLAM/pose", 10, scanCallback);//ros::Subscriber sub = n.subscribe<sensor_msgs::LaserScan>("/scan", 10, scanCallback);//ros::Publisher scan_pub = n.advertise<sensor_msgs::LaserScan>("scan3", 50); odom_pub=n.advertise<nav_msgs::Odometry>("odom", 50);     ros::spin();return 0;
}

这两个其实都是通过订阅的回调函数完成的,最后输出的都是视觉的位姿估计信息。然后通过launch文件打开这两个文件:

<launch><node pkg="exper4213" type="broadcaster" args="/base_footprint" name="broadcast"/><node pkg="exper4213" type="talker" name="talker"/><node pkg="tf" type="static_transform_publisher" name="footprint_base_broadcaster" args="0 0 0 0 0 0 /base_footprint /base_link 100" /></launch>

关于tf变换这里需要注意一下,ROS中的tf变换存在两种,一种静态变换一种动态变换。使用ORB_SLAM作为odom的时候使用的是动态变换。另外在global_costmap_params.yaml文件中robot_base_frame: base_footprint设置的是base_footprint为机器人的位姿,所以这里的odom信息其实是变换到这个坐标系下的。也就是说odom相对于base_footprint进行动态变换。

这里有点不太好理解,在move_base中,map与odom两个坐标系是相对固定的,其他坐标系与机器人一同运动。不知道这里是否可以更改。

另外还有关于odom中的线速度角速度问题,在这里暂时设置为0了,该参数其实是给到DWA使用的,但是设置为0好像影响也不是特别大。

3.2更新频率的问题

这个问题其实在仿真中就存在了,当时没注意,在运动过程中move_base一直报警告:


当时频率设置的是1Hz但是实际上更新时间远远超出了这个时间。不过也能跑跑所以没有太在意,后来算法移植到自己的机器人上面之后,地图更新一次变成了20-30s,控制周期也长达十几秒,完全控制不住。

这个部分是来自于可能使用的CPU运算能力问题,但是很大一部分原因是来自于参数设置,当时在上面参数设置的时候将local_costmap_params.yaml下的rolling_window参数设置为了 false。注意这里好像是把局部路径规划下的滑动窗口关闭了,可能导致了一系列奇奇怪怪的问题引起了时间的消耗。将这里修改回来之后仿真时间缩短为原来的十分之一,大概能控制在5Hz左右。

另外除了调整该参数外,其他的参数例如地图分辨率,仿真时间等参数修改也会引起时间变化。最高的时候试过将频率调整为20Hz,再高就没有修改过了。

具体的参数可以在ROS导航功能调优指南中查看。下载地址:

https://download.csdn.net/download/YiYeZhiNian/74781706

ROS navigation调试基础(实现真实机器人导航)相关推荐

  1. ros小车控制学习-------控制真实机器人做圆周运动

    ros课程的期末作业是控制真实机器人做运动,自行设计小车运动轨迹为特定图案.接下来描述一下控制真实机器人做圆周运动的简单步骤以及可能遇到的问题和我的解决办法. 个人建议是使用软件Bitvise SSH ...

  2. ROS Navigation Tuning Guide(导航调试指南)

    ROS Navigation Tuning Guide 导航调试指南 准备工作 距离传感器 里程计 定位 速度与加速度的设置 获得最大速度 获得最大加速度 设置最小值 XY方向的速度 Global P ...

  3. ROS机器人程序设计归档材料、大纲、基础turtlesim轨迹和导航测评题目

    ROS实践测试分为: 基础编程测评轨迹类-主要测评编程语言等掌握情况. 案例如: 导航类测评-主要考察导航功能包应用情况. 案例如: ROS1云课-导航实践测评_zhangrelay的博客-CSDN博 ...

  4. 在Jetson Nano上学习ROS的记录(版本Ubuntu18.04,课程来源赵虚左老师的《ROS理论与实践》)第十二章 机器人导航(仿真)

    系列文章目录 第一章 ROS空间创建.helloworld的实现.开启多个节点 第二章 话题通信 第三章 服务通信 第四章 参数服务器 第五章 常用指令 第六章 通信机制实操 第七章 ROS通信机制进 ...

  5. ROS学习笔记08、机器人导航仿真(slam、map_server、amcl、move_base与导航消息介绍)

    文章目录 前言 一.导航概述 1.1.导航模块 1.2.导航之坐标系 二.导航实现 准备工作(安装导航包和新建工程包) 2.1.SLAM建图 2.1.1.认识gmapping 2.1.2.实操 2.2 ...

  6. ROS系统SLAM基础学习:gazebo仿真机器人自主导航

    ROS系统SLAM基础学习:gazebo仿真机器人自主导航 move_base节点配置 amcl节点配置 导航仿真 导航SLAM仿真 自主探索SLAM仿真 自主导航:避障 遇到的问题及解决方法和总结 ...

  7. ROS Navigation插件注册自定义导航避障算法

    前言 最近开组会的时候,导师催促我寻找创新点,着实让我头疼.因为说实话,我真的不想找什么创新点,我只想学习一些招聘简历上的技能类的东西,比如熟悉A*.Dijkstra和DWA导航避障算法,熟悉ROS, ...

  8. 遨博协作机器人ROS开发 - Gazebo仿真与控制真实机器人

    目录 一.简介 二.环境版本 三.Gazebo仿真 1.gazebo使用 2. 仿真遨博机械臂 四.ROS控制真实机器人 1. 网络配置 2. 真实机器人控制 3. 真实机器人运动速度调节 五.小结 ...

  9. ros学习之多机器人导航(仿真)

    系列文章目录 (一).从solidworks转urdf文件,在gazebo中添加小车模型 提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档 目录 系列文章目录 目录 前言 一.插件 二 ...

最新文章

  1. 如何简单学会ajax,学会自己封装简单AJAX
  2. 基于 HTML5 WebGL 的 3D 棉花加工监控系统
  3. xampp的Apache无法启动解决方法
  4. OSError:[Errno 13] Permission denied:'my_library' 问题解决方法
  5. DNS服务器全面解析--转
  6. 几个常用算法的适应场景及其优缺点
  7. toad软件 insert批量操作_AE创建、PR制作,动态字幕跨软件也能批量操作
  8. java求解LeetCode题目,实现求解数组中的majority element
  9. gm21模型python_GM11灰色模型
  10. 软件开发常用图标网址大全
  11. 如何查看我的订单-REST的流程API设计案例
  12. 如何将开发好的安卓应用程序发布到安卓市场或商店
  13. mysql的七种查询命令_MySQL数据库查询指令大全
  14. windows端的MarginNote:BookxNote
  15. 日本超人气洛比(Robi)声控机器人
  16. Infor SyteLine ERP 客户端使用设置
  17. OpenCV每日函数 特征检测和描述模块(5) KAZE类/AKAZE类(提取关键点和计算描述符)
  18. Node.js当中的ioredis设置timeout的问题
  19. Flutter 里的语法糖解析,知其所然方能潇洒舞剑,安卓开发面试题及答案
  20. 韦东山嵌入式教程第四篇Linux驱动基础知识学习笔记(1)——Hello驱动程序

热门文章

  1. Windows10 下同一局域网两台电脑互传文件及其取消
  2. 前端学习——html、css
  3. 若依前后端分离框架验证码的学习
  4. 【skLearn 练习】随机森林回归填补缺失值
  5. 二分查找、分治算法——汉诺塔问题
  6. USB设备驱动开发之扩展(利用USB虚拟总线驱动模拟USB摄像头)
  7. 无驱、代码自动植入:新一代软件保护锁——圣天诺LDK-HL
  8. java 递归习题训练,Java蓝桥杯——递归练习题:走台阶(偶数版)
  9. 最优性理论(无约束)
  10. 大数据Spark实战第七集 机器学习和数据处理