ROS-多回调队列-示例

来源:https://answers.ros.org/question/214629/multiple-callback-queues/

#include <ros/spinner.h>
#include <ros/callback_queue.h>

#include <std_msgs/Header.h>
#include <std_msgs/String.h>

// callbacks to be added to internal ROS queue
void headerCallback(const std_msgs::Header::ConstPtr& msg)
{
  ROS_INFO("stamp.sec: %d", msg->stamp.sec);
}

// callbacks to be added to user queue
void stringCallback(const std_msgs::String::ConstPtr& msg)
{
  ROS_INFO("string: %s", msg->data.c_str());
}

int main(int argc, char** argv)
{
  ros::init(argc, argv, "my_queues");

  ros::NodeHandle n;
  // nothing special for internal ROS queue
  ros::Subscriber sub1 = n.subscribe("/header_topic", 10, headerCallback);

  // define user callback queue
  ros::CallbackQueue string_queue;
  // create options for subscriber and pass pointer to our custom queue
  ros::SubscribeOptions ops =
    ros::SubscribeOptions::create<std_msgs::String>(
      "/string_topic", // topic name
      10, // queue length
      stringCallback, // callback
      ros::VoidPtr(), // tracked object, we don't need one thus NULL
      &string_queue // pointer to callback queue object
    );
  // subscribe
  ros::Subscriber sub2 = n.subscribe(ops);

  // spawn async spinner with 1 thread, running on our custom queue
  ros::AsyncSpinner async_spinner(1, &string_queue);
  // start the spinner
  async_spinner.start();

  while (ros::ok())
  {
    // process one message from ROS internal queue
    ros::spinOnce();
    ROS_INFO("Press Enter to process available messages from ROS internal queue");
    // wait for user input
    getchar();
  }

  return 0;
}
<think>在ROS中,控制回调函数的频率通常有两种方法: 1. 使用ros::Rate在循环中控制频率,但这种方法通常用于主循环,而不是直接用于回调函数。 2. 使用ROS的时间器(timer)来定时调用回调函数,这是控制回调函数频率的推荐方法。 具体来说,我们可以创建一个定时器(timer),设置其触发频率,然后将需要定时执行的回调函数与该定时器绑定。这样,ROS就会按照指定的频率调用该回调函数。 下面是一个简单的示例,展示如何使用ROS的定时器来控制回调函数的执行频率。 假设我们有一个节点,需要以10Hz的频率执行某个任务(即每秒10次)。 ```cpp #include <ros/ros.h> void timerCallback(const ros::TimerEvent& event) { // 这里写定时执行的代码 ROS_INFO("Timer callback triggered"); } int main(int argc, char **argv) { ros::init(argc, argv, "timer_example_node"); ros::NodeHandle nh; // 创建一个定时器,设置频率为10Hz(即每0.1秒触发一次) ros::Timer timer = nh.createTimer(ros::Duration(0.1), timerCallback); // 进入事件循环 ros::spin(); return 0; } ``` 解释: - 首先,我们包含必要的头文件。 - 定义了一个回调函数`timerCallback`,它将在定时器触发时被调用。参数`const ros::TimerEvent& event`包含了定时事件的信息,比如当前时间、上一次触发时间等(可以根据需要使用)。 - 在`main`函数中,初始化ROS节点。 - 使用`ros::NodeHandle`的`createTimer`方法创建一个定时器。第一个参数是触发的时间间隔(`ros::Duration`类型),这里设置为0.1秒(即10Hz)。第二个参数是回调函数。 - 最后,调用`ros::spin()`进入事件循环,等待并处理回调。 注意:定时器的频率设置是理想的频率,实际执行频率可能会因为系统负载、回调函数执行时间等因素而有所偏差。如果回调函数的执行时间超过了定时周期,那么下一次回调可能会被延迟,或者如果使用`ros::Timer`的选项,也可以设置是否一次性执行或自动重置等。 另外,`createTimer`还有其他参数,例如: - `oneshot`:如果为true,则定时器只触发一次;默认为false,即循环触发。 - `autostart`:如果为true(默认),定时器在创建后立即启动;如果为false,需要手动调用`timer.start()`来启动。 - `callback_queue`:指定回调队列,默认为全局队列。 例如,创建一个只触发一次的定时器: ```cpp ros::Timer timer = nh.createTimer(ros::Duration(1.0), timerCallback, true); ``` 在Python中,也可以使用类似的定时器机制。以下是一个Python版本的示例: ```python #!/usr/bin/env python import rospy def timer_callback(event): rospy.loginfo("Timer callback triggered at %s", event.current_real) if __name__ == '__main__': rospy.init_node('timer_example_py') # 创建定时器,每0.1秒(10Hz)触发一次 timer = rospy.Timer(rospy.Duration(0.1), timer_callback) rospy.spin() ``` 在Python中,使用`rospy.Timer`类来创建定时器。第一个参数是时间间隔(`rospy.Duration`),第二个参数是回调函数。同样,使用`rospy.spin()`保持节点运行。 注意事项: -回调函数中,避免执行耗时操作,以免影响定时器的准确性。 - 如果需要停止定时器,可以在回调函数中调用`timer.stop()`(例如满足某个条件时)。在C++中,保存`ros::Timer`对象,然后调用`stop()`方法;在Python中,调用`timer.shutdown()`。 例如,在Python中,5秒后停止定时器: ```python def timer_callback(event): rospy.loginfo("Timer callback triggered") if event.last_real is not None and event.current_real - event.last_real >= rospy.Duration(5): rospy.loginfo("Stopping timer") timer.shutdown() # 停止定时器 ``` 在C++中,可以在回调函数中停止定时器: ```cpp void timerCallback(const ros::TimerEvent& event) { static int count = 0; ROS_INFO("Timer callback triggered"); if (++count >= 50) // 假设触发50次后停止 { timer.stop(); ROS_INFO("Timer stopped."); } } ``` 注意:在C++中,`timer`对象需要在能够被回调函数访问到的地方定义(比如作为全局变量,或者通过类成员等方式)。 综上所述,使用ROS的定时器是控制回调函数频率的标准方法。 §§相关问题§§: 1. ROS中如何创建一次性定时器? 2. 如果回调函数的执行时间超过了定时周期,会发生什么? 3. 如何在ROS中实现个定时器? 4. 在ROS Python节点中,如何动态改变定时器的触发频率?</think>在ROS中控制回调函数的频率通常有两种方法:使用`ros::Rate`在循环中控制处理频率,或使用`ros::Timer`创建定时回调。以下是两种方法的详细实现和解释: ### 方法1:使用`ros::Timer`(推荐) 这是ROS推荐的方式,通过创建定时器精确控制回调函数的执行频率。 ```cpp #include <ros/ros.h> void timerCallback(const ros::TimerEvent& event) { // 这里写需要周期性执行的代码 ROS_INFO_STREAM("Timer triggered at: " << event.current_real); } int main(int argc, char** argv) { ros::init(argc, argv, "timer_node"); ros::NodeHandle nh; // 创建定时器(10Hz = 每秒10次) ros::Timer timer = nh.createTimer( ros::Duration(0.1), // 时间间隔(秒) timerCallback, // 回调函数 false, // oneshot(是否只执行一次) true // autostart(是否自动启动) ); ros::spin(); return 0; } ``` **代码解释:** 1. `ros::Timer timer = nh.createTimer(...)` 创建定时器对象 2. `ros::Duration(0.1)` 设置执行间隔(0.1秒 = 10Hz) 3. `timerCallback` 是定时触发的回调函数 4. `TimerEvent` 参数包含时间信息(如当前时间、上次触发时间等) 5. `ros::spin()` 保持节点运行并处理回调 ### 方法2:使用`ros::Rate`在循环中控制 适用于需要在主循环中控制处理频率的场景。 ```cpp #include <ros/ros.h> int main(int argc, char** argv) { ros::init(argc, argv, "rate_control_node"); ros::NodeHandle nh; ros::Rate rate(10); // 10Hz频率控制 while (ros::ok()) { // 这里写需要周期性执行的代码 ROS_INFO_STREAM("Processing at: " << ros::Time::now()); // 处理回调队列 ros::spinOnce(); // 睡眠保持频率 rate.sleep(); } return 0; } ``` **代码解释:** 1. `ros::Rate rate(10)` 设置10Hz执行频率 2. `while (ros::ok())` 保持节点运行 3. `ros::spinOnce()` 处理所有待处理回调 4. `rate.sleep()` 自动计算并休眠以维持设定频率 ### 两种方法对比 | **特性** | `ros::Timer` | `ros::Rate` | |------------------|----------------------------------|------------------------------| | 执行机制 | 独立定时器线程 | 主循环手动控制 | | 精度 | 更高(系统级定时器) | 依赖循环执行时间 | | 回调类型 | 专用定时回调 | 需手动处理所有回调 | | 适用场景 | 精确周期任务 | 简单循环处理 | | 资源占用 | 较低(事件驱动) | 较高(持续循环) | ### 最佳实践建议 1. **精确计时**:对时间敏感的任务(如控制循环)使用`ros::Timer` 2. **简单任务**:数据处理等简单任务可用`ros::Rate` 3. **避免阻塞**:回调函数执行时间应小于定时周期 4. **时间同步**:需要时间同步时使用`TimerEvent`的时间戳 5. **动态调整**:可通过`timer.setPeriod()`动态改变频率
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值