首页 > 其他分享 >重复spin问题

重复spin问题

时间:2024-01-19 18:22:05浏览次数:20  
标签:node std 重复 rclcpp client 问题 future spin

ROS2 重复spin问题

报错描述:
在执行回调函数时,报错 terminate called after throwing an instance of 'std::runtime_error'what(): Node '/workflow_control_node' has already been added to an executor.[ros2run]: Aborted ;

原因

在回调函数中又执行了 rclcpp::spin 监听函数;

举例说明

#include "example_interfaces/srv/add_two_ints.hpp"
#include "rclcpp/rclcpp.hpp"
#include <chrono>
#include <cinttypes>
#include <memory>

using namespace std::chrono_literals;
using AddTwoInts = example_interfaces::srv::AddTwoInts;

class Client {
public:
  Client(std::shared_ptr<rclcpp::Node> node) : node_(node) {
    client_ = node_->create_client<AddTwoInts>("add_two_ints");
    timer_ =
        node_->create_wall_timer(1s, std::bind(&Client::timer_callback, this));
  }
  ~Client() {}

private:
  std::shared_ptr<rclcpp::Node> node_ = nullptr;
  rclcpp::TimerBase::SharedPtr timer_ = nullptr;
  ::rclcpp::Client<AddTwoInts>::SharedPtr client_ = nullptr;

  void timer_callback() {
    while (!client_->wait_for_service(std::chrono::seconds(1))) {
      if (!rclcpp::ok()) {
        RCLCPP_ERROR(node_->get_logger(),
                     "client interrupted while waiting for service to appear.")
        return;
      }
      RCLCPP_INFO(node_->get_logger(), "waiting for service to appear...")
    }
    auto request = std::make_shared<AddTwoInts::Request>();
    request->a = 41;
    request->b = 1;
    auto result_future = client_->async_send_request(request);
    if (rclcpp::spin_until_future_complete(node_, result_future) !=
        rclcpp::executor::FutureReturnCode::SUCCESS) {
      RCLCPP_ERROR(node_->get_logger(), "service call failed :(")
      return;
    }
    auto result = result_future.get();
    RCLCPP_INFO(node_->get_logger(),
                "result of %" PRId64 " + %" PRId64 " = %" PRId64, request->a,
                request->b, result->sum);
  }
};

int main(int argc, char *argv[]) {
  rclcpp::init(argc, argv);
  auto node = rclcpp::Node::make_shared("minimal_client");
  Client client(node);
  rclcpp::spin(node);
  rclcpp::shutdown();
  return 0;
}

main() 函数中,已经通过 rclcpp::spin(node) 调用了 spin() 函数;然后在类的回调函数 timer_callback 中再一次通过 spin_until_future_complete 使当前节点去监听回调函数,所以会报错;

我们可以查看 spin_until_future_complete 函数,会发现在一层一层嵌套调用的背后,有这样一个函数:

/// Spin (blocking) until the future is complete, it times out waiting, or rclcpp is interrupted.
/**
 * \param[in] executor The executor which will spin the node.
 * \param[in] node_ptr The node to spin.
 * \param[in] future The future to wait on. If `SUCCESS`, the future is safe to
 *   access after this function
 * \param[in] timeout Optional timeout parameter, which gets passed to
 *   Executor::spin_node_once.
 *   `-1` is block forever, `0` is non-blocking.
 *   If the time spent inside the blocking loop exceeds this timeout, return a `TIMEOUT` return code.
 * \return The return code, one of `SUCCESS`, `INTERRUPTED`, or `TIMEOUT`.
 */
template<typename FutureT, typename TimeRepT = int64_t, typename TimeT = std::milli>
rclcpp::FutureReturnCode
spin_node_until_future_complete(
  rclcpp::Executor & executor,
  rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr,
  const FutureT & future,
  std::chrono::duration<TimeRepT, TimeT> timeout = std::chrono::duration<TimeRepT, TimeT>(-1))
{
  // TODO(wjwwood): does not work recursively; can't call spin_node_until_future_complete
  // inside a callback executed by an executor.
  executor.add_node(node_ptr);
  auto retcode = executor.spin_until_future_complete(future, timeout);
  executor.remove_node(node_ptr);
  return retcode;
}

所以,ROS2 中的节点回调都是先将当前节点加入到 rclcpp::executors::SingleThreadedExecutor executor 中,等待 future 成功获得后,再就将当前节点从 executor 中删除掉,才可以进入下一个回调函数;如果当前节点还在 executor 中,就会报出上述错误;

解决方法

在回调函数中,不要使用 spin() 相关的监听功能;如果是 service 通信的话,可以使用绑定回调函数的方法,避免使用 spin_until_future_complete 函数;

例如将上述代码修改为:

#include "example_interfaces/srv/add_two_ints.hpp"
#include "rclcpp/rclcpp.hpp"
#include <chrono>
#include <cinttypes>
#include <memory>

using namespace std::chrono_literals;
using namespace std::placeholders;
using AddTwoInts = example_interfaces::srv::AddTwoInts;

class Client {
public:
  Client(std::shared_ptr<rclcpp::Node> node) : node_(node) {
    client_ = node_->create_client<AddTwoInts>("add_two_ints");
    timer_ =
        node_->create_wall_timer(1s, std::bind(&Client::timer_callback, this));
  }
  ~Client() {}

  void client_callback(const rclcpp::Client<AddTwoInts>::SharedFuture future){
    const auto& response = future.get();
    RCLCPP_INFO(node_->get_logger(),"result is " PRId64, result->sum);
    return;
  }

private:
  std::shared_ptr<rclcpp::Node> node_ = nullptr;
  rclcpp::TimerBase::SharedPtr timer_ = nullptr;
  ::rclcpp::Client<AddTwoInts>::SharedPtr client_ = nullptr;

  void timer_callback() {
    while (!client_->wait_for_service(std::chrono::seconds(1))) {
      if (!rclcpp::ok()) {
        RCLCPP_ERROR(node_->get_logger(),
                     "client interrupted while waiting for service to appear.")
        return;
      }
      RCLCPP_INFO(node_->get_logger(), "waiting for service to appear...")
    }
    auto request = std::make_shared<AddTwoInts::Request>();
    request->a = 41;
    request->b = 1;
    client_->async_send_request(request, std::bind(&Client::client_callback, this, _1));
  }
};

int main(int argc, char *argv[]) {
  rclcpp::init(argc, argv);
  auto node = rclcpp::Node::make_shared("minimal_client");
  Client client(node);
  rclcpp::spin(node);
  rclcpp::shutdown();
  return 0;
}

或者是,在创建类的时候并不是将 rclcpp::Node 作为参数传入类中,而是将类设置为从 rclcpp::Node 继承过来,便可以在回调函数中使用 spin_until_future_complete 函数;

参考链接:

标签:node,std,重复,rclcpp,client,问题,future,spin
From: https://www.cnblogs.com/Dyp-/p/17975325

相关文章

  • /lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.34' not found问题解决
    有一个go实现的项目代码最近有更新,自己在开发环境上手动构建并运行都没有问题(构建和运行时相同环境,肯定没有问题^_^)。后面通过jenkins构建镜像也没有问题,运行时却报错 之前的版本在jenkins上构建也是成功的,后沟通得知jenkins集群版本最近有更新 那么,大概知道原因了,由于jenk......
  • vue3 之 问题总结(一)
    Vue3官网:https://cn.vuejs.org/guide/introduction.html一、为什么要使用ref?使用ref来创建响应式数据,当你在模板中使用了一个ref,然后改变了这个ref的值时,Vue会自动检测到这个变化,并且相应地更新DOM。在标准的JavaScript中,检测普通变量的访问或修改是行......
  • 如何防止订单二次重复支付?
    1背景用户第一次点击下单操作时,会弹出支付页面待支付。但可能存在用户在支付时发现账户金额不够,后续选择:其他渠道支付(如微信支付转为支付宝支付)或采用不同终端来支付(如由电脑端支付转为app端支付)这时就面临二次支付场景。2方案1由于用户支付的时候的支付页面是html文件或是一个支......
  • telegraf解析嵌套json遇到的问题
    问题描述kafka中的数据格式如下:{"customerId":1652,"deviceId":"13011304383","timestamp":1705637828000,"parameters":{"acc":0,"locationStatus":1,&......
  • 动态规划(6) 打劫问题
    目录打家劫舍打家劫舍第一题应该不难想classSolution{public:introb(vector<int>&nums){//dp含义,偷到第n号房间最多能偷多少intn=nums.size();if(n==1){returnnums[0];}vector<int>dp(n,0);......
  • ThreadLocal 在实战场景下的内存泄漏、逻辑混乱问题总结
    前言很早之前虽然看过ThreadLocal的源码,但是对于真实业务场景下可能存在的问题没有做过总结,刚好前几天在分析Mybatis内存泄漏的问题,想着ThreadLocal不是也可能会发生内存泄漏吗?于是乎本文出现了。本文相关博客1:ThreadLocal还存在内存泄漏?源码级别解读2:高质量实现单文件......
  • 解决前端传递日期参数后端接收报错问题
    当controller中的方法直接用参数接收前端传递的参数时日期格式不匹配会报如下错误:Failedtoconvertvalueoftype'java.lang.String'torequiredtype'java.util.Date';,就是说类型转换异常@PostMapping("/1")publicStringdoSign1(LonguserId,DatesignDate){S......
  • IIS处理并发请求时出现的问题及解决
    原文链接:http://www.cnblogs.com/hgamezoom/p/3082538.html一个ASP.NET项目在部署到生产环境时,当用户并发量达到200左右时,IIS出现了明显的请求排队现象,发送的请求都进入等待,无法及时响应,系统基本处于不可用状态。因经验不足,花了很多时间精力解决这个问题,本文记录了我查找问题的......
  • React问题自问自答
    React学习❓:问题1、实现通过一个异步的接口,接口返回结果之前你需要加载进度,接口返回结果了你需要立马进行终止,success/fail,3s内没接收到接口结果,则一直卡着99,成功则do,不成功则nodo......
  • 【计算机算法设计与分析】罗密欧与朱丽叶的迷宫问题(C++_回溯法)
    文章目录题目描述测试样例算法原理算法实现参考资料题目描述罗密欧与朱丽叶的迷宫。罗密欧与朱丽叶身处一个mxn的迷宫中,如图所示。每一个方恪表示迷宫中的一个房间。这mxn个房间中有一些房间是封闭的。不允任何人进入。在迷宫中任何位置均可沿8个方向进入未封闭的房间。罗密......