重要说明:本文从网上资料整理而来,仅记录博主学习相关知识点的过程,侵删。
一、参考资料
ROS相关技术介绍,可参考之前的文章:ROS相关技术(概念版)
Autolabor主站
ROS官网
ROS
ROS教程
鱼香ROS机器人
ROS编程技术,CSDN
ROS书籍整理:机器人操作系统 ROS 相关书籍整理合集 [古月居推荐],CSDN
二、ROS相关介绍
ROS与Apollo对比
Apollo可以看作是ROS的改进版,Apollo从通信功能优化、去中心化网络拓扑以及数据兼容性扩展三个方面做了定制化的改进。
单对多的传输场景下,ROS 在处理一对多的消息传输时,底层实现实际是多个点对点的连接,当把一份数据要发给三个节点时,相同的数据会传输三次,这会造成很大的资源浪费。而Apollo采用了共享内存的解决方案,减少传输过程中的数据拷贝,大幅度提升效率。
ROS系统非常依赖Master,一旦Master出现异常,将导致整个系统崩溃,且缺乏恢复自动机制。Apollo的整个网络拓扑不再以 master 为中心构建,当一个新的节点加入网络时,会通过 RTPS 协议向域内的所有其他节点发送广播信息,各个节点也会将自己的服务信息发送给新的节点,以代替 Master 的信息交换功能。
ROS 系统为了保证收发双方的消息格式一致,会对message定义做 MD5 校验,任何字段的增减或顺序调整都会使 MD5 变化。然而这种严格的限制也引起了兼容性的问题,OTA升级后可能出现兼容问题,在Apollo中,做了一整套对protobuf的支持, 在工程中可以不需要做格式转换,直接publish protobuf格式的消息,调试工具也能够非常正确的解析出来正确的protobuf消息。这样既能够很好解决兼容性问题,也不会产生额外的学习成本和使用成本。
三、常用指令
roscore
功能描述:用于启动ROS系统的核心功能。
启动roscore是ROS系统的先决条件,必须运行roscore才能使ROS节点进行通信。
启动roscore将:
- 启动ROS Master节点;
- ros参数服务器;
- rosout日志节点;
ROS Master节点是ROS系统中的核心管理节点,负责管理所有节点之间的通信,包括节点的发现、消息的路由、参数服务器等。当执行 roscore
指令时,ROS Master节点将在计算机上启动,并将等待其他节点连接。只有在启动 roscore
之后,才能启动其他节点,并进行ROS操作。因此,通常在开始进行任何ROS项目之前,第一步是启动 roscore
。
rosrun
功能描述:启动ROS包中的节点。
# 启动小海龟节点用于演示机器人控制
rosrun turtlesim turtlesim_node
rosnode
功能描述:用于获取节点信息。
# 测试节点的连接状态
rosnode ping
# 列出活动的节点
rosnode list
# 打印节点信息
rosnode info
# 列出指定设备上的节点
rosnode machine
# 杀死节点
rosnode kill
# 清除不可连接的节点
# ctrl+c并没有清除节点,需要用cleanup彻底清除
rosnode cleanup
roslaunch
功能描述:执行功能包的launch文件。
roslaunch是一个用于自动启动一系列ROS节点的命令行工具。
roslaunch <package_name> <launch文件名>
rostopic
功能描述:用于显示有关ROS 话题的调试信息,包括发布者,订阅者,发布频率和ROS消息。
# 显示话题的相关信息
rostopic info
# 列举所有的话题
rostopic list
# 列举活动状态下的话题
rostopic list -v
# 打印话题的消息到屏幕
rostopic echo
# 打印话题的消息类型
rostopic type
# 根据消息类型查找话题
rostopic find
# 显示话题使用的带宽
rostopic bw
# 显示话题的发布频率
rostopic hz
# 显示带有header的话题延迟
rostopic delay
# 将数据发布到话题
rostopic pub </topic_name> <message_type> <message_content>
# 以10HZ的频率发送信息
rostopic pub -r 10 </topic_name> <message_type> <message_content>
rosmsg
功能描述:显示有关 ROS消息类型的信息。
# 显示消息描述
rosmsg show <message_type>
# 显示消息信息
rosmsg info
# 列出所有消息
rosmsg list
# 显示md5加密后的消息
rosmsg hd5
# 显示某个功能包下的所有消息
rosmsg package <package_name>
# 列出包含消息的所有包
rosmsg packages
rosservice
功能描述:用于列出和查询ROS服务。
# 打印服务参数
rosservice args </service_name>
# 按照服务类型查找服务
rosservice find <servervice_name>
# 打印指定服务的信息
rosservice info
# 列出所有活动的服务
rosservice list
# 打印服务的类型
rosservice type
# 打印服务的ROSRPC uri
rosservice uri
# 调用指定服务
rosservice call </service_name>
rosparam
功能描述:用于管理ROS参数。
# 设置指定参数的值
rosparam set param_name value
rosbag指令
rosbag是ROS中的一种数据记录工具,它可以记录和回放ROS的消息数据。我们可以使用rosbag来记录ROS系统中的消息流,包括传感器数据、机器人状态等信息,然后在之后的时间点上回放这些消息数据,以便我们进行离线分析和调试。
rosbag可以将消息数据存储在文件中,并使用一种高效的压缩算法来减少文件大小。这样,我们可以在之后的时间点上使用rosbag回放工具来重新播放这些消息,以便我们在没有实际硬件的情况下进行测试和调试。rosbag还支持多个包(bag)之间的合并和分割,以便我们更好地管理消息数据。
rosbag常用指令
record
:用指定的话题录制一个 bag 包;info
:显示一个 bag 包的基本信息,比如包含哪些话题;play
:回放一个或者多个 bag 包;reindex
:重新索引一个或多个损坏 bag 包。
记录bag
rosbag record 是一个用于记录 ROS 消息到 ROS Bag 文件中的命令行工具。它可以记录指定主题的消息,将消息保存到一个ROS Bag文件中。例如,以下命令将记录名为/scan
的激光雷达数据并将其保存到名为scan.bag
的文件中:
rosbag record /scan -O scan.bag
以下是 rosbag record 命令的常用参数:
-a, --all
:记录所有主题。-O, --output-name
:指定输出的 ROS Bag 文件名。-b, --buffer-size
:设置ROS Bag文件的缓冲区大小。-d, --duration
:设置记录时间长度,以秒为单位。-l, --limit
:设置记录的消息数量限制。-j, --bz2
:使用bzip2压缩来压缩ROS Bag文件。-z, --lz4
:使用LZ4压缩来压缩ROS Bag文件。-p, --split
:设置ROS Bag文件的分段大小。-t, --topics
:指定要记录的主题列表。-x, --exclude
:指定要排除的主题列表。
# 录制所有话题数据
rosbag record -a
# 录制指定话题数据
rosbag record <topic_name1> <topic_name2> <topic_name3> ...
# 指定数据包名称
rosbag record -O <bagname> <topic_name1> <topic_name2> <topic_name3> ...
查看bag信息
rosbag info指令可以数据包中包含的话题名称,话题数量,话题消息类型等信息。
rosbag info <bagname>
播放bag
使用 rosbag play
指令可以从指定的ROS包文件中回放消息数据。
# 回放指定bag
rosbag play <bagname>
# 回放当前目录下所有bag
rosbag play *
# 从指定时间(n = 指定时间s)开始回放bag
rosbag play -s n <bagname>
# 延时指定时间(n = 延时时间s)开始回放bag
rosbag play -d n <bagname>
# 播放指定时间(n = 指定秒数)的bag
rosbag play -u n <bagname>
# 从指定时间(n = 开始时间s)开始播放指定时间(m = 指定播放时间s)的bag
rosbag play -s n -u m <bagname>
# 按指定倍率(n = 倍率)回放bag
rosbag play -r n <bagname>
修复bag
回放不成功,提示 reindex ,修复bag。
rosbag reindex
catkin
catkin
由一系列 CMake
宏以及定制的Python脚本组成,以便在正常的 CMake 流程上增加额外的功能。
catkin_init_workspace
功能表述:初始化catkin工作空间。
# 创建工作空间
mkdir -p ~/catkin_ws/src
cd ~/catkin_ws/src
# 初始化工作空间
catkin_init_workspace
工作空间的名称可以任意命名,catkin_ws
是最常用的工作空间名称。初始化工作空间之后,会在 src
路径下自动创建 CMakeLists.txt
文件,实际上是创建了一个全系统可见的 CMakeLists.txt
的符号链接。
catkin_make
功能描述:编译工作空间,生成 build、devel 文件夹。
cd ~/catkin_ws
catkin_make
执行 catkin_make
命令,将会自动创建 build
和 devel
两个文件夹。其中,当使用C++时,build
文件夹用于存放 catkin
的库文件和可执行程序。当使用Python时,可以忽略 build
目录下的文件。devel
文件夹包含了多个文件和路径,其中最需要注意的是 setup.bash
文件,运行该文件可以使系统使用该工作空间以及其中包含的代码。每次打开新终端,都需要使 setup.bash
生效。执行如下指令,使得setup生效:
source devel/setup.bash
catkin_create_pkg
功能描述:创建ROS功能包。
catkin_create_pkg <package_name> <依赖包>
执行 catkin_create_pkg
指令,将创建一个同名的文件夹,并在该新文件夹下创建了 CMakeLists.txt
、package.xml
以及 src
文件夹。
示例
cd ~/catkin_ws/src
catkin_create_pkg my_package rospy roscpp
切换到 src
目录下,并创建一个名为 my_package
的功能包,该包依赖已经存在的的 rospy
包。
rqt_graph
ROS 分布式系统中不同进程需要进行数据交互,计算图可以以点对点的网络形式表现数据交互过程。rqt_graph能够创建一个显示当前系统运行情况的动态图形。
# 安装rqt_graph
# <distro>是ROS的版本,例如:kinetic、melodic、Noetic等
sudo apt install ros-<distro>-rqt
sudo apt install ros-<distro>-rqt-common-plugins
# 启动rqt_graph
rosrun rqt_graph rqt_graph
四、机器人系统建模与仿真
1. 机器人系统仿真
机器人系统仿真是通过计算机对实体机器人系统进行模拟的技术。在ROS中,仿真实现涉及的内容主要有三:对机器人建模(URDF)、创建仿真环境(Gazebo)以及感知环境(Rviz)等系统性实现。
2. 机器人系统仿真所需组件
2.1 创建机器人模型(URDF)
URDF (Unified Robot Description Format,统一(标准化)机器人描述格式),以XML的方式描述机器人的部分结构,比如地盘、摄像头、激光雷达、机械臂以及不同关节的自由度,该文件可以被C++内置的解释器转换成可视化的机器人模型,是ROS中实现机器人仿真的重要组件。
2.2 显示各种传感器信息(rviz)
关于rviz相关介绍, 请查阅:常用ROS工具包相关介绍
2.3 搭建仿真环境(Gazebo)
关于Gazebo相关介绍, 请查阅:常用ROS工具包相关介绍
五、ROS Bag
ROS Bag是一种文件格式,用于存储ROS系统中的消息。ROS Bag可以将ROS节点发布的消息记录下来,然后在需要的时候将这些消息再次播放回ROS系统中。
1. ROS Bag适用场景
- 离线数据处理:在无法连接实时ROS系统时,可以使用ROS Bag记录消息,然后在离线状态下进行数据处理和算法开发;
- 数据集手机:ROS Bag可以用于收集数据集,以用于机器学习和深度学习等应用;
- 调试和测试:ROS Bag可以用于调试和测试ROS节点和程序的行为。
2. 生成ROS Bag文件的方式
ROS Bag文件以 .bag
拓展名结尾,可以通过以下两种方式保存:
rosbag
方式。在终端中使用rosbag record
命令来记录指定主题的消息,将消息保存到ROS Bag文件中。- ROS API方式。在ROS程序中使用ROS API来记录消息,将消息保存到ROS Bag文件中。
2.1 rosbag
方式
rosbag
方式,参见rosbag指令。
2.2 ROS API方式
以下代码段将记录名为/scan
的激光雷达数据并将其保存到名为scan.bag
的文件中。
C++代码示例
#include <ros/ros.h>
#include <sensor_msgs/LaserScan.h>
#include <rosbag/bag.h>
void scanCallback(const sensor_msgs::LaserScan::ConstPtr& msg)
{
static rosbag::Bag bag;
if (!bag.isOpen()) {
bag.open("scan.bag", rosbag::bagmode::Write);
}
bag.write("/scan", ros::Time::now(), *msg);
/*
当程序退出时,ROS Bag 的析构函数会自动关闭文件。
如果您想手动关闭文件,可以在程序退出前显式地删除ROS Bag对象或调用rosbag::Bag::close()方法来关闭
文件。
*/
}
int main(int argc, char** argv)
{
ros::init(argc, argv, "scan_logger");
ros::NodeHandle nh;
ros::Subscriber sub = nh.subscribe<sensor_msgs::LaserScan>("/scan", 1, scanCallback);
ros::spin();
return 0;
}
python代码示例
#!/usr/bin/env python
import rospy
import rosbag
from sensor_msgs.msg import LaserScan
def scanCallback(msg):
global bag
if bag is None:
bag = rosbag.Bag('scan.bag', 'w')
bag.write('/scan', msg)
if __name__ == '__main__':
rospy.init_node('scan_logger')
bag = None
sub = rospy.Subscriber('/scan', LaserScan, scanCallback)
rospy.spin()
bag.close()
3. 解析ROS Bag文件
rosbag::View
是ROS Bag文件中的一组消息的视图,它提供了一些方便的方法来遍历和访问这些消息。
rosbag::View
实际上是一个包含 rosbag::Connection
对象的集合,每个对象代表一个主题。rosbag::Connection
对象包含一组时间戳和消息,表示该主题的所有消息。您可以使用以下方法来访问这些消息:
begin()
:返回指向第一条消息的迭代器。end()
:返回指向最后一条消息后面的位置的迭代器。rbegin()
:返回指向最后一条消息的迭代器。rend()
:返回指向第一条消息前面的位置的迭代器。size()
:返回视图中消息的数量。
在使用 rosbag::View
遍历Bag文件中的所有消息时,每个迭代器会返回一个 rosbag::MessageInstance
对象。这个对象包含了消息的时间戳、消息类型和消息数据等信息。可以使用以下方法来访问这些信息:
getTime()
:返回消息的时间戳。getTopic()
:返回消息所属的主题名称。getDataType()
:返回消息的数据类型。instantiate()
:将消息实例化为指定的数据类型。如果无法实`例化,则返回空指针。
3.1 C++代码示例
#include <ros/ros.h>
#include <rosbag/bag.h>
#include <std_msgs/String.h>
int main(int argc, char** argv) {
// 初始化ROS节点
ros::init(argc, argv, "rosbag_parser");
ros::NodeHandle n;
// 打开bag文件
rosbag::Bag bag;
bag.open("/path/to/bagfile.bag", rosbag::bagmode::Read);
// 遍历bag文件中的所有消息
rosbag::View view(bag);
for (rosbag::MessageInstance const m : view) {
std_msgs::String::ConstPtr msg = m.instantiate<std_msgs::String>();
if (msg != nullptr && m.getTopic() == "/my_topic") {
// 打印消息内容
ROS_INFO("Message: %s", msg->data.c_str());
}
}
// 关闭bag文件
bag.close();
return 0;
}
3.2 python代码示例
import rosbag
import rospy
from std_msgs.msg import String
# 初始化ROS节点
rospy.init_node('rosbag_parser')
# 打开bag文件
bag = rosbag.Bag('/path/to/bagfile.bag')
# 遍历bag文件中的所有消息
for topic, msg, t in bag.read_messages():
# 检查消息类型和主题名称
if topic == '/my_topic' and isinstance(msg, String):
# 打印消息内容
rospy.loginfo("Message: %s", msg.data)
# 关闭bag文件
bag.close()
在这个示例中,我们使用了ROS提供的rospy.loginfo
函数来输出消息,它类似于Python的print
函数,但是它会将消息输出到ROS的日志系统,这有助于调试和记录机器人的运行情况。
python执行
rosrun <package_name> <filename.py>
3.3 C++与Python接口对比
C++与Python 解析ROS Bag文件两种方式的对比。
- Python 中使用
rosbag.Bag
读取 Bag 文件,C++ 中使用rosbag::Bag
。 - Python 中使用
bag.read_messages()
方法遍历 Bag 文件中的消息,C++ 中使用rosbag::View
类和 C++11 的范围 for 循环来遍历消息。 - Python 中的 t 变量表示消息的时间戳,C++ 中使用
rosbag::MessageInstance::getTime()
方法来获取消息的时间戳。 - Python 中使用 msg 变量来存储消息的内容,C++ 中使用
rosbag::MessageInstance::instantiate()
方法将消息实例化为指定的数据类型,如果无法实例化,则返回空指针。 - Python 中使用 topic 变量来表示消息所属的主题,C++ 中使用
rosbag::MessageInstance::getTopic()
方法来获取消息所属的主题名称。 - Python 中使用
rospy.loginfo()
函数来输出消息,C++ 中可以使用 ROS 的日志系统来输出消息,例如使用 ROS_INFO 宏来输出消息。
使用 C++ 代码来解析 ROS Bag 文件,大致流程如下:
- 打开 Bag 文件并创建
rosbag::Bag()
对象。 - 创建
rosbag::View()
对象并遍历 Bag 文件中的消息。 - 对于每条消息,使用
rosbag::MessageInstance::instantiate()
方法将消息实例化为指定的数据类型,并使用rosbag::MessageInstance::getTopic()
方法获取消息所属的主题名称和rosbag::MessageInstance::getTime()
方法获取消息的时间戳。 - 对于符合条件的消息,使用 ROS 的日志系统或其他方式输出消息内容。
- 关闭 Bag 文件。
六、ROS相关经验
1. ROS Melodic
待补充。