1.初始化
/*
作用:ROS初始化函数
参数:
1.argc --- 封装实参个数(n+1)
2.argv --- 封装参数的数组
3.name --- 为节点命名(唯一性),节点不可重名
4.options --- 节点启动选项
返回值:void
使用:
1.argc与argv的使用
如果按照ROS中的特定格式传入实参,那么ROS可以加以使用,比如用来设置全局参数、给节点重命名...
2.options的使用
节点名称需要保证唯一,会导致一个问题;同一个节点不能重复启动。
结果:ROS中当有重名的节点启动时,之前的节点就会被关闭。
需求:特定场景下,需要一个节点多次启动且能正常运行,怎么办?
解决:设置启动项 ros::init(argc,argv,"chang_bgColor",ros::init_options::AnonymousName);
当创建ROS节点时,会在用户自定义的节点名称后缀随机数,从而避免重名问题。
*/
void init(int &argc, char **argv, const std::string& name, uint32_t options = 0);
2.话题与服务相关对象
在roscpp中,话题和服务的相关对象一般由NodeHandle创建。NodeHandle有一个重要作用是可以用于设置命名空间。
/*
作用:创建发布者对象
模板:被发布的消息的类型
参数:
1.话题名称
2.队列长度
3.latch(可选) 如果设置为true,会保存为发布方的最后一条消息,并且
新的订阅对象连接到发布方时,发布方会将这条消息发送给订阅者
使用:
latch设置为true的作用?
以静态地图发布为例,方案1:可以使用固定频率发送地图数据,但是效率太低;方案2:可以将
地图发布对象的latch设置为true,并且发布方只发布一次数据,每当订阅者连接时,将地图数
据发送给订阅者(只发送一次),这样提高数据的发送效率。
*/
template <class M>
Publisher advertise(const std::string& topic, uint32_t queue_size, bool latch = false)
3.回旋函数
在ROS程序中,频繁的使用了ros::spin()和ros::spinOnce()两个回旋函数,可以用于处理回调函数。
spinOnce()
/**
* \brief 处理一轮回调
*
* 一般应用场景:
* 在循环体内,处理所有可用的回调函数
*
*/
ROSCPP_DECL void spinOnce();
spin()
/**
* \brief 进入循环处理回调
*/
ROSCPP_DECL void spin();
二者相同点:二者都用于处理回调函数
二者不同点:ros::spin()是进入了循环执行回调函数,而ros::spinOnce()只会执行一次回调函数(没有循环执行回调函数)。在ros::spin()后的语句不会执行,而ros::spinOnce()后的语句可以执行。
4.时间
ROS中时间相关的API是极其常用,比如:获取当前时刻、持续时间的设置、执行频率、休眠、定时器...都与时间相关。
时刻、持续时间、持续时间与时刻运算、设置运行频率、定时器
#include "ros/ros.h"
/*
需求1:演示时间相关操作(获取当前时刻+设置指定时刻)
实现:
1.准备(头文件、节点初始化、NodeHandle创建...)
2.获取当前时刻
3.设置制定时刻
需求2:程序执行中停顿5秒
实现:
1.创建持续时间对象
2.休眠
需求3:已知程序开始运行的时刻和程序运行的时间,求运行结束的时间
实现:
1.获取开始执行的时刻
2.模拟运行时间(N秒)
3.计算结束时刻=开始+持续时间
注意:
1.时刻与持续时间可以执行加减
2.持续时间也可以执行加减
3.时刻之间只可以相减,不可以相加。
需求4:每隔1秒钟,在控制台输出一段文本
文本:
策略1:ros::Rate(1)
策略2:定时器
注意:
创建:nh.createTimer()
参数1:时间间隔
参数2:回调函数(时间时间 TimerEvent)
参数3:是否只执行一次
参数4:是否自动启动(当设置为false,需要手动调用timer.start())
定时器启动后:ros::spin()
*/
//回调函数
void cb(const ros::TimerEvent& event){
ROS_INFO("-----------------");
ROS_INFO("函数被调用的时刻:%.2f",event.current_real.toSec());
}
int main(int argc, char *argv[])
{
/* code */
setlocale(LC_ALL,"");
// 1.准备(头文件、节点初始化、NodeHandle创建...)
ros::init(argc,argv,"hello_time");
ros::NodeHandle nh;//注意:必须,否则会导致时间API调用失败(在NodeHandle会初始化时间操作)
// 2.获取当前时刻
//now函数会将当前时刻封装并返回
//当前时刻:now被调用执行的那一刻
//参考系:1970年01月01日 00:00:00
ros::Time right_now=ros::Time::now();
ROS_INFO("当前时刻:%.2f",right_now.toSec());
ROS_INFO("当前时刻:%d",right_now.sec);
// 3.设置制定时刻
ros::Time t1(20,312345678);
ros::Time t2(100.35);
ROS_INFO("t1=%.2f",t1.toSec());
ROS_INFO("t2=%.2f",t2.toSec());
//--------------------------------------------------------------------------
ROS_INFO("------------------------持续时间--------------------------");
ros::Time start=ros::Time::now();
ROS_INFO("开始休眠:%.2f",start.toSec());
//核心
ros::Duration du(4.5);
du.sleep();//休眠
ros::Time end=ros::Time::now();
ROS_INFO("休眠结束:%.2f",end.toSec());
//--------------------------------------------------------------------------
ROS_INFO("------------------------时间运算--------------------------");
//时刻与持续时间运算
// 1.获取开始执行的时刻
ros::Time begin=ros::Time::now();
// 2.模拟运行时间(N秒)
ros::Duration du1(5);
// 3.计算结束时刻=开始+持续时间
ros::Time stop=begin+du1;//也可以减-
ROS_INFO("开始时刻:%.2f",begin.toSec());
ROS_INFO("结束时刻:%.2f",stop.toSec());
//时刻和时刻运算
//ros::Time sum=begin+stop;//不可以相加
ros::Duration du2=begin-stop;
ROS_INFO("时刻之间相减:%.2f",du2.toSec());
//持续时间与持续时间
ros::Duration du3=du1+du2;//5
ros::Duration du4=du1-du2;//10
ROS_INFO("du1+du2=%.2f",du3.toSec());
ROS_INFO("du1-du2=%.2f",du4.toSec());
//--------------------------------------------------------------------------
ROS_INFO("------------------------定时器--------------------------");
/*
ros::Timer createTimer(ros::Duration period, //时间间隔---1s
const ros::TimerCallback &callback, //回调函数---封装业务
bool oneshot = false, //是否是一次性
bool autostart = true) //自动启动
*/
//ros::Timer timer=nh.createTimer(ros::Duration(1),cb);
//ros::Timer timer=nh.createTimer(ros::Duration(1),cb,true);
ros::Timer timer=nh.createTimer(ros::Duration(1),cb,false,false);
timer.start();//手动启动
ros::spin();//需要回旋
return 0;
}
5.其他函数
在发布实现时,一般会循环发布消息,循环的判断条件一般由节点状态来控制,C++中可以通过ros::ok()来判断节点状态是否正常,导致节点退出的原因主要有如下几种:
节点接收到关闭信息,比如常用的ctrl+c快捷键就是关闭节点的信号;
同名节点启动,导致现有节点退出;
程序中的其他部分调用了节点关闭相关的API(C++是ros::shutdown());
日志相关的函数也是极其常用的,在ROS中日志被划分为如下级别:
DEBUG(调试):只在调试时使用,此类消息不会输出到控制台;
INFO(信息):标准信息,一般用于说明系统内正在执行的操作;
WARN(警告):提醒一些异常情况,但程序仍然可以执行;
ERROR(错误):提示错误信息,此类错误会影响程序运行;
FATAL(严重错误):此类错误将阻止节点接续运行。
节点状态判断
/** \brief 检查节点是否已经退出
*
* ros::shutdown() 被调用且执行完毕后,该函数将会返回 false
*
* \return true 如果节点还健在, false 如果节点已经火化了。
*/
bool ok();
节点关闭函数
/*
* 关闭节点
*/
void shutdown();
日志函数
#include "ros/ros.h"
/*
ROS中日志:
演示不同级别日志的基本使用
*/
int main(int argc, char *argv[])
{
/* code */
setlocale(LC_ALL,"");
ros::init(argc,argv,"hello_log");
ros::NodeHandle nh;
//日志输出
ROS_DEBUG("调试信息");
ROS_INFO("一般信息");
ROS_WARN("警告信息");
ROS_ERROR("错误信息");
ROS_FATAL("严重错误");
return 0;
}