目录
简介
C++
初始化
话题与服务相关对象
回旋函数
ros::spinOnce()
ros::spin()
时间相关API
时刻
持续时间
持续时间与时刻运算
定时器
节点生命周期API
日志输出API
Python
初始化API
anonymous
话题与服务对象相关API
回旋函数
时间相关API
时刻API
持续时间(设置一个间隔)
持续时间与时刻运算
运行频率
定时器 (间隔某个时间间隔执行操作)
节点相关API
节点状态判断
节点关闭函数
日志输出
简介
API个人理解:比如说ros::init这个函数其实你可以在代码中输入这个函数,然后编译后,ros系统内部会根据这个函数来对应重置节点,所以中文把API理解为接口,其实API就是一个命令去改动封装好的东西里的参数,对齐进行使用和更改
首先,建议参考官方API文档或参考源码:
- ROS节点的初始化相关API;
- NodeHandle 的基本使用相关API;
- 话题的发布方,订阅方对象相关API;
- 服务的服务端,客户端对象相关API;
- 时间相关API;
- 日志输出相关API。
参数服务器相关API在第二章已经有详细介绍和应用,在此不再赘述。2.3.2 参数操作A(C++) · Autolabor-ROS机器人入门课程《ROS理论与实践》零基础教程
另请参考:
-
APIs - ROS Wiki
-
roscpp: roscpp
![](https://img-blog.csdnimg.cn/ff75593c819e40728b6095d00cded0f4.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5ZWl5Lmf5LiN5piv55qEcHnkuro=,size_18,color_FFFFFF,t_70,g_se,x_16)
这个包就包含了很多重要的消息封装,速度消息,传感器消息等
是很重要的API
![](https://img-blog.csdnimg.cn/3d30179243844296bf6f271ddaa7638e.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5ZWl5Lmf5LiN5piv55qEcHnkuro=,size_20,color_FFFFFF,t_70,g_se,x_16)
这个库就是之前一直用的很多命令,roscpp、rosparam、rospy等
![](https://img-blog.csdnimg.cn/ba23eed89c1d4e5eb8d878cb86585867.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5ZWl5Lmf5LiN5piv55qEcHnkuro=,size_20,color_FFFFFF,t_70,g_se,x_16)
roscpp是ros一个关于c++实现的库,实现和ros的快速交互
通过话题、服务、参数来进行交互
是一个高性能库实现
还有关于roscpp的API文档roscpp: roscpp
![](https://img-blog.csdnimg.cn/208eec54ee774e619d1b21ef1a75a08a.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5ZWl5Lmf5LiN5piv55qEcHnkuro=,size_20,color_FFFFFF,t_70,g_se,x_16)
上面介绍了常用的API
以ros::NodeHandle为例roscpp: ros::NodeHandle Class Reference
里面介绍了该接口的详细应用
rospy
![](https://img-blog.csdnimg.cn/4388cb198b4c4f909a23a8af23a3cd61.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5ZWl5Lmf5LiN5piv55qEcHnkuro=,size_20,color_FFFFFF,t_70,g_se,x_16)
介绍中说rospy并不是旨在提升性能的,而是为了更便利
C++
初始化
新建功能包
![](https://img-blog.csdnimg.cn/3cbc49b7fee846d4ad6b178b5f56e501.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5ZWl5Lmf5LiN5piv55qEcHnkuro=,size_20,color_FFFFFF,t_70,g_se,x_16)
ros::init(argc,argv,"erGouzi")
/*
参数:
1.argc-----封装实参的个数(N+1)传入的第一个是文件自身
2.argv-----封装参数的数组
3.name-----为节点命名(唯一性)
4.options--节点启动选项
返回值:void
使用:
1.argc与argv的使用
如果按照ROS中的特定格式传入实参,那么ROS可以加以使用,比如用来设置全局参数,给节点重命名
2.options的使用
节点名称需要保证唯一,会导致一个问题,同一个节点不能重复启动,如果重名,之前的节点会被关闭。
需求:特定场景下,需要一个节点多次启动且能正常运行,怎么办,此时就用options
解决:设置启动项 ros::init_options::AnonymousName
当创建ROS节点时,会在用户自定义的节点名称后缀岁数,从而避免重名问题
*/
![](https://img-blog.csdnimg.cn/0269ba23092147a2a06d6fc6a6863f9c.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5ZWl5Lmf5LiN5piv55qEcHnkuro=,size_17,color_FFFFFF,t_70,g_se,x_16)
此时_length就被传递给argv了
ROS就会解析它,把它放进参数服务器
![](https://img-blog.csdnimg.cn/712ad911855b4e8eb48efbe61a51a2a1.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5ZWl5Lmf5LiN5piv55qEcHnkuro=,size_9,color_FFFFFF,t_70,g_se,x_16)
![](https://img-blog.csdnimg.cn/c950cdf3c5fd4bfdb50c6f7ebb41ecfe.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5ZWl5Lmf5LiN5piv55qEcHnkuro=,size_9,color_FFFFFF,t_70,g_se,x_16)
需要一个节点启动多个
![](https://img-blog.csdnimg.cn/b2be1018bbc54e588e0e0d666a87b782.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5ZWl5Lmf5LiN5piv55qEcHnkuro=,size_15,color_FFFFFF,t_70,g_se,x_16)
这样每次启动会给节点名后面加上一个随机数避免了名称重复问题
![](https://img-blog.csdnimg.cn/fa390b84151d46c98c70853289deeadb.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5ZWl5Lmf5LiN5piv55qEcHnkuro=,size_8,color_FFFFFF,t_70,g_se,x_16)
成功一个节点被启动两次
![](https://img-blog.csdnimg.cn/ba97e18aaf0747ed9d2c7fde649453d7.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5ZWl5Lmf5LiN5piv55qEcHnkuro=,size_12,color_FFFFFF,t_70,g_se,x_16)
此时可以看到有后缀
其实就是产生的随机数值(不重复)
话题与服务相关对象
在 roscpp 中,话题和服务的相关对象一般由 NodeHandle 创建。
NodeHandle有一个重要作用是可以用于设置命名空间,这是后期的重点,但是本章暂不介绍。
创建发布对象调用的API是来自
ros::NodeHandle nh;
再通过NodeHandle调用nh.advertise函数
这个函数会返回一个发布者对象
nh.advertise
作用:创建发布者对象
模板:被发布的消息类型
参数:
1.话题名称
2.队列长度
3.latch(可选)
是一个bool值 如果设置了true它会保存发布方最后一条消息,并且新的订阅对象连接到发布方时,发布方会将这条消息发送给订阅者![](https://img-blog.csdnimg.cn/48f7b7167cbf4980b513f20f7df53271.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5ZWl5Lmf5LiN5piv55qEcHnkuro=,size_20,color_FFFFFF,t_70,g_se,x_16)
使用:
latch设置为true的作用?
只发一次地图信息,但将latch设置为true,只要有新的订阅方连接就发送一次
以静态地图发布为例,
方案1:可以使用固定频率发送地图数据,但效率太低了
方案2:可以将latch设置为true,并且发布方只发送一次数据,每当订阅者连接时,将地图数据 发送给订阅者 (只发送一次),这样提高了数据的发送效率
![](https://img-blog.csdnimg.cn/2f624a8b9c204d0d886479218c8113c0.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5ZWl5Lmf5LiN5piv55qEcHnkuro=,size_20,color_FFFFFF,t_70,g_se,x_16)
ctrl+【 组合键是代码对齐
启动终端
先让发布方发完十条消息,此时发布方不发数据了
![](https://img-blog.csdnimg.cn/76e983ff804d491caa0af7066a314e2b.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5ZWl5Lmf5LiN5piv55qEcHnkuro=,size_20,color_FFFFFF,t_70,g_se,x_16)
编写接收方(跟之前的话题通信的代码一致)
![](https://img-blog.csdnimg.cn/dcb5ddb4b9be4a23aa97b2b9d2814149.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5ZWl5Lmf5LiN5piv55qEcHnkuro=,size_20,color_FFFFFF,t_70,g_se,x_16)
然后对应修改配置并且进行编译
运行终端
![](https://img-blog.csdnimg.cn/2da95da5ae604dd1b1f4ee697a49e165.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5ZWl5Lmf5LiN5piv55qEcHnkuro=,size_20,color_FFFFFF,t_70,g_se,x_16)
没有接收到任何数据
然后关掉终端使用latch--》改为true
![](https://img-blog.csdnimg.cn/4e75ec2806484f57bd14d721b69a25a0.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5ZWl5Lmf5LiN5piv55qEcHnkuro=,size_20,color_FFFFFF,t_70,g_se,x_16)
然后编译终端运行
先启动发布方等数据发完
![](https://img-blog.csdnimg.cn/372bdd735a6d4e25abab8c5aa4c5daa6.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5ZWl5Lmf5LiN5piv55qEcHnkuro=,size_20,color_FFFFFF,t_70,g_se,x_16)
数据发完了,然后启动订阅方
![](https://img-blog.csdnimg.cn/93eb704c85234f748cd50248529d6a64.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5ZWl5Lmf5LiN5piv55qEcHnkuro=,size_20,color_FFFFFF,t_70,g_se,x_16)
此时可以看到不同于上面,改为true后,订阅方拿到了最后一条数据
回旋函数
在ROS程序中,频繁的使用了 ros::spin() 和 ros::spinOnce() 两个回旋函数,可以用于处理回调函数。
ros::spinOnce()![](https://img-blog.csdnimg.cn/0b22a1b58f6743acaa3a9a5fcb973ac7.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5ZWl5Lmf5LiN5piv55qEcHnkuro=,size_20,color_FFFFFF,t_70,g_se,x_16)
相同点:二者都用于处理回调函数;
不同点:ros::spin() 是进入了循环执行回调函数,而 ros::spinOnce() 只会执行一次回调函数(没有循环),在 ros::spin() 后的语句不会执行到,而 ros::spinOnce() 后的语句可以执行。
![](https://img-blog.csdnimg.cn/72e79e3897634a3ca4e7e1a30a520c4e.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5ZWl5Lmf5LiN5piv55qEcHnkuro=,size_20,color_FFFFFF,t_70,g_se,x_16)
![](https://img-blog.csdnimg.cn/cf551269671a436384b38f39d0653b68.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5ZWl5Lmf5LiN5piv55qEcHnkuro=,size_20,color_FFFFFF,t_70,g_se,x_16)
加这一句代码,编译执行
![](https://img-blog.csdnimg.cn/ff2c4d658d324aa290f12100bbcaa9a7.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5ZWl5Lmf5LiN5piv55qEcHnkuro=,size_14,color_FFFFFF,t_70,g_se,x_16)
可以看到ros::spinOnce()只回调一次函数(回调到循环前的位置)
ros::spin()![](https://img-blog.csdnimg.cn/98d7fff6370c4383aa930818372586e4.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5ZWl5Lmf5LiN5piv55qEcHnkuro=,size_20,color_FFFFFF,t_70,g_se,x_16)
![](https://img-blog.csdnimg.cn/583205843cad45078cb7383036d7053e.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5ZWl5Lmf5LiN5piv55qEcHnkuro=,size_20,color_FFFFFF,t_70,g_se,x_16)
加上这一句
编译执行
![](https://img-blog.csdnimg.cn/f962966d83d04975be217bde582ffc37.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5ZWl5Lmf5LiN5piv55qEcHnkuro=,size_20,color_FFFFFF,t_70,g_se,x_16)
看不到ROS_INFO的打印信息
证明spin函数回调到回调函数并且让它自身内循环
时间相关API
时刻
ROS中时间相关的API是极其常用,比如:获取当前时刻、持续时间的设置、执行频率、休眠、定时器...都与时间相关。
新建文件并配置
![](https://img-blog.csdnimg.cn/ae2e03c595074e899416bc78f26c6877.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5ZWl5Lmf5LiN5piv55qEcHnkuro=,size_11,color_FFFFFF,t_70,g_se,x_16)
这是初始化操作
注意ros::NodeHandle这一步是初始化时间,如果没有会导致API调用失败
ros::Time right_now::Time::now();
//now函数会将当前时刻封装并返回
//当前时刻:now被调用执行的那一刻
//时间有参考系:(ex:当前时间是参考公元元年计算)计算机的时间参考是参考1970年1月1日00:00:00
(我们是东八区加8个小时)
right_now.toSec()可以将逝去时刻转换为秒为单位,输出数据类型时double类型
right_now.sec返回的时整形的
完整代码如下
![](https://img-blog.csdnimg.cn/63e397e189c943feb623dd6a58b907c6.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5ZWl5Lmf5LiN5piv55qEcHnkuro=,size_20,color_FFFFFF,t_70,g_se,x_16)
编译执行
![](https://img-blog.csdnimg.cn/91580dbd38d64788991cd6d42b747de0.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5ZWl5Lmf5LiN5piv55qEcHnkuro=,size_12,color_FFFFFF,t_70,g_se,x_16)
此时就获取到时间了
ros::Time t1(20,312424);设置指定时刻
ros::Time t2(20.312424);
ROS_INFO("t1=%.2f",t1.toSec());
ROS_INFO("t2=%.2f",t2.toSec());
![](https://img-blog.csdnimg.cn/92623c6557dc457684aa784ecd4fc477.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5ZWl5Lmf5LiN5piv55qEcHnkuro=,size_20,color_FFFFFF,t_70,g_se,x_16)
编译执行
![](https://img-blog.csdnimg.cn/4de54a18729f4aed8a3924928b9c1513.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5ZWl5Lmf5LiN5piv55qEcHnkuro=,size_14,color_FFFFFF,t_70,g_se,x_16)
持续时间
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());
复制以下代码编译执行
![](https://img-blog.csdnimg.cn/0c11c8f2849d48aba76a09169c81c297.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5ZWl5Lmf5LiN5piv55qEcHnkuro=,size_20,color_FFFFFF,t_70,g_se,x_16)
![](https://img-blog.csdnimg.cn/c8d73191f9bd4a5da477b9d224c73b8d.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5ZWl5Lmf5LiN5piv55qEcHnkuro=,size_12,color_FFFFFF,t_70,g_se,x_16)
休眠了4.5s
持续时间与时刻运算
需求3:已知程序开始运行时刻,和程序运行时间,求运行结束时刻
1.获取开始运行时刻
2.模拟运行时间
3.计算时间=开始-持续时间
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;//异常
复制以下代码
![](https://img-blog.csdnimg.cn/3de27de5a5684bea8154de3d63bbfb36.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5ZWl5Lmf5LiN5piv55qEcHnkuro=,size_20,color_FFFFFF,t_70,g_se,x_16)
编译运行
![](https://img-blog.csdnimg.cn/2824e61c16eb459ab032c2ca765629fd.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5ZWl5Lmf5LiN5piv55qEcHnkuro=,size_11,color_FFFFFF,t_70,g_se,x_16)
![](https://img-blog.csdnimg.cn/4bde26ea03fa44e39a51a2e1f395705d.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5ZWl5Lmf5LiN5piv55qEcHnkuro=,size_20,color_FFFFFF,t_70,g_se,x_16)
编译运行
![](https://img-blog.csdnimg.cn/9f40850213784577a2096166bfb1d7e2.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5ZWl5Lmf5LiN5piv55qEcHnkuro=,size_12,color_FFFFFF,t_70,g_se,x_16)
运行时间5s
定时器
ROS 中内置了专门的定时器,可以实现与 ros::Rate 类似的效果:
ros::NodeHandle nh;//必须创建句柄,否则时间没有初始化,导致后续API调用失败
需求4:每隔1s再控制台输出一段文本
1.策略1:ros::Rate()
2.策略2:定时器
ros::Timer =nh.createTimer()
ros::Timer createTimer(ros::Duration period, 时间间隔---1s
const ros::TimerCallback &callback, 回调函数----封装业务(输出文本)
bool oneshot = false, 是否是一次性--根据bool值
bool autostart = true) 自动启动
![](https://img-blog.csdnimg.cn/e0a5b371aeb148c28677d69d20f30842.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5ZWl5Lmf5LiN5piv55qEcHnkuro=,size_20,color_FFFFFF,t_70,g_se,x_16)
编写回调函数(因为需求里就要求输出一段文本,所以就写了一个ROS_INFO
以下为定时器实现
![](https://img-blog.csdnimg.cn/bd2db4ec1d734bfeb393e8d6984b2b78.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5ZWl5Lmf5LiN5piv55qEcHnkuro=,size_20,color_FFFFFF,t_70,g_se,x_16)
打开终端测试
![](https://img-blog.csdnimg.cn/01321afa4dbf406596fc465cb0c34310.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5ZWl5Lmf5LiN5piv55qEcHnkuro=,size_12,color_FFFFFF,t_70,g_se,x_16)
成功
间隔一秒输出文本------
下面是修改bool oneshot这个参数
ros::Timer createTimer(ros::Duration period, 时间间隔---1s
const ros::TimerCallback &callback, 回调函数----封装业务(输出文本)
bool oneshot = false, 是否是一次性--根据bool值
![](https://img-blog.csdnimg.cn/c7ac2a64b72e480780839007572ba435.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5ZWl5Lmf5LiN5piv55qEcHnkuro=,size_20,color_FFFFFF,t_70,g_se,x_16)
![](https://img-blog.csdnimg.cn/b5d36487326b4c65b642141b5d7f5ea7.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5ZWl5Lmf5LiN5piv55qEcHnkuro=,size_12,color_FFFFFF,t_70,g_se,x_16)
此时只输出一条消息
再看最后一个参数
bool autostart = true) 自动启动
![](https://img-blog.csdnimg.cn/bced72605cd04f87a0efd606434dd3b3.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5ZWl5Lmf5LiN5piv55qEcHnkuro=,size_20,color_FFFFFF,t_70,g_se,x_16)
编译执行
![](https://img-blog.csdnimg.cn/22125d6b6bfe4e7cbd7a2369e9e8f898.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5ZWl5Lmf5LiN5piv55qEcHnkuro=,size_17,color_FFFFFF,t_70,g_se,x_16)
未发送消息,这是由于关闭了自动启动
![](https://img-blog.csdnimg.cn/56721c13b6454fbca42b07b1d232356f.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5ZWl5Lmf5LiN5piv55qEcHnkuro=,size_20,color_FFFFFF,t_70,g_se,x_16)
此时可以调用上图的函数进行手动启动
![](https://img-blog.csdnimg.cn/d54527e50eff4ff9b755d22b00bb78d4.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5ZWl5Lmf5LiN5piv55qEcHnkuro=,size_10,color_FFFFFF,t_70,g_se,x_16)
定时器启动成功
下图函数作用为显示函数被调用的时刻![](https://img-blog.csdnimg.cn/38dbf2ea552047fdbacef9bef596dfd6.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5ZWl5Lmf5LiN5piv55qEcHnkuro=,size_20,color_FFFFFF,t_70,g_se,x_16)
编译执行
![](https://img-blog.csdnimg.cn/5e5b03e209bf411fb8108418b810704a.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5ZWl5Lmf5LiN5piv55qEcHnkuro=,size_10,color_FFFFFF,t_70,g_se,x_16)
成功了
注意:
创建:nh.createTimer()
参数一:时间间隔
参数二:回调函数(时间事件 TimerEvent)
参数三:是否只执行一次
参数四:是否自动启动(当设置为false 需要手动调用 time.start())
定时器启动:ros::spin()
节点生命周期API
在发布实现时,一般会循环发布消息,循环的判断条件一般由节点状态来控制,C++中可以通过 ros::ok() 来判断节点状态是否正常
- 节点接收到了关闭信息,比如常用的 ctrl + c 快捷键就是关闭节点的信号;
- 同名节点启动,导致现有节点退出;
- 程序中的其他部分调用了节点关闭相关的API(C++中是ros::shutdown()
![](https://img-blog.csdnimg.cn/48174b6fdaee48fb85ec6da355a977f3.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5ZWl5Lmf5LiN5piv55qEcHnkuro=,size_20,color_FFFFFF,t_70,g_se,x_16)
加上这一段
![](https://img-blog.csdnimg.cn/bf794ea812c24c07bee7a4b02b12bde4.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5ZWl5Lmf5LiN5piv55qEcHnkuro=,size_20,color_FFFFFF,t_70,g_se,x_16)
编译执行
![](https://img-blog.csdnimg.cn/4495d5facb344c53963a187cc757c4c5.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5ZWl5Lmf5LiN5piv55qEcHnkuro=,size_14,color_FFFFFF,t_70,g_se,x_16)
然后自动终止了
日志输出API
日志相关的函数也是极其常用的,在ROS中日志被划分成如下级别:
- DEBUG(调试):只在调试时使用,此类消息不会输出到控制台;
- INFO(信息):标准消息,一般用于说明系统内正在执行的操作;
- WARN(警告):提醒一些异常情况,但程序仍然可以执行;
- ERROR(错误):提示错误信息,此类错误会影响程序运行;
- FATAL(严重错误):此类错误将阻止节点继续运行。
新建文件
并进行相关配置
![](https://img-blog.csdnimg.cn/7032d73fc27a467c89738d5c15a42d1c.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5ZWl5Lmf5LiN5piv55qEcHnkuro=,size_20,color_FFFFFF,t_70,g_se,x_16)
ROS中的日志
演示不同级别的日志基本使用
复制以下代码
ROS_DEBUG("hello,DEBUG"); //不会输出
ROS_INFO("hello,INFO"); //默认白色字体
ROS_WARN("Hello,WARN"); //默认黄色字体
ROS_ERROR("hello,ERROR");//默认红色字体
ROS_FATAL("hello,FATAL");//默认红色字体
ctrl + 】 组合键是右对齐
编译执行
![](https://img-blog.csdnimg.cn/3b0b0ff4d0514a9a9480d341a52ec047.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5ZWl5Lmf5LiN5piv55qEcHnkuro=,size_11,color_FFFFFF,t_70,g_se,x_16)
Python
- ROS节点的初始化相关API;
- NodeHandle 的基本使用相关API;
- 话题的发布方,订阅方对象相关API;
- 服务的服务端,客户端对象相关API;
- 时间相关API;
- 日志输出相关API。
初始化API
新建对应文件和修改配置添加权限
![](https://img-blog.csdnimg.cn/94d97cc3f10040f28cafbe5c8bf1be98.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5ZWl5Lmf5LiN5piv55qEcHnkuro=,size_20,color_FFFFFF,t_70,g_se,x_16)
ros::init_node该API函数作用是初始化
def init_node(name,--设置节点名称
argv=None, -----封装节点调用时传递到=的参数
anonymous=False, ----可以为节点名称生成随机后缀,可以解决重名问题
log_level=None,
disable_rostime=False,
disable_rosout=False,
disable_signals=False,
xmlrpc_port=0,
tcpros_port=0):
不传参的话就是单纯发布
![](https://img-blog.csdnimg.cn/3195e0419aec44dbbe7f950e09e2c174.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5ZWl5Lmf5LiN5piv55qEcHnkuro=,size_13,color_FFFFFF,t_70,g_se,x_16)
当我们传参时
![](https://img-blog.csdnimg.cn/b1df73c33de64fc5b989313e99540ff0.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5ZWl5Lmf5LiN5piv55qEcHnkuro=,size_20,color_FFFFFF,t_70,g_se,x_16)
可以看到参数传入成功
anonymous
anonymous=False, ----可以为节点名称生成随机后缀,可以解决重名问题
此时启动
![](https://img-blog.csdnimg.cn/9a0b76680c8041399004ec66a776ef74.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5ZWl5Lmf5LiN5piv55qEcHnkuro=,size_10,color_FFFFFF,t_70,g_se,x_16)
再开一个终端启动时
![](https://img-blog.csdnimg.cn/8ce73acec05d4712a1d43e780e2a657e.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5ZWl5Lmf5LiN5piv55qEcHnkuro=,size_9,color_FFFFFF,t_70,g_se,x_16)
上面的节点被关闭
如果我们想让一个节点多次启动,就得让它的节点名称每次不一样
![](https://img-blog.csdnimg.cn/6da4ac50644249c3a895a7305a362ebc.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5ZWl5Lmf5LiN5piv55qEcHnkuro=,size_15,color_FFFFFF,t_70,g_se,x_16)
![](https://img-blog.csdnimg.cn/2df0f843eb674377ad24e606788892a8.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5ZWl5Lmf5LiN5piv55qEcHnkuro=,size_10,color_FFFFFF,t_70,g_se,x_16)
![](https://img-blog.csdnimg.cn/7dc594358a9545c8ab9b317849b61f04.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5ZWl5Lmf5LiN5piv55qEcHnkuro=,size_9,color_FFFFFF,t_70,g_se,x_16)
原因是后缀了随机数,避免重复问题
话题与服务对象相关API
class Publisher(Topic):
"""
在ROS master注册为相关话题的发布方
"""
def __init__(self, name, data_class, subscriber_listener=None, tcp_nodelay=False, latch=False, headers=None, queue_size=None):
"""
Constructor
@param name: 话题名称
@type name: str
@param data_class: 消息类型
@param latch: 如果为 true,该话题发布的最后一条消息将被保存,并且后期当有订阅者连接时会将该消息发送给订阅者
@type latch: bool
@param queue_size: 等待发送给订阅者的最大消息数量
@type queue_size: int
"""
latch:bool值,默认是Falese
如果设置为True,可以将最后一条发布数据保存,且后续有连接者时,会将该数据发送给订阅者
复制以下代码
import rospy
from std_msgs.msg import String
if __name__ == "__main__":
#2.初始化 ROS 节点:命名(唯一)
rospy.init_node("talker_p",anonymous=True)
#3.实例化 发布者 对象
pub = rospy.Publisher("chatter",String,queue_size=10,latch=True)
#4.组织被发布的数据,并编写逻辑发布数据
msg = String() #创建 msg 对象
msg_front = "hello 你好"
count = 0 #计数器
# 设置循环频率
rate = rospy.Rate(1)
while not rospy.is_shutdown():
count += 1
#拼接字符串
if count<=10:
msg.data = msg_front + str(count)
pub.publish(msg)
rate.sleep()
rospy.loginfo("写出的数据:%s",msg.data)
rate.sleep()
编译终端实现
![](https://img-blog.csdnimg.cn/7e829ad7d9b34e2683440d5178391c74.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5ZWl5Lmf5LiN5piv55qEcHnkuro=,size_10,color_FFFFFF,t_70,g_se,x_16)
此时订阅了最后一条消息
但如果不改latch
结果如下
![](https://img-blog.csdnimg.cn/689bea0e5f9c4fdd9f0342095daa655b.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5ZWl5Lmf5LiN5piv55qEcHnkuro=,size_10,color_FFFFFF,t_70,g_se,x_16)
没有消息
回旋函数
def spin():
"""
进入循环处理回调
"""
时间相关API
ROS中时间相关的API是极其常用,比如:获取当前时刻、持续时间的设置、执行频率、休眠、定时器...都与时间相关。
'''
需求1:演示时间相关操作(获取当前时刻+设置指定时刻)
需求2:程序执行中停顿五秒
需求3:获取程序开始执行时刻,且已知持续运行时间,计算程序结束时间
需求4:创建定时器,实现类似于ros::Rate 的功能(隔某个时间间隔执行某种操作)
'''
时刻API
![](https://img-blog.csdnimg.cn/3debfb533756488dbb7f5962b9f3d72b.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5ZWl5Lmf5LiN5piv55qEcHnkuro=,size_20,color_FFFFFF,t_70,g_se,x_16)
新建文件并授权编译
复制以下代码
#! /usr/bin/env python
#coding=utf-8
import rospy
if __name__ == "__main__":
rospy.init_node("hello_time")
# 获取当前时刻
right_now = rospy.Time.now()
rospy.loginfo("当前时刻:%.2f",right_now.to_sec())
rospy.loginfo("当前时刻:%.2f",right_now.to_nsec())
# 自定义时刻
some_time1 = rospy.Time(1234.567891011)
some_time2 = rospy.Time(1234,567891011)
rospy.loginfo("设置时刻1:%.2f",some_time1.to_sec())
rospy.loginfo("设置时刻2:%.2f",some_time2.to_sec())
# 从时间创建对象
# some_time3 = rospy.Time.from_seconds(543.21)
some_time3 = rospy.Time.from_sec(543.21) # from_sec 替换了 from_seconds
rospy.loginfo("设置时刻3:%.2f",some_time3.to_sec())
pass
编译并且运行
![](https://img-blog.csdnimg.cn/d2046d85019c44cf8316f69baad89df5.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5ZWl5Lmf5LiN5piv55qEcHnkuro=,size_10,color_FFFFFF,t_70,g_se,x_16)
持续时间(设置一个间隔)
设置一个时间区间(间隔):
加入以下代码
#需求2:程序执行中停顿5s
#1.封装一个时间持续对象
# 持续时间相关API
rospy.loginfo("持续时间测试开始.....")
du = rospy.Duration(3.3)
rospy.loginfo("du1 持续时间:%.2f",du.to_sec())
rospy.sleep(du) #休眠函数
rospy.loginfo("持续时间测试结束.....")
ctrl+ 】时对齐代码
持续时间与时刻运算
'''
需求3:获取程序开始执行的时刻,且已知持续运行时间,计算程序结束的时间
'''
#获取一个时间
rospy.loginfo("时间运算")
now = rospy.Time.now()
du1 = rospy.Duration(10)
du2 = rospy.Duration(20)
rospy.loginfo("当前时刻:%.2f",now.to_sec())
before_now = now - du1
after_now = now + du1
dd = du1 + du2
# now = now + now #非法
rospy.loginfo("之前时刻:%.2f",before_now.to_sec())
rospy.loginfo("之后时刻:%.2f",after_now.to_sec())
rospy.loginfo("持续时间相加:%.2f",dd.to_sec())
注意:时刻之间无法相加,但可以相减
运行频率
# 设置执行频率
rate = rospy.Rate(0.5)
while not rospy.is_shutdown():
rate.sleep() #休眠
rospy.loginfo("+++++++++++++++")
定时器 (间隔某个时间间隔执行操作)
ROS 中内置了专门的定时器,可以实现与 ros::Rate 类似的效果:
需求:创建定时器
#定时器设置
"""
def __init__(self, period, callback, oneshot=False, reset=False):
Constructor.
@param period: 回调函数的时间间隔
@type period: rospy.Duration
@param callback: 回调函数
@type callback: function taking rospy.TimerEvent
@param oneshot: 设置为True,就只执行一次,否则循环执行
@type oneshot: bool
@param reset: if True, timer is reset when rostime moved backward. [default: False]
@type reset: bool
"""
rospy.Timer(rospy.Duration(1),doMsg)
# rospy.Timer(rospy.Duration(1),doMsg,True) # 只执行一次
rospy.spin()
![](https://img-blog.csdnimg.cn/230aa0ac2f31496880da6d7ac5351847.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5ZWl5Lmf5LiN5piv55qEcHnkuro=,size_17,color_FFFFFF,t_70,g_se,x_16)
然后创建回调函数,传入一个变量event
![](https://img-blog.csdnimg.cn/a8d0abff06394286b122c11164d75a4d.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5ZWl5Lmf5LiN5piv55qEcHnkuro=,size_11,color_FFFFFF,t_70,g_se,x_16)
执行结果每隔1s发布info
更改回调函数为
def doMsg(event):
rospy.loginfo("+++++++++++")
rospy.loginfo("当前时刻:%s",str(event.current_real))
这就是里面变量的作用
可以调用回调函数的时刻
执行结果如下
![](https://img-blog.csdnimg.cn/f61a37a494094e2cbc6728b15e04beb4.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5ZWl5Lmf5LiN5piv55qEcHnkuro=,size_10,color_FFFFFF,t_70,g_se,x_16)
节点相关API
- 节点接收到了关闭信息,比如常用的 ctrl + c 快捷键就是关闭节点的信号;
- 同名节点启动,导致现有节点退出;
- 程序中的其他部分调用了节点关闭相关的API(C++中是ros::shutdown(),python中是rospy.signal_shutdown())
节点状态判断
def is_shutdown():
"""
@return: True 如果节点已经被关闭
@rtype: bool
"""
节点关闭函数
def signal_shutdown(reason):
"""
关闭节点
@param reason: 节点关闭的原因,是一个字符串
@type reason: str
"""
![](https://img-blog.csdnimg.cn/3771d9a25e6f449cbeafa0c9de358372.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5ZWl5Lmf5LiN5piv55qEcHnkuro=,size_20,color_FFFFFF,t_70,g_se,x_16)
保存执行
![](https://img-blog.csdnimg.cn/58984e550e8a40dd911830d19903279b.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5ZWl5Lmf5LiN5piv55qEcHnkuro=,size_12,color_FFFFFF,t_70,g_se,x_16)
被关闭
def on_shutdown(h):
"""
节点被关闭时调用的函数
@param h: 关闭时调用的回调函数,此函数无参
@type h: fn()
"""
on_shutdown会在结束时调用回调函数
![](https://img-blog.csdnimg.cn/65ecdc9da438416f89b441187bb4846b.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5ZWl5Lmf5LiN5piv55qEcHnkuro=,size_19,color_FFFFFF,t_70,g_se,x_16)
然后再写一个回调函数
![](https://img-blog.csdnimg.cn/577fd23eaf4a413d9a3288d0c9643ad7.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5ZWl5Lmf5LiN5piv55qEcHnkuro=,size_20,color_FFFFFF,t_70,g_se,x_16)
保存执行
![](https://img-blog.csdnimg.cn/4c32577499654752a781f79a1c9420b0.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5ZWl5Lmf5LiN5piv55qEcHnkuro=,size_11,color_FFFFFF,t_70,g_se,x_16)
日志输出
rospy.logdebug("hello,debug") #不会输出
rospy.loginfo("hello,info") #默认白色字体
rospy.logwarn("hello,warn") #默认黄色字体
rospy.logerr("hello,error") #默认红色字体
rospy.logfatal("hello,fatal") #默认红色字体
![](https://img-blog.csdnimg.cn/37255509f6db4172a318d32d5c24b803.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5ZWl5Lmf5LiN5piv55qEcHnkuro=,size_20,color_FFFFFF,t_70,g_se,x_16)
编译并且执行
![](https://img-blog.csdnimg.cn/545a1dd33a7e431680730fa37be9f538.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5ZWl5Lmf5LiN5piv55qEcHnkuro=,size_10,color_FFFFFF,t_70,g_se,x_16)
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)