文章目录
- 其他文章
- 说明
- ROS
- launch文件
- 基本概念
- 定义
- 用途
- 文件结构
- 根标签
- 常用标签
- \<node>
- \<param>
- \<rosparam>
- \<remap>
- \<include>
- \<arg>
- \<group>
- 示例
- 基本示例
- 嵌套示例
- 使用方法
- 启动 *.launch 文件
- 传递参数
- 总结
- ROS topic
- Topic 的基本概念
- Topic 的工作原理
- 常用命令
- 示例
- 总结
- ROS常用命令
- rosrun
- roslaunch
- rosbag
- 主要功能
- roscore
- rosnode
- rostopic
- rosservice
- rosparam
- rqt
- ros::spin()
- 功能
- 使用场景
- 示例
- 解释
- 替代方案
- 总结
- ros::NodeHandle
- 主要功能
- 示例代码
- 详细解释
- advertise
- 语法和参数
- 参数说明
- 示例
- subscribe
- 语法和参数
- 参数说明
- 示例代码
- 详细解释
- 成员函数作为回调函数
- 详细解释
- ros::Publisher
- 主要功能
- 创建和使用 ros::Publisher
- 示例代码
- 详细解释
- 其他常用方法
- RViz
- RViz 的主要特点
- RViz 的使用
- 启动 RViz
- 加载配置文件
- 示例中的 RViz 配置
- 总结
- EVO
- EVO的主要功能
- 使用EVO进行性能测试的基本步骤
- 示例命令
- 结论
其他文章
- VINS_MONO视觉导航算法【一】基础知识介绍
- VINS_MONO视觉导航算法【二】论文讲解+GPU实现调研
- VINS_MONO视觉导航算法【三】ROS基础知识介绍
- VINS_MONO视觉导航算法【四】VINS_Mono代码解释
说明
这是第三篇,这一部分介绍一些ROS的基础概念,方便后面理解VINS_Mono代码。
ROS
launch文件
*.launch 文件是 ROS(Robot Operating System)中的一种 XML 文件,用于描述和配置多个节点、参数以及其他 ROS 系统组件。通过 *.launch 文件,可以一次性启动多个节点,并设置各种参数,从而简化了复杂系统的启动和配置过程。下面是关于 *.launch 文件的详细解释:
基本概念
定义
.launch 文件: 是一种 XML 文件,用于描述和配置 ROS 系统中的节点、参数、话题、服务等。
roslaunch 命令: 用于启动 *.launch 文件中定义的节点和配置。
用途
启动多个节点: 可以同时启动多个节点,而不需要在多个终端中分别运行 rosrun 命令。
设置参数: 可以在启动文件中设置节点的参数,这些参数会被加载到参数服务器中。
命名空间管理: 可以使用命名空间来组织和隔离节点,避免命名冲突。
重映射话题和服务: 可以在启动文件中重映射话题和服务的名称,使节点之间的通信更加灵活。
嵌套启动文件: 可以在一个启动文件中包含其他启动文件,实现模块化配置。
文件结构
根标签
: 是 *.launch 文件的根标签,所有其他标签都必须包含在这个标签内。
常用标签
<node>
描述: 用于启动一个 ROS 节点。在 *.launch 文件中,节点通过 标签来定义。每个节点代表一个独立的 ROS 进程,可以执行特定的任务,如传感器数据处理、导航算法等。
常用属性:
pkg: 节点所在的功能包名称。
type: 节点的可执行文件名称。
name: 节点启动时的名称。
args: 传递给节点的命令行参数。
output: 控制节点的日志输出,可以是 “screen”(输出到终端)或 “log”(输出到日志文件)。
respawn: 如果节点退出,是否自动重启,可以是 “true” 或 “false”。
required: 如果节点退出,是否终止其他节点,可以是 “true” 或 “false”。
ns: 节点的命名空间。
示例:
<node pkg="my_robot" type="navigation_node" name="nav_node" output="screen">
<param name="target_location" value="room_1" >
<remap from="odom" to="base_odom" >
</node>
- pkg=“my_robot”: 节点所在的包名是 my_robot。
- type=“navigation_node”: 节点的可执行文件名是 navigation_node。
- name=“nav_node”: 节点启动时的名称是 nav_node。
- output=“screen”: 节点的日志输出到终端。
- : 设置节点的参数。
- : 重映射话题。
<param>
描述: 用于设置参数服务器中的参数。
常用属性:
name: 参数的名称。
value: 参数的值。
type: 参数的类型,可以是 “string”, “int”, “float”, “bool”, “yaml” 等。
textfile: 从文本文件中读取参数值。
binfile: 从二进制文件中读取参数值。
command: 通过命令行命令生成参数值。
<rosparam>
描述: 用于从 YAML 文件中加载参数到参数服务器,或从参数服务器中删除参数。
常用属性:
command: 操作类型,可以是 “load”, “dump”, “delete”。
file: YAML 文件的路径。
param: 参数的名称。
<remap>
描述: 用于重映射话题或服务的名称。
常用属性:
from: 原始名称。
to: 新名称。
<include>
描述: 用于包含其他启动文件。
常用属性:
file: 要包含的启动文件的路径。
<arg>
描述: 用于定义启动文件中的变量。
常用属性:
name: 变量的名称。
default: 变量的默认值。
value: 变量的值(如果在命令行中指定了值,则使用命令行中的值)。
<group>
描述: 用于将一组节点或配置项组织在一起。
常用属性:
ns: 组的命名空间。
if: 条件属性,如果条件为真,则执行组内的内容。
unless: 条件属性,如果条件为假,则执行组内的内容。
示例
基本示例
<launch>
<!-- 定义一个参数 -->
<param name="max_velocity" value="1.0" >
<!-- 启动一个节点 -->
<node pkg="my_robot" type="navigation_node" name="nav_node" output="screen">
<!-- 设置节点的参数 -->
<param name="target_location" value="room_1" >
<!-- 重映射话题 -->
<remap from="odom" to="base_odom" >
</node>
<!-- 包含另一个启动文件 -->
<include file="$(find my_robot)/launch/sensors.launch" >
</launch>
嵌套示例
<launch>
<!-- 定义一个变量 -->
<arg name="use_sim_time" default="false" >
<!-- 使用变量 -->
<param name="use_sim_time" value="$(arg use_sim_time)" >
<!-- 启动一个节点组 -->
<group ns="robot1">
<node pkg="my\_robot" type="navigation\_node" name="nav\_node" output="screen">
<param name="target\_location" value="room\_1" />
</node>
<node pkg="my_robot" type="navigation_node" name="nav_node" output="screen">
<param name="target_location" value="room_1" >
</node>
</group>
<!-- 启动另一个节点组 -->
<group ns="robot2" if="$(arg use_sim_time)">
<node pkg="my\_robot" type="navigation\_node" name="nav\_node" output="screen">
<param name="target\_location" value="room\_2" />
</node>
</group>
</launch>
使用方法
启动 *.launch 文件
roslaunch package_name launch_file_name.launch
package_name: 节点所在的功能包名称。
launch_file_name: 启动文件的名称。
传递参数
roslaunch package_name launch_file_name.launch arg_name:=arg_value
arg_name: 变量的名称。
arg_value: 变量的值。
总结
*.launch 文件是 ROS 中非常重要的配置文件,通过它可以方便地管理和启动多个节点,设置参数,重映射话题和服务,以及嵌套其他启动文件。合理使用 *.launch 文件可以大大提高开发效率,简化复杂系统的启动和配置过程。
ROS topic
在 ROS(Robot Operating System)中,topic 是一种通信机制,用于节点之间的异步消息传递。通过 topic,多个节点可以发布和订阅消息,实现数据的共享和交换。以下是关于 ROS topic 的详细解释:
Topic 的基本概念
发布者(Publisher):
发布者是向特定 topic 发送消息的节点。一个发布者可以向多个 topic 发布消息,但通常情况下,一个发布者只向一个 topic 发布消息。
订阅者(Subscriber):
订阅者是从特定 topic 接收消息的节点。一个订阅者可以订阅多个 topic,但通常情况下,一个订阅者只订阅一个 topic。
消息(Message):
消息是发布者和订阅者之间传递的数据单元。每条消息都有一个特定的数据结构,这个结构由 ROS 消息类型(如 std_msgs/String、sensor_msgs/Image 等)定义。
Topic 的工作原理
注册:
当一个节点启动时,它会向 ROS 主节点(master node)注册自己作为一个发布者或订阅者,并指定它所发布的或订阅的 topic。
发现:
主节点维护一个所有注册节点的列表,并将这些信息提供给其他节点。当一个订阅者节点启动时,它会向主节点查询是否有发布者节点正在发布它感兴趣的 topic。如果有,主节点会将发布者的地址提供给订阅者。
连接:
订阅者节点会直接与发布者节点建立 TCP/IP 连接,以便接收消息。这种连接是点对点的,不经过主节点。
消息传递:
发布者节点通过已建立的连接将消息发送给订阅者节点。订阅者节点接收到消息后,可以对其进行处理。
常用命令
列出所有活动 topic:
rostopic list
查看某个 topic 的消息类型:
rostopic type /topic_name
查看某个 topic 的消息内容:
rostopic echo /topic_name
发布消息到某个 topic:
rostopic pub /topic_name std_msgs/String "data: 'Hello, World!'"
查看某个 topic 的频率:
rostopic hz /topic_name
示例
假设有一个发布者节点发布 /chatter topic 的字符串消息,一个订阅者节点订阅该 topic 并打印接收到的消息。
发布者节点代码示例(Python)
#!/usr/bin/env python
import rospy
from std_msgs.msg import String
def talker():
pub = rospy.Publisher('chatter', String, queue_size=10)
rospy.init_node('talker', anonymous=True)
rate = rospy.Rate(1) # 1 Hz
while not rospy.is_shutdown():
hello_str = "Hello, World! %s" % rospy.get_time()
rospy.loginfo(hello_str)
pub.publish(hello_str)
rate.sleep()
pub = rospy.Publisher('chatter', String, queue_size=10)
rospy.init_node('talker', anonymous=True)
rate = rospy.Rate(1) # 1 Hz
while not rospy.is_shutdown():
hello_str = "Hello, World! %s" % rospy.get_time()
rospy.loginfo(hello_str)
pub.publish(hello_str)
rate.sleep()
if name == 'main':
try:
talker()
except rospy.ROSInterruptException:
pass
订阅者节点代码示例(Python)
#!/usr/bin/env python
import rospy
from std_msgs.msg import String
def callback(data):
rospy.loginfo(rospy.get_caller_id() + " I heard %s", data.data)
def listener():
rospy.init_node('listener', anonymous=True)
rospy.Subscriber("chatter", String, callback)
rospy.spin()
if name == 'main':
listener()
总结
ROS topic 是一种轻量级、高效的通信机制,适用于节点之间的异步消息传递。通过发布者和订阅者的模式,ROS topic 实现了数据的解耦和灵活的通信架构。熟悉如何使用 topic 和相关的命令行工具,可以帮助开发者更好地管理和调试 ROS 系统。
ROS常用命令
rosrun
描述: 用于启动单个 ROS 节点。
语法:
rosrun package_name executable_name
示例:
rosrun my_robot navigation_node
用途: 主要用于调试和测试单个节点。
roslaunch
描述: 用于启动 *.launch 文件,可以同时启动多个节点,并设置参数、重映射话题等。
语法:
roslaunch package_name launch_file_name.launch
示例:
roslaunch my_robot navigation.launch
用途: 适用于启动复杂的系统,包含多个节点和参数配置。
rosbag
rosbag 是 ROS(Robot Operating System)中一个非常重要的命令行工具,用于记录和回放ROS话题消息。它主要用于数据采集、测试和回放,帮助开发者收集和分析机器人在实际运行中的数据。
主要功能
记录数据:
使用 rosbag record 命令可以记录指定的话题数据到一个文件中。
例如,记录所有话题的数据:
rosbag record -a
记录特定话题的数据:
rosbag record /topic1 /topic2
回放数据:
使用 rosbag play 命令可以回放记录的数据文件。
例如,回放一个数据文件:
rosbag play my_bag_file.bag
控制回放速度:
rosbag play my_bag_file.bag --rate=0.5 # 慢速回放
查看数据:
使用 rosbag info 命令可以查看数据文件的信息,包括话题列表、消息数量等。
例如,查看数据文件的信息:
rosbag info my_bag_file.bag
转换数据:
使用 rosbag filter 命令可以过滤和转换数据文件。
例如,过滤出特定话题的消息:
rosbag filter input.bag output.bag “topic == ‘/topic1’”
roscore
启动 ROS 核心节点,它是所有 ROS 节点通信的基础。
例如:
roscore
rosnode
查看和管理 ROS 节点。
例如,列出所有活动节点:
rosnode list
查看节点的信息:
rosnode info /node_name
rostopic
查看和管理 ROS 话题。
例如,列出所有活动话题:
rostopic list
查看某个话题的消息类型:
rostopic type /topic_name
发布消息到某个话题:
rostopic pub /topic_name std_msgs/String “data: ‘Hello, World!’”
查看某个话题的消息内容:
rostopic echo /topic_name
rosservice
查看和管理 ROS 服务。
例如,列出所有活动服务:
rosservice list
查看某个服务的服务类型:
rosservice type /service_name
调用某个服务:
rosservice call /service_name “request_data”
rosparam
查看和管理 ROS 参数服务器上的参数。
例如,列出所有参数:
rosparam list
设置参数:
rosparam set /param_name value
获取参数:
rosparam get /param_name
rqt
ROS 的图形用户界面工具,提供了多种插件用于可视化和调试ROS系统。
例如,启动 rqt:
rqt
ros::spin()
ros::spin(); 是 ROS(Robot Operating System)中的一个函数调用,用于启动一个循环,不断处理接收到的消息和服务请求。这是 ROS 节点的标准入口点之一,通常用于节点的主循环中。以下是详细的解释:
功能
消息处理:
ros::spin(); 会进入一个无限循环,监听并处理订阅的 topic 上的消息。
它会调用所有已注册的回调函数,处理接收到的消息。
服务请求处理:
如果节点提供了服务(services),ros::spin(); 也会处理这些服务请求。
定时器处理:
如果节点中有定时器(timers),ros::spin(); 也会处理定时器的回调。
使用场景
单线程节点:对于大多数简单的 ROS 节点,使用 ros::spin(); 是最常见的方式。它会阻塞当前线程,直到节点被关闭。
多线程节点:如果需要更细粒度的控制,可以使用 ros::spinOnce(); 结合自定义的循环来实现多线程处理。
示例
以下是一个简单的 ROS 节点示例,展示了如何使用 ros::spin();:
#include <ros/ros.h>
#include <sensor_msgs/Image.h>
// 回调函数,处理接收到的图像消息
void imageCallback(const sensor_msgs::ImageConstPtr& msg)
{
// 处理图像消息
ROS_INFO("Received an image with timestamp: %f", msg->header.stamp.toSec());
}
int main(int argc, char **argv)
{
// 初始化 ROS 节点
ros::init(argc, argv, "image_subscriber");
// 创建节点句柄
ros::NodeHandle nh;
// 订阅图像话题
ros::Subscriber sub = nh.subscribe("camera/image_raw", 1, imageCallback);
// 进入消息处理循环
ros::spin();
return 0;
}
解释
初始化节点:
ros::init(argc, argv, “image_subscriber”); 初始化 ROS 节点,节点名称为 image_subscriber。
创建节点句柄:
ros::NodeHandle nh; 创建一个节点句柄,用于订阅话题、发布消息等。
订阅话题:
ros::Subscriber sub = nh.subscribe(“camera/image_raw”, 1, imageCallback); 订阅名为 camera/image_raw 的话题,并指定回调函数 imageCallback 处理接收到的消息。
进入消息处理循环:
ros::spin(); 进入消息处理循环,监听并处理订阅的话题上的消息。
替代方案
ros::spinOnce();:
如果需要在主循环中进行其他操作,可以使用 ros::spinOnce(); 结合自定义的循环。
示例:
int main(int argc, char **argv)
{
ros::init(argc, argv, "image_subscriber");
ros::NodeHandle nh;
ros::Subscriber sub = nh.subscribe("camera/image_raw", 1, imageCallback);
while (ros::ok())
{
// 处理一次消息
ros::spinOnce();
// 其他操作
// ...
// 控制循环频率
ros::Rate loop_rate(10); // 10 Hz
loop_rate.sleep();
}
return 0;
ros::init(argc, argv, "image_subscriber");
ros::NodeHandle nh;
ros::Subscriber sub = nh.subscribe("camera/image_raw", 1, imageCallback);
while (ros::ok())
{
// 处理一次消息
ros::spinOnce();
// 其他操作
// ...
// 控制循环频率
ros::Rate loop_rate(10); // 10 Hz
loop_rate.sleep();
}
return 0;
}
在这个示例中,ros::spinOnce(); 会在每次循环中处理一次消息,然后执行其他操作,最后通过 ros::Rate 控制循环频率。
总结
ros::spin(); 是一个简单而强大的函数,适用于大多数单线程的 ROS 节点。它会持续监听并处理消息,直到节点被关闭。如果你需要更细粒度的控制,可以考虑使用 ros::spinOnce(); 结合自定义的循环。
ros::NodeHandle
ros::NodeHandle 是ROS(Robot Operating System)中的一个核心类,用于管理ROS节点的各种资源,包括创建发布者(publishers)、订阅者(subscribers)、服务客户端和服务服务器等。ros::NodeHandle 对象是每个ROS节点的主要入口点,提供了许多方法来与ROS系统进行交互。
主要功能
创建发布者:
使用 advertise<>() 方法创建发布者,用于发布消息到特定的话题。
创建订阅者:
使用 subscribe<>() 方法创建订阅者,用于订阅特定话题的消息。
创建服务客户端:
使用 serviceClient<>() 方法创建服务客户端,用于调用服务。
创建服务服务器:
使用 advertiseService<>() 方法创建服务服务器,用于处理服务请求。
获取参数:
使用 getParam()、param() 等方法获取节点的参数。
设置参数:
使用 setParam() 方法设置节点的参数。
私有命名空间:
使用 ros::NodeHandle nh(“~”) 创建一个私有命名空间的节点句柄,用于访问节点私有的参数。
示例代码
以下是一个简单的示例,展示了如何使用 ros::NodeHandle 创建发布者和订阅者:
#include <ros/ros.h>
#include <std_msgs/String.h>
// 回调函数,处理接收到的消息
void chatterCallback(const std_msgs::String::ConstPtr& msg)
{
ROS_INFO("I heard: [%s]", msg->data.c_str());
}
int main(int argc, char **argv)
{
// 初始化ROS节点
ros::init(argc, argv, "example_node");
// 创建ROS节点句柄
ros::NodeHandle nh;
// 创建发布者,发布字符串消息到 "chatter" 话题
ros::Publisher chatter_pub = nh.advertise<std_msgs::String>("chatter", 1000);
// 创建订阅者,订阅 "chatter" 话题的字符串消息
ros::Subscriber chatter_sub = nh.subscribe("chatter", 1000, chatterCallback);
// 主循环
ros::Rate loop_rate(10); // 10 Hz
int count = 0;
while (ros::ok())
{
// 创建消息
std_msgs::String msg;
std::stringstream ss;
ss << "hello world " << count;
msg.data = ss.str();
// 发布消息
chatter_pub.publish(msg);
// 处理ROS回调函数
ros::spinOnce();
// 控制主循环的频率
loop_rate.sleep();
++count;
}
return 0;
}
详细解释
初始化ROS节点:
ros::init(argc, argv, “example_node”);
初始化ROS节点,传递命令行参数和节点名称。
创建ROS节点句柄:
ros::NodeHandle nh;
创建一个 ros::NodeHandle 对象,用于管理节点的资源。
创建发布者:
ros::Publisher chatter_pub = nh.advertise<std_msgs::String>(“chatter”, 1000);
使用 advertise<>() 方法创建一个发布者,发布 std_msgs::String 类型的消息到 chatter 话题,队列大小为1000。
创建订阅者:
ros::Subscriber chatter_sub = nh.subscribe(“chatter”, 1000, chatterCallback);
使用 subscribe<>() 方法创建一个订阅者,订阅 chatter 话题的 std_msgs::String 类型的消息,并指定回调函数 chatterCallback。
回调函数:
void chatterCallback(const std_msgs::String::ConstPtr& msg)
{
ROS_INFO("I heard: [%s]", msg->data.c_str());
}
回调函数 chatterCallback 处理接收到的消息,并打印消息内容。
主循环:
ros::Rate loop_rate(10); // 10 Hz
int count = 0;
while (ros::ok())
{
// 创建消息
std_msgs::String msg;
std::stringstream ss;
ss << "hello world " << count;
msg.data = ss.str();
// 发布消息
chatter_pub.publish(msg);
// 处理ROS回调函数
ros::spinOnce();
// 控制主循环的频率
loop_rate.sleep();
++count;
}
主循环中创建消息并发布,处理ROS回调函数,并控制主循环的频率。
advertise
在ROS(Robot Operating System)中,n.advertise<>() 是一个用于创建发布者(publisher)的方法。这个方法属于 ros::NodeHandle 类,用于声明一个节点将要发布某个话题的消息。
语法和参数
ros::Publisher advertise(const std::string& topic, uint32_t queue_size, bool latch = false);
参数说明
topic (const std::string& topic):
话题名称,用于标识发布的消息流。例如 “imu_propagate”。
queue_size (uint32_t queue_size):
消息队列的大小。这是发布者内部用于缓冲消息的队列大小。如果消息生成速度快于消息传输速度,队列可以暂时存储多余的消息,防止消息丢失。
latch (bool latch = false):
是否启用消息缓存(可选参数,默认为 false)。如果设置为 true,则最后一条消息会被缓存,新订阅者订阅时会立即收到这条消息,而不需要等待新的消息发布。
示例
ros::Publisher pub_latest_odometry = n.advertise<nav_msgs::Odometry>(“imu_propagate”, 1000);
详细解释
创建发布者对象:
n 是一个 ros::NodeHandle 对象,用于管理ROS节点的各种资源。
advertise<>() 方法创建一个发布者对象,并将其赋值给 pub_latest_odometry 变量。
指定消息类型:
<nav_msgs::Odometry> 是模板参数,指定了发布者将要发布的消息类型。在这个例子中,消息类型是 nav_msgs::Odometry。
指定话题名称:
“imu_propagate” 是话题名称,表示发布的消息将通过这个话题进行传输。
指定队列大小:
1000 是消息队列的大小,表示发布者内部可以缓存最多1000条消息。
subscribe
subscribe<>() 方法是ROS(Robot Operating System)中 ros::NodeHandle 类的一个重要方法,用于创建订阅者(subscriber)。通过订阅者,节点可以接收来自特定话题的消息。订阅者会在每次接收到新消息时调用指定的回调函数。
语法和参数
subscribe(const std::string& topic, uint32_t queue_size, void(*fp)(M const&), const boost::shared_ptr& obj, const TransportHints& transport_hints = TransportHints());
参数说明
topic (const std::string& topic):
话题名称,用于标识订阅的消息流。例如 “chatter”。
queue_size (uint32_t queue_size):
消息队列的大小。这是订阅者内部用于缓冲接收到的消息的队列大小。如果消息生成速度快于消息处理速度,队列可以暂时存储多余的消息,防止消息丢失。
fp (void(*fp)(M const&)):
回调函数指针,用于处理接收到的消息。M 是消息类型,例如 std_msgs::String。
obj (const boost::shared_ptr& obj):
回调函数所属的对象。如果你的回调函数是类的成员函数,需要传递一个指向该对象的智能指针。
transport_hints (const TransportHints& transport_hints = TransportHints()):
传输提示(可选参数),用于指定传输方式,例如TCP或UDP。默认情况下,使用系统的默认传输方式。
示例代码
以下是一个完整的示例,展示了如何使用 subscribe<>() 创建订阅者并处理接收到的消息。
示例代码
#include <ros/ros.h>
#include <std_msgs/String.h>
// 回调函数,处理接收到的消息
void chatterCallback(const std_msgs::String::ConstPtr& msg)
{
ROS_INFO("I heard: [%s]", msg->data.c_str());
}
int main(int argc, char **argv)
{
// 初始化ROS节点
ros::init(argc, argv, "example_subscriber");
// 创建ROS节点句柄
ros::NodeHandle nh;
// 创建订阅者,订阅 "chatter" 话题的字符串消息
ros::Subscriber sub = nh.subscribe("chatter", 1000, chatterCallback);
// 主循环
ros::spin();
return 0;
}
详细解释
初始化ROS节点:
ros::init(argc, argv, “example_subscriber”);
初始化ROS节点,传递命令行参数和节点名称。
创建ROS节点句柄:
ros::NodeHandle nh;
创建一个 ros::NodeHandle 对象,用于管理节点的资源。
创建订阅者:
ros::Subscriber sub = nh.subscribe(“chatter”, 1000, chatterCallback);
使用 subscribe<>() 方法创建一个订阅者,订阅 chatter 话题的 std_msgs::String 类型的消息,队列大小为1000,并指定回调函数 chatterCallback。
回调函数:
void chatterCallback(const std_msgs::String::ConstPtr& msg)
{
ROS_INFO("I heard: [%s]", msg->data.c_str());
}
回调函数 chatterCallback 处理接收到的消息,并打印消息内容。
主循环:
ros::spin();
使用 ros::spin() 进入主循环,处理所有的回调函数。ros::spin() 会一直阻塞,直到节点关闭。
成员函数作为回调函数
如果你的回调函数是类的成员函数,需要传递一个指向该对象的智能指针。以下是一个示例:
示例代码
#include <ros/ros.h>
#include <std_msgs/String.h>
class ExampleSubscriber
{
public:
// 构造函数
ExampleSubscriber()
{
// 创建ROS节点句柄
ros::NodeHandle nh;
// 创建订阅者,订阅 "chatter" 话题的字符串消息
sub = nh.subscribe("chatter", 1000, &ExampleSubscriber::chatterCallback, this);
}
// 回调函数,处理接收到的消息
void chatterCallback(const std_msgs::String::ConstPtr& msg)
{
ROS_INFO("I heard: [%s]", msg->data.c_str());
}
private:
ros::Subscriber sub;
};
int main(int argc, char **argv)
{
// 初始化ROS节点
ros::init(argc, argv, "example_subscriber");
// 创建订阅者对象
ExampleSubscriber example_subscriber;
// 主循环
ros::spin();
return 0;
}
详细解释
构造函数:
ExampleSubscriber()
{
// 创建ROS节点句柄
ros::NodeHandle nh;
// 创建订阅者,订阅 "chatter" 话题的字符串消息
sub = nh.subscribe("chatter", 1000, &ExampleSubscriber::chatterCallback, this);
}
在构造函数中创建ROS节点句柄,并使用 subscribe<>() 方法创建订阅者,订阅 chatter 话题的 std_msgs::String 类型的消息,队列大小为1000,并指定成员函数 chatterCallback 作为回调函数,同时传递 this 指针。
成员函数作为回调函数:
void chatterCallback(const std_msgs::String::ConstPtr& msg)
{
ROS_INFO("I heard: [%s]", msg->data.c_str());
}
成员函数 chatterCallback 处理接收到的消息,并打印消息内容。
ros::Publisher
ros::Publisher 是ROS(Robot Operating System)中的一个类,用于发布消息到特定的话题。通过 ros::Publisher,你可以将数据发送到其他节点订阅的同一个话题,从而实现节点之间的通信。
主要功能
发布消息:
使用 publish() 方法将消息发布到指定的话题。
检查是否有订阅者:
使用 getNumSubscribers() 方法检查当前话题是否有订阅者。
获取话题名称:
使用 getTopic() 方法获取当前发布者的话题名称。
创建和使用 ros::Publisher
创建发布者
创建发布者通常通过 ros::NodeHandle 的 advertise<>() 方法完成。以下是创建发布者的步骤:
初始化ROS节点:
ros::init(argc, argv, “example_node”);
创建ROS节点句柄:
ros::NodeHandle nh;
创建发布者:
ros::Publisher chatter_pub = nh.advertise<std_msgs::String>(“chatter”, 1000);
发布消息
使用 publish() 方法发布消息:
std_msgs::String msg;
msg.data = “Hello, World!”;
chatter_pub.publish(msg);
示例代码
以下是一个完整的示例,展示了如何创建和使用 ros::Publisher:
#include <ros/ros.h>
#include <std_msgs/String.h>
int main(int argc, char **argv)
{
// 初始化ROS节点
ros::init(argc, argv, "example_publisher");
// 创建ROS节点句柄
ros::NodeHandle nh;
// 创建发布者,发布字符串消息到 "chatter" 话题
ros::Publisher chatter_pub = nh.advertise<std_msgs::String>("chatter", 1000);
// 主循环
ros::Rate loop_rate(10); // 10 Hz
int count = 0;
while (ros::ok())
{
// 创建消息
std_msgs::String msg;
std::stringstream ss;
ss << "hello world " << count;
msg.data = ss.str();
// 发布消息
chatter_pub.publish(msg);
// 输出发布信息
ROS_INFO("Published: %s", msg.data.c_str());
// 处理ROS回调函数
ros::spinOnce();
// 控制主循环的频率
loop_rate.sleep();
++count;
}
return 0;
}
详细解释
初始化ROS节点:
ros::init(argc, argv, “example_publisher”);
初始化ROS节点,传递命令行参数和节点名称。
创建ROS节点句柄:
ros::NodeHandle nh;
创建一个 ros::NodeHandle 对象,用于管理节点的资源。
创建发布者:
ros::Publisher chatter_pub = nh.advertise<std_msgs::String>(“chatter”, 1000);
使用 advertise<>() 方法创建一个发布者,发布 std_msgs::String 类型的消息到 chatter 话题,队列大小为1000。
主循环:
ros::Rate loop_rate(10); // 10 Hz
int count = 0;
while (ros::ok())
{
// 创建消息
std_msgs::String msg;
std::stringstream ss;
ss << "hello world " << count;
msg.data = ss.str();
// 发布消息
chatter_pub.publish(msg);
// 输出发布信息
ROS_INFO("Published: %s", msg.data.c_str());
// 处理ROS回调函数
ros::spinOnce();
// 控制主循环的频率
loop_rate.sleep();
++count;
}
在主循环中,创建消息并发布,输出发布信息,处理ROS回调函数,并控制主循环的频率。
其他常用方法
检查是否有订阅者:
int num_subscribers = chatter_pub.getNumSubscribers();
if (num_subscribers > 0)
{
ROS_INFO("There are %d subscribers to the topic.", num_subscribers);
}
获取话题名称:
std::string topic_name = chatter_pub.getTopic();
ROS_INFO(“Topic name: %s”, topic_name.c_str());
RViz
RViz (Robot Visualization)是 Robot Operating System (ROS) 中的一款强大的三维可视化工具,广泛用于机器人系统的开发和调试。它可以帮助开发者以图形化的方式查看和分析机器人模型、传感器数据、环境地图等多种类型的数据。通过 RViz,用户可以直观地了解机器人的状态和行为,从而提高开发和调试的效率。
RViz 的主要特点
可视化机器人模型:
RViz 可以加载和显示机器人的三维模型,并根据实际的关节状态进行动态更新。这使得用户能够直观地了解机器人的外观和姿态。12
显示传感器数据:
RViz 可以接收和显示来自机器人传感器(如激光雷达、摄像头、IMU 等)的数据。用户可以实时查看和分析传感器数据,帮助理解机器人周围的环境。16
生成导航地图:
RViz 可以通过接收来自 SLAM(Simultaneous Localization and Mapping)或其他建图算法的数据,生成并显示机器人所在环境的二维或三维地图。16
调试运动规划:
RViz 可以显示机器人的路径规划结果,并提供交互式界面来调试和优化运动规划算法。用户可以可视化虚拟路径、障碍物和碰撞检测等信息。16
可定制性:
RViz 提供了丰富的配置选项,允许用户按照自己的需求自定义界面布局、可视化对象和颜色风格等。用户可以根据实际情况进行个性化设置,以满足特定的可视化需求。16
RViz 的使用
启动 RViz
RViz 可以通过命令行启动,通常需要先启动 ROS 核心节点 roscore,然后再启动 RViz。启动命令如下:
roscore
rosrun rviz rviz
加载配置文件
RViz 支持加载配置文件,这些文件包含了 RViz 的初始设置,如显示的内容、参数配置等。配置文件通常以 .rviz 为扩展名。在启动 RViz 时,可以通过 -d 参数指定配置文件的路径:
rosrun rviz rviz -d path/to/config_file.rviz
示例中的 RViz 配置
在你提供的 *.launch 文件中,RViz 的启动配置如下:
<launch>
<node name="rvizvisualisation" pkg="rviz" type="rviz" output="log" args="-d $(find vins_estimator)/../config/vins_rviz_config.rviz" >
</launch>
解释
标签: 用于定义一个 ROS 节点。
name=“rvizvisualisation”: 节点的名称为 rvizvisualisation。
pkg=“rviz”: 节点所在的包名是 rviz。
type=“rviz”: 节点的可执行文件名是 rviz。
output=“log”: 节点的日志输出到日志文件。
args=“-d $(find vins_estimator)/…/config/vins_rviz_config.rviz”: 传递给节点的命令行参数,用于指定 RViz 配置文件的路径。
$(find vins_estimator): 这是一个 ROS 变量,用于查找 vins_estimator 包的路径。
/…/config/vins_rviz_config.rviz: 从 vins_estimator 包的路径向上一级目录,然后进入 config 文件夹,找到 vins_rviz_config.rviz 配置文件。
总结
RViz 是 ROS 中一个非常强大的三维可视化工具,通过它可以直观地查看和分析机器人系统的各种数据。通过 *.launch 文件,可以方便地启动 RViz 并加载预设的配置文件,从而快速进入开发和调试状态。在你的示例中,rvizvisualisation 节点启动时会加载 vins_rviz_config.rviz 配置文件,确保 RViz 以预设的显示效果启动。17
EVO
EVO(Evaluation of Visual Odometry / SLAM)是一款开源软件工具,专门用于评估视觉里程计(Visual Odometry, VO)和同时定位与地图构建(Simultaneous Localization and Mapping, SLAM)系统的性能。EVO 提供了一套完整的评估框架,包括数据处理、结果可视化以及多种评价指标计算等功能,适用于研究人员和工程师对不同VO/SLAM算法的性能进行比较和分析。
EVO的主要功能
多格式支持:
EVO 支持多种数据格式,包括但不限于 TUM RGB-D 数据集格式、Kitti 数据集格式、Euroc MAV 数据集格式等,这使得它能够兼容不同的实验数据源。
丰富的评价指标:
EVO 提供了多种评价指标,如绝对轨迹误差(ATE)、相对位姿误差(RPE)等,这些指标能够从不同角度评估VO/SLAM系统的精度和稳定性。
灵活的结果可视化:
EVO 具有强大的结果可视化功能,可以生成轨迹对比图、误差分布图等多种图表,帮助用户直观地理解评估结果。
命令行接口和Python API:
EVO 不仅提供了命令行工具,还提供了Python API,这使得用户可以在脚本中集成EVO的功能,实现自动化评估流程。
易于扩展:
EVO 的设计考虑到了灵活性和可扩展性,用户可以根据需要添加新的数据格式支持或自定义评估指标。
使用EVO进行性能测试的基本步骤
准备数据:
准备好待评估的VO/SLAM系统的输出数据,通常是估计的轨迹文件,格式应符合所选数据集的标准。
获取真值数据:
获取相应的真值数据(ground truth),这些数据通常来自高精度的测量设备或数据集本身提供的真值轨迹。
运行EVO工具:
使用EVO的命令行工具或Python API,将估计的轨迹与真值轨迹进行比较,计算评价指标。
分析结果:
分析EVO生成的报告和图表,评估VO/SLAM系统的性能。
示例命令
以下是一个使用EVO命令行工具评估ATE的例子:
evo_ape tum groundtruth.txt trajectory.txt --plot --plot_mode=xy --save_plot plot.png
evo_ape:调用EVO的绝对轨迹误差评估模块。
tum:指定数据格式为TUM格式。
groundtruth.txt:真值轨迹文件。
trajectory.txt:待评估系统输出的估计轨迹文件。
–plot:生成轨迹对比图。
–plot_mode=xy:指定绘制模式为二维平面图。
–save_plot plot.png:将生成的图表保存为图片文件。
结论
EVO 是一个功能全面且灵活的评估工具,对于从事VO/SLAM研究和开发的人员来说,是非常有用的工具。通过EVO,可以轻松地评估和比较不同算法的性能,从而推动相关技术的发展。