ROS2 Ubuntu20.04基于ORB-SLAM3库搭建SLAM系统;基于Gazebo搭建移动机器人,并结合SLAM系统进行定位和建图仿真;进行运动规划及运动仿真;踩坑与总结。

kirco_567 2024-08-10 08:07:04 阅读 86

一、ROS环境配置

1.1 环境配置过程

ROS支持在Linux系统上安装部署,因为ROS与Ubuntu兼容性最好,使用它的首选开发平台是Ubuntu,并且Ubuntu版本和ROS版本要一致。

1.1.1 Ubuntu镜像文件下载

进入Ubuntu官网地址:https://ubuntu.com

点击download,然后点击Ubuntu desktop

进入下一个界面,往下滑,可以选择最新版本或者旧版本进行下载

若选择旧版本,点击之后进入下一界面,往下滑找到 past release,双击past release(个人建议是下载新版本)

选择自己想要的版本进行下载

下载完成

1.1.2 创建虚拟机

新建虚拟机 ,点击下一步

点击下一步

选择稍后安装驱动器 ,点击下一步

选择Linux,Ubuntu 64位,点击下一步

命名虚拟机(可选择默认,也可自行修改),选择安装位置(可选择默认,也可自行修改,建议不要安装在C盘,特别是个别C盘大神),点击下一步

按照默认就行(可根据需要自行修改,班级需要我改成2处理器,2内核),点击下一步

按照默认虚拟机内存(可根据需要自行修改,我这里是班级需要内存改到8GB以上,不然后期有大工程,会卡),点击下一步

默认选择“使用网络地址转换(NAT)”,点击下一步

选择LSI Logic,点击下一步

选择SCSI,点击下一步

选择创建新虚拟磁盘,点击下一步

修改磁盘大小(可选择默认或更大的,我建议改成80GB,以后存东西多了,完全不够用),选择将磁盘拆分为多个文件,点击下一步

选择磁盘文件存储位置,选择之前的存放路径下,点击下一步

选择自定义硬件

选择使用ISO映像文件,打开刚刚下载的镜像文件路径,点击关闭

点击完成

打开虚拟机 ,等待一会儿会出现如下界面,最左侧语言栏往下滑选择简体中文,然后点击安装Ubuntu

点击继续

去掉安装时更新,点击继续

 点击现在安装

 点击继续,将改动写入磁盘

点击继续

设置名字,密码,点击继续进行安装

安装中

安装完成,重启即可

1.1.3 在Ubuntu20.04系统中安装ROS

一 前言

新人笔者到目前为止,已经在不同版本的Ubuntu系统下反复安装了多次ROS软件包,考虑到每次重新安装ROS都需要在网络中查找大量的参考资料,非常麻烦,所以笔者决定结合自身过往的安装过程,将自身遇到的问题和对应的解决方法记录下来,不仅方便自己日后安装ROS,也能帮助其他尝试安装ROS的人少走一些弯路。具体安装流程如下所述,如果能够对各位看官老爷有所有所帮助的话,就麻烦各位看官老爷点赞、收藏加关注了QAQ。

二 配置Ubuntu系统软件源
2.1 安装环境

本文对应安装环境为DELL G16 7630 + Ubuntu 20.04.6

此时系统的软件源为默认的软件源,用户想要下载或更新软件就必须要访问国外的服务器,下载速度慢不说,甚至还有可能被拒绝访问,只有登录外网才能稳定下载软件和更新。幸好国内有许多业内顶尖团队开发出了高质量的免费镜像网站,通过它们我们就能够快速下载对应的软件以及更新。清华大学推出的开源镜像站:

清华大学开源软件镜像站 | Tsinghua Open Source Mirror

2.2 配置系统官方软件源

进入清华大学开源软件镜像站 | Tsinghua Open Source Mirror,在提供的镜像列表中找到ubuntu选项,点击那个问号,进入到使用帮助界面。

如上图所示,在该界面中调整Ubuntu的版本为Ubuntu20.04LTS(用户使用的系统版本),复制界面中的软件源配置内容进入系统终端,输入以下命令

<code># 复制原有的软件源配置文件到桌面

cp /etc/apt/sources.list ~/Desktop

# 编辑系统当前的软件源配置文件

sudo gedit /etc/apt/sources.list

将sources.list文件中的内容替换为上文中复制的软件源配置内容,保存,此时ubuntu系统的官方软件源已经替换为清华镜像网站,用户可以自由下载Ubuntu系统所需要的软件和更新。但如果想要下载ROS软件包,还需要额外添加第三方软件源。

2.3 配置第三方(ROS)软件源

还是进入到清华大学开源软件镜像站 | Tsinghua Open Source Mirror,在提供的镜像列表中找到ros选项,如上图所示,点击那个问号,进入到使用帮助界面。

如上图所示,在ROS软件仓库镜像使用帮助界面中调整系统版本为Ubuntu20.04LTS(用户使用的系统版本),复制界面中的软件源配置内容,不必理会界面中提示你输入的命令。

<code># 默认注释了源码镜像以提高 apt update 速度,如有需要可自行取消注释

deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ focal main restricted universe multiverse

# deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ focal main restricted universe multiverse

deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ focal-updates main restricted universe multiverse

# deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ focal-updates main restricted universe multiverse

deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ focal-backports main restricted universe multiverse

# deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ focal-backports main restricted universe multiverse

# deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ focal-security main restricted universe multiverse

# # deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ focal-security main restricted universe multiverse

deb http://security.ubuntu.com/ubuntu/ focal-security main restricted universe multiverse

# deb-src http://security.ubuntu.com/ubuntu/ focal-security main restricted universe multiverse

# 预发布软件源,不建议启用

# deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ focal-proposed main restricted universe multiverse

# # deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ focal-proposed main restricted universe multiverse

deb https://mirrors.tuna.tsinghua.edu.cn/ros/ubuntu/ focal main

在替换成功之后的sources.list文件末尾添加ROS的软件源地址,此时sources.list文件内容如上所示,保存文件并退出。

sudo touch /etc/apt/sources.list.d/ros-latest.list

sudo gedit /etc/apt/sources.list.d/ros-latest.list

# 在文件中黏贴之前复制的ROS源,保存并退出

如果不希望直接在Ubuntu系统官方软件源文件(sources.list)中混入第三方软件源(ROS),那可以在sources.list.d文件夹中新建一个ROS的软件源配置文件ros-latest.list,并将清华镜像站中的ROS软件源存储到该文件中,不必在sources.list文件末尾添加ROS软件源。具体指令过程如上所示。

sudo apt update

成功配置好系统软件源之后,在终端中键入以上命令,更新系统软件包索引。

三 安装ROS
3.1 配置公钥

#生成公钥

sudo apt-key adv --keyserver 'hkp://keyserver.ubuntu.com:80' --recv-key C1CF6E31E6BADE8868B172B4F42ED6FBAB17C654

# 更新软件包索引

sudo apt update

如果想要让系统安装ROS,就必须要先去公钥服务器生成公钥,更新系统的软件包索引,让系统信任ROS软件源,具体命令如上所示。如果成功生成公钥,终端内容如下所示。

user@user-Dell-3579:~/Desktop$ sudo apt-get update

Executing: /tmp/apt-key-gpghome.qZuk0VBa87/gpg.1.sh --keyserver hkp://keyserver.ubuntu.com:80 --recv-key C1CF6E31E6BADE8868B172B4F42ED6FBAB17C654

gpg: key F42ED6FBAB17C654: public key "Open Robotics <info@osrfoundation.org>" imported

gpg: Total number processed: 1

gpg: imported: 1

user@user-Dell-3579:~/Desktop$ sudo apt-get update

Hit:1 https://mirrors.tuna.tsinghua.edu.cn/ubuntu focal InRelease

Hit:2 https://mirrors.tuna.tsinghua.edu.cn/ubuntu focal-updates InRelease

Hit:3 https://mirrors.tuna.tsinghua.edu.cn/ubuntu focal-backports InRelease

Hit:4 http://security.ubuntu.com/ubuntu focal-security InRelease

Reading package lists... Done

3.2 ROS版本选取

ACMer笔者使用的系统为Ubuntu 20.04系统,对应的是ROS Noetic。ROS Noetic是ROS1中的最后一个版本,也是ROS1和ROS2之间的一个过渡版本。

3.3 二进制安装ROS

ROS软件包包含了许多的函数库和工具包,所以在二进制下载过程中,用户根据自身需要选择合适的方法下载ROS软件包,截至目前有四种不同的安装方式。

第一种 桌面完整版安装

<code>sudo apt install ros-noetic-desktop-full

使用该安装方式能够安装ROS及其附带的所有库,包含ROS、rqt、rviz、通用机器人函数库、2D/3D仿真器、导航以及2D/3D感知功能,安装时间会长一些,大概用了3、4个G,笔者就是使用上述命令安装的ROS桌面完整版。

第二种 桌面版安装

sudo apt install ros-noetic-desktop

使用该安装方式能够安装ROS、rqt、rviz、通用机器人函数库。

第三种 基础版安装

sudo apt install ros-noetic-ros-base

仅仅只包含ROS核心软件包、构建工具、通信相关的程序库,无GUI工具,不过安装速度很快,也不占用太大的空间

第四种 指定软件包安装

sudo apt install ros-noetic-ros-PACKAGE

使用该安装方式能够安装指定的ROS软件包,上述指令中的PACKAGE指代的是用户想要安装的软件包的名称。如果不太清楚软件包的具体名称,可以使用如下命令查找可用的ROS包。

apt-cache search ros-noetic

四 ROS相关配置
4.1 初始化rosdep

sudo apt install python3-rosdep

rosdep是ROS软件包运行的基础,一些ROS核心组件的运行都需要用到它,并且它还能在用户编译ROS源码时自动检查和安装对应的依赖。在终端输入以上命令,用户能够成功安装rosdep了。

sudo rosdep init && rosdep update

接下来我们需要初始化并更新rosdep,对应的终端指令如上,这一步经常会出现问题,网络中关于这个问题的解决方法也有很多,我之前也遇到过几次,但是不知道为什么我这次使用命令反而很顺利,一次性就成功了,成功时终端界面内容如下图所示。

user@user-Dell-3579:~/Desktop$ sudo rosdep init && rosdep update

Wrote /etc/ros/rosdep/sources.list.d/20-default.list

Recommended: please run

rosdep update

reading in sources list data from /etc/ros/rosdep/sources.list.d

Hit https://raw.githubusercontent.com/ros/rosdistro/master/rosdep/osx-homebrew.yaml

Hit https://raw.githubusercontent.com/ros/rosdistro/master/rosdep/base.yaml

Hit https://raw.githubusercontent.com/ros/rosdistro/master/rosdep/python.yaml

Hit https://raw.githubusercontent.com/ros/rosdistro/master/rosdep/ruby.yaml

Hit https://raw.githubusercontent.com/ros/rosdistro/master/releases/fuerte.yaml

Query rosdistro index https://raw.githubusercontent.com/ros/rosdistro/master/index-v4.yaml

Skip end-of-life distro "ardent"

Skip end-of-life distro "bouncy"

Skip end-of-life distro "crystal"

Skip end-of-life distro "dashing"

Skip end-of-life distro "eloquent"

Skip end-of-life distro "foxy"

Skip end-of-life distro "galactic"

Skip end-of-life distro "groovy"

Add distro "humble"

Skip end-of-life distro "hydro"

Skip end-of-life distro "indigo"

Add distro "iron"

Skip end-of-life distro "jade"

Skip end-of-life distro "kinetic"

Skip end-of-life distro "lunar"

Skip end-of-life distro "melodic"

Add distro "noetic"

Add distro "rolling"

updated cache in /home/fly/.ros/rosdep/sources.cache

如果各位在这一步出现了问题的话,可以看看文章末尾关于这个问题的解决方法。事实上,根据ACMer笔者自身的实际体验,即使不进行这一步也不会影响ROS的正常使用。

4.2 安装依赖工具包

sudo apt install python3-rosinstall python3-rosinstall-generator python3-wstool build-essential

截至目前,ROS 的核心程序包已经安装成功,但是为了更好地构建和管理开发者自己的 ROS 工作空间,我们需要再额外安装一些 ROS 依赖的工具包,比如rosinstall和wstool,具体安装指令如上所示。部分版本的ROS安装这些工具包时,可能需要将上述软件包名称中的python3替换为python。

4.3 配置ROS环境变量

此时ROS的安装工作都已经完成,但是如果想要在以普通用户的权限使用ROS就需要.bashrc内添加ROS相关的环境变量,否则会提示你找不到该命令

# 导入ROS环境变量

echo "source /opt/ros/noetic/setup.bash" >> ~/.bashrc

# 立即刷新终端当前的环境

source ~/.bashrc

在终端中输入以上命令,就能够彻底将ROS嵌入到系统中,第一行命令本质上是在 .bashrc 文件末尾添加 source /opt/ros/noetic/setup.bash这一行命令,以便每次启动终端时都会默认激活ROS。

至此,ROS的安装工作已经完成,为确保ROS安装过程中没有出现问题,我们需要对ROS进行一些简单的测试。

五 ROS测试
5.1 查看ROS版本

rosversion -d

在终端中输入以上命令,如果ROS成功安装,那么会显示系统当前安装的ROS版本(比如noetic)。

5.2 运行turtlesim

第一步,启动roscore

roscore

在终端中输入以上命令,如果ROS成功安装,那么终端会显示以下内容

... logging to /home/fly/.ros/log/209d0f14-2f6e-11ee-97dc-8787e7df2d53/roslaunch-fly-Dell-G16-7630-33464.log

Checking log directory for disk usage. This may take a while.

Press Ctrl-C to interrupt

Done checking log file disk usage. Usage is <1GB.

started roslaunch server http://fly-Dell-G16-7630:34131/

ros_comm version 1.16.0

SUMMARY

========

PARAMETERS

* /rosdistro: noetic

* /rosversion: 1.16.0

NODES

auto-starting new master

process[master]: started with pid [33477]

ROS_MASTER_URI=http://fly-Dell-G16-7630:11311/

setting /run_id to 209d0f14-2f6e-11ee-97dc-8787e7df2d53

process[rosout-1]: started with pid [33487]

started core service [/rosout]

第二步,运行turtlesim结点

rosrun turtlesim turtlesim_node

另开一个终端,在其中输入以上命令,正常情况下会显示以下信息。

[ INFO] [1690878977.127981832]: Starting turtlesim with node name /turtlesim

[ INFO] [1690878977.130397796]: Spawning turtle [turtle1] at x=[5.544445], y=[5.544445], theta=[0.000000]

与此同时,会弹出一个窗口,窗口如下所示,背景为蓝色,中心处是一个静止不动的海龟(海龟的类型可能不一样)。

第三步,运行turtlesim键盘控制结点

<code>rosrun turtlesim turtle_teleop_key

再开启一个终端,在其中输入以上命令,终端会显示以下信息。用户可在终端内输入方向键,控制窗口中的海龟向对应方向运动。

Reading from keyboard

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

Use arrow keys to move the turtle. 'q' to quit.

5.3 测试成功

如果上述步骤执行没有遇到任何问题,那么恭喜你,ROS已经安装完成,如果你在安装过程中遇到了什么问题的话,可以看看下一届的内容,其中包含了ACMer笔者之前遇到的一些问题以及对应的解决方法。

最后,写文章不易麻烦各位点点赞。

1.2 遇到的问题及原因及解决问题的方法

1.2.1 安装ROS的过程中出现了无法定位软件包的问题

user@user-Dell-3579:~/Desktop$ sudo apt install ros-noetic-desktop-full

Reading package lists... Done

Building dependency tree

Reading state information... Done

E: Unable to locate package ros-noetic-desktop-full

出现上述问题的原因是用户没有将ROS的软件源添加到系统的软件源配置文件中,想要解决该问题,可以参考本节2.3 配置第三方(ROS)软件源这一小节的内容,将ROS软件源加入到系统中。

1.2.2 初始化rosdep时出现错误

user@user-Dell-3579:~/Desktop$ sudo rosdep init && rosdep update

sudo: rosdep: command not found

上述问题的出现是因为没有安装rosdep,运行以下命令就能解决这个问题

sudo apt install python3-rosdep

有些文章可能会让你安装python-rosdep或者python3-rosdep2,如果是在ubuntu20.04系统安装ROS的用户可以直接忽略这些文章,前者已经被废弃了,后者千万不要安装,一旦安装就会删除掉大量的ROS库,想要恢复就只能重装(QVVQ)。

1.2.3 roscore运行出错

user@user-Dell-3579:~/Desktop$ roscore

Command ‘roscore’ not found,but can be installed with:

sudo apt install python-roslaunch

当你尝试使用roscore命令时,却突然报错并提示你安装python-roslaunch,此时你先去运行以下命令。

gedit ~/.bashrc

查看该文件中是否有这么一段话“source /opt/ros/noetic/setup.bash”,如果没有,请跳转到重新配置系统的环境变量;如果有这句话,但是被注释掉了,那么你可以在.bashrc文件中删除这句话开头的"#"号,保存并退出后重新开启一个终端,再次输入roscore指令进行测试。

如果.bashrc文件中有“source /opt/ros/noetic/setup.bash”,又或者在完成上述操作之后仍旧无法使用roscore,那么可以尝试运行如下命令

sudo apt install python-roslaunch

安装成功之后,再次开启一个终端,在这个新终端中输入roscore,查看是否能够成功运行。

1.2.4 rosdep update 报错

这个问题出现的主要原因在于请求的raw.githubusercontent.com网站在国内不能用了,如果不在科学上网的情况下直接使用该命令往往就会报timed out的问题。目前网络中的主流解决方法是修改 /etc/ros/rosdep/sources.list.d/20-default.list 的请求地址,关于这方面的相关指令可以参考这个网站中的内容。

1.2.5 正在等待缓存锁 报错

出现问题:

解决方法:

参考链接:https://blog.csdn.net/diaodaa/article/details/104516036

根据参考链接文章,依次输入

然后重新安装ros

完事!

二、仿真模型的建立

2.1 模型建立过程

2.1.1 车体建模

<code><robot name="mycar" xmlns:xacro="http://www.ros.org/wiki/xacro">code>

<xacro:property name="PI" value="3.141"/>code>

<material name="black">code>

<color rgba="0.0 0.0 0.0 1.0" />code>

</material>

<xacro:property name="footprint_radius" value="0.001" /> code>

<xacro:property name="base_radius" value="0.1" /> code>

<xacro:property name="base_length" value="0.08" /> code>

<xacro:property name="earth_space" value="0.015" /> code>

<xacro:property name="base_joint_z" value="${base_length / 2 + earth_space}" /> code>

<xacro:property name="base_mass" value="2" /> code>

<!-- base -->

<link name="base_footprint">code>

<visual>

<geometry>

<sphere radius="${footprint_radius}" />code>

</geometry>

</visual>

</link>

<link name="base_link">code>

<visual>

<geometry>

<cylinder radius="${base_radius}" length="${base_length}" />code>

</geometry>

<origin xyz="0 0 0" rpy="0 0 0" />code>

<material name="baselink_color">code>

<color rgba="1.0 0.5 0.2 0.7" />code>

</material>

</visual>

<collision>

<geometry>

<cylinder radius="${base_radius}" length="${base_length}" />code>

</geometry>

<origin xyz="0 0 0" rpy="0 0 0" />code>

</collision>

<xacro:cylinder_inertial_matrix m="${base_mass}" r="${base_radius}" h="${base_length}" />code>

</link>

<gazebo reference="base_link">code>

<material>Gazebo/Yellow</material>

</gazebo>

<joint name="link2footprint" type="fixed">code>

<parent link="base_footprint" />code>

<child link="base_link" />code>

<origin xyz="0 0 ${earth_space + base_length / 2 }" rpy="0 0 0"/>code>

</joint>

<!-- qudong wheel -->

<xacro:property name="wheel_radius" value="0.0325" />code>

<xacro:property name="wheel_length" value="0.015" />code>

<xacro:property name="wheel_mass" value="0.05" />code>

<xacro:macro name="add_wheels" params="name flag">code>

<link name="${name}_wheel">code>

<visual>

<geometry>

<cylinder radius="${wheel_radius}" length="${wheel_length}" />code>

</geometry>

<origin xyz="0.0 0.0 0.0" rpy="${PI / 2} 0.0 0.0" />code>

<material name="black" />code>

</visual>

<collision>

<geometry>

<cylinder radius="${wheel_radius}" length="${wheel_length}" />code>

</geometry>

<origin xyz="0.0 0.0 0.0" rpy="${PI / 2} 0.0 0.0" />code>

</collision>

<xacro:cylinder_inertial_matrix m="${wheel_mass}" r="${wheel_radius}" h="${wheel_length}" />code>

</link>

<gazebo reference="${name}_wheel">code>

<material>Gazebo/Red</material>

</gazebo>

<joint name="${name}_wheel2base_link" type="continuous">code>

<parent link="base_link" />code>

<child link="${name}_wheel" />code>

<origin xyz="0 ${flag * base_radius} ${-(earth_space + base_length / 2 - wheel_radius) }" />code>

<axis xyz="0 1 0" />code>

</joint>

</xacro:macro>

<xacro:add_wheels name="left" flag="1" />code>

<xacro:add_wheels name="right" flag="-1" />code>

<!-- zhicheng/wanxiang wheel -->

<xacro:property name="support_wheel_radius" value="0.0075" /> code>

<xacro:property name="support_wheel_mass" value="0.01" /> code>

<xacro:macro name="add_support_wheel" params="name flag" >code>

<link name="${name}_wheel">code>

<visual>

<geometry>

<sphere radius="${support_wheel_radius}" />code>

</geometry>

<origin xyz="0 0 0" rpy="0 0 0" />code>

<material name="black" />code>

</visual>

<collision>

<geometry>

<sphere radius="${support_wheel_radius}" />code>

</geometry>

<origin xyz="0 0 0" rpy="0 0 0" />code>

</collision>

<xacro:sphere_inertial_matrix m="${support_wheel_mass}" r="${support_wheel_radius}" />code>

</link>

<gazebo reference="${name}_wheel">code>

<material>Gazebo/Red</material>

</gazebo>

<joint name="${name}_wheel2base_link" type="continuous">code>

<parent link="base_link" />code>

<child link="${name}_wheel" />code>

<origin xyz="${flag * (base_radius - support_wheel_radius)} 0 ${-(base_length / 2 + earth_space / 2)}" />code>

<axis xyz="1 1 1" />code>

</joint>

</xacro:macro>

<xacro:add_support_wheel name="front" flag="1" />code>

<xacro:add_support_wheel name="back" flag="-1" />code>

</robot>这是什么语言代码

2.1.2 添加摄像头雷达传感器

一、在 Rviz 中显示一个盒状机器人

结果演示:

1.1 创建ROS功能包

找一个空闲地方,新建文件夹catkin_ws01,输入以下命令

导入依赖,在src目录下输入以下命令:

<code>catkin_create_pkg jubot_demo urdf xacro #创建功能包、添加依赖

cd jubot_demo/

mkdir urdf

mkdir launch

mkdir meshes #存放渲染机器人模型的文件

mkdir config #存放rviz配置的文件

urdf文件夹中添加一个box_urdf.urdf文件,复制如下内容:

<robot name="mycar">code>

<link name="base_link">code>

<visual>

<geometry>

<box size="0.5 0.2 0.1" />code>

</geometry>

</visual>

</link>

</robot>

1.2 在 launch 文件中集成 URDF 与 Rviz

在launch目录下,新建一个 box_launch.launch 文件,该 launch 文件需要启动 Rviz,并导入 urdf 文件,Rviz 启动后可以自动载入解析urdf文件,并显示机器人模型,核心问题:如何导入 urdf 文件? 在 ROS 中,可以将 urdf 文件的路径设置到参数服务器,使用的参数名是:robot_description,示例代码如下:

<launch>

<!-- 设置参数 -->

<param name="robot_description" textfile="$(find jubot_demo)/urdf/box_urdf.urdf" />code>

<!-- 启动 rviz -->

<node pkg="rviz" type="rviz" name="rviz" />code>

</launch>

1.3 在 Rviz 中显示机器人模型

先进行编译,再配置环境变量,然后启动rviz

rviz 启动后,会发现并没有盒装的机器人模型,这是因为默认情况下没有添加机器人显示组件,需要手动添加,添加方式如下。

完毕后,可以正常显示了

1.4 优化 rviz 启动

重复启动launch文件时,Rviz 之前的组件配置信息不会自动保存,需要重复执行步骤4的操作,为了方便使用,可以使用如下方式优化:

首先,将当前配置保存进config目录

然后,launch文件中 Rviz 的启动配置添加参数:args,值设置为-d 配置文件路径

<code><launch>

<param name="robot_description" textfile="$(find 包名)/urdf/urdf/urdf01_HelloWorld.urdf" />code>

<node pkg="rviz" type="rviz" name="rviz" args="-d $(find 报名)/config/rviz/show_mycar.rviz" />code>

</launch>

现在的launch文件如下:

<launch>

<!-- 设置参数 -->

<param name="robot_description" textfile="$(find jubot_demo)/urdf/box_urdf.urdf" />code>

<!-- 启动 rviz -->

<node pkg="rviz" type="rviz" name="rviz" />code>

<param name="robot_description" textfile="$find jubot_demo)/urdf/box_urdf.urdf" />code>

<node pkg="rviz" type="rviz" name="rviz" args="-d $(find jubot_demo)/config/rviz/show_mycar.rviz" />code>

</launch>

再启动时,就可以包含之前的组件配置了,使用更方便快捷

二、创建一个四轮圆柱状机器人模型

需求描述:(可改)

创建一个四轮圆柱状机器人模型,机器人参数如下,底盘为圆柱状,半径 10cm,高 8cm,四轮由两个驱动轮和两个万向支撑轮组成,两个驱动轮半径为 3.25cm,轮胎宽度1.5cm,两个万向轮为球状,半径 0.75cm,底盘离地间距为 1.5cm(与万向轮直径一致)

结果演示:

实现流程:

创建机器人模型可以分步骤实现

新建 urdf 文件,并与 launch 文件集成

搭建底盘

在底盘上添加两个驱动轮

在底盘上添加两个万向轮

2.1 配置urdf和launch文件

在一的urdf和launch文件基础上进行修改

box_urdf.urdf文件

<!-- <robot name="mycar">code>

<link name="base_link">code>

<visual>

<geometry>

<box size="0.5 0.2 0.1" />code>

</geometry>

</visual>

</link>

</robot> -->

<robot name="mycar">code>

<!-- 设置 base_footprint -->

<link name="base_footprint">code>

<visual>

<geometry>

<sphere radius="0.001" />code>

</geometry>

</visual>

</link>

<!-- 添加底盘 -->

<!--

参数

形状:圆柱

半径:10 cm

高度:8 cm

离地:1.5 cm

-->

<link name="base_link">code>

<visual>

<geometry>

<cylinder radius="0.1" length="0.08" />code>

</geometry>

<origin xyz="0 0 0" rpy="0 0 0" />code>

<material name="yellow">code>

<color rgba="0.8 0.3 0.1 0.5" />code>

</material>

</visual>

</link>

<joint name="base_link2base_footprint" type="fixed">code>

<parent link="base_footprint" />code>

<child link="base_link"/>code>

<origin xyz="0 0 0.055" />code>

</joint>

<!-- 添加驱动轮 -->

<!-- 添加驱动轮 -->

<!--

驱动轮是侧翻的圆柱

参数

半径: 3.25 cm

宽度: 1.5 cm

颜色: 黑色

关节设置:

x = 0

y = 底盘的半径 + 轮胎宽度 / 2

z = 离地间距 + 底盘长度 / 2 - 轮胎半径 = 1.5 + 4 - 3.25 = 2.25(cm)

axis = 0 1 0

-->

<link name="left_wheel">code>

<visual>

<geometry>

<cylinder radius="0.0325" length="0.015" />code>

</geometry>

<origin xyz="0 0 0" rpy="1.5705 0 0" />code>

<material name="black">code>

<color rgba="0.0 0.0 0.0 1.0" />code>

</material>

</visual>

</link>

<joint name="left_wheel2base_link" type="continuous">code>

<parent link="base_link" />code>

<child link="left_wheel" />code>

<origin xyz="0 0.1 -0.0225" />code>

<axis xyz="0 1 0" />code>

</joint>

<link name="right_wheel">code>

<visual>

<geometry>

<cylinder radius="0.0325" length="0.015" />code>

</geometry>

<origin xyz="0 0 0" rpy="1.5705 0 0" />code>

<material name="black">code>

<color rgba="0.0 0.0 0.0 1.0" />code>

</material>

</visual>

</link>

<joint name="right_wheel2base_link" type="continuous">code>

<parent link="base_link" />code>

<child link="right_wheel" />code>

<origin xyz="0 -0.1 -0.0225" />code>

<axis xyz="0 1 0" />code>

</joint>

<!-- 添加万向轮(支撑轮) -->

<!-- 添加万向轮(支撑轮) -->

<!--

参数

形状: 球体

半径: 0.75 cm

颜色: 黑色

关节设置:

x = 自定义(底盘半径 - 万向轮半径) = 0.1 - 0.0075 = 0.0925(cm)

y = 0

z = 底盘长度 / 2 + 离地间距 / 2 = 0.08 / 2 + 0.015 / 2 = 0.0475 axis= 1 1 1

-->

<link name="front_wheel">code>

<visual>

<geometry>

<sphere radius="0.0075" />code>

</geometry>

<origin xyz="0 0 0" rpy="0 0 0" />code>

<material name="black">code>

<color rgba="0.0 0.0 0.0 1.0" />code>

</material>

</visual>

</link>

<joint name="front_wheel2base_link" type="continuous">code>

<parent link="base_link" />code>

<child link="front_wheel" />code>

<origin xyz="0.0925 0 -0.0475" />code>

<axis xyz="1 1 1" />code>

</joint>

<link name="back_wheel">code>

<visual>

<geometry>

<sphere radius="0.0075" />code>

</geometry>

<origin xyz="0 0 0" rpy="0 0 0" />code>

<material name="black">code>

<color rgba="0.0 0.0 0.0 1.0" />code>

</material>

</visual>

</link>

<joint name="back_wheel2base_link" type="continuous">code>

<parent link="base_link" />code>

<child link="back_wheel" />code>

<origin xyz="-0.0925 0 -0.0475" />code>

<axis xyz="1 1 1" />code>

</joint>

</robot>

box_launch.launch文件

<launch>

<!-- 将 urdf 文件内容设置进参数服务器 -->

<param name="robot_description" textfile="$(find jubot_demo)/urdf/box_urdf.urdf" />code>

<!-- 启动 rviz -->

<!-- <node pkg="rviz" type="rviz" name="rviz" /> -->code>

<node pkg="rviz" type="rviz" name="rviz" args="-d $(find jubot_demo)/config/rviz/show_four_wheel_car.rviz" />code>

<!-- 启动机器人状态和关节状态发布节点 -->

<node pkg="robot_state_publisher" type="robot_state_publisher" name="robot_state_publisher" />code>

<node pkg="joint_state_publisher" type="joint_state_publisher" name="joint_state_publisher" />code>

<!-- 启动图形化的控制关节运动节点 -->

<node pkg="joint_state_publisher_gui" type="joint_state_publisher_gui" name="joint_state_publisher_gui" />code>

</launch>

然后启动roslaunch,在rviz中显示机器人模型:

2.2 添加摄像头和雷达传感器

结果演示:

实现分析:

机器人模型由多部件组成,可以将不同组件设置进单独文件,最终通过文件包含实现组件的拼装。

实现流程:

1.首先编写摄像头和雷达的 xacro 文件

2.然后再编写一个组合文件,组合底盘、摄像头与雷达

3.最后,通过 launch 文件启动 Rviz 并显示模型

摄像头和雷达 Xacro 文件实现

摄像头<code>my_camera.urdf.xacro 文件:

<!-- 摄像头相关的 xacro 文件 -->

<robot name="my_camera" xmlns:xacro="http://wiki.ros.org/xacro">code>

<!-- 摄像头属性 -->

<xacro:property name="camera_length" value="0.01" /> <!-- 摄像头长度(x) -->code>

<xacro:property name="camera_width" value="0.025" /> <!-- 摄像头宽度(y) -->code>

<xacro:property name="camera_height" value="0.025" /> <!-- 摄像头高度(z) -->code>

<xacro:property name="camera_x" value="0.08" /> <!-- 摄像头安装的x坐标 -->code>

<xacro:property name="camera_y" value="0.0" /> <!-- 摄像头安装的y坐标 -->code>

<xacro:property name="camera_z" value="${base_link_length / 2 + camera_height / 2}" /> <!-- 摄像头安装的z坐标:底盘高度 / 2 + 摄像头高度 / 2 -->code>

<!-- 摄像头关节以及link -->

<link name="camera">code>

<visual>

<geometry>

<box size="${camera_length} ${camera_width} ${camera_height}" />code>

</geometry>

<origin xyz="0.0 0.0 0.0" rpy="0.0 0.0 0.0" />code>

<material name="black" />code>

</visual>

</link>

<joint name="camera2base_link" type="fixed">code>

<parent link="base_link" />code>

<child link="camera" />code>

<origin xyz="${camera_x} ${camera_y} ${camera_z}" />code>

</joint>

</robot>

雷达 my_laser.urdf.xacro文件:

<!--

小车底盘添加雷达

-->

<robot name="my_laser" xmlns:xacro="http://wiki.ros.org/xacro">code>

<!-- 雷达支架 -->

<xacro:property name="support_length" value="0.15" /> <!-- 支架长度 -->code>

<xacro:property name="support_radius" value="0.01" /> <!-- 支架半径 -->code>

<xacro:property name="support_x" value="0.0" /> <!-- 支架安装的x坐标 -->code>

<xacro:property name="support_y" value="0.0" /> <!-- 支架安装的y坐标 -->code>

<xacro:property name="support_z" value="${base_link_length / 2 + support_length / 2}" /> <!-- 支架安装的z坐标:底盘高度 / 2 + 支架高度 / 2 -->code>

<link name="support">code>

<visual>

<geometry>

<cylinder radius="${support_radius}" length="${support_length}" />code>

</geometry>

<origin xyz="0.0 0.0 0.0" rpy="0.0 0.0 0.0" />code>

<material name="red">code>

<color rgba="0.8 0.2 0.0 0.8" />code>

</material>

</visual>

</link>

<joint name="support2base_link" type="fixed">code>

<parent link="base_link" />code>

<child link="support" />code>

<origin xyz="${support_x} ${support_y} ${support_z}" />code>

</joint>

<!-- 雷达属性 -->

<xacro:property name="laser_length" value="0.05" /> <!-- 雷达长度 -->code>

<xacro:property name="laser_radius" value="0.03" /> <!-- 雷达半径 -->code>

<xacro:property name="laser_x" value="0.0" /> <!-- 雷达安装的x坐标 -->code>

<xacro:property name="laser_y" value="0.0" /> <!-- 雷达安装的y坐标 -->code>

<xacro:property name="laser_z" value="${support_length / 2 + laser_length / 2}" /> <!-- 雷达安装的z坐标:支架高度 / 2 + 雷达高度 / 2 -->code>

<!-- 雷达关节以及link -->

<link name="laser">code>

<visual>

<geometry>

<cylinder radius="${laser_radius}" length="${laser_length}" />code>

</geometry>

<origin xyz="0.0 0.0 0.0" rpy="0.0 0.0 0.0" />code>

<material name="black" />code>

</visual>

</link>

<joint name="laser2support" type="fixed">code>

<parent link="support" />code>

<child link="laser" />code>

<origin xyz="${laser_x} ${laser_y} ${laser_z}" />code>

</joint>

</robot>

组合底盘摄像头与雷达的 xacro 文件

my_car_camera.urdf.xacro文件

<!-- 组合小车底盘与摄像头与雷达 -->

<robot name="my_car_camera" xmlns:xacro="http://wiki.ros.org/xacro">code>

<xacro:include filename="my_base.urdf.xacro" />code>

<xacro:include filename="my_camera.urdf.xacro" />code>

<xacro:include filename="my_laser.urdf.xacro" />code>

</robot>

launch 文件

<launch>

<!-- 将 urdf 文件内容设置进参数服务器 -->

<!-- <param name="robot_description" textfile="$(find jubot_demo)/urdf/box_urdf.urdf" /> -->code>

<!-- 使用xacro优化urdf文件 -->

<!-- <param name="robot_description" command="$(find xacro)/xacro $(find jubot_demo)/urdf/xacro/my_base.urdf.xacro" /> -->code>

<param name="robot_description" command="$(find xacro)/xacro $(find jubot_demo)/urdf/xacro/my_car_camera.urdf.xacro" />code>

<!-- 启动 rviz -->

<!-- <node pkg="rviz" type="rviz" name="rviz" /> -->code>

<node pkg="rviz" type="rviz" name="rviz" args="-d $(find jubot_demo)/config/rviz/show_four_wheel_car.rviz" />code>

<!-- 启动机器人状态和关节状态发布节点 -->

<node pkg="robot_state_publisher" type="robot_state_publisher" name="robot_state_publisher" />code>

<node pkg="joint_state_publisher" type="joint_state_publisher" name="joint_state_publisher" />code>

<!-- 启动图形化的控制关节运动节点 -->

<node pkg="joint_state_publisher_gui" type="joint_state_publisher_gui" name="joint_state_publisher_gui" />code>

</launch>

在rviz中显示机器人

2.2 Gazebo通信原理(速度控制及图像传输)

2.2.1 Gazebo通信原理

Gazebo通过发布-订阅机制来进行通信,这与ROS的通信模式类似。发布-订阅机制允许多个实体之间进行数据交换而无需直接连接,数据发布者(Publisher)将数据发布到特定主题(Topic)上,订阅者(Subscriber)可以订阅这些主题以接收数据。

服务

服务是一种同步通信机制,客户端发送请求并等待服务器的响应。Gazebo中的一些操作,如设置模型位置或属性,通常通过服务来实现。

2.2.2 速度控制

Gazebo中机器人的速度控制通常通过控制器插件来实现,特别是通过关节控制器(Joint Controller)来调节机器人关节的速度和位置。

示例:控制机器人车轮速度

定义控制器插件:在URDF或SDF文件中定义控制器插件。

<plugin name="wheel_controller" filename="libgazebo_ros_joint_position_controller.so">code>

<ros>

<namespace>/robot</namespace>

<remapping>cmd_vel:=/robot/cmd_vel</remapping>

</ros>

<joint>left_wheel_joint</joint>

<joint>right_wheel_joint</joint>

</plugin>

2.发布速度命令:通过ROS节点发布速度命令到/robot/cmd_vel主题。

import rospy

from geometry_msgs.msg import Twist

rospy.init_node('velocity_publisher')

pub = rospy.Publisher('/robot/cmd_vel', Twist, queue_size=10)

rate = rospy.Rate(10) # 10 Hz

while not rospy.is_shutdown():

vel_msg = Twist()

vel_msg.linear.x = 0.5 # 设定线速度

vel_msg.angular.z = 0.1 # 设定角速度

pub.publish(vel_msg)

rate.sleep()

2.2.3 图像传输

Gazebo中的相机传感器可以模拟真实摄像机的功能,并通过主题发布图像数据。

示例:相机图像传输

定义相机传感器:在URDF或SDF文件中定义相机传感器。

<sensor type="camera" name="camera_sensor">code>

<update_rate>30</update_rate>

<camera>

<horizontal_fov>1.3962634</horizontal_fov>

<image>

<width>800</width>

<height>800</height>

<format>R8G8B8</format>

</image>

<clip>

<near>0.1</near>

<far>100</far>

</clip>

</camera>

<always_on>true</always_on>

<update_rate>30</update_rate>

<visualize>true</visualize>

<plugin name="camera_controller" filename="libgazebo_ros_camera.so">code>

<alwaysOn>true</alwaysOn>

<updateRate>30.0</updateRate>

<cameraName>camera</cameraName>

<imageTopicName>camera/image_raw</imageTopicName>

<cameraInfoTopicName>camera/camera_info</cameraInfoTopicName>

<frameName>camera_link</frameName>

</plugin>

</sensor>

     2.订阅图像主题:通过ROS节点订阅图像数据。

import rospy

from sensor_msgs.msg import Image

from cv_bridge import CvBridge

import cv2

def image_callback(msg):

bridge = CvBridge()

cv_image = bridge.imgmsg_to_cv2(msg, "bgr8")

cv2.imshow("Camera Image", cv_image)

cv2.waitKey(3)

rospy.init_node('image_subscriber')

rospy.Subscriber('/camera/image_raw', Image, image_callback)

rospy.spin()

总结

速度控制:通过控制器插件实现,使用发布-订阅机制发送速度命令。图像传输:通过相机传感器发布图像数据,ROS节点订阅图像主题并处理。


2.3 遇到问题及原因及解决问题的方法

2.3.1 前面 URDF 文件构建机器人模型问题

问题1:在设计关节的位置时,需要按照一定的公式计算,公式是固定的,但是在 URDF 中依赖于人工计算,存在不便,容易计算失误,且当某些参数发生改变时,还需要重新计算。

问题2:URDF 中的部分内容是高度重复的,驱动轮与支撑轮的设计实现,不同轮子只是部分参数不同,形状、颜色、翻转量都是一致的,在实际应用中,构建复杂的机器人模型时,更是易于出现高度重复的设计,按照一般的编程涉及到重复代码应该考虑封装。

如果在编程语言中,可以通过变量结合函数直接解决上述问题,在 ROS 中,已经给出了类似编程的优化方案,称之为:Xacro

概念

Xacro 是 XML Macros 的缩写,Xacro 是一种 XML 宏语言,是可编程的 XML。

原理

Xacro 可以声明变量,可以通过数学运算求解,使用流程控制控制执行顺序,还可以通过类似函数的实现,封装固定的逻辑,将逻辑中需要的可变的数据以参数的方式暴露出去,从而提高代码复用率以及程序的安全性。

作用

较之于纯粹的 URDF 实现,可以编写更安全、精简、易读性更强的机器人模型文件,且可以提高编写效率。

解决方法:

配置xacro文件

在urdf文件夹下新建一个xacro文件夹,在文件夹中新建my_base.urdf.xacro文件

编写修改Xacro 文件

<!--

使用 xacro 优化 URDF 版的小车底盘实现:

实现思路:

1.将一些常量、变量封装为 xacro:property

比如:PI 值、小车底盘半径、离地间距、车轮半径、宽度 ....

2.使用 宏 封装驱动轮以及支撑轮实现,调用相关宏生成驱动轮与支撑轮

-->

<!-- 根标签,必须声明 xmlns:xacro -->

<robot name="my_base" xmlns:xacro="http://www.ros.org/wiki/xacro">code>

<!-- 封装变量、常量 -->

<xacro:property name="PI" value="3.141"/>code>

<!-- 宏:黑色设置 -->

<material name="black">code>

<color rgba="0.0 0.0 0.0 1.0" />code>

</material>

<!-- 底盘属性 -->

<xacro:property name="base_footprint_radius" value="0.001" /> <!-- base_footprint 半径 -->code>

<xacro:property name="base_link_radius" value="0.1" /> <!-- base_link 半径 -->code>

<xacro:property name="base_link_length" value="0.08" /> <!-- base_link 长 -->code>

<xacro:property name="earth_space" value="0.015" /> <!-- 离地间距 -->code>

<!-- 底盘 -->

<link name="base_footprint">code>

<visual>

<geometry>

<sphere radius="${base_footprint_radius}" />code>

</geometry>

</visual>

</link>

<link name="base_link">code>

<visual>

<geometry>

<cylinder radius="${base_link_radius}" length="${base_link_length}" />code>

</geometry>

<origin xyz="0 0 0" rpy="0 0 0" />code>

<material name="yellow">code>

<color rgba="0.5 0.3 0.0 0.5" />code>

</material>

</visual>

</link>

<joint name="base_link2base_footprint" type="fixed">code>

<parent link="base_footprint" />code>

<child link="base_link" />code>

<origin xyz="0 0 ${earth_space + base_link_length / 2 }" />code>

</joint>

<!-- 驱动轮 -->

<!-- 驱动轮属性 -->

<xacro:property name="wheel_radius" value="0.0325" /><!-- 半径 -->code>

<xacro:property name="wheel_length" value="0.015" /><!-- 宽度 -->code>

<!-- 驱动轮宏实现 -->

<xacro:macro name="add_wheels" params="name flag">code>

<link name="${name}_wheel">code>

<visual>

<geometry>

<cylinder radius="${wheel_radius}" length="${wheel_length}" />code>

</geometry>

<origin xyz="0.0 0.0 0.0" rpy="${PI / 2} 0.0 0.0" />code>

<material name="black" />code>

</visual>

</link>

<joint name="${name}_wheel2base_link" type="continuous">code>

<parent link="base_link" />code>

<child link="${name}_wheel" />code>

<origin xyz="0 ${flag * base_link_radius} ${-(earth_space + base_link_length / 2 - wheel_radius) }" />code>

<axis xyz="0 1 0" />code>

</joint>

</xacro:macro>

<xacro:add_wheels name="left" flag="1" />code>

<xacro:add_wheels name="right" flag="-1" />code>

<!-- 支撑轮 -->

<!-- 支撑轮属性 -->

<xacro:property name="support_wheel_radius" value="0.0075" /> <!-- 支撑轮半径 -->code>

<!-- 支撑轮宏 -->

<xacro:macro name="add_support_wheel" params="name flag" >code>

<link name="${name}_wheel">code>

<visual>

<geometry>

<sphere radius="${support_wheel_radius}" />code>

</geometry>

<origin xyz="0 0 0" rpy="0 0 0" />code>

<material name="black" />code>

</visual>

</link>

<joint name="${name}_wheel2base_link" type="continuous">code>

<parent link="base_link" />code>

<child link="${name}_wheel" />code>

<origin xyz="${flag * (base_link_radius - support_wheel_radius)} 0 ${-(base_link_length / 2 + earth_space / 2)}" />code>

<axis xyz="1 1 1" />code>

</joint>

</xacro:macro>

<xacro:add_support_wheel name="front" flag="1" />code>

<xacro:add_support_wheel name="back" flag="-1" />code>

</robot>

集成launch文件

在 launch 文件中直接加载 xacro

box_launch.launch 内容示例:

<launch>

<!-- 将 urdf 文件内容设置进参数服务器 -->

<!-- <param name="robot_description" textfile="$(find jubot_demo)/urdf/box_urdf.urdf" /> -->code>

<!-- 使用xacro优化urdf文件 -->

<param name="robot_description" command="$(find xacro)/xacro $(find jubot_demo)/urdf/xacro/my_base.urdf.xacro" />code>

<!-- 启动 rviz -->

<!-- <node pkg="rviz" type="rviz" name="rviz" /> -->code>

<node pkg="rviz" type="rviz" name="rviz" args="-d $(find jubot_demo)/config/rviz/show_four_wheel_car.rviz" />code>

<!-- 启动机器人状态和关节状态发布节点 -->

<node pkg="robot_state_publisher" type="robot_state_publisher" name="robot_state_publisher" />code>

<node pkg="joint_state_publisher" type="joint_state_publisher" name="joint_state_publisher" />code>

<!-- 启动图形化的控制关节运动节点 -->

<node pkg="joint_state_publisher_gui" type="joint_state_publisher_gui" name="joint_state_publisher_gui" />code>

</launch>

核心代码:

<!-- 使用xacro优化urdf文件 -->

<param name="robot_description" command="$(find xacro)/xacro $(find jubot_demo)/urdf/xacro/my_base.urdf.xacro" />code>

加载robot_description时使用command属性,属性值就是调用 xacro 功能包的 xacro 程序直接解析 xacro 文件。

重新启动launch,正常显示小车

2.3.2 输入了字母,小车不动

原因:没有点进控制器所在终端,字母输到别的地方去了。

解决方法:点进控制器所在终端即可正常操作。

2.3.3 相机节点连接后没有画面

原因:生成的世界是空世界,什么都没有,故没有画面。

解决方法:在Gazebo中加一些物件,让相机能够有东西显示。

2.3.4 显示图像很卡:

把虚拟机中的3D图形加速开启可以解决


三、ORB_SLAM3的安装

3.1 ORB_SLAM3原理

ORB-SLAM最早的版本在2014年的RSS上发布,在2016年作者又发布了ORB-SLAM2,接着在2020年发布了ORB-SLAM 3。ORB-SLAM1只能针对单目相机数据进行处理;ORB-SLAM 2 增加了对于双目和RGB-D相机的处理,在回环检测模块增加了Full Global BA的处理;ORB-SLAM 3则增加了对于IMU融合的支持,兼容鱼眼相机模型,并且增加了Altas多地图的支持;同时,回环检测为了支持多地图的模式,提供了一种叫Welding BA的优化方式。ORB -SLAM的作者将上述提到的各版本项目都开源了,为学术研究还是工程落地都提供了很好的参考。

1 基础概念

首先来介绍一下ORB SLAM中涉及的一些基础概念。

帧&关键帧:视觉SLAM都是对一个图像序列进行处理,每一张图像被称为帧,而关键帧则是通过一定筛选机制得到的、具有一定代表性的图像帧。

地图点/路标点:将图像上被观察到的特征点通过三角化等方式进行深度恢复,我们就可以得到其对应的在三维空间的位置,同时包含帧的观测信息,这种点就被称为地图点或路标点。

共视:当一个地图点被多帧观察到时,我们就可以称这几帧有基于该地图点的共视关系。

共视图&本质图:我们可以把共视关系作用边表示,关键帧用节点表示,就可以建立共视图,而本质图在共视图基础上只保留具有较强共视关系的边。

Altas(地图集):ORB-SLAM 3提供了多地图的存储和拼接功能,在跟踪丢失后可以尝试将现有地图和历史地图进行匹配、融合,并更新当前的活跃地图(Active Map)。

数据关联:在语义SLAM中,第k帧检测到物体Obj1、Obj2,第k+1帧检测到物体Obj3、Obj4,确定Obj1和Obj3、Obj4中的哪一个是对真实世界中同一个物体的多次观测,这是数据关联的一个直观例子。在间接法(特征法)SLAM中,表现为不同帧中的特征,哪些是对应于同一个空间路标点/地图点的。在ORB3中考虑到的数据关联包括短期内滑动窗口中关键帧观测到的路标点和图像特征的数据关联;中期的数据关联是指图像特征与局部地图点的关联;长期的数据关联包括利用场景识别技术和词袋模型,在回环检测、重定位等过程中的数据关联;而多地图的数据关联还可以实现地图之间地图点的匹配和融合。

ORB-SLAM 3的基本流程和此前的ORB版本没有显著的改变,只是也增加了部分新特性。基于词袋模型的关键帧数据和之前差不多,每一个关键帧均会被存入数据库用于回环检测。地图结构上进行了改进,ORB-SLAM3使用Altas地图集的结构,地图中包含一个Active Map和若干个Non-active Map,每个Map均包括地图点,关键帧,共视图,Spanning Tree等信息。跟踪线程添加了IMU的积分,并且和以前一样进行关键帧的判断和构造;在LocalMapping线程中中执行IMU的初始化,以及和之前相同的冗余关键帧和地图点剔除、新地图点的创建等工作;在回环检测部分主要添加了多地图的融合。

ORB-SLAM 3框架

总结一下,ORB-SLAM3的贡献包括:

1、 提供了一个单双目VI-SLAM的系统;

2、 改善召回率的场景识别技术;

3、 多地图机制;

4、 抽象的相机表示。

2 抽象相机模型介绍

为什么ORB-SLAM3需要一个抽象的相机模型呢?

相比于传统相机,鱼眼相机超过180度的广视角可以获取更多的信息,但是因为它不符合针孔模型数学建模的假设,导致uniform reprojection error的假设失效;如果对于图像直接进行裁剪,将会导致外围图像丢失,反而丧失了鱼眼相机大视角的优势。在ORB-SLAM3中,相机成像模型提供投影、反投影和相关的雅克比计算等函数,并且将此前系统中的EPNP更换为MAP-PNP, 从而实现了相机成像模型与SLAM部分的解耦,还可以扩展,能将相同的SLAM pipeline用于大部分类型的相机。       whaosoft aiot http://143ai.com

此外,针对双目相机模型,ORB-SLAM3也提供了一定的改善。ORB-SLAM2假设我们针对双目相机预先进行了极线矫正,但是很多时候由于无法保证左右目相机光轴的绝对平行,极线矫正的效果也往往不好;而有些时候,我们需要使用两个参数不同的相机进行观测,而ORB-SLAM2无法兼容这类双目相机,如类似RGB-D相机中焦距、分辨率相差巨大的彩色相机+近红外相机,如果将彩色图像和近红外图像组成双目图像,ORB-SLAM2无法综合利用这对图像估计相机位姿。在ORB-SLAM3中将左右两目相机视作为具有固定位姿变换的两台单目相机使用,并且也不再限制两台相机必须具有足够面积的、重叠的共视区域,解决了这个问题。

3 VISLAM实现和IMU初始化

ORB-SLAM3中VI-SLAM在ORB-SLAM-VI上进行了改进,包括:提供快速,准确的IMU初始化;支持单双目VI-SLAM;支持针孔/鱼眼相机模型。在视觉和IMU融合方面,ORB-SLAM3在位姿求解时所建立优化问题的残差项,包括所有关键帧和上一帧IMU估计的残差项,以及所有路标点观测的视觉误差项。其中针对视觉路标点的观测,为了避免错误匹配造成的极端值的影响,嵌套了鲁棒核函数。

IMU初始化的目的是为了得到Body系速度、重力方向和IMU偏置。ORB-SLAM3中初始化流程的设计建立在作者的几点思考上:

1、ORB-SLAM纯单目已经可以初始化得到精确的地图,尺度信息可以通过IMU得到;双目图像输入下则尺度客观,可以不考虑尺度信息的问题;

2、如果将尺度单独作为优化变量进行表示和优化,效果比在BA中的隐式表达收敛更快;

3、IMU初始化过程中必须考虑传感器的不确定性,否则会产生难以预测的巨大误差。

接下来的讨论IMU初始化问题时,均指单目输入时的初始化。ORB3中IMU初始化的步骤包含三步,第一步是纯视觉最大后验估计(MAP),第二步是纯惯性MAP,第三步是视觉+惯性MAP。针对纯视觉MAP,我们提取初始化后2s内10帧图像进行纯视觉BA,从而得到没有尺度信息的相机位姿和路标点位置。接下来我们进行只有IMU参与的初始化,最终得到的优化结果是:帧位姿、速度和地图点,并都具有正确的尺度;Body系Z轴将被旋转到和重力方向一致;IMU的偏置被更新。第三步是视觉IMU联合后验估计,ORB-SLAM3只需要2秒就可以完成尺度的初始化,误差在5%左右,此外,ORB-SLAM3还将进行只包含尺度因子和重力方向的优化,10秒一次,用于避免传感器运动缓慢时IMU激励不够的情况。

ORB-SLAM3中的跟踪和建图和ORB-SLAM-VI类似,在短期跟丢后,在满足一定条件时会尝试利用IMU积分得到的位姿信息进行重定位;当丢失持续一定时间后,将会重新初始化,创建新的Active map。

4 改进的回环检测与多地图融合

这一部分的内容很大程度上和ORB-SLAM2是相同的,我们首先来回顾一下基本概念。

准确率(PrecisionRate):检测到的回环中正确的比率。

召回率(RecallRate):检测到的回环占总真实回环数的比率。

在ORB-SLAM1/2中,仅通过DBoW词袋数据库就可实现50%~80%的准确率和召回率。在回环时,通过增加几何一致性和时间一致性检验,牺牲召回率来增加准确率。ORB-SLAM3改进了回环检测的速度,提高了召回率,并且增加了多地图的部分。

寻找闭环帧的过程可以分为六步:

1.针对每一个新关键帧,在数据库中查询到三个最相似的关键帧;

2.尝试对新关键帧及其共视关键帧,和候选关键帧及其共视关键帧进行数据关联;

3.利用匹配的特征点和地图点求解位姿转换;

4.利用位姿变换的初始估计,进行点云重投影寻找新的匹配,并且进行位姿的优化求精;

5.对时间一致性的检验,此前的步骤相对复杂,在ORB-SLAM3中局部地图里面已有关键帧的共视信息进行判断;

6.利用重力方向对于回环结果进行检查。

回环检测后是进行回环还是地图合并,取决于当前关键帧检测到的回环关键帧是在当前的active map还是在其他map。当对non-active map和active map进行融合时,共视图和本质图同步更新,active map中的信息被追加到历史地图中,匹配到的non-active map变成新的map。

5 总结

总体来说,ORB-SLAM3的流程和ORB-SLAM1/2非常相似,对于ORB-SLAM系列熟悉的同学应该很容易上手;相机模型的抽象处理,使得SLAM位姿求解过程和相机成像模型解耦,理论上支持绝大多数成像模型的相机;通过对于IMU的支持,ORB-SLAM系列加入了VI-SLAM的大家庭,也表明多传感器融合的SLAM是目前一大发展趋势;多地图的机制有利于跟丢后保留尽可能多的信息用于后续补救,也为后续实现多机器协同的SLAM提供了工作基础。


3.2 ORB_SLAM3编译安装过程

1.安装Pangolin v0.5

(1)安装依赖功能包

<code>sudo apt-get install libglew-dev   ####apt-get/ubuntu  pip/python python 第三方库 python/lib/site-package

sudo apt-get install cmake

sudo apt-get install libpython2.7-dev

sudo apt-get install ffmpeg libavcodec-dev libavutil-dev libavformat-dev libswscale-dev

sudo apt-get install libdc1394-22-dev libraw1394-dev

sudo apt-get install libjpeg-dev libpng-dev libtiff5-dev libopenexr-dev

(2)编译及安装pangolin

      从一网畅学上下载Pangolin-0.5源程序包,或者从gitbub上下载

cd Pangolin-0.5/Pangolin-0.5   ##cd至Pangolin路径下

mkdir build      ##创建名为build的文件夹

cd build         #进入build文件夹

cmake ..          #使用cmake命令将cmakelist.txt中语句转换换为make的编译命令。当前我们位于build文件夹,所以cmake命令生成的文件会保存在build文件夹下, cmake .. 中的 “..” 表示的是cmakelist.txt所在的文件夹, 即当前build路径的上一级路径,也就是 /Panglin-0.5这个文件夹,我们可以在此文件夹下找到相应的cmakelist.txt文件。

make -j8      #使用make命令对源程序进行编译,其中-j8表示使用8个核并行编译 因此可以改变为其他核数如 -j2 -j4

sudo make install   ##make编译后的文件仍然保存在build文件夹下,为了使得其他程序能够在系统中找到Pangolin这个程序,通过make install命令将pangolin的安装至/usr/local/include文件夹下,以便其他程序调用头文件,类似于设置了环境变量。

2.安装opencv 3.2.0

(1)安装依赖

sudo apt-get install build-essential

sudo apt-get install cmake git libgtk2.0-dev pkg-config libavcodec-dev libavformat-dev libswscale-dev

sudo apt-get install python-dev python-numpy libtbb2 libtbb-dev libjpeg-dev libpng-dev libtiff-dev libjasper-dev libdc1394-22-dev

(2)编译及安装opencv

****注意增大虚拟机内存,防止编译过程中内存溢出

    将opencv 和opencv_contrib文件夹并列放置在某一路径下 

cd opencv     ##进入opencv文件夹

mkdir build

cd build

cmake -D CMAKE_BUILD_TYPE=RELEASE -D CMAKE_INSTALL_PREFIX=/usr/local -D INSTALL_C_EXAMPLES=ON -D ENABLE_PRECOMPILED_HEADERS=OFF -D INSTALL_PYTHON_EXAMPLES=ON -D WITH_TBB=ON -D WITH_V4L=ON -D WITH_QT=ON -D WITH_OPENGL=ON -D OPENCV_EXTRA_MODULES_PATH=../../opencv_contrib/modules ..              ###注意OPENCV_EXTRA_MODULES_PATH 的对应路径要设置正确 不然找不到opencv_contrib

make -j4

sudo make install

3.安装ORB-SLAM3

(1) 安装依赖(Pangolin, opencv, boost) boost可以先不安装,如果编译出错再补充安装

(2) 编译ORB-SLAM3

      Cd 至 ORB-SLAM文件夹下

     

 Chmod +x build.sh    ##将build.sh文件添加运行权限

      查看Cmakelist.txt, 修改 find_package(OpenCV 4.4) 为 find_package(OpenCV 3.2)      #指定opencv版本

     

  ./build.sh     #运行build.sh脚本安装ORB—SLAM3

(3) 编译ORB_SLAM3 ROS模块     

cp -r Examples_old/ROS Examples     ##将examples_old 文件夹下的ROS文件夹 复制到 Example文件夹下

vim ~/.bashrc  ##打开用户根目录下的.bashrc文件 添加ROS功能包路径 export ##ROS_PACKAGE_PATH=${ROS_PACKAGE_PATH}:/home/raypc/codes/orb/Examples/ROS/    ##此处路径需要根据每个人放置功能包的位置进行调整,不能直接复制

<code>chmod +x build_ros.sh ##修改Build_ros.sh权限

./build_ros.sh ##安装 orb_slam 中的 ros模块


3.3 遇到的问题及原因及解决问题的方法

3.3.1 在ROS(noetic)环境下编译报错的解决方式

本人电脑系统为Ubuntu 20.04,ROS安装的版本为noetic,在用./build_ros.sh命令编译ORB_SLAM3时出现下面的错误:

仔细往上面找其实还有一个这个报错:

ERROR: Rosdep experienced an error: The read operation timed out Please go to.........

此时已经在.bashrc中添加了下面这段语句,且也source ~/.bashrc了,但还是出现上面的错误。

export ROS_PACKAGE_PATH=${ROS_PACKAGE_PATH}:YOUR_PATH/ORB_SLAM3/Examples/ROS

仔细看问题后面有提示:sudo rosdep init和rosdep update来解决,但是我sudo rosdep init后还是报下面的错误:

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.

当时我按照网上的很多方式去做,最后都失败了,这个问题卡了我两天,不经意间看到学ROS1时候的书,是一个赵虚左老师的教程,最后解决了上面的问题。

解决方式如下:

1、进入"/usr/lib/python3/dist-packages/"目录下修改相关文件,主要有: ./rosdistro/__init__.py、./rosdep2/gbpdistro_support.py、./rosdep2/sources_list.py 、./rosdep2/rep3.py。

sudo gedit ./rosdistro/__init__.py

sudo gedit ./rosdep2/gbpdistro_support.py

sudo gedit ./rosdep2/sources_list.py

sudo gedit ./rosdep2/rep3.py

2、文件中涉及的 URL 内容,如果是:raw.githubusercontent.com/ros/rosdistro/master都替换成步骤1中准备的gitee.com/zhao-xuzuo/rosdistro/raw/master即可,步骤1中的四个文件修改完后的内容如下:

DEFAULT_INDEX_URL = 'https://gitee.com/zhao-xuzuo/rosdistro/raw/master/index-v4.yaml'

FUERTE_GBPDISTRO_URL = 'https://gitee.com/zhao-xuzuo/rosdistro/raw/master/releases/fuerte.yaml'

DEFAULT_SOURCES_LIST_URL = 'https://gitee.com/zhao-xuzuo/rosdistro/raw/master/rosdep/sources.list.d/20-default.list'

REP3_TARGETS_URL = 'https://gitee.com/zhao-xuzuo/rosdistro/raw/master/releases/targets.yaml'

3、修改完毕,再重新执行命令:

sudo rosdep init

rosdep update

最后删除YOUR_PATH/ORB_SLAM3/Examples/ROS/ORB_SLAM3下的build文件夹,从新执行./build_ros.sh命令,编译成功。

最后说一下ORB_SLAM3为什么要在ROS环境运行,我认为是方便将其部署到机器人上,这样大大简化了后期实体机器人的开发周期,同时有些数据集也是rosbag的格式,在ROS环境下运行比较方便。但是目前ROS环境还是相对冗余。学ROS1我非常推荐赵虚左老师的视频,讲的太棒了。

3.3.2 编译Pangolin0.5报错error: ‘AV_PIX_FMT_XVMC_MPEG2_MC’ was not declared in this scope

原因:找到的解决教程中未详细描述,根据修改的代码猜测是系统和版本方面的一些不兼容

解决方法:参考https://github.com/stevenlovegrove/Pangolin/pull/318/files?diff=split&w=0做修改

3.3.3 编译ORB_SLAM3_ROS2报错找不到一系列library

原因:在CMakeLists.txt中没能正常找到这些library

解决方法:修改CMakeLists.txt,CMakeLists.txt如下:


四、实验验证

4.1 ORB_SLAM与Gazebo联合仿真实验


五、ROS机器人操作系统心得体会,改bug!QAQ

        前前后后,对于ROS的学习已经很多遍了,可以说走的弯路有点多,导致浪费了很多时间,其实ROS不应该是一个专门需要去学习的东西,它更像是一种工具,像我们的操作系统一样,将我们各种项目的程序和模块连接起来,其实对于我们这种工程师而言,不需要将ROS学习的特别精通,这里我将自己的学习经验和踩坑指南分享。

        

        随着国内机器人领域的发展,ROS(机器人操作系统)逐渐被工程师们用到自己的项目中,国内的ROS学习资源也多了起来。

对于视频资源比较出名的就是古月居老师的【ROS入门21讲】,可以说是很多同学的ROS启蒙课程了,这门课程个人感觉挺通俗易懂的,将ROS基本的概念和常用的工具都进行了介绍,胡老师对ROS通讯机制的一些代码也进行了讲解,总体感觉还是不错的,但是我当时学了一遍之后感觉还是很懵,写代码还是不太会,感觉如果单纯是了解ROS的话下面的视频可能也不错。

这个是师兄跟我说的的一个视频,中科院软件所-机器人操作系统入门(ROS入门教程),这个视频我觉得最好的一点就是短,每一集的时长都很友好,可以帮初学者快速了解ROS,古月居的入门教程是以海龟为例说明的,中科院的是用自己的代码,后面中级篇介绍都是自己的xbot移动机器人,我觉得这个视频虽然短,但是讲出了精髓,我看这个视频的时候是复习的时候看的,比如:他中间讲用键盘驱动gazebo仿真环境里的小车时,是先用rostopic查看了一下发布的话题,然后查看了话题类型,这相当于帮我们把接口分析明白了,以后面对不同的话题,我们也有了一个摸索的思路,这一点我觉得是很好的。

        

书籍学习

第一本我看过的ROS书也是古月居老师的书籍,ROS开发与实践,这本书就涉及到了一些中高级操作,而且中间有实物操作,配套示例代码可以跟着搭建一个小车系统,如果条件允许可以做实物出来,没钱买也可以用它的仿真环境,但是这本书感觉更偏向于导论,对于代码的讲解不是很细致,这本书配套了一个视频-【古月居】ROS机器人开发实践,但是感觉还是像导论性质的,依然不太懂,但对于非ROS开发工程师已经够用了,毕竟能看懂代码,能简单修改就行。

第二本书,我愿称之为神书,是美国怀亚特·S纽曼写的-ROS机器人编程原理与应用,github上有配套的代码程序,这本书对于ROS的各种工具讲解非常细致,对其中的代码基本上是进行了逐行讲解,真的太细了,缺点一是书中使用的版本是ubuntu14,感觉有点老了,github上有更新,但是有些程序还是不支持;二是它讲的太细了,有很多内容我们根本用不到,在看这本书的时候要学会挑选我们需要的知识进行学习 

个人感悟

        我认为,对于ROS的学习,我们毕竟不是专门研究ROS开发的,我们只是想把ROS当成一个类似于Windows或者Linux一样的工具,来支持我们的机器人,我们只需要了解它的通信原理以及一些常用工具的使用即可,对于程序代码,我们的关注点应该是我们的研究方向,而不是ROS的语法(其实说白了就是一些库的调用和约定),我们只要能看懂代码,能进行修改即可。

最后,最重要的是不要光学习不实操,最好学一节用一节,自己写写代码,改改别人的程序,甚至搞个实物机器人,挂个嵌入式系统跑跑ROS,应用和实验绝对是学习最快的方式。


由于早期遇到的报错已经不能复现,参考整理了一些遇到常见报错及解决方案 

参考链接:

传送门link1、link4、link5

link2

link3



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。