ROS编程: 一些Tips
接触了快一年的ROS, 这段时间个人而言, 似乎完全没在日常的编程中对线程/IPC有所考虑.
这说明ROS是一个很易用的框架, 在编程上了屏蔽了很多系统知识, 可以更加专注于逻辑/算法等.
但是从程序员的角度, 其实还是蛮不安的, 需要了解其背后的机制.
1. 背景
ROS就不再介绍了, 网上有非常多的文档, 我们主要关注下ROS内部的一些行为.
下图是我之前画的ROS内部线程视角的时序图, 看看能不能帮助理解:
一个ROS节点起来后, 主要是有5个线程:
main
- main函数运行的线程
pollmanager
- io运行的线程, 主要是收发topic, 然后把数据queue后待callback消费
xmlrpcmanager
- 处理service连接的线程
internalcallbackqueue
- 调用内部callback的线程
rosoutappender
- rosout线程
其他还有asyncspinner/multithreadspinner线程可以创建, 其执行逻辑可以理解为:
thread_func() {if (特殊callbackqueue)call 特殊callbackqueueelsecall 全局callbackqueue
}
从topic收发的解读理解:
pollmanager部分:
- socket poll + recv
- 把收到的数据放到内部的data queue, 如果data queue满则丢弃
main或者asyncspinner部分:
ros::spin()
- call global_callbackqueue
- 从data queue取出数据, 调用topic的callback
2. Tips
理解完背景, 下面就可以分析一些常见的问题
2.1. spinOnce使用
while ( ros::ok()) {ros::spinOnce();Proc();sleep(50ms);
}
如上写法是很常见的ros程序写法.
ros::spinOnce()
会调用topic allback, 处理完50ms内queue里保存的topic数据.
这种写法会出现几个现象:
spinOnce
50ms调用一次, topic消费慢, 丢包严重spinOnce
耗时大/波动, 导致Proc的延迟大/波动
合适的写法如下:
auto timer_func = [](void) {Proc();
}create_timer(timer_func);
ros::spin();
在timer_func
外的空闲时间, 程序会处理topic callback
, 从而减少丢包和Proc的延迟波动
2.2. AsyncSpinner/MultiThreadedSpinner使用
void callback1() {data = 1;
}void callback2() {cout << data;
}void main() {......ros::AsyncSpinner spinner(4); // Use 4 threadsspinner.start();ros::waitForShutdown();
}
就是没有锁保护的问题....
别笑, 搜一下, 肯定有用错的.
2.3. callback阻塞
默认情况下, 可以认为ros上所有的callback, 包括topic/timer, 全是顺序执行的. 有一个地方出现阻塞/延迟, 就会引起整个节点的卡死/延迟.
void callback_a() {复杂逻辑;
}
void callback_b() {复杂逻辑;
}
void callback_c() {复杂逻辑;
}
在上面的程序中, 很容易出现一种情况, 就是程序花费了大量时间在callback_a
上, 而程序核心可能是callback_c
, 但因为顺序执行的关系, 得不到足够的资源.
合适的做法应该是:
void callback_a() {保存数据;
}
void callback_b() {保存数据;
}
void callback_c() {复杂逻辑;
}
这样牺牲掉a的一些数据, 换来c的稳定执行.
2.4. topic频率过高
jacob@ubuntu:~$ rostopic hz /tf
subscribed to [/tf]
average rate: 957.470min: 0.000s max: 0.010s std dev: 0.00207s window: 939jacob@ubuntu:~$ rostopic bw /tf
subscribed to [/tf]
average: 89.24KB/smean: 0.10KB min: 0.09KB max: 0.10KB window: 100
如上一个topic, 从带宽上看不高, 所以我们很容易认定这个topic"无害".
但从ros内部行为理解, 会发现这种高频的topic会无端消耗非常多的资源.
以十收一发为例:
发送者:
- 1w的Socket Send Per Second
接受者:
- 1k的Socket Read Per Second
- 1k的Callback Call Per Second(如果queue长度够, 没有丢包)
对发送者, 可能光是发送开销就占到单核10-20%.
对接受者来说, 会浪费执行时间在pollmanager/spinner
高频topic处理上.
2.5. 订阅过多topic
在没有架构梳理+基于ros编程的情况下, 我们很容易写出订阅数超过10个topic的节点.
按照之前解释的内部逻辑, 所有topic的socket都在pollmanager
, callback都在global callbackqueue
处理, topic间不区分重要性, 共享处理资源.
这种情况下, 如果出现上述的高频topic, 那有可能影响到其他关键topic, 导致丢包和延迟.
合适的解法:
架构梳理
- Topic合并, 减少Topic数量/频率
- 不订阅非必要的Topic
使用AsyncSpinner
- 要注意有可能增加程序复杂度, 不建议使用
- 合理设置queue_size, 对不重要的topic进行丢包(建议)
- topic callback不做复杂逻辑
- ros层优化, 增加qos特性, 区分topic重要性
2.6. 大topic传输
一个1080p/60fps的RGB图像topic, 其带宽是300MB/s.
如果是基于rostopic socket来传输, 那么因为序列化反序列化的存在, 带宽还要*3.
基础背景:
在一般的x86架构下, 单线程memcpy的速度大概在2000MB+/S.
在risc(arm)架构下, 这个值会低的多, 单线程memcpy的速度大概在5-600MB+/S.
合适的解法:
架构梳理足够强势的情况下, 可以:
- 全链路的图像/lidar使用share dmabuffer轮转, 这样不管是CPU/GPU, 都可以高效使用
- 或者在驱动侧拷贝共享内存轮转
- ros传输优化
2.7. 订阅topic的queue太大
很多人喜欢随意给queue设个100.
这种情况下, 就会出现一次SpinOnce
消费100个数据的情况!
所以不要随意给不重要的topic设置大queue.
2.7. service 长连接/短连接
ros::ServiceClient client = nh.serviceClient<my_package::Foo>("my_service_name", true);
对与频繁调用的ros service来说, 后面的true很重要, 可以建立起长连接.
如果不使用长连接, 每次ros service调用都会消耗掉毫秒级的开销来重新建立连接.
3. 其他
虽然有这个那个的问题, 但是最好不要为了解决这些问题, 用上酷炫的解法.
ros这样做的本意就是不让开发者天马行空.
对于项目而言, 合适的做法应该是整理出系统性的解决方法然后统一处理.
ROS编程: 一些Tips相关推荐
- 12.ROS编程学习:ROS常用指令
目录 rosnode 1.rosnode list--list active nodes 2.rosnode ping--test connectivity to node 3.rosnode inf ...
- 13.ROS编程学习:话题发布控制乌龟
目录 准备工作 c++控制乌龟运动 python控制乌龟运动 参考学习资料:赵虚左的课程+古月的ROS机器人开发实践P56的例程. 准备工作 打开ROS乌龟仿真器 roscore rosrun tur ...
- ROS编程入门(C++ 及 思路)
使用Autolabor官方入门教程,笔记内容为注意事项 首页 - Autolabor开源ROS机器人底盘 - 官方网站 ________________________________________ ...
- 1.ROS编程学习:helloworld的c++与python实现
目录 一.c++实现 1.创建工作空间 3.CMakeLists.txt配置 4.catkin_make编译 5.source一下,配置环境变量 6.roscore+rosrun 二.python实现 ...
- 7.ROS编程学习:自定义服务数据c++调用
目录 一.准备工作--配置vscode 二.服务端创建 1.创建文件demo01_server.cpp 2.服务端的CMakeList.txt配置 3.测试服务端 三.客户端实现 1.创建domo01 ...
- 编程小TIPS:使用函数式风格Either来编程
最近在一些国外的技术博客中见到一个以前自己没太见过的编程风格,那就是Either,觉得非常有意思,稍微了解了下.分享给大家. 同时,我会基于最流行的后端语言Java来简单的演示下如何使用Either. ...
- 机器人操作系统ROS 编程开发--详细总结
最近工作涉及到自动驾驶的,需要学习ROS,学习中总结了一些知识点,分享给大家. ROS基本介绍 机器人操作系统ROS,是一种分布式处理框架(又名Nodes),ROS常用C++和python编程语言开发 ...
- ROS编程入门教程(全过程示例代码)
ROS系统目前主流语言为C++/Python , Python需要安装vscode, 所以本文使用C++ .编译使用catkin (catkin和rosbuild都能用) 首先创建工作空间(worki ...
- ROS编程Raspberry Pi机器人模型
使用机器人操作系统 (ROS) 基础设施将物理机器人连接到机器人模拟是 ROS 工程师面临的最常见挑战之一. 您将学习如何在虚拟环境中模拟机器人,并在等效的真实世界场景中实现所需的行为. 首先介绍了 ...
最新文章
- oracle19c数据库清理,Oracle 19c集群重装
- 独立按键控制数码管c语言,各位大佬,独立按键控制数码管为什么按下的时候要加一呢?...
- 7 天玩转 ASP.NET MVC — 第 6 天
- 软件专业毕业生之一个月攻略
- 【vue】vue.config.js
- SAP Cloud Platform Neo环境的权限管理
- php 迭代器迭代中文时重复,3种方式解决iterator迭代器并发修改异常
- python基础之列表、元组和字典
- C++中的L和_T()
- 十二个开源UML工具
- uniapp app中导出手机号码到通讯录
- 软件测试方案和计划的区别和联系
- 标准C语言程序设计第七版pdf,C语言程序设计 201607.pdf
- 安防大数据在智慧城市建设中的地位与深度应用
- Kubernetes | 《Kubernetes in Action中文版》第8章错误
- ROS 解决pr2_motor_diagnostic_tool/plugin.xml缺失报错
- mac的python换字体_matplotlib在MAC系统下中文字体显示问题
- SQL SERVER 递归查询(4)——递归层级查询
- 深入浅出JavaScript-老杜JavaScript基础教程全套完整版+老杨JS应用篇
- unapp微信小程序转发分享、携带参数