服务——————>ros2节点间交互式通信机制

首先明确一下什么是ros2服务,这是了解后续ros2机器人运行中最关键的东西。

答:ros2服务通俗的讲就是一种交互式的通信方式,她更强调是请求响应机制,而区别与纯话题的单方向的话题的发布,这种通信方式是普遍的,由于各个节点之间需要建立联系,而且单纯的节点是不具有任何意义的,所以说要对节点进行“升级”,使其具有“与人沟通的能力”,也是实现一个大的机器人项目的核心。

光说不练,那是胡话,接下来看个简单例子(纯服务端)【虽然写起来不方便,但这是模板,以后熟练了,可以直接Ctrl+V】

#include <rclcpp/rclcpp.hpp>
#include <geometry_msgs/msg/twist.hpp>
#include <turtlesim/msg/pose.hpp>
#include <chapt4_interfaces/srv/partol.hpp>
#include <functional>  // 添加该头文件,用于使用 std::bind 和 std::placeholders

// 为自定义服务类型 Partol 创建别名,方便后续使用
using Partol = chapt4_interfaces::srv::Partol;

// 定义 TurtleController 类,继承自 rclcpp::Node,代表一个 ROS 2 节点
class TurtleController : public rclcpp::Node
{
private:
    // 存储目标位置的 x 坐标
    double target_x_;
    // 存储目标位置的 y 坐标
    double target_y_;
    // 速度控制系数,用于计算线性速度
    double k_ = 1.0;  
    // 最大线性速度,用于限制海龟的运动速度
    double max_speed_ = 1.0;  

    // 回调函数,当接收到海龟的位置消息时被调用
    void on_pose_received_(const turtlesim::msg::Pose::SharedPtr pose) {
        // 创建一个 Twist 消息对象,用于存储速度指令
        auto message = geometry_msgs::msg::Twist();
        // 获取当前海龟的 x 坐标
        double current_x = pose->x;
        // 获取当前海龟的 y 坐标
        double current_y = pose->y;
        // 记录当前海龟的位置信息到日志中
        RCLCPP_INFO(this->get_logger(), "当前位置:(x=%f,y=%f)", current_x, current_y);
        // 计算当前位置与目标位置之间的距离
        double distance = std::sqrt((target_x_ - current_x) * (target_x_ - current_x) +
                                    (target_y_ - current_y) * (target_y_ - current_y));
        // 计算当前朝向与目标位置方向的角度差
        double angle = std::atan2(target_y_ - current_y, target_x_ - current_x) - pose->theta;

        // 如果距离目标位置大于 0.1 个单位
        if (distance > 0.1) {
            // 如果角度差的绝对值大于 0.2 弧度
            if (std::fabs(angle) > 0.2) {
                // 只进行旋转,设置角速度为角度差的绝对值
                message.angular.z = std::fabs(angle);
            } else {
                // 角度差较小时,朝着目标位置前进,设置线速度为距离乘以系数 k_
                message.linear.x = k_ * distance;
            }
        }
        // 如果计算得到的线速度超过最大速度
        if (message.linear.x > max_speed_) {
            // 将线速度限制为最大速度
            message.linear.x = max_speed_;
        }
        // 发布速度指令消息到 /turtle1/cmd_vel 话题,控制海龟运动
        velocity_publisher_->publish(message);
    }

    // 服务对象指针,用于处理 partol 服务请求
    rclcpp::Service<Partol>::SharedPtr partol_service_;
    // 订阅者对象指针,用于订阅 /turtle1/pose 话题,获取海龟的位置信息
    rclcpp::Subscription<turtlesim::msg::Pose>::SharedPtr pose_subscription_;
    // 发布者对象指针,用于发布速度指令消息到 /turtle1/cmd_vel 话题
    rclcpp::Publisher<geometry_msgs::msg::Twist>::SharedPtr velocity_publisher_;

public:
    // 构造函数,初始化节点名为 turtle_controller
    TurtleController() : Node("turtle_controller")
    {
        // 创建 partol 服务,当接收到服务请求时,执行 lambda 函数进行处理
        partol_service_ = this->create_service<Partol>("partol", [&](const Partol::Request::SharedPtr request, Partol::Response::SharedPtr response) {
            // 检查请求中的目标位置是否在有效范围内(x 和 y 坐标都在 0 到 12 之间)
            if ((0 < request->target_x && request->target_x < 12.0f) && (0 < request->target_y && request->target_y < 12.0f)) {
                // 如果目标位置有效,更新节点内部的目标位置
                this->target_x_ = request->target_x;
                this->target_y_ = request->target_y;
                // 设置服务响应结果为成功,注意这里的 SUCESS 可能拼写错误,应为 SUCCESS
                response->result = Partol::Response::SUCESS;  
            } else {
                // 如果目标位置无效,设置服务响应结果为失败
                response->result = Partol::Response::FAIL;
            }
        });
        // 创建一个发布者,用于发布 geometry_msgs::msg::Twist 类型的消息到 /turtle1/cmd_vel 话题,队列长度为 10
        velocity_publisher_ = this->create_publisher<geometry_msgs::msg::Twist>("/turtle1/cmd_vel", 10); 
        // 创建一个订阅者,订阅 /turtle1/pose 话题,队列长度为 10,当接收到消息时调用 on_pose_received_ 回调函数处理
        pose_subscription_ = this->create_subscription<turtlesim::msg::Pose>("/turtle1/pose", 10,
                                                                              std::bind(&TurtleController::on_pose_received_, this, std::placeholders::_1)); 
    }

    // 定时器回调函数,目前代码中未使用该函数,可用于定时发布消息
    void timer_callback()// 消息的发布内容 
    {
        // 创建一个 Twist 消息对象,设置线速度和角速度
        auto msg = geometry_msgs::msg::Twist();
        msg.linear.x = 1.0;
        msg.angular.z = 1.0;
        // 发布消息到 /turtle1/cmd_vel 话题
        velocity_publisher_->publish(msg);
    }
};

// 主函数,程序的入口点
int main(int argc, char** argv)
{
    // 初始化 ROS 2 系统,解析命令行参数
    rclcpp::init(argc, argv);
    // 创建 TurtleController 节点的智能指针
    auto node = std::make_shared<TurtleController>();
    // 启动节点,进入事件循环,处理接收到的消息和服务请求
    rclcpp::spin(node);
    // 关闭 ROS 2 系统,清理资源
    rclcpp::shutdown();
    return 0;
}

看完上述代码你也许就会一头雾水,还是不清楚上述服务端代码的底层逻辑,那么我将我自己对于服务的理解通过语言描述来展示出来。

语言描述:

此代码定义了一个名为 TurtleController 的 ROS 2 节点,其主要功能是控制 turtlesim 仿真环境里的海龟运动。该节点能够接收外部传入的目标位置(这里就是),接着依据海龟当前的位置信息,计算出合适的速度指令并发布,以此引导海龟朝着目标位置移动。

具体原理:

当 partol 服务接收到客户端(这里是zhong'd手动输入)发送的请求后,其回调函数会被触发。回调函数会检查请求中目标位置的有效性(在代码中是检查 target_x 和 target_y 是在 0 到 12.0f 之间)。如果目标位置有效,就会更新 TurtleController 节点内部位置 target_x_ 和 target_y_,并在响应中设置结果为成功;如果目标位置无效,则设置响应结果为失败。之后,节点会根据新的目标位置和海龟当前位置(通过订阅 /turtle1/pose 话题获取)来计算并发布控制海龟运动的速度指令。

总结:

整个代码的逻辑是:通过 partol 服务接收外部传入的目标位置,订阅 /turtle1/pose 话题获取海龟的当前位置信息,在 on_pose_received_ 回调函数中根据目标位置和当前位置计算速度指令,最后通过发布者将速度指令发布到 /turtle1/cmd_vel 话题,从而控制海龟朝着目标位置移动。

最后如果帮助你理解的话,麻烦花一秒点一个赞吧  ^_^  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值