将非静态类成员回调函数传递给 ros::subscriber

问题描述

我正在尝试编写一个类来处理基于 ROS 的机器人(特别是使用 Arduino IDE 的 ESP32 芯片)的电机控制。

为此,我想实现一个 ROS 串行订阅者,它在收到新消息时调用回调函数。回调函数将消息数据存储在类的实例变量中,因此我可以轻松地将其与类的其他函数一起使用。

这使得回调函数成为非静态的,因此我使用 std::bind 将它绑定到我的类的特定实例,同时为消息保留一个占位符。

一个简化的代码示例:

#include <ros.h>
#include <geometry_msgs/Twist.h>

ros::NodeHandle nh;

const char cmd_vel_topic[] = "/cmd_vel"

class MotorControlInterface {

  public:
    // FreeRTOS task to call from main
    void TaskMotorControl(void *pvParameters)
    {    
      ros::Subscriber<geometry_msgs::Twist> twist_sub
      ( 
          // The topic to subscribe to
          cmd_vel_topic,// Reference to callback function that is bound to this instance of the class
          &std::bind(&MotorControlInterface::twistCallback,this,std::placeholders::_1),);
      nh.subscribe(twist_sub);
      
      for(;;)
      {
        // Do some stuff with veLocity commands
        handleMotorSpeeds();
        vTaskDelay( 10 );
      }
    };

  private:
    // Instance variable I want to store message data in
    double vel_cmd_x;

    // Callback function 
    void twistCallback(const geometry_msgs::Twist& twist_msg)
    {
      // Save veLocity command
      vel_cmd_x = twist_msg.linear.x;
    };

};

我收到的错误信息:

Arduino: 1.8.15 (Linux),Board: "ESP32 Dev Module,disabled,Default 4MB with spiffs (1.2MB APP/1.5MB SPIFFS),240MHz (WiFi/BT),QIO,80MHz,4MB (32Mb),921600,None"


/home/ubuntu/Arduino/***/*****.ino: In member function 'void MotorControlInterface::TaskMotorControl(void*)':
*****:83:87: error: taking address of temporary [-fpermissive]
         &std::bind( &MotorControlInterface::twistCallback,std::placeholders::_1)
                                                                                       ^
*****:84:7: error: no matching function for call to 'ros::Subscriber<geometry_msgs::Twist>::Subscriber(const char [9],std::_Bind_helper<false,void (MotorControlInterface::*)(const geometry_msgs::Twist&),MotorControlInterface*,const std::_Placeholder<1>&>::type*)'
       );
       ^
In file included from /home/ubuntu/Arduino/libraries/ros_lib/ros/node_handle.h:60:0,from /home/ubuntu/Arduino/libraries/ros_lib/ros.h:38,from /home/ubuntu/Arduino/*****/*****.ino:28:
/home/ubuntu/Arduino/libraries/ros_lib/ros/subscriber.h:107:3: note: candidate: ros::Subscriber<MsgT,void>::Subscriber(const char*,ros::Subscriber<MsgT,void>::CallbackT,int) [with MsgT = geometry_msgs::Twist; ros::Subscriber<MsgT,void>::CallbackT = void (*)(const geometry_msgs::Twist&)]
   Subscriber(const char * topic_name,CallbackT cb,int endpoint = rosserial_msgs::TopicInfo::ID_SUBSCRIBER) :
   ^
/home/ubuntu/Arduino/libraries/ros_lib/ros/subscriber.h:107:3: note:   no kNown conversion for argument 2 from 'std::_Bind_helper<false,const std::_Placeholder<1>&>::type* {aka std::_Bind<std::_Mem_fn<void (MotorControlInterface::*)(const geometry_msgs::Twist&)>(MotorControlInterface*,std::_Placeholder<1>)>*}' to 'ros::Subscriber<geometry_msgs::Twist>::CallbackT {aka void (*)(const geometry_msgs::Twist&)}'
/home/ubuntu/Arduino/libraries/ros_lib/ros/subscriber.h:101:7: note: candidate: constexpr ros::Subscriber<geometry_msgs::Twist>::Subscriber(const ros::Subscriber<geometry_msgs::Twist>&)
 class Subscriber<MsgT,void>: public Subscriber_
       ^
/home/ubuntu/Arduino/libraries/ros_lib/ros/subscriber.h:101:7: note:   candidate expects 1 argument,2 provided
/home/ubuntu/Arduino/libraries/ros_lib/ros/subscriber.h:101:7: note: candidate: constexpr ros::Subscriber<geometry_msgs::Twist>::Subscriber(ros::Subscriber<geometry_msgs::Twist>&&)
/home/ubuntu/Arduino/libraries/ros_lib/ros/subscriber.h:101:7: note:   candidate expects 1 argument,2 provided

exit status 1
taking address of temporary [-fpermissive]

在我看来,回调函数std::bind 没有返回我期望的内容,因此引用失败。不过,根据 this 的说法,返回值是一个函数对象。

由于这是我一段时间以来的第一个 C/C++ 项目,我非常感谢您提供有关如何使其工作的任何建议。我在这上面花了好几个小时,但我不确定如何继续,因为错误消息并没有真正帮助我。

解决方案: This solution 有效:

#include <ros.h>
#include <geometry_msgs/Twist.h>

ros::NodeHandle nh;

const char cmd_vel_topic[] = "/cmd_vel"

class MotorControlInterface {

  public:
    MotorControlInterface() 
    : twistSubscriber(cmd_vel_topic,&MotorControlInterface::twistCallback,this)
    {
      ; // Stuff to do in the constructor
    };

    // FreeRTOS task to call from main
    void TaskMotorControl(void *pvParameters)
    {    
      nh.subscribe(twist_sub);

      for(;;)
      {
        // Do some stuff with veLocity commands
        handleMotorSpeeds();
        vTaskDelay( 10 );
      }
    };

  private:
    // Instance variable I want to store message data in
    double vel_cmd_x;
    ros::Subscriber<geometry_msgs::Twist,MotorControlInterface> twistSubscriber;

    // Callback function 
    void twistCallback(const geometry_msgs::Twist& twist_msg)
    {
      // Save veLocity command
      vel_cmd_x = twist_msg.linear.x;
    };

};

解决方法

  • 对于 ROS,无需将 std::bindros::Subscriber 构造函数结合使用:对于非静态成员函数 ros::NodeHandle::subscribe 实际上有一个相应的重载(其中 nh 是您的节点句柄)

    ros::Subscriber<geometry_msgs::Twist> twist_sub = nh.subscribe(cmd_vel_topic,1,&MotorControlInterface::twistCallback,this);
    
  • 对于 rosserial,语法略有不同(参见 here

    ros::Subscriber<geometry_msgs::Twist> twist_sub {cmd_vel_topic,this};
    nh.subscribe(twist_sub);
    

    此外 - 与 ROS 不同 - boost::function 没有过载,这意味着您根本无法使用 boost::bindstd::bind 的语法。>

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...