You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

C++ ROS技术问询:如何用单回调函数处理多Pose主题信息

用单个回调函数处理多个ROS Pose主题订阅的实用方案

嘿,这个问题我之前在项目里碰到过好几次,其实ROS中用单个回调函数处理多主题订阅的核心就是把主题名称和回调绑定在一起,这样每次回调触发时就能明确数据来自哪个主题。下面给你两种针对海龟模拟器turtlesim/Pose消息的C++实现方案:

方案一:用boost::bind绑定主题名称(快速落地)

这种方法适合快速实现,无需额外类封装,核心是通过绑定把主题名作为参数传递给回调函数:

#include <ros/ros.h>
#include <turtlesim/Pose.h>
#include <map>
#include <boost/bind.hpp>

// 用map存储每个主题的最新Pose数据,键是主题名,值是Pose消息
std::map<std::string, turtlesim::Pose> topic_pose_storage;

// 带主题名称参数的回调函数
void poseCallback(const turtlesim::Pose::ConstPtr& msg, const std::string& topic_name) {
    // 更新对应主题的Pose数据
    topic_pose_storage[topic_name] = *msg;
    ROS_INFO("Received Pose from %s: x=%.2f, y=%.2f", topic_name.c_str(), msg->x, msg->y);
}

int main(int argc, char** argv) {
    ros::init(argc, argv, "multi_pose_subscriber");
    ros::NodeHandle nh;

    // 假设你已经获取到所有Pose主题的列表,比如存在这个vector里
    std::vector<std::string> pose_topics = {"/turtle1/pose", "/turtle2/pose", "/turtle3/pose"};

    // 遍历主题列表,逐个订阅并绑定主题名到回调
    for (const auto& topic : pose_topics) {
        // _1是占位符,代表ROS会自动传入的消息指针参数
        nh.subscribe<turtlesim::Pose>(topic, 10, boost::bind(poseCallback, _1, topic));
    }

    ros::spin();
    return 0;
}

代码说明

boost::bind在这里把当前遍历的topic作为第二个参数绑定到回调函数,ROS订阅器会自动将消息指针作为第一个参数传入回调,这样每次触发回调时就能直接识别数据来源,更新对应的存储条目。

方案二:类封装实现(适合复杂场景)

如果你的项目逻辑较多,用类封装可以避免全局变量,同时更优雅地管理订阅器和数据:

#include <ros/ros.h>
#include <turtlesim/Pose.h>
#include <map>
#include <vector>
#include <boost/bind.hpp>

class MultiPoseHandler {
private:
    ros::NodeHandle nh_;
    std::map<std::string, turtlesim::Pose> pose_data_;
    std::vector<ros::Subscriber> subscribers_; // 存储订阅器对象,防止被销毁

public:
    MultiPoseHandler() {
        // 假设已获取到Pose主题列表
        std::vector<std::string> pose_topics = {"/turtle1/pose", "/turtle2/pose"};
        
        for (const auto& topic : pose_topics) {
            // 绑定类成员函数、当前对象指针、消息占位符和主题名
            subscribers_.push_back(
                nh_.subscribe<turtlesim::Pose>(topic, 10, 
                    boost::bind(&MultiPoseHandler::poseCallback, this, _1, topic))
            );
        }
    }

    // 类成员回调函数
    void poseCallback(const turtlesim::Pose::ConstPtr& msg, const std::string& topic_name) {
        pose_data_[topic_name] = *msg;
        ROS_INFO("Topic %s: theta=%.2f", topic_name.c_str(), msg->theta);
    }

    // 对外提供获取指定主题Pose数据的接口
    turtlesim::Pose getLatestPose(const std::string& topic) {
        if (pose_data_.count(topic)) {
            return pose_data_[topic];
        }
        // 如果主题不存在,返回默认Pose(也可以根据需求抛出异常)
        return turtlesim::Pose();
    }
};

int main(int argc, char** argv) {
    ros::init(argc, argv, "multi_pose_handler");
    MultiPoseHandler handler;
    ros::spin();
    return 0;
}

代码说明

类封装的好处是把数据和逻辑聚合在一起,同时subscribers_向量会保存所有订阅器对象,避免循环结束后订阅器被销毁导致订阅失效。

一些关键注意事项

  • 动态主题过滤:如果是通过ros::master::getTopics动态获取主题,记得用ros::master::getTopicTypes过滤出类型为turtlesim/Pose的主题,避免订阅错误主题导致程序崩溃。
  • 回调轻量化:回调函数里尽量只做数据存储或简单处理,复杂逻辑放到单独线程或定时器中,防止阻塞ROS的消息循环。
  • C++11+替代方案:如果你的环境支持C++11及以上,可以用std::bind代替boost::bind,只需替换头文件为<functional>,语法完全一致。

内容的提问来源于stack exchange,提问作者Red_Capra

火山引擎 最新活动