ROS初始化函数
argc------------封装实参的个数(n+1)
argv------------封装参数的数组
name----------为 节点命名,需要保证其唯一性
options---------节点启动选项
返回值:void
如果按照ROS中的特定格式传入实参,那么ROS可以加以使用,比如可以用来设置全局参数、给节点重命名
传参用法:
source--------------- 刷新环境变量,就是刷新本地文件
plumbing_apis----------功能包名
demo01——apis——pub节点名
length——参数名
2——具体值
_length:=2
结果:
节点名称需要保证唯一性,会导致:同一个节点不能重复启动。
ROS中当有重名的节点启动时,之前启动的同名节点会被关闭。
需求:特定场景下,需要一个节点多次启动且能正常运行
解决方法:设置启动项
机制:当创建ROS节点时,会在用户自定义节点名称("ergouzi")后加入随机数,从而避免重名问题
在 roscpp 中,话题和服务的相关对象一般由 NodeHandle 创建。
作用:创建发布者对象
模板:被发布的消息的类型
话题名称
队列长度
latch(可选) 如果设置为true,会保存发布方的最后一条消息,并且新的订阅对象连接到发布方时,发布方会将这条消息发送给订阅者;
使用:
latch设置为true的作用
以静态地图发布为例, 方案1:可以使用固定频率发送地图数据,但是效率过低;
方案2:可以将地图发布对象的latch设置为true,并且发布方之发送一次数据,每当订阅者连接时,将地图数据发送给订阅者(只发送一次),这样提高了数据的发送效率
使用细节:
在ROS程序中,频繁的使用了 ros::spin() 和 ros::spinOnce() 两个回旋函数,可以用于处理回调函数。
/**
* \brief 处理一轮回调
*
* 一般应用场景:
* 在循环体内,处理所有可用的回调函数
*
*/
ROSCPP_DECL void spinOnce();
/**
* \brief 进入循环处理回调
*/
ROSCPP_DECL void spin();
相同点:二者都用于处理回调函数;
不同点:ros::spin() 是进入了循环执行回调函数,而 ros::spinOnce() 只会执行一次回调函数(没有循环),在 ros::spin() 后的语句不会执行到,而 ros::spinOnce() 后的语句可以执行。
ROS中时间相关的API是极其常用,比如:获取当前时刻、持续时间的设置、执行频率、休眠、定时器...都与时间相关。
获取时刻,或是设置指定时刻:
ros::init(argc,argv,"hello_time");
ros::NodeHandle nh;//必须创建句柄,否则时间没有初始化,导致后续API调用失败
ros::Time right_now = ros::Time::now();//将当前时刻封装成对象
ROS_INFO("当前时刻:%.2f",right_now.toSec());//获取距离 1970年01月01日 00:00:00 的秒数
ROS_INFO("当前时刻:%d",right_now.sec);//获取距离 1970年01月01日 00:00:00 的秒数
ros::Time someTime(100,100000000);// 参数1:秒数 参数2:纳秒
ROS_INFO("时刻:%.2f",someTime.toSec()); //100.10
ros::Time someTime2(100.3);//直接传入 double 类型的秒数
ROS_INFO("时刻:%.2f",someTime2.toSec()); //100.30
设置一个时间区间(间隔):
ROS_INFO("当前时刻:%.2f",ros::Time::now().toSec());
ros::Duration du(10);//持续10秒钟,参数是double类型的,以秒为单位
du.sleep();//按照指定的持续时间休眠
ROS_INFO("持续时间:%.2f",du.toSec());//将持续时间换算成秒
ROS_INFO("当前时刻:%.2f",ros::Time::now().toSec());
为了方便使用,ROS中提供了时间与时刻的运算:
获取开始执行的时刻--->模拟运行时间(N秒)---->计算结束时刻=开始+持续时间
ROS_INFO("时间运算");
ros::Time now = ros::Time::now();
ros::Duration du1(10);
ros::Duration du2(20);
ROS_INFO("当前时刻:%.2f",now.toSec());
//1.time 与 duration 运算
ros::Time after_now = now + du1;
ros::Time before_now = now - du1;
ROS_INFO("当前时刻之后:%.2f",after_now.toSec());
ROS_INFO("当前时刻之前:%.2f",before_now.toSec());
//2.duration 之间相互运算
ros::Duration du3 = du1 + du2;
ros::Duration du4 = du1 - du2;
ROS_INFO("du3 = %.2f",du3.toSec());
ROS_INFO("du4 = %.2f",du4.toSec());
//PS: time 与 time 不可以运算
// ros::Time nn = now + before_now;//异常
ros::Rate rate(1);//指定频率
while (true)
{
ROS_INFO("-----------code----------");
rate.sleep();//休眠,休眠时间 = 1 / 频率。
}
ROS 中内置了专门的定时器,可以实现与 ros::Rate 类似的效果:
ros::NodeHandle nh;//必须创建句柄,否则时间没有初始化,导致后续API调用失败
// ROS 定时器
/**
* \brief 创建一个定时器,按照指定频率调用回调函数。
*
* \param period 时间间隔
* \param callback 回调函数
* \param oneshot 如果设置为 true,只执行一次回调函数,设置为 false,就循环执行。
* \param autostart 如果为true,返回已经启动的定时器,设置为 false,需要手动启动。
*/
//Timer createTimer(Duration period, const TimerCallback& callback, bool oneshot = false,
// bool autostart = true) const;
// ros::Timer timer = nh.createTimer(ros::Duration(0.5),doSomeThing);
ros::Timer timer = nh.createTimer(ros::Duration(0.5),doSomeThing,true);//只执行一次
// ros::Timer timer = nh.createTimer(ros::Duration(0.5),doSomeThing,false,false);//需要手动启动
// timer.start();
ros::spin(); //必须 spin
Duration period-------------时间间隔;
const TimerCallback& callback---------回调函数(封装业务)
bool oneshot---------------定时器是否是一次性的
bool autostart-------------定时器是否自动启动
定时器的回调函数:
void doSomeThing(const ros::TimerEvent &event){
ROS_INFO("-------------");
ROS_INFO("event:%s",std::to_string(event.current_real.toSec()).c_str());
}
在发布实现时,一般会循环发布消息,循环的判断条件一般由节点状态来控制,C++中可以通过 ros::ok() 来判断节点状态是否正常,而 python 中则通过 rospy.is_shutdown() 来实现判断,导致节点退出的原因主要有如下几种:
/** \brief 检查节点是否已经退出
*
* ros::shutdown() 被调用且执行完毕后,该函数将会返回 false
*
* \return true 如果节点还健在, false 如果节点已经火化了。
*/
bool ok();
/*
* 关闭节点
*/
void shutdown();
在ROS中日志被划分成如下级别:
ROS_DEBUG("hello,DEBUG"); //不会输出
ROS_INFO("hello,INFO"); //默认白色字体
ROS_WARN("Hello,WARN"); //默认黄色字体
ROS_ERROR("hello,ERROR");//默认红色字体
ROS_FATAL("hello,FATAL");//默认红色字体
主要介绍ROS的C++实现中,如何使用头文件与源文件的方式封装代码,具体内容如下:
在ROS中关于头文件的使用,核心内容在于CMakeLists.txt文件的配置,不同的封装方式,配置上也有差异。
需求:设计头文件,可执行文件本身作为源文件。
流程:
需求:设计头文件与源文件,在可执行文件中包含头文件。
流程: