ROS学习(十四):ROS Spinning

本文介绍了ROS中的单线程与多线程Spinning机制,包括ros::spin()与ros::spinOnce()的使用方法,ros::MultiThreadedSpinner与ros::AsyncSpinner的工作原理,以及如何自定义回调序列来实现更复杂的线程管理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

ROS 单线程与多线程 Spinning

roscpp 不为应用程序指定具体的线程模型。

允许回调函数调用任意数量的线程

必须要调用线程,否则订阅、服务等回调将永远不会被调用

常见的解决方案是,在主函数开头用 ros::spin()

注意:回调序列对内部的网络通讯没有影响,只能影响回调发生的时刻。它们将影响订阅序列,取决于处理回调函数的速度、消息到达的速度、消息是否丢弃等。

1、单线程

最简单最常用的单线程为:ros::spin()

ros::init(argc, argv, "my_node");
ros::NodeHandle nh;
ros::Subscriber sub = nh.subscribe(...);
...
ros::spin();

所有用户的调用程序将从 ros::spin()开始调用。只到节点关闭(ros::shutdown() or a Ctrl-C),ros::spin()才有返回值。

另一个常用的模式是周期性的 ros::spinOnce()

ros::Rate r(10); // 10 hz
while (should_continue)
{
  ... do some work, publish some messages, etc. ...
  ros::spinOnce();
  r.sleep();
}

ros::spinOnce()将在那一刻调用所有等待调用的函数。

执行一个自己的spin()函数很简单:

#include <ros/callback_queue.h>
ros::NodeHandle n;
while (ros::ok())
{
  ros::getGlobalCallbackQueue()->callAvailable(ros::WallDuration(0.1));
}

spinOnce() 类似:

#include <ros/callback_queue.h>

ros::getGlobalCallbackQueue()->callAvailable(ros::WallDuration(0));

注:spin()和spinonce()真的意味着单线程应用程序,而不是从多个线程中调用的一次优化。

2、多线程

ros::MultiThreadedSpinner

阻塞式线程,类似于ros::spin(),你可以在它的构造器上指定一定数量的线程数,但如果不指定或设置为0,则会在每个CPU上执行一个线程。

ros::MultiThreadedSpinner spinner(4); // Use 4 threads
spinner.spin(); // spin() will not return until the node has been shutdown

ros::AsyncSpinner (since 0.10)

一个更有用的线程。它不用spin() 回调,而用start()stop(),当它销毁时候就会自动关闭

ros::AsyncSpinner spinner(4); // Use 4 threads
spinner.start();
ros::waitForShutdown();

3、回调序列

创建回调序列:


#include <ros/callback_queue.h>
...
ros::CallbackQueue my_queue;

CallbackQueue 有两种方法调用回调函数: callAvailable() 和callOne()。

callavailable()调用队列里所有的。callone()只会调用回调在队列最旧的。

callAvailable()和callOne()都会有一个超时选项,在返回前,它会在超时时间内等待回调有效。

如果是0,同时队列没有回调,则直接返回。

4、高级回调序列

上面spin()执行的语句,有调用到 ros::getGlobalCallbackQueue(),默认所有的回调都会放到全局队列,由ros::spin() 处理。

自定义回调序列:

1、subscribe(), advertise(), advertiseService()

2、NodeHandle

ros::AsyncSpinner spinner(0, &my_callback_queue);
spinner.start();

这个可使用所有订阅,服务,定时器等,回调通过my_callback_queue而不是 roscpp的默认队列。意味着ros::spin() 和ros::spinOnce() 不会处理这些回调。你需要单独处理这些回调。

你可以通过手工调用 ros::CallbackQueue::callAvailable() 和ros::CallbackQueue::callOne()方法处理。

my_callback_queue.callAvailable(ros::WallDuration());
// alternatively, .callOne(ros::WallDuration()) to only call a single callback instead of all available

各种*Spinner对象可以使用指向回调队列的指针而不是默认值:

ros::AsyncSpinner spinner(0, &my_callback_queue);
spinner.start();

或者:

ros::MultiThreadedSpinner spinner(0);
spinner.spin(&my_callback_queue);

参考

http://wiki.ros.org/roscpp/Overview/Callbacks%20and%20Spinning

### 使用 `ros::spin()` 和线程管理 为了确保在多线程环境中正确使用 `ros::spin()` 并随后执行线程的 `join()` 操作,可以采用如下方法: #### 创建并启动自定义线程来运行 `ros::spin()` ```cpp #include "ros/ros.h" #include <thread> void spinThread() { ros::spin(); } ``` 在此代码片段中,`spinThread` 函数被创建用来专门负责调用 `ros::spin()`[^1]。 #### 主函数中的线程管理和资源释放 ```cpp int main(int argc, char **argv) { ros::init(argc, argv, "multi_thread_spin"); ros::NodeHandle nh; // Create and start a new thread that runs the ROS event loop. std::thread spinner(spinThread); // Your other code here... // Wait for shutdown request or some condition... while (ros::ok()) { // Do something useful here... std::this_thread::sleep_for(std::chrono::seconds(1)); } // Signal all callbacks to stop processing messages. ros::shutdown(); // Join with the spinning thread before exiting. if(spinner.joinable()){ spinner.join(); } return 0; } ``` 这段代码展示了如何在一个独立的线程里启动 `ros::spin()` 来处理所有的回调请求。当接收到关闭信号时,先调用 `ros::shutdown()` 停止所有活动的消息循环,再通过 `spinner.join()` 方法等待该线程完成其工作后再退出程序[^2]。 对于更复杂的场景,比如想要控制多个线程的数量或者希望异步地处理订阅者更新,则可以选择使用 `ros::MultiThreadSpinner` 或 `ros::AsyncSpinner` 类来进行更加灵活的任务调度[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值