上回说道添加轨迹函数Node::AddTrajectory
中的订阅话题与注册回调函数,在此之前计算了一个由topicname和sensor类型组合成的一个集合expected_sensor_ids,轨迹IDtrajectory_id,新添加了一个位姿估计器,和一个传感器采样器1 2 LaunchSubscribers (options, trajectory_id);
订阅话题与注册回调函数 在整个LaunchSubscribers函数里面,会根据配置信息对相应的topic进行订阅并且注册回调函数。对于有多个可能设备的topic,通过ComputeRepeatedTopicNames来获取topic名字。并且订阅并进行回调
1 2 3 4 5 6 7 8 9 for (const std::string& topic : ComputeRepeatedTopicNames (kLaserScanTopic, options.num_laser_scans)) { subscribers_[trajectory_id].push_back ( {SubscribeWithHandler<sensor_msgs::LaserScan>( &Node::HandleLaserScanMessage, trajectory_id, topic, &node_handle_, this ), topic}); }
对于只有单个设备的例如里程计,简单if判断即可
1 2 3 4 5 6 7 8 if (options.use_odometry) { subscribers_[trajectory_id].push_back ( {SubscribeWithHandler<nav_msgs::Odometry>(&Node::HandleOdometryMessage, trajectory_id, kOdometryTopic, &node_handle_, this ), kOdometryTopic}); }
订阅者定义 1 std::unordered_map<int , std::vector<Subscriber>> subscribers_;
订阅者为key为轨迹ID,value是一个Subscriber的一个vector。Subscriber由一个ros提供的订阅者和订阅的topic组合而成
1 2 3 4 5 6 7 8 9 struct Subscriber { ::ros::Subscriber subscriber; std::string topic; };
SubscribeWithHandler SubscribeWithHandler返回ros的一个Subscriber
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 template <typename MessageType>::ros::Subscriber SubscribeWithHandler ( void (Node::*handler)(int , const std::string&, const typename MessageType::ConstPtr&), const int trajectory_id, const std::string& topic, ::ros::NodeHandle* const node_handle, Node* const node) { return node_handle->subscribe<MessageType>( topic, kInfiniteSubscriberQueueSize, boost::function<void (const typename MessageType::ConstPtr&)>( [node, handler, trajectory_id, topic](const typename MessageType::ConstPtr& msg) { (node->*handler)(trajectory_id, topic, msg); })); }
因此
1 2 3 4 5 6 7 if (options.use_odometry) { subscribers_[trajectory_id].push_back ( {SubscribeWithHandler<nav_msgs::Odometry>(&Node::HandleOdometryMessage, trajectory_id, kOdometryTopic, &node_handle_, this ), kOdometryTopic}); }
这里push_back的就是一个Subscriber的一个结构体
即就是std::unordered_map<int, std::vector<Subscriber>> subscribers_;
这里的Subscriber
参数列表 1 2 3 4 5 6 7 8 9 10 11 /** * @brief 在node_handle中订阅topic,并与传入的回调函数进行注册 * * @tparam MessageType 模板参数,消息的数据类型 * @param[in] handler 函数指针, 接受传入的函数的地址 * @param[in] trajectory_id 轨迹id * @param[in] topic 订阅的topic名字 * @param[in] node_handle ros的node_handle * @param[in] node node类的指针 * @return ::ros::Subscriber 订阅者 */
注意,函数的形参中定义了一个函数指针
1 2 void (Node::*handler)(int , const std::string&, const typename MessageType::ConstPtr&),
这个函数指针的返回值为void,指向Node类里面的一个函数,函数指针的名字为handler,接受三个参数。
再看参数是如何传入进来的
1 2 3 4 5 6 7 if (options.use_odometry) { subscribers_[trajectory_id].push_back ( {SubscribeWithHandler<nav_msgs::Odometry>(&Node::HandleOdometryMessage, trajectory_id, kOdometryTopic, &node_handle_, this ), kOdometryTopic}); }
函数Node::HandleOdometryMessage是一个Node的一个函数,并且他的参数和函数指针所定义的保持一致
1 2 3 void Node::HandleMultiEchoLaserScanMessage ( const int trajectory_id, const std::string& sensor_id, const sensor_msgs::MultiEchoLaserScan::ConstPtr& msg)
在这里的模板类型为nav_msgs::Odometry
注册回调 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 template <typename MessageType>::ros::Subscriber SubscribeWithHandler ( void (Node::*handler)(int , const std::string&, const typename MessageType::ConstPtr&), const int trajectory_id, const std::string& topic, ::ros::NodeHandle* const node_handle, Node* const node) { return node_handle->subscribe<MessageType>( topic, kInfiniteSubscriberQueueSize, boost::function<void (const typename MessageType::ConstPtr&)>( [node, handler, trajectory_id, topic](const typename MessageType::ConstPtr& msg) { (node->*handler)(trajectory_id, topic, msg); })); }
对于topic的回调是通过node_handle->subscribe<MessageType>
来实现的,topic为订阅的消息名字,缓冲区数据队列长度,回调函数。
按正常的ros格式来说,这里需要一个函数的名字。这里函数的类型为boost:function ,<>里面的是函数的返回值void和参数,后面的括号是函数里面的内容,在这里通过lambda表达式来进行一个函数的传递
lambda 表达式 lambda表达式是匿名的
1 [函数对象的参数 = &]()(->返回值类型){函数体}
[]是不可以少的,=表示这里的参数用值拷贝使用,&表示这里的参数是通过引用的方式进行使用
1 2 3 [node, handler, trajectory_id, topic](const typename MessageType::ConstPtr& msg) { (node->*handler)(trajectory_id, topic, msg); }
前面[]表示这里需要使用的参数,是以值拷贝的方式来使用,()表示此函数需要用的参数
(node->*hander)表示函数指针的使用