目录
- 1. ros安装
- 2. 注意注意
- 3. 创建工作空间与功能包
- 4. 话题
- 5. 服务
- 6. 参数服务Parameter Server (全局字典)
- 7. ros中坐标系管理系统
- 8. launch启动文件
- 9. 可视化工具介绍
- 10. ROS-Navigation安装
- 11. turtlebot仿真(利用karto)
- 12. turtlebot仿真(利用cartographer_ros替换karto)
- 13. move_base详解
- 14. ROSCostMap2D详解
- 15. cartographer_ros详解
- 16. 常用的指令
1. ros安装
- 安装过程参考https://www.cnblogs.com/letisl/p/11815191.html
- 安装wsl略,利用mobaxterm进行连接与可视化。
1.1. 添加源
sudo sh -c '. /etc/lsb-release && echo "deb http://mirrors.ustc.edu.cn/ros/ubuntu/ $DISTRIB_CODENAME main" > /etc/apt/sources.list.d/ros-latest.list'
1.2. 添加私钥
wget https://raw.githubusercontent.com/ros/rosdistro/master/ros.key -O - | sudo apt-key add -
打不开则:sudo apt-key adv --keyserver hkp://pool.sks-keyservers.net --recv-key 0xB01FA116
1.3. 更新软件列表
sudo apt-get update
,会报GPG错误,解决办法是添加公钥:sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys F42ED6FBAB17C654
1.4. 安装ros
sudo apt-get install ros-melodic-desktop-full
安装目录在/opt/ros下。
1.5. 初始化
sudo rosdep init
rosdep update
1.5.1. 问题1
提示:sudo rosdep:找不到命令提示
,执行下面指令:sudo apt install python-rosdep
1.5.2. 问题2
提示:ERROR: cannot download default sources list from: https://raw.githubusercontent.com/ros/rosdistro/master/rosdep/sources.list.d/20-default.list Website may be down.
- 解决办法1:
打开https://site.ip138.com/raw.Githubusercontent.com/
,输入raw.githubusercontent.com查询出一个可用的ip地址,然后在/etc/hosts文件最后添加:199.232.28.133 raw.githubusercontent.com
151.101.228.133 raw.github.com
结果可能不行,此时参考方法2. - 解决办法2:
a.利用代理手段*(可以直接拷贝此处的链接),打开链接https://raw.githubusercontent.com/ros/rosdistro/master/rosdep/sources.list.d/20-default.list,拷贝其中的内容:#os-specific listings first
yaml https://raw.githubusercontent.com/ros/rosdistro/master/rosdep/osx-homebrew.yaml osx
# generic
yaml https://raw.githubusercontent.com/ros/rosdistro/master/rosdep/base.yaml
yaml https://raw.githubusercontent.com/ros/rosdistro/master/rosdep/python.yaml
yaml https://raw.githubusercontent.com/ros/rosdistro/master/rosdep/ruby.yaml
gbpdistro https://raw.githubusercontent.com/ros/rosdistro/master/releases/fuerte.yaml fuerte
# newer distributions (Groovy, Hydro, ...) must not be listed anymore, they are being fetched from the rosdistro index.yaml instead
b.创建目录,新建文件20-default.list,将内容拷贝到文件中sudo mkdir -p /etc/ros/rosdep/sources.list.d
touch /etc/ros/rosdep/sources.list.d/20-default.list
c.执行rosdep update
1.6. 环境变量配置
放到~/.bashrc文件最后source /opt/ros/melodic/setup.bash
然后执行source ~/.bashrc
1.7. 安装rosinstall
sudo apt-get install python-rosinstall python-rosinstall-generator python-wstool build-essential
安装ros插件sudo apt install ros-melodic-jsk-rviz-plugins
1.8. 测试小乌龟
- 启动roscore
roscore &
rosrun turtlesim turtlesim_node
1.9. 测试rviz
rosrun rviz rviz
,发现报段错误,需要将export LIBGL_ALWAYS_INDIRECT=
添加到.bashrc文件最后。
1.10. 卸载ros
sudo apt-get remove ros-melodic-*
2. 注意注意
建议启动新的节点时,将roscore重启一下,防止莫名其妙的错误!!
3. 创建工作空间与功能包
ros的工作空间是我们开发ros项目的一个工作目录
- src:代码空间,包含代码、lanuch文件、配置文件等等,是源码所在的目录;
- build:编译空间,包含编译过程中产生的中间文件;
- devel:开发空间,编译生成的可执行文件、库等等;
- install:安装空间,用install命令安装的文件放置的位置(开发基本用不到)。
3.1. 创建工作空间
mkdir -p ~/catkin_ws/src
cd ~/catkin_ws/src
catkin_init_workspace
3.2. 编译工作空间
cd ~/catkin_ws
catkin_make
或catkin_make install
,后者会将可执行文件放入install文件夹
3.3. 设置环境变量
在.bashrc最后添加:source ~/catkin_ws/devel/setup.bash
source ~/.bashrc
3.4. 检查环境变量
echo $ROS_PACKAGE_PATH
3.5. 创建功能包
功能包是放置代码的最小单元cd ~/catkin_ws/src
catkin_create_pkg test_pkg std_msgs roscpp
(catkin_create_pkg<功能包名>[依赖1][依赖2])
3.6. 编译功能包
cd ~/catkin_ws
catkin_make
3.7. 编译指定的功能包
- 当src下包比较多时,编译检查会非常慢,可以在某个包内创建文件夹
CATKIN_IGNORE
,此时该包会被跳过。 - 利用指令:
catkin_make -DCATKIN_WHITELIST_PACKAGES="源码存放的文件夹名字"
来编译指定的功能包,执行该指令后,再执行catkin_make还会编译那个包,如果编译所有包则执行catkin_make -DCATKIN_WHITELIST_PACKAGES=""
。
4. 话题
4.1. 发布与订阅
- 容易理解的是,话题是节点与节点之前通信的一种方式,(服务是节点之间通信的另一种方式)
- 话题与服务的区别是:
节点只管投递,数据可能会丢失;
服务会有request和response,相当于RPC调用。
话题用.msg文件定义数据,服务用.srv文件定义数据 - 代码示例:
1、pos_subscriber和velocity_publisher是订阅和发布ros系统已有的一个话题;利用advertise()
创建发布者对象。
2、person_pubsher和person_subscriber是自定义了一个话题来进行测试。利用subscribe()
创建订阅者对象。
4.2. 自定义话题消息
- 创建Xxx.msg文件,和编程语言无关,建议放到src同级目录,统一叫msg的文件夹中,首字母大写
- 在package.xml中添加创建message的功能包依赖
<build_export_depend>message_generation</build_export_depend>
<exec_depend>message_runtime</exec_depend>
- CMakeList.txt中添加编译选项(针对创建message的依赖包)
find_package(... message_generation)
add_message_files(FILES Xxx.msg)
generate_messages(DEPENDENCIES std_msgs)
catkin_package(...message_runtime)
- 编译,生成消息对应的.h文件
5. 服务
5.1. 客户端与服务
-
客户端:参考test_service包中turtle_spawn.cpp,其中会向服务/spawn发送请求数据,并显示应答信息。利用
serviceClient()
创建通信对象。
也可以参考test_param包中param_config.cpp,直接调用call()
接口。 -
服务端:参考test_service包中的turtle_command_srv.cpp,其中自定义服务对象,接收一个系统定义格式的数据,收到数据后进行处理。利用
advertiseService()
创建服务对象。使用时可以通过rosservice
指令查看当前的服务信息,rosservice call
来传递消息给服务.
5.2. 自定义服务数据
- 创建Xxx.srv文件,文件中
---
分割线表示上面是请求request的数据格式,下面是response响应的数据格式。 - 在package.xml中添加创建message的功能包依赖(同上)
- CMakeList.txt中添加编译选项(针对创建srv的依赖包)(同上),改为add_service_files
- 编译,生成消息对应的.h文件
6. 参数服务Parameter Server (全局字典)
使用命令rosparam操作变量,参考上面。ROS用的是YAML格式文件。
参考test_param包。
7. ros中坐标系管理系统
【 原理 】 : 通过tf包的发布功能,可以发布一组坐标之间的转换关系(平移+旋转), 构建一个树形结构,然后通过lookupTransform()接口可以获取任意两个坐标之间的转换关系,不用自己再计算了,强大!!
即通过tf广播器广播坐标映射关系,通过tf监听器得到任意两个坐标系之间的关系
7.1. 代码解释
- 创建坐标映射
static tf::TransformBroadcaster tfb;
tfb.sendTransform(tf::StampedTransform(tr, ros::Time::now(), "world", strTurtlename));
- 获取坐标映射
tf::TransformListener listener;
tf::StampedTransform tr;
listener.waitForTransform("/turtle2", "/turtle1", ros::Time(0), ros::Duration(3.0)); //从当前时间开始,等待一段时间3s,没有数据应该会异常(猜的),有数据返回
listener.lookupTransform("/turtle2", "/turtle1", ros::Time(0), tr); //获取转换关系
7.2. 相关指令
- rosrun rqt_tf_tree rqt_tf_tree :显示tf关系图
- rosrun tf view_frames : tf功能包中的一个工具,监听一段时间,5s内所有坐标系之间的关系保存成pdf文件。其中展示tf树,表示有多少个坐标系及他们之间的关系。
-
rosrun tf tf_echo 坐标系1 坐标系2 : 实时打印两个坐标系之间的关系:
Translation: [-0.000, -0.000, 0.000]
表示坐标系之间的平移向量Rotation: in Quaternion [0.000, 0.000, 0.599, 0.801]
表示坐标系之间的旋转向量,本行是四元数方式[x,y,z,w]in RPY (radian) [0.000, -0.000, 1.284]
弧度表示,表示每个轴旋转的角度in RPY (degree) [0.000, -0.000, 73.549]
度表示,表示每个轴旋转的角度 - 可视化工具rviz:rosrun rviz rviz -d
rospack find turtle_tf
/rviz/turtle_rviz.rviz - 用
rosmsg info tf2_msgs/TFMessage
查看消息内容:
geometry_msgs/TransformStamped[] transforms
std_msgs/Header header
uint32 seq
time stamp
string frame_id
string child_frame_id #子坐标系
geometry_msgs/Transform transform
geometry_msgs/Vector3 translation
float64 x
float64 y
float64 z
geometry_msgs/Quaternion rotation
float64 x
float64 y
float64 z
float64 w
7.3. ROS基本坐标系理解:map, odom, base_link, base_footprint, base_laser等等
参考链接https://blog.csdn.net/weixin_46181372/article/details/109694300
- map
地图坐标系,顾名思义,一般设该坐标系为固定坐标系,一般与机器人所在的世界坐标是重合的。 - base_link
机器人本体坐标系,与机器人中心重合。 - base_footprint
为base_link原点在地面的投影,和base_link只是z值不同。 - odom
里程计坐标系,这里主要区分odom topic,这是两个概念,一个是坐标系,一个是根据编码里程计计算得到的里程计信息。但是两者也有关系,odom topic转化得到的位姿矩阵是odom->base_link的tf关系。
两个问题:
①位姿矩阵得到的不应该是map->base_link的tf关系吗?map坐标系与odom坐标系又有什么关系?
其实机器人刚开始运动时,odom坐标系与map坐标系是重合的,即odom->base_link与map->base_link的tf是一致的。
但是随着时间的推移,出现了偏差,出现的偏差也就是里程计的累计误差,如果里程计精确度很高,没有计算误差,那么map坐标系与odom坐标系会一直是重合的。
然而实际情况中,里程计是有偏差的,所以我们计算里程计得到的位姿矩阵是odom->base_link,与map->base_link还是有偏差的。
②那么map->odom的tf是怎么得到的呢?
除去里程计还有一些合作校正的传感器例如imu,这些传感器可以估计出机器人在地图中的位置也就是map->base_link的tf,我们估计机器人在地图中的位置和解算里程计得到的位置的偏差也就是map->odom的tf。
- base_laser
激光雷达坐标系,与激光雷达的安装点有关,它与base_link的tf是固定的。
8. launch启动文件
任何包含两个或两个以上节点的系统都可以利用启动文件来指定和配置需要使用的节点。通常的命名方案是以.launch作为启动文件的后缀,启动文件是XML文件。一般把启动文件存储在取名为launch的目录中。
8.1. 指令
roslaunch [options] [package] <filename> [arg_name:=value...]
roslaunch [options] <filename> [<filename>...] [arg_name:=value...]
options: 未知
package: 包名
filename: launch文件
arg_name:=value : arg_name参数名,可覆盖launch文件中的指定para值
8.2. 节点说明
- <launch>
根元素,launch文件中的根元素采用标签定义,文件中的其他内容都必须包含在这个标签之中。 - <node>
<node pkg=”package-name” type=”executable-name” name=”node-name”/>
pkg是节点所在的功能包名称,type时节点的可执行文件名称,这两个是rosrun时输入的两个参数。name定义节点运行的名称,将覆盖节点中ros::init()赋予节点的名称。这三个是常用的,还有下面的:output = “screen”:将节点的标准输出打印到终端屏幕,默认输出为日志文件,该日志文件的名称是:
~/.ros/log/run_id/node_name-number-stout.log
其中,run_id 是节点管理器(master)启动时生成的一个唯一标示符;
respawn = “true”:复位属性,该节点停止时,会自动重启,默认为false;
required = “true”:必要节点,当该节点终止时,launch文件中的其他节点也被终止;
ns = “namespace”:命名空间,为节点内的相对名称添加命名空间前缀,同一个launch文件中,允许不同namespace中出现相同的node names;
args = “arguments”:节点运行时输入的参数。
- <param>
<param name="sensor_timeout" value="1.0"/>
launch文件执行后,这些param就加载到ROS的参数服务器上了,可以通过ros::param::get()接口来获取。 - <arg>
类似于launch文件内部的局部变量,仅限于launch文件使用。<arg name="open_rviz" default="true"/>
<arg name="model" value="$(arg model)"
其中default可以在roslaunch指令中重新覆盖。
使用方式:<paramname="foo" value="$(argarg-name)" />
- <remap>
重映射相当于换名,每次重映射需提供一个original name和一个new name。每次node使用它的original name, ROS都会将其替换为remapping name。<remap from=”original_name” to “new_name”>
例如:<remap from =”turtle1/pose” to “tim”>
- <rosparam>
当参数的数量很多时,一个一个设置非常麻烦,此时<rosparam>可以帮助我们将一个yaml格式文件中的参数全部加载到ROS参数服务器中,需要设置command属性为“load”,还可以选择设置命名空间“ns”。 例如:<rosparam command="load" file="$(find turtlebot3_slam)/config/karto_mapper_params.yaml" />
- <include>
可以使用标签包含其他launch文件,这和C语言中的include几乎是一样的。例如<include file="$(find turtlebot3_bringup)/launch/turtlebot3_remote.launch" />
9. 可视化工具介绍
10. ROS-Navigation安装
官方链接:http://wiki.ros.org/navigation
10.1. 安装方式
注意,如果通过turtlebot来仿真,当用apt-get安装时它会自动下载导航包。这里因为可能会研究源码,利用源码进行安装。
方式一,通过apt-get安装编译好的包;
方式二,通过源码编译安装–推荐。会安装如下包:
navigation (metapackage) map_server amcl fake_localization voxel_grid costmap_2d nav_core base_local_planner carrot_planner clear_costmap_recovery dwa_local_planner move_slow_and_clear navfn global_planner rotate_recovery move_base
10.2. 源码安装
- 下载地址
https://github.com/ros-planning/navigation/tree/melodic-devel
- 拷贝到
~/catkin_ws/src
下面 - 执行安装
cd ~/catkin_ws/
catkin_make
编译报错“Could NOT find SDL”,执行指令sudo apt-get install libsdl1.2-dev
编译报错“Could NOT find SDL_image”,执行指令sudo apt-get install libsdl-image1.2-dev
编译报错“Could NOT find tf2_sensor_msgs”,需要下载该包进行编译,地址https://github.com/ros/geometry2/tree/melodic-devel,拷贝到~/catkin_ws/src
下面继续安装。
编译报错“Could NOT find move_base_msgs”,需下载该包进行编译,地址https://github.com/ros-planning/navigation_msgs,拷贝到~/catkin_ws/src
下面继续安装。
编译报错“mmap: Cannot allocate memory”,(这是因为编译的时候开了6个进程,最后申请的虚拟内存爆了)方法一重新编译即可,方法二echo 1 > /proc/sys/vm/overcommit_memory
,表示内核允许分配所有的物理内存,而不管当前的内存状态如何。
10.3. 安装teb_local_planner插件
传统的navigation里的路径规划策略性能不佳。在尝试了teb_local_planner后,发现机器人导航性能得到非常大的提升。
为了编译时不再检查前面装的包,可以在前面包源码目录下新建CATKIN_IGNORE文件夹。
-
拷贝到
~/catkin_ws/src
,执行catkin_make"
进行编译。
a. 编译报错“Could NOT find costmap_converter”,下载链接https://github.com/rst-tu-dortmund/costmap_converter,拷贝到~/catkin_ws/src
下面继续安装。
b. 编译报错“Could NOT find mbf_costmap_core”,下载链接https://github.com/magazino/move_base_flex/tree/melodic,拷贝到~/catkin_ws/src
下面继续安装。
c. 编译报错“unable to find SuiteSparse”,执行sudo apt-get install libsuitesparse-dev
。
e. 编译报错“Could not find libg2o!”,这里选择源码安装libg2o图优化库:首先下载该库:https://github.com/RainerKuemmerle/g2o
安装依赖:(cmake已经安装)sudo apt-get install libeigen3-dev libeigen3-doc
编译:cd g2o-master
mkdir build
cd build
cmake ../
make && make install
f.继续编译
catkin_make
-
注意,teb_local_planner源码中关于plugin的配置文件均已写好,直接编译源码就能完成plugin的注册及插入,非常方便。
10.4. 安装cartographer_ros模块
参考链接https://google-cartographer-ros.readthedocs.io/en/latest/compilation.html
10.4.1. 安装wstool和rosdep以及Ninjia
sudo apt-get update
sudo apt-get install -y python3-wstool python3-rosdep ninja-build stow
这里会报错缺少依赖包,因为之前装的都是python2的包,按照提示添加上依赖包后会导致ROS重新安装(我擦),所以用下面的指令代替:sudo apt-get install -y python-wstool python-rosdep ninja-build stow
10.4.2. 安装cartographer_ros
mkdir catkin_ws
cd catkin_ws
wstool init src
wstool merge -t src https://raw.githubusercontent.com/cartographer-project/cartographer_ros/master/cartographer_ros.rosinstall
wstool update -t src
这里最后一步速度很慢,对hosts文件进行如下配置,然后重启网卡(结果不太明显):
#解决git clone 速度慢的问题
192.30.253.112 github.com
151.101.185.194 github.global.ssl.fastly.net
#解决浏览器下载master-zip包的问题
192.30.253.120 codeload.github.com
#解决github不能查看图片的问题
199.232.68.133 raw.githubusercontent.com
10.4.3. 安装依赖
rosdep update
rosdep install --from-paths src --ignore-src --rosdistro=${ROS_DISTRO} -y
发现依赖了下面的包:libatlas3-base libgflags2.2 libgoogle-glog0v5 libceres1 libgflags-dev libgoogle-glog-dev libceres-dev
10.4.4. 手动安装abseil-cpp library
src/cartographer/scripts/install_abseil.sh
执行后下载了https://github.com/abseil/abseil-cpp.git,并编译该库。该库是google开源的内部通用库。
10.4.5. 构建cartographer_ros
catkin_make_isolated --install --use-ninja
source install_isolated/setup.bash
编译构建过程中可能出现内存不足的问题,参考上面ROS-Navigation解决办法。
11. turtlebot仿真(利用karto)
参考链接
https://blog.csdn.net/qq_40078576/article/details/104773668
https://blog.csdn.net/lingchen2348/article/details/79503970
11.1. 安装Gazebo仿真软件
安装ros时已经安装。
11.2. 下载Gazobo的模型包
不需要再下载模型包了,turtlebot包会加载出模型。
11.3. 安装slam建图包
turtlebot3支持gmapping、cartographer、hector、karto、frontier_exploration。
11.3.1. karto安装
sudo apt-get install ros-melodic-slam-karto
11.3.2. 安装cartographer_turtlebot(待完善TODO)
注意这里不是cartographer_ros,参考链接https://www.ncnynl.com/archives/201801/2230.html
11.4. 安装turtlebot相关包
sudo apt-get install ros-melodic-turtlebot-*
11.5. 加载环境
roslaunch turtlebot3_gazebo turtlebot3_world.launch
报错:‘TURTLEBOT3_MODEL’ is not set。表示TURTLEBOT3_MODEL没有设置, 需要给它指定[burger, waffle, waffle_pi]中的一个, 我们随便指定一个:export TURTLEBOT3_MODEL=burger
(可以写入.bashrc文件),重新执行命令。
11.6. 启动键盘遥控节点
可以遥控小车行走。新建终端,roslaunch turtlebot3_teleop turtlebot3_teleop_key.launch
11.7. 建图
这里选择了karto。新建终端,roslaunch turtlebot3_slam turtlebot3_slam.launch slam_methods:=karto
11.8. 保存地图
控制小车扫描完地图后,保存地图:mkdir -p ~/maps/turtlebot3
rosrun map_server map_saver -f ~/maps/turtlebot3/map1
11.9. 导航
利用刚才扫描的地图进行导航控制。关闭建图终端,关闭手动键盘控制后:roslaunch turtlebot3_navigation turtlebot3_navigation.launch map_file:=~/maps/turtlebot3/map1.yaml
记录本机地址:roslaunch turtlebot3_navigation turtlebot3_navigation.launch map_file:=/mnt/e/wsl/maps/turtlebot3/map1.yaml
接下来首先要做的是调整RViz中turtlebot在地图中的位姿,使其与Gazebo中turtlebot在环境中的位姿一致。做法是用鼠标选中RViz工具栏中的“2D Pose Estimate”,然后根据Gazebo中tuetlebot的位姿点击RViz地图中的相应位置,注意姿态也要相同。
接下来我们就开始导航了,鼠标选择工具栏中的“2D Nav Goal”,在地图中任意设置一个目标位姿,turtlebot便会自动规划路径,然后慢悠悠地移动过去。
11.10. 分析
11.10.1. 建图分析
11.10.1.1. 建图启动文件
建图的指令中用到了turtlebot3_slam.launch文件
<launch>
2 <!-- Arguments -->
3 <arg name="model" default="$(env TURTLEBOT3_MODEL)" doc="model type [burger, waffle, waffle_pi]"/>
4 <arg name="slam_methods" default="gmapping" doc="slam type [gmapping, cartographer, hector, karto, frontier _exploration]"/>
5 <arg name="configuration_basename" default="turtlebot3_lds_2d.lua"/>
6 <arg name="open_rviz" default="true"/>
7
8 <!-- TurtleBot3 -->
9 <include file="$(find turtlebot3_bringup)/launch/turtlebot3_remote.launch">
10 <arg name="model" value="$(arg model)" />
11 </include>
12
13 <!-- SLAM: Gmapping, Cartographer, Hector, Karto, Frontier_exploration, RTAB-Map -->
14 <include file="$(find turtlebot3_slam)/launch/turtlebot3_$(arg slam_methods).launch">
15 <arg name="model" value="$(arg model)"/>
16 <arg name="configuration_basename" value="$(arg configuration_basename)"/>
17 </include>
18
19 <!-- rviz -->
20 <group if="$(arg open_rviz)">
21 <node pkg="rviz" type="rviz" name="rviz" required="true"
22 args="-d $(find turtlebot3_slam)/rviz/turtlebot3_$(arg slam_methods).rviz"/>
23 </group>
24 </launch>
可以看到默认的slam_methods是gmapping,我们传入的是karto。有三大部分:TurtleBot3、SLAM和rviz。
TurtleBot3加载了turtlebot3_remote.launch文件,对应了/robot_state_publisher节点,SLAM加载了turtlebot3_karto.launch,对应了/slam_karto节点,打开了rviz软件。
11.10.1.2. 建图节点视图
执行rosrun rqt_graph rqt_graph
如图所示:[(./img/0.png)]
我们通过rosnode info /slam_karto
查看到/slam_karto发布的主题/map是nav_msgs/OccupancyGrid类型,可以供保存地图使用。
11.10.2. 保存地图
保存地图使用的指令:rosrun map_server map_saver -f ~/maps/turtlebot3/map1
这里利用了map_server的命令行功能,从/map
中接收了[nav_msgs/OccupancyGrid]
类型的数据,可以通过rosmsg show nav_msgs/OccupancyGrid
查看数据的格式,并把它写到map1.pgm和map1.yaml中。.pgm
说明如下:
- 该图像描述了相应像素的颜色中的世界的每个单元的占用状态。白色像素是*的,黑色像素被占据,并且两者之间的像素是未知的。接受彩色和灰度图像,但大多数地图都是灰色的(即使它们可能存储为彩色)。YAML文件中的阈值用于划分三个类别; 阈值在map_server内部完成。
- 当与阈值参数比较时,图像像素的占用概率计算如下:occ =(255-color_avg)/ 255.0其中color_avg是通过在所有通道上求平均值而得到的8位值,例如,如果图像是24位颜色,具有颜色0x0a0a0a的像素具有0.96的概率,这是非常占用的。颜色0xeeeeee产生0.07,这是非常空的。
- 当通过ROS消息通信时,占用被表示为范围[0,100]中的整数,0意味着完全*,100意味着完全被占用,并且特殊值-1用于完全未知。
.yaml
格式如下:
image:包含占用数据的图像文件的路径; 可以是绝对的,或相对于YAML文件的位置
resolution:地图的分辨率,米/像素
origin:地图中左下像素的2-D姿态为(x,y,yaw),偏航为逆时针旋转(yaw = 0表示无旋转)。系统的许多部分目前忽略偏航。通过该值可以计算出地图的(0,0)点。
occupancy_thresh:占据概率大于该阈值的像素被认为完全占用。
free_thresh:占有概率小于该阈值的像素被认为是完全*的。
negate:白/黑*/占用语义是否应该被反转(阈值的解释不受影响)
其余具体参考/map_server章节。
11.10.3. 导航分析
11.10.3.1. 启动文件
导航用的是turtlebot3_navigation.launch文件:
<launch>
<!-- Arguments -->
<arg name="model" default="$(env TURTLEBOT3_MODEL)" doc="model type [burger, waffle, waffle_pi]"/> <!--模型-->
<arg name="map_file" default="$(find turtlebot3_navigation)/maps/map.yaml"/> <!--地图参数-->
<arg name="open_rviz" default="true"/>
<arg name="move_forward_only" default="false"/>
<!-- Turtlebot3 -->
<include file="$(find turtlebot3_bringup)/launch/turtlebot3_remote.launch">
<arg name="model" value="$(arg model)" />
</include>
<!-- Map server -->
<node pkg="map_server" name="map_server" type="map_server" args="$(arg map_file)"/>
<!-- AMCL -->
<include file="$(find turtlebot3_navigation)/launch/amcl.launch"/>
<!-- move_base -->
<include file="$(find turtlebot3_navigation)/launch/move_base.launch">
<arg name="model" value="$(arg model)" />
<arg name="move_forward_only" value="$(arg move_forward_only)"/>
</include>
<!-- rviz -->
<group if="$(arg open_rviz)">
<node pkg="rviz" type="rviz" name="rviz" required="true"
args="-d $(find turtlebot3_navigation)/rviz/turtlebot3_navigation.rviz"/>
</group>
</launch>
11.10.3.2. 节点视图
如图所示:
[(./img/1.png)]
11.10.3.3. /tf和/tf_static
/tf是只要有节点发布就会有消息;
/tf_static是在已知的坐标发生变化或有新的节点订阅的时候才会发布新的消息。(未验证)
11.10.3.4. /gazebo
-
发布的消息队列
-
/scan [sensor_msgs/LaserScan] 一帧激光数据
参考http://docs.ros.org/en/api/sensor_msgs/html/msg/LaserScan.html
std_msgs/Header header #头部数据 uint32 seq #序列号 time stamp #时间戳 string frame_id #扫描数据的名称 float32 angle_min #开始扫描的角度[rad] float32 angle_max #结束扫描的角度[rad] float32 angle_increment #测量之间的角度增量[rad] float32 time_increment #测量之间的时间增量[Sec] float32 scan_time #扫描之间的时间[Sec] float32 range_min #最小范围数值[m] float32 range_max #最大范围数值[m] float32[] ranges #范围内的数据[m] float32[] intensities #强度数据
sensor_msgs::LaserScan转pcl::PointCloud:参考https://www.guyuehome.com/12913
-
/odom [nav_msgs/Odometry] 里程计数据
里程计模型,参考链接https://zhuanlan.zhihu.com/p/218319127
std_msgs/Header header #头部数据 ... string child_frame_id geometry_msgs/PoseWithCovariance pose #位姿,包含两部分 geometry_msgs/Pose pose #Pose,位姿 ... float64[36] covariance #6*6协方差矩阵 geometry_msgs/TwistWithCovariance twist #线速度和角速度 协方差矩阵 geometry_msgs/Twist twist ... float64[36] covariance
协方差矩阵待完善
-
/joint_states [sensor_msgs/JointState]
关节状态
Header header string[] name #名称,是数组,对应多个关节 float64[] position #the position of the joint (rad or m), 关节位置??? float64[] velocity #the velocity of the joint (rad/s or m/s),关节速度 float64[] effort #the effort that is applied in the joint (Nm or N) 施加在关节上的力
这里name是
wheel_right_joint, wheel_left_joint
,关节位置应该是左右*的位置,但具体意义不明? -
/scan [sensor_msgs/LaserScan] 一帧激光数据
-
订阅的消息队列
- /cmd_vel [geometry_msgs/Twist]
线速度和角速度
geometry_msgs/Vector3 linear float64 x float64 y float64 z geometry_msgs/Vector3 angular float64 x float64 y float64 z
- /cmd_vel [geometry_msgs/Twist]
11.10.3.5. /robot_state_publisher
- /tf_static
11.10.3.6. /amcl
定位模块,这里不关注amcl用的粒子滤波算法,我们要将其替换为cartographer算法。
11.10.3.7. /map_server
map_server有两个功能:
- 当做map_server节点服务,它从磁盘加载静态地图数据作为一个ROS服务提供地图数据。
- 提供map_saver地图保存命令行功能,通过指令从
/map
中接收网格地图数据,然后保存成ROS专用格式的地图,参考上面的[保存地图]章节。
本节点发布话题/map给move_base用,具体格式参考上面的[保存地图]章节。
11.10.3.8. /move_base
参考链接https://www.pianshen.com/article/3044120500/
局部路径规划之DWAPlannerROS分析:https://www.cnblogs.com/sakabatou/p/8297479.html
参考<move_base详解>
12. turtlebot仿真(利用cartographer_ros替换karto)
参考链接
https://blog.csdn.net/qq_34935373/article/details/111991920
https://www.freesion.com/article/9794248452/
12.1. 官方纯定位Pure localization仿真测试
12.1.1. 下载数据集
wget -P mnt/e/wsl/cartographer_demo/pureLoc/ https://storage.googleapis.com/cartographer-public-data/bags/backpack_2d/b2-2016-04-05-14-44-52.bag
wget -P mnt/e/wsl/cartographer_demo/pureLoc/ https://storage.googleapis.com/cartographer-public-data/bags/backpack_2d/b2-2016-04-27-12-31-41.bag
第一个用于建图,第二个用于定位。
12.1.2. 建图
自己的路径:
roslaunch cartographer_ros offline_backpack_2d.launch bag_filenames:=/mnt/e/wsl/cartographer_demo/pureLoc/b2-2016-04-05-14-44-52.bag
生成.pbstream
的地图文件.
12.1.3. 定位
自己的路径:
roslaunch cartographer_ros demo_backpack_2d_localization.launch \
load_state_filename:=/mnt/e/wsl/cartographer_demo/pureLoc/b2-2016-04-05-14-44-52.bag.pbstream \
bag_filename:=/mnt/e/wsl/cartographer_demo/pureLoc/b2-2016-04-27-12-31-41.bag
这里第一个参数是用的地图,第二个参数是下载的数据集进行定位测试。
12.2. turtlebot3仿真建图
- 首先修改
/opt/ros/melodic/share/turtlebot3_slam/config/turtlebot3_lds_2d.lua
中的tracking_frame = "base_footprint"
- 然后执行:
roslaunch turtlebot3_slam turtlebot3_slam.launch slam_methods:=cartographer
- 保存地图,参考https://google-cartographer-ros.readthedocs.io/en/latest/assets_writer.html:
rosservice call /write_state /mnt/e/wsl/maps/cartographer_map/map.pbstream 1
写的地图数据可以通过assets_writer_backpack_3d.lua
来进行配置。
12.3. turtlebot3仿真导航
[关键]上个步骤中实际已经启动了move_base节点,可以一边扫图一边导航控制。具体可查看turtlebot3_cartographer.launch文件。
如果需要单独进行定位导航,那么需要修改cartographer_ros的源码,将那行代码注释掉,不然你纯定位过程建立的子图会覆盖在之前建好的地图上。但建图是需要的,所以得改一种传参的办法:
//cartographer_ros-master\cartographer_ros\cartographer_ros\occupancy_grid_node_main.cc:
//occupancy_grid_publisher_.publish(*msg_ptr); //注释掉
13. move_base详解
13.1. move_base配置参数解读
参考链接:https://blog.csdn.net/banzhuan133/article/details/90239252
https://wenku.baidu.com/view/1ae6d9a56729647d27284b73f242336c1fb9304d.html
https://blog.csdn.net/qq_29313679/article/details/106237063
在仿真中,查看turtlebot3_navigation/param/move_base.launch
,发现局部规划器用的是DWAPlannerROS
,同时加载了下面几个yaml文件:
costmap_common_params_$(arg model).yaml
local_costmap_params.yaml
global_costmap_params.yaml
move_base_params.yaml
dwa_local_planner_params_$(arg model).yaml
这里的(arg model)用的burger模型。
在rviz下,直接用鼠标选定2D navigation goal就可以指定目标了,但是,你可能会发现效果不太好,比如,疯狂原地打转,走S型,绕大圈等现象。这些都可以通过调参搞定。
重点看后两个move_base_params.yaml、dwa_local_planner_params_$(arg model).yaml文件。
move_base_params.yaml:
shutdown_costmaps: false #当move_base不在活动状态时,是否关掉costmap的加载。
controller_frequency: 10.0 #局部规划器执行频率,也是发送cmd_vel命令的频率。
planner_patience: 5.0 #全局规划器未规划出路线超过本时间,进行清理操作。
controller_patience: 15.0 #局部规划器未规划出路线超过本时间,进行清理操作。
conservative_reset_dist: 3.0 #在恢复模块执行后,重置的代价地图范围
planner_frequency: 5.0 #全局规划操作频率,如果为0,则仅在接收到新的目标点或局部路径堵塞时才重新规划路径。
oscillation_timeout: 10.0 #执行修复机制前,允许的震荡时长(秒)
oscillation_distance: 0.2 #来回运动在多大距离以上不会认为是震荡
recovery_behavior_enabled: true #是否使用恢复模块
dwa_local_planner_params.yaml:
DWAPlannerROS:
# Robot Configuration Parameters (机器人配置参数)
max_vel_x: 0.22 #最大x轴方向线速度
min_vel_x: -0.22 #最小x轴方向线速度
max_vel_y: 0.0
min_vel_y: 0.0
# The velocity when robot is moving in a straight line (机器人直线运行时的速度)
max_vel_trans: 0.22 #最大线速度
min_vel_trans: 0.11
max_vel_theta: 2.75 #最大角速度
min_vel_theta: 1.37
acc_lim_x: 2.5 #x轴方向最高加速度
acc_lim_y: 0.0
acc_lim_theta: 3.2
# Goal Tolerance Parametes (目标误差参数)
xy_goal_tolerance: 0.05 #路径规划到达点与目标点的距离允许偏差
yaw_goal_tolerance: 0.17 #路径规划到达点与目标点的角度允许偏差
latch_xy_goal_tolerance: false
# Forward Simulation Parameters(前向仿真参数)
sim_time: 1.5 #仿真时间,仿真距离=sim_time*vel
vx_samples: 20 #x轴方向速度采样数量,至少是1,否则会被强制设定为1
vy_samples: 0
vth_samples: 40
controller_frequency: 10.0 #发布控制速度的频率
# Trajectory Scoring Parameters(轨迹评分参数),系数影响的是本代价算法在最终代价中的一个比例
path_distance_bias: 32.0 #局部规划路径与全局路径保持一致的权重系数, path_costs和alignment_costs使用
goal_distance_bias: 20.0 #无论从什么路径以多大权重尝试到达目标点,增大后于全局规划路径一致性降低,goal_costs_和goal_front_costs_使用
occdist_scale: 0.02 #权衡机器人以多大的权重躲避障碍物。该值过大会导致机器人陷入困境,obstacle_costs_使用
forward_point_distance: 0.325 #将机器人与目标点连线并延长forward_point_distance距离作为一个评分点
stop_time_buffer: 0.2 #为防止碰撞,机器人必须提前停止的时间长度
scaling_speed: 0.25 #开始缩放机器人足迹时的速度的绝对值,单位为m/s
max_scaling_factor: 0.2 #最大缩放因子
# Oscillation Prevention Parameters(避免振荡参数)
oscillation_reset_dist: 0.05 #机器人运动多远距离才会重置振荡标记
# Debugging
publish_traj_pc : true
publish_cost_grid_pc: true #是否发布计算后的cost话题
13.2. 启动流程
move_base节点源文件位于/navigation-melodic-devel/move_base/move_base_node.cpp
,其调用了move_base.cpp
中的MoveBase的构造。主要的初始化过程也在这个构造中。大概内容如下:
- 构建MoveBaseActionServer,基于ROS的actionlib机制启动了一个action服务,主要作用是接收客户端发送的目标点,在回调中处理目标点并驱动AGV行走,回调函数是
executeCb
; - 各个参数的初始化,在上面的仿真中,很多默认参数已经被配置文件修改了,如上面的仿真中局部规划器被改为了DWA;
- 创建全局规划器线程;
- 创建全局地图和局部地图;
- 加载全局规划器、局部规划器的插件;
- 初始化状态机状态等等。
13.3. executeCb回调
该回调函数在收到目标点时会触发调用,收到新的目标点的话会通过ROS的actionlib机制进行处理,可以调用一些接口来判断,actionlib我们不关心,大概的流程如下:
- 通过一个信号量
planner_cond_
激发了全局规划器线程进行了规划, - 定义变量
ros::Rate r(controller_frequency_)
来控制下面while循环的频率,这个频率比较关键,最终体现在发送cmd_vel指令的频率上,我们称之为“控制频率”,程序中好多地方用到; - 中间过程不关心,调用了
executeCycle()
接口,来控制AGV行走,该接口会返回AGV是否到达目标点。
13.4. executeCycle接口
本函数的核心在状态CONTROLLING
处理逻辑中。通过调用dwa_planner_ros.cpp
中DWAPlannerROS
类的computeVelocityCommands()
接口来计算下发给AGV底盘的指令。这里AGV清除恢复CLEARING
及流程我们不关心。
13.5. computeVelocityCommands接口
DWA参考链接: http://gaoyichao.com/Xiaotu/?book=turtlebot&title=dwa_local_planner
在DWAPlannerROS
类中的本接口实际调用的是dwa_planner.cpp
中DWAPlanner
类的findBestPath()
接口,它又调用了SimpleScoredSamplingPlanner
接口中的findBestTrajectory()
,后者会通过SimpleTrajectoryGenerator
类创建出轨迹,并调用代价算法来计算轨迹得分。DWAPlanner
类中存储了7种代价计算方法,分别是:
- OscillationCostFunction oscillation_costs_ // 减少晃动
- ObstacleCostFunction obstacle_costs_ // 减少机器人装上障碍物
- MapGridCostFunction goal_front_costs_ // prefers trajectories that make the nose go towards (local) nose goal
- MapGridCostFunction alignment_costs_ // prefers trajectories that keep the robot nose on nose path
- MapGridCostFunction path_costs_ // 通过波传播的一种代价算法,更倾向于贴近全局轨迹
- MapGridCostFunction goal_costs_ // 通过波传播的一种代价算法,更倾向于全局路径和局部地图的交点
- TwirlingCostFunction twirling_costs_ // 减少原地打转
通过设定的比例系数,来对各个代价进行加权得出最后的代价数值。
13.5.1. SimpleTrajectoryGenerator轨迹创建
该类中主要的接口有两个:initialise
和generateTrajectory
。
- initialise接口首先根据<x,y,th>三个加速度,根据当前速度和“控制频率”时间,计算一个时间片后的最大和最小速度;然后根据设定的采样数量对三个速度方向进行<min_vec,max_vec>范围采样,得到一堆速度(turtlebot3仿真中的参数得到了800个速度);
- generateTrajectory接口,一个速度计算一段轨迹,轨迹中点的个数计算公式:
线速度仿真距离 = 线速度 * 采样时间;
角速度速度仿真距离 = 角速度速度 * 采样时间;
线速度方向个数 = 线速度仿真距离 / 线速度仿真间隔;
角速度方向个数 = 角速度仿真距离 / 角速度仿真间隔;
总数量 = max(线速度方向个数,角速度方向个数);
13.6. MapGridCostFunction代价计算
有四种代价都是通过本类实现的,这里只关注了两个主要的:贴近全局轨迹和贴近全局轨迹与局部地图的交点。
这个类主要的接口有两个:prepare()
和scoreTrajectory()
。
- prepare():其中调用的逻辑是由
MapGrid
类实现的,需要提前调用,生成代价数据表来加速代价查询。该函数有两个作用:
一个是提前算好局部网格地图(注意是网格地图)中每个网格点距离全局路径在局部地图中的单位长度,如图,其中0是全局路径占用的网格,其他网格数据通过一个扩散的算法实现的(不是欧式距离):
3 2 1 0 1 - -
3 2 1 0 1 2
2 1 0 1 1 2 -
1 0 1 2 - -
1 0 1 2 - - -
另一个是计算每个网格到全局路径和局部地图交点的单位长度,也是通过同样的扩散算法实现的:
- 2 1 0 1 2 -
- 3 2 1 2 3 -
- 4 3 2 3 4 -
- scoreTrajectory():这个函数很简单,就是直接查询代价表,来通过三种策略计算轨迹的代价值:
Last:默认是这个,只保留最后一个轨迹点,作为最终的代价;
Sum:代价累加;
Product:代价累乘。
14. ROSCostMap2D详解
15. cartographer_ros详解
16. 常用的指令
16.1. ros相关,rosxxx
-
roscore : 启动ros服务,一般是roscore &
-
rosnode : 查看节点信息,后可接指令:cleanup info kill list machine ping
rosnode info /xxx
: 查看节点详细信息,包括发布数据的队列、订阅数据的队列、服务、连接信息等等。 -
rosrun : 运行某个节点,(
rosrun 包 节点 [参数]
) -
rosmsg : rosmsg is a command-line tool for displaying information about ROS Message types.是一个显示ROS话题数据类型的命令工具
(rosmsg show MSG
显示指定的消息信息) -
rossrv :rossrv is a command-line tool for displaying information about ROS Service types.是一个显示ROS服务数据类型的命令工具
(rossrv show SRV
:展示SRV的具体格式信息)
(rossrv list
:展示所有的服务类型列表) -
rostopic : 话题操作指令
(rostopic list
打印topic列表)
(rostopic pub [-r 频率] /xxx话题 数据类型 数据
给指定的topic输入数据,这里数据类型+数据用tab键自动补全, 可选的-r是发送的频率,如果没有则只发送一次。)
(rostopic echo /xxx话题
打印话题数据) -
rosservice : 服务操作指令
(rosservice list
列出当前所有的服务)
(rosservice call /xxx "{}"
调用某个服务并传入某些参数,可tab给出参数); -
rosparam : 针对参数服务的操作指令。
(rosparam list
列出当前所有参数)
(rosparam get param_key
显示某个参数值)
(rosparam set param_key param_value
设置某个参数值)
(rosparam dump file_name
保存参数到文件)
(rosparam load file_name
从文件读取参数)
(rosparam delete param_key
删除参数) -
rosbag : 话题数据记录工具指令,记录当前所有话题的数据,来进行数据回放仿真用。
(rosbag record -a -O xxx文件
开始记录,保存到文件xxx中)
(rosbag play xxx文件
播放文件中的内容,重现) -
rospack : (
rospack find move_base
#寻找move_base包的位置)
16.2. catkin_相关
-
catkin_create_pkg :cd到src/目录下,执行"catkin_create_pkg 创建的包 依赖包",这里需要依赖包,不然不会生成src和include目录。
-
catkin_make : 进入工作空间的*目录运行该命令可以一次性编译该工作空间中的多个程序包,此时各程序包的命名空间是相互依赖的。
-
catkin_make_isolated : 不依赖的单独编译工作空间中的各个程序包。
-
catkin_make –source xx文件所在路径 : 编译程序包的源文件不在当前工作空间中的包。
16.3. 其他命令工具
- rosrun rqt_graph rqt_graph : 列出当前正在运行的节点node,以及它们之间的通讯关系。
- rosrun rqt_tf_tree rqt_tf_tree :显示tf关系图