跳转至

服务通信

本节主要介绍ROS2中的服务通信,包括服务通信的基本概念,服务通信的创建,服务通信的调用,以及服务通信的示例。

【0】自定义服务消息接口

  • 创建功能包
ros2 pkg create self_interfaces --dependencies rosidl_default_generators --license Apache-2.0
  • CMakeLists.txt 添加
find_package(rosidl_default_generators REQUIRED)

rosidl_generate_interfaces(${PROJECT_NAME}
  "srv/MyService.srv"
  DEPENDENCIES xxx
)
  • package.xml 添加
<member_of_group>rosidl_interface_packages</member_of_group>
  • 消息接口实例 -srv/MyService.srv
# Request
int32 x
int32 y
---
# Response
int32 result

【1】CPP模板

#include <rclcpp/rclcpp.hpp>
#include <self_interfaces/srv/partol.hpp>
#include <geometry_msgs/msg/twist.hpp>
#include <turtlesim/msg/pose.hpp>

class ControlNode : public rclcpp::Node
{
    private:
    //
    rclcpp::Publisher<geometry_msgs::msg::Twist>::SharedPtr publisher_;
    // 订阅者
    rclcpp::Subscription<turtlesim::msg::Pose>::SharedPtr subscription_;
    // 服务端
    rclcpp::Service<self_interfaces::srv::Partol>::SharedPtr server_;
    float target_x = {5.0};    // 普通赋值初始化
    float target_y = {5.0};  // 列表初始化
    void pose_callback(const turtlesim::msg::Pose::SharedPtr pose)
    {
        //RCLCPP_INFO(this->get_logger(),"乌龟位置:x=%.2f, y=%.2f", pose->x, pose->y);
    }
    void server_callback(const std::shared_ptr<self_interfaces::srv::Partol::Request> req,
       std::shared_ptr<self_interfaces::srv::Partol::Response> res                 
    )
    {   
        if((req->target_x>0 && req->target_x<12.0) && (req->target_y>0 && req->target_y<12.0))
            {
                this->target_x = req->target_x;
                this->target_y = req->target_y;
                res->result = self_interfaces::srv::Partol::Response::SECCESS;
                RCLCPP_INFO(this->get_logger(),"乌龟位置:x=%.2f, y=%.2f", this->target_x, this->target_y);
            }
        else
            {
                res->result = self_interfaces::srv::Partol::Response::FAIL;
                RCLCPP_INFO(this->get_logger(),"fail");
            }
    }

    public:
    ControlNode(const std::string& node_name) : Node(node_name) //构造函数
    {
        publisher_ = this ->create_publisher<geometry_msgs::msg::Twist>("/turtle1/cmd_vel",10);
        subscription_ = this->create_subscription<turtlesim::msg::Pose>("/turtle1/pose",10,std::bind(&ControlNode::pose_callback,this,std::placeholders::_1));
        server_ = this->create_service<self_interfaces::srv::Partol>("turtle1/partol",std::bind(&ControlNode::server_callback,this,std::placeholders::_1,std::placeholders::_2));
    }

};

int main(int argc, char **argv)
{
    rclcpp::init(argc, argv);
    /*创建对应节点的共享指针对象*/
    auto node = std::make_shared<ControlNode>("ControlNode");
    /* 运行节点,并检测退出信号*/
    rclcpp::spin(node);
    rclcpp::shutdown();
    return 0;
}

【2】Python模板

import rclpy
from rclpy.node import Node
from geometry_msgs.msg import Twist
from turtlesim.msg import Pose
from self_interfaces.srv import Partol

class ControlNode(Node):
    def __init__(self):
        super().__init__('ControlNode')
        self.publisher_ = self.create_publisher(Twist, '/turtle1/cmd_vel', 10)
        self.subscription_ = self.create_subscription(Pose, '/turtle1/pose', self.pose_callback, 10)
        self.server_ = self.create_service(Partol, 'turtle1/partol', self.server_callback)
        self.target_x = 5.0
        self.target_y = 5.0

    def pose_callback(self, pose):
        self.get_logger().info('乌龟位置:x=%.2f, y=%.2f' % (pose.x, pose.y))

    def server_callback(self, request, response):
        if (request.target_x > 0 and request.target_x < 12.0) and (request.target_y > 0 and request.target_y < 12.0):
            self.target_x = request.target_x
            self.target_y = request.target_y
            response.result = Partol.Response.SECCESS
            self.get_logger().info('乌龟位置:x=%.2f, y=%.2f' % (self.target_x, self.target_y))
        else:
            response.result = Partol.Response.FAIL
            self.get_logger().info('fail')
        return response

def main(args=None):
    rclpy.init(args=args)
    node = ControlNode()
    rclpy.spin(node)
    rclpy.shutdown()

if __name__ == '__main__':
    main()

【3】参数调用与更新

    // 获取参数
        this->declare_parameter("k",1.0);
        this->get_parameter("k",k_);
    // 更新参数
        this->set_parameter(rclcpp::Parameter("k",2.0));
  • 引入头文件
#include <rcl_interfaces/msg/set_parameters_result.hpp>
  • 参数更新回调函数
    rcl_interfaces::msg::SetParametersResult parameter_callback(const std::vector<rclcpp::Parameter> &parameters)
    {
        rcl_interfaces::msg::SetParametersResult result;
        result.successful = true;
        for(const auto &parameter : parameters)
        {
            if (parameter.get_name() == "k")
            {
                k_ = parameter.as_double();
                RCLCPP_INFO(this->get_logger(),"k参数已更新为: %f", k_);
            }
        }
        return result;

    }
  • 参数更新订阅者
    OnParameterCallbackHandle::SharedPtr parameter_callback_handle_;//声明
  • 参数更新订阅者初始化 ```cpp parameter_callback_handle_ = this->add_on_set_parameters_callback(std::bind(&ControlNode::parameter_callback, this, std::placeholders::_1));