文章目录
roscpp
初始化函数
ros::init() 是节点中第一个调用的函数,参考。一般是将 argc,argv 还有这个节点的名字传入。
Handler
ros::Handler 类。关于消息,服务在第一篇中已经说过。
Timer
创建定时器,每隔一段时间会自动调用函数。感觉像是 ROS 里面自带的多线程。
比如写传感器节点时,希望传感器每隔一段时间发布传感器信息,就可以用定时器。
补充:ROS Time
ROS 中有两种时间,ROS Time 与 Wall Time。ROS Time 指 ROS 网格中的时间。在非仿真环境下和 Wall Time 相同,就是当前时间。但是在仿真环境,或者数据包回放时,这个时间可以被修改(加速,减速),但是 Wall Time 不行。
两种时间类型。Time 指时刻,Duration 指时间间隔。
ros::Time cur = ros::Time::now(); // get current time
注意时间!!在 Rosbag 中有两个时间戳,外层的是记录这个数据包时候的时间,数据里面 header 才是真正数据发布的时间,不要弄混了。
定时器创建
CallbackQueue
回调队列,用于管理和响应 ROS 回调函数。
补充 ros spinning
ROS 将线程模型隐藏,通过 spin 机制调用。单线程 spin:
// one way
ros::init(argc, argv, "my_node");
ros::NodeHandle nh;
ros::Subscriber sub = nh.subscribe(...);
ros::spin();
// 设置调用频率 -- 10Hz
ros::Rate r(10); // 10 hz
while (should_continue)
{
// do some work, publish some messages, etc. ...
ros::spinOnce();
r.sleep();
}
// Use default callback queue
#include <ros/callback_queue.h>
ros::NodeHandle n;
while (ros::ok())
{
// call one the functions with timeout 0.1
ros::getGlobalCallbackQueue()->callAvailable(ros::WallDuration(0.1));
}
Usage
- 配置
也可以自己定义 Callback Queue 将不同速率的任务放在不同队列中,实现速度匹配。配置时,可以在各个 subscire 等函数的 Option 中指定 CallbackQueue。或者是直接用 NodeHandle, 将同一 NodeHandle 下所有消息,服务都指定为一个 CallbackQueue
ros::NodeHandle nh;
nh.setCallbackQueue(&my_callback_queue);
// 使用 for 循环处理回调函数队列
while (ros::ok())
{
// call one the functions with timeout 0.1
my_callback_queue.callAvailable(ros::WallDuration());
}
// 使用 Spinner 处理回调函数队列(Spinner 见下文)
ros::AsyncSpinner spinner(0, &my_callback_queue);
spinner.start();
ros::MultiThreadedSpinner spinner(0);
spinner.spin(&my_callback_queue);
- Manually call
CallbackQueue 有两种方式调用队列中的等待函数。
callAvailable()调用队列中所有的等待函数。callOne()调用当前队列中等待时间最长的函数。
传入参数 Timeout 是空队列等待新的回调函数的时限。
MultiThreadedSpinner & AsyncSpinner
推荐使用
AsyncSpinner。
ros::MultiThreadedSpinner
ros::MultiThreadedSpinner 是阻塞式的。如果创建时不显示声明,会创建与 CPU 核数相同的线程数。
ros::MultiThreadedSpinner spinner(4); // Use 4 threads
spinner.spin(); // spin() will not return until the node has been shutdown
ros::AsyncSpinner
ros::AsyncSpinner 是非阻塞式的。Instead of a blocking spin() call, it has start() and stop() calls, and will automatically stop when it is destroyed. (or stop() / ros::shutdown() is called) Please note that the ros::waitForShutdown() function does not spin on its own, so the example above will spin with 4 threads in total.
ros::AsyncSpinner spinner(4); // Use 4 threads
spinner.start();
ros::waitForShutdown();
Logging
日志文件位置:Everything enabled goes into the log file. Your node’s log file will be in ~/.ros/log unless you override it with the ROS_HOME or ROS_LOG_DIR environment variables.
使用 rqt_console 实时查看日志信息。
设置等级
总共有五等:DEBUG, LOG, WARN, ERROR, FATAL,命名规则是ROS_<verbosity level>[_STREAM][_<other>]
Logging Output
- stdout
DEBUG and INFO messages arrive on stdout if they are enabled. Note that this may not be sent to the screen depending on the value of the roslaunch/XML/node output parameter.
指 launch 文件中
output = "screen | log"
- stderr
WARN, ERROR and FATAL messages arrive on stderr if they are enabled.
Setting Verbosity Levels
- through a configuration file which sets the verbosity level for every node in the system.
- the second is at runtime through the rqt_logger_level (former rxloggerlevel) or rqt_console (former rxconsole) tools.
- the third is programmatically through the rosconsole API (*)
#include <ros/console.h>
if( ros::console::set_logger_level(ROSCONSOLE_DEFAULT_NAME, ros::console::levels::Debug) ) {
ros::console::notifyLoggerLevelsChanged();
}
命名空间
ROS 中元素(Node, Parameters, Topics, Services)都有自己的名字,这就需要一个统一的管理方式 – 命名空间。ROS 的命名方式和 Linux 文件管理相似。命名默认使用相对路径,这样即使变量名相同,两个包可以分别调用各自变量而无需担心冲突。
- relative 命名
对于在 /wg 下的节点 node1,如果使用"node2"则根据相对路径解析到/wg/node2。
global 命名,同 Linux
private 命名
对于在 /wg 下的节点 node1,如果使用 "~/param1" 则解析到 /wg/node1/param1 。
Remapping
remap 应用于如下场景:比如在迁移一个 CV 软件包时,里面接受图片是用的 camera 话题,但实际图片由相机发布在 "/kinetic/left/color"。如果不想更改代码,可以使用 remap 方法,将
<remap from="/kinetic/left/color" to="camera"/>
remapping 规则见 link。
参数服务器(Parameter Server)
运行机器人节点时传入参数:相机的内参,雷达的有效距离。ROS 使用参数服务器管理参数。可以通过 roslaunch 在启动时设置参数,也可以通过程序在运行时动态设置
Get Param
推荐使用 nh.getParam() 函数读取,nh.setParam() 设置。