目录
初始化函数 init() 之 option
void ros::init (int& argc,
char ** argv,
const std::string & name,
uint32_t options = 0
)
- options 是4个参数中的可选项,当在 init() 函数中加入参数 ros::init_options::AnonymousName 时,同一个节点在同一时间段可以运行多个,因为这一参数会将这些节点名称之后添加随机数用于区分。
- 常用于:一个节点需要同时启动多个
init() 需要注意:该函数可能会在运行过程中修改一些参数:- argc:参数量会减少。
- 在使用 launch 文件启动该节点时,该节点默认会传入三个参数,分别是 程序文件路径、节点名称、log文件路径
- 在运行 init() 之后会将 argc 值减2,argv 中存储节点名称和log记录的下标进行互换
- init() 库函数说明
* This function will parse any ROS arguments (e.g., topic name
* remappings), and will consume them (i.e., argc and argv may be modified
* as a result of this call).
中文释义:
*此函数将解析任何ROS参数(例如,主题名称
*重新映射),并将使用它们(即,可能会修改argc和argv
*作为该调用的结果)。
消息打印格式(以 ROS_INFO_STREAM 为例)
此处直接跳转至chill_chill博主的文章:ROS_INFO_STREAM输出彩色信息
话题发布者 NodeHandle::advertise() 函数之 latch
Publisher ros::NodeHandle::advertise(const std::string & topic,
uint32_t queue_size,
bool latch = false
)
- latch 是3个参数中的可选项,当该参数值设置为true时,在发布者运行情况下,无论发布者是否在发布数据,新注册的订阅者均能接收到发布者最后发布的数据,即此时发布者总会保存队列中最新的一个数据。
- 常用于:发布者只发布一次或者运行时偶尔发布数据,但有在新的订阅者加入时,均能够获得发布者最新的一个消息。
- 例如,机器人导航中用到的静态地图,只需启动时发布一次即可,新加入的订阅者需要时都可以获取到。
话题订阅者回旋函数之 spin()、spinOnce()
- spin():当执行到该语句时,程序会卡在此处不再继续执行之后的操作,转而开始重复调用回调函数( 专指订阅者中的 callback 函数)
- 最初在学这一块儿的时候,会想:那我把 sipon() 放到循环里呢?这里就涉及到编程的循环语法上,循环语句只是重复执行一块儿代码,抛开 sipn() 函数本身的功能,当执行到 spin() 时又会重新执行该片段的程序,就等同于执行之后的程序
- spinOnce():只调用一次回调函数,并继续执行之后的操作
- 把 spinOnce() 嵌套入循环中时,会起到循环调用回调函数的功能
- 综上,二者的功能只能算是 spinOnce() + 循环 ≈ spin(),只有循环调用回调函数这一条功能一样,其他功能不同
时间函数
基本函数之ros::Time::now()、ros::Time()、ros::Duration()、ros::Rate()
- 时间函数在使用之前需要进行初始化
ros::Time::init();
一般情况下,在编写 ROS 程序时不会单独去写这一行,因为在初始化句柄 NodeHandle 的同时会对时间函数也进行初始化,所以为了减少代码量,只需要初始化句柄即可
- ros::Time::now(): 获取当前的时刻,默认单位为 秒( s ),是以 1970年01月01日 00:00:00 为初始时间
- ros::Time(): 设置指定时刻,该函数主要有两种形式,如下所示,结果均相同
-
- 指定秒值和纳秒值,时刻值为二者相加所得值
-
- 直接指定时刻值
-
// 参数1单位为s,参数2单位为ns,即指定时刻为 参数1+参数2
ros::Time t1(10, 123456789);
// 直接指定时刻
ros::Time t2(10.123456789);
- ros::Duration(): 设置持续时间,使用 sleep() 函数来直观展示持续时间,样例如下:
// 持续时间
ROS_INFO("现在时刻:%.2f,开始休眠...", ros::Time::now().toSec());
ros::Duration du(3.5); // 设置休眠时间
du.sleep(); // 休眠
ROS_INFO("现在时刻:%.2f,休眠结束。", ros::Time::now().toSec());
-
时间运算(用到的自变量如下)
ros::Time start_time = ros::Time::now(); ros::Duration duration_time1(5); ros::Duration duration_time2(3);
-
时刻之间,只可进行相减运算,结果的数据类型为持续时间(ros::Duration())
// 时刻之间,只可进行相减运算 ROS_INFO("时间间隔为 %.2f", (end_time - start_time).toSec());
-
持续时间之间,可进行加减运算,结果的数据类型为持续时间(ros::Duration())
// 持续时间之间,可进行相加减运算 ROS_INFO("持续时间之和为 %.2f", (duration_time1 + duration_time2).toSec()); ROS_INFO("持续时间之和为 %.2f", (duration_time1 - duration_time2).toSec());
-
时刻与持续时间之间,可进行加减运算,结果的数据类型为时刻(ros::Time())
// 时刻与持续时间,可以进行加减运算 ros::Time end_time = start_time + duration_time1; ROS_INFO("开始时刻为 %.2f", start_time.toSec()); ROS_INFO("结束时刻为 %.2f", end_time.toSec());
-
-
ros::Rate(): 设置运行频率,当一个周期循环的时间满足频率,则通过使用 sleep() 函数来满足
ros::Rate r(10); // 指定以 10Hz 的频率运行
while(ros::ok())
{
r.sleep(); // 如果一个周期内时间未用完,则休眠一定时间以满足频率
}
定时器函数 creatTimer()
Timer ros::NodeHandle::createTimer ( Duration period,
const TimerCallback & callback,
bool oneshot = false,
bool autostart = true
)
- Duration: 时间间隔
- callback: 定时器回调函数
- oneshot: 若为 true,回调函数只执行一次
- autostart: 若为 false,需要运行 start() 启动,如下
// onshot 与 autostart 均为 false 的情况,需要手动启动
ros::Timer timer = nh.createTimer(ros::Duration(1), eventCallback, false, false);
timer.start(); // 手动启动
由于定时器函数可能会涉及到回调函数,所以需要加入回旋函数 spin() 或 spiOnce() 来调用回调函数
- 时间API 参考代码如下:
#include "ros/ros.h"
void eventCallback(const ros::TimerEvent &event)
{
ROS_INFO("定时器回调函数,被调用时间:%.2f", event.current_real.toSec());
}
int main(int argc, char *argv[])
{
// 设置中文编码
setlocale(LC_ALL, "");
// ros初始化
ros::init(argc, argv, "api_time_node");
// 句柄初始化
ros::NodeHandle nh; // 句柄初始化时,会同时运行 ros::Time::init();,可以尝试注释该行运行看一看
// ros::Time::init();
// 获取当前时间
ros::Time right_now = ros::Time::now(); // 从初始时间1970年01月01日 00:00:00,到现在所经历的时间
std::cout << "ros::Time::now() = " << right_now << std::endl;
ROS_INFO("当前时间(浮点型):%.2f", right_now.toSec());
ROS_INFO("当前时间(整型):%d", right_now.sec);
// 设置指定时刻
ros::Time t1(10, 123456789); // 参数1单位为s,参数2单位为ns,即指定时刻为 参数1+参数2
ros::Time t2(10.123456789); // 直接指定时刻
ROS_INFO("t1 = %.2f", t1.toSec());
ROS_INFO("t2 = %.2f", t2.toSec());
// 持续时间
ROS_INFO("现在时刻:%.2f,开始休眠...", ros::Time::now().toSec());
ros::Duration du(3.5); // 设置休眠时间
// du.sleep(); // 休眠
ROS_INFO("现在时刻:%.2f,休眠结束。", ros::Time::now().toSec());
// 时间运算
ros::Time start_time = ros::Time::now();
ros::Duration duration_time1(5);
ros::Duration duration_time2(3);
ros::Time end_time = start_time + duration_time1; // 时刻与持续时间,可以进行加减运算
ROS_INFO("开始时刻为 %.2f", start_time.toSec());
ROS_INFO("结束时刻为 %.2f", end_time.toSec());
ROS_INFO("时间间隔为 %.2f", (end_time - start_time).toSec()); // 时刻之间,只可进行相减运算
ROS_INFO("持续时间之和为 %.2f", (duration_time1 + duration_time2).toSec()); // 持续时间之间,可进行相加减运算
ROS_INFO("持续时间之和为 %.2f", (duration_time1 - duration_time2).toSec());
// 定时器
// ros::Timer timer = nh.createTimer(ros::Duration(1), eventCallback); // 默认情况
// ros::Timer timer = nh.createTimer(ros::Duration(1), eventCallback, true); // onshot 为 true,只启动一次回调函数
ros::Timer timer = nh.createTimer(ros::Duration(1), eventCallback, false, false); // onshot 与 autostart 均为 false 的情况,需要手动启动
timer.start(); // 手动启动
ros::spin();
return 0;
}
程序中断函数 shutdown()
- 一般用于需要自动结束当前节点的程序,调用方式为直接在需要结束程序之后加如下一行:
ros::shutdown(); // 结束当前节点程序
日志输出函数
- 共有5个级别,依次如下
// 日志输出
ROS_DEBUG("调试信息"); // 默认情况下不会显示在控制台
ROS_INFO("一般信息");
ROS_WARN("警告信息");
ROS_ERROR("错误信息");
ROS_FATAL("严重错误信息");
- 显示DEBUG的方法;
- 全局设置:
sudo vim $ROS_ROOT/config/rosconsole.config
,将 log4j.logger.ros=INFO 改为 log4j.logger.ros=DEBUG - 单一节点设置:
rqt_logger_level
,找到对应节点名进行设置 (必须是正处于运行状态的节点。另一种在代码中添加打印信息的方式,比较繁琐且会使得代码冗杂,不推荐,此处也不细说,想尝试的可以自行查找)
- 全局设置:
- 建议:
- 不要因为 WARN 是带颜色的打印信息好区分而疯狂用,如果是在开发大工程,这样的打印信息会在后期非常痛苦。
- 如果相区分打印信息可以用 DEBUG, 也是带颜色的,但默认情况是 INFO 需要手动调整打印等级,所以后期与其他模块对接也会方便很多
- 建议是啥信息就选择相应的打印等级,慎重使用 ROS_ERROR、ROS_FATAL 这是代表错误的打印信息