提炼总结—ROS2机器人开发(第4章)(下)

发布于:2025-06-23 ⋅ 阅读:(12) ⋅ 点赞:(0)

写在最前面的话

为什么做该博客?该博客的特点是什么?
随着DeepSeek、ChatGPT等AI技术的崛起,促使机器人技术发展到了新的高度,诞生了宇树科技、特斯拉为代表的人形机器人,四足机器人等等,越来越多的科技巨头涌入机器人赛道,行业对于相关人才的需求也随之达到了顶峰。本博客的内容是替你阅读所有关于机器人的经典书籍,采用书籍瘦身计划,帮你提炼出核心内容,采用最通俗易懂的语言来解释原理,将书读薄。大大缩短学习时间,助你快速成为机器人时代的佼佼者。



一、书籍介绍

《ROS2机器人开发 从入门到实践》是一本从零基础到实战落地的机器人开发指南,通过"基础理论+工具详解+项目实战"的三阶学习路径,带你掌握机器人操作系统ROS2核心架构。书中不仅包含通信机制、URDF建模等必备知识,更有SLAM导航、机械臂控制等12个工业级案例,配合Git代码库和仿真环境配置指南,帮助开发者快速实现从算法仿真到真机部署的完整闭环。无论是学生、工程师还是科研人员,都能通过本书构建系统的ROS2开发能力,抢占机器人技术前沿阵地。
在这里插入图片描述


二、第4章 服务和参数—深入ROS2通信(提炼总结)

4.3.3节 客户端代码实现(提炼总结)

1、在demo_cpp_service/src下添加patrol_client.cpp,编写如下代码:

#include <cstdlib>
#include <ctime>
#include "rclcpp/rclcpp.hpp"
#include "chapt4_interfaces/srv/patrol.hpp"
#include <chrono>
using namespace std::chrono_literals;
using Patrol = chapt4_interfaces::srv::Patrol;
class PatrolClient : public rclcpp::Node
{
public:
PatrolClient() : Node("patrol_client")
  {
        patrol_client_ = this->create_client<Patrol>("patrol");
        timer_ = this->create_wall_timer(
            10s, std::bind(&PatrolClient::timer_callback, this));
            srand(time(NULL));

    }
    void timer_callback(){
        while (!patrol_client_->wait_for_service(std::chrono::seconds(1)))
        {
            if (!rclcpp::ok())
            {
                RCLCPP_ERROR(this->get_logger(), "Interrupted while waiting for the service. Exiting.");
                return;
            }
            RCLCPP_INFO(this->get_logger(), "Waiting for service to be ready...");
        }
        auto request = std::make_shared<Patrol::Request>();
        request -> target_x = rand() % 15;
        request -> target_y = rand() % 15;
        RCLCPP_INFO(this->get_logger(), "Sending request : (%f, %f)",request->target_x, request->target_y);
        patrol_client_->async_send_request(
            request, 
            [&](rclcpp::Client<Patrol>::SharedFuture result_future) -> void{
                auto response = result_future.get();
                if (response->result == Patrol::Response::SUCCESS)
                { 
                    RCLCPP_INFO(this->get_logger(), "SUCCESS");
                }else if(response -> result == Patrol::Response::FAIL){
                    RCLCPP_INFO(this->get_logger(), "FAIL");
                }
            });
        

    }

private:
  rclcpp::TimerBase::SharedPtr timer_;
  rclcpp::Client<Patrol>::SharedPtr patrol_client_;

};

int main(int argc, char **argv){
  rclcpp::init(argc, argv);
  auto node = std::make_shared<PatrolClient>();
  rclcpp::spin(node);
  rclcpp::shutdown();
  return 0;
}

2、在CMakeLists.txt中对patrol_client节点进行注册,新增如下内容:

add_executable(patrol_client src/patrol_client.cpp)
ament_target_dependencies(patrol_client rclcpp geometry_msgs turtlesim chapt4_interfaces)
install(TARGETS patrol_client DESTINATION lib/${PROJECT_NAME})

3、在chapt4_ws目录下运行如下代码,构建功能包

colcon build

4、在chapt4_ws目录下,添加环境变量

source install/setup.bash

5、新建一个终端,在chapt4_ws目录下,运行服务端节点

ros2 run demo_cpp_service turtle_control

6、新建一个终端,运行节点

ros2 run turtlesim turtlesim_node

7、在chapt4_ws目录下,运行客户端节点

ros2 run demo_cpp_service patrol_client

在这里插入图片描述
在这里插入图片描述
至此C++服务通信学习完成,下面来学习参数通信

4.4节 在Python节点中使用参数(提炼总结)

  • 本节将结合参数通信机制,将人脸检测服务中采样次数和检测模型进行参数化

4.4.1节 参数声明与设置(提炼总结)

  • 将人脸检测服务中采样次数face_locations_upsample_times、检测模型model,从默认参数值,改成人脸检测服务节点的参数

1、修改src/demo_python_service/demo_python_service/face_detect_node.py文件,添加如下代码:

        self.declare_parameter('face_locations_upsample_times', 1)
        self.declare_parameter('face_locations_model', 'hog')
        self.model = self.get_parameter('face_locations_model').value
        self.upsample_times = self.get_parameter('face_locations_upsample_times').value

2、在chapt4_ws目录下运行如下代码,构建功能包

colcon build

3、在chapt4_ws目录下,添加环境变量

source install/setup.bash

4、新建一个终端,在chapt4_ws目录下,运行服务端节点

ros2 run demo_python_service face_detect_node

5、打开新的终端,使用命令来查看参数

source install/setup.bash
ros2 param list

在这里插入图片描述
可以看到声明的两个参数已经被检测到了
6、可以使用如下命令,来设置人脸检测算法参数

ros2 param set /face_detection_node face_locations_model cnn

在这里插入图片描述
7、还可以在启动节点时指定参数的值,只需要使用–ros-args和-p来指定即可

ros2 run demo_python_service face_detect_node --ros-args -p face_locations_model:=cnn

4.4.2节 订阅参数更新(提炼总结)

  • 参数被设置后,要想第一时间获取参数更新并赋予值给对应的属性,需要订阅参数设置事件

1、修改src/demo_python_service/demo_python_service/face_detect_node.py文件,添加如下代码:

头文件添加:

from rcl_interfaces.msg import SetParametersResult

def__init__(self):添加

        self.add_on_set_parameters_callback(self.parameters_callback)

class FaceDetectorionNode(Node):添加

    def parameters_callback(self, parameters):
        for parameter in parameters:
            self.get_logger().info(f'参数{parameter.name}被修改为{parameter.value}')
            if parameter.name == 'face_locations_upsample_times':
                self.upsample_times = parameter.value
            if parameter.name == 'face_locations_model':
                self.model = parameter.value
        return SetParametersResult(successful=True)

2、在chapt4_ws目录下运行如下代码,构建功能包

colcon build

3、在chapt4_ws目录下,添加环境变量

source install/setup.bash

4、在chapt4_ws目录下,运行服务端节点

ros2 run demo_python_service face_detect_node

5、打开新的终端,可以使用如下命令,来设置人脸检测算法参数

source install/setup.bash
ros2 param set /face_detection_node face_locations_model cnn

可以看到face_detect_node节点输出了设置信息
在这里插入图片描述
下面来学习在人脸检测客户端动态的修改服务端检测的模型参数

4.4.3节 修改其他节点参数(提炼总结)

  • 编写一个服务的客户端来修改其他节点的参数

1、修改src/demo_python_service/demo_python_service/face_detect_client_node.py文件,添加如下代码:

在头文件添加如下内容:

from cv_bridge import CvBridge
from rcl_interfaces.srv import SetParameters
from rcl_interfaces.msg import Parameter,ParameterValue,ParameterType

在class FaceDetectorClient(Node):添加如下内容:

    def call_set_parameters(self,parameters):
         client = self.create_client(SetParameters, '/face_detection_node/set_parameters')
         while not client.wait_for_service(timeout_sec=1.0):
             self.get_logger().info('等待服务启动...')
         request = SetParameters.Request()
         request.parameters = parameters
         future = client.call_async(request)
         rclpy.spin_until_future_complete(self, future)
         response = future.result()
         return response
    
    def update_detect_model(self, model):
         param = Parameter()
         param.name = 'face_locations_model'
         new_model_value = ParameterValue()
         new_model_value.type = ParameterType.PARAMETER_STRING
         new_model_value.string_value = model
         param.value = new_model_value
         response = self.call_set_parameters([param])
         for result in response.results:
             if result.successful:
                 self.get_logger().info(f'参数{param.name}设置为{model}')
             else:
                 self.get_logger().info(f'参数设置失败,原因为:{result.reason}')

在main添加如下内容:

def main(args=None):
        rclpy.init(args=args)
        face_detect_client = FaceDetectorClient()
        face_detect_client.update_detect_model('hog')
        face_detect_client.send_request()
        face_detect_client.update_detect_model('cnn')
        face_detect_client.send_request()
        rclpy.spin(face_detect_client)
        rclpy.shutdown()

2、在chapt4_ws目录下运行如下代码,构建功能包

colcon build

3、在chapt4_ws目录下,添加环境变量

source install/setup.bash

4、在chapt4_ws目录下,运行客户端节点

ros2 run demo_python_service face_detect_client_node

5、在chapt4_ws目录下,运行服务端节点

source install/setup.bash
ros2 run demo_python_service face_detect_node 

在这里插入图片描述
至此python中节点声明和设置参数的方法学习完毕

4.5节 在C++节点中使用参数(提炼总结)

  • 本节结合4.3节海龟巡逻项目,将服务端中控制器的比例系数k_和最大速度max_speed_参数化

4.5.1节 参数声明与设置(提炼总结)

1、修改src/demo_cpp_service/src/turtle_control.cpp文件,添加如下代码:
在TurtleController() : Node(“turtle_controller”) {添加代码

            this->declare_parameter("k", 1.0);
            this->declare_parameter("max_speed", 1.0);
            this->get_parameter("k",k_);
            this->get_parameter("max_speed",max_speed_);

2、在chapt4_ws目录下运行如下代码,构建功能包

colcon build

3、在chapt4_ws目录下,添加环境变量

source install/setup.bash

4、在chapt4_ws目录下,运行服务端节点

ros2 run demo_cpp_service turtle_control

5、在chapt4_ws目录下,使用命令查看参数

ros2 param list

在这里插入图片描述
可以看到节点中声明的参数已经被检测到了
6、使用如下命令来设置参数的值

ros2 param set /turtle_controller k 2.0

在这里插入图片描述
参数已经被重新设置了,此时节点中属性K_的值并没有被更新,实现动态更新需要订阅参数更新事件

4.5.2节 接收参数事件(提炼总结)

1、修改src/demo_cpp_service/src/turtle_control.cpp文件,添加如下代码:
头文件添加:

#include "rcl_interfaces/msg/set_parameters_result.hpp"
using SetParametersResult = rcl_interfaces::msg::SetParametersResult;

在TurtleController() : Node(“turtle_controller”) {添加代码

            parameters_callback_handle_ = this->add_on_set_parameters_callback(
                [&](const std::vector<rclcpp::Parameter>& params)
                -> SetParametersResult{
                    for (auto param : params) {
                        RCLCPP_INFO(this->get_logger(), "更新参数%s值为:%f",
                            param.get_name().c_str(), param.as_double());
                        if (param.get_name() == "k") {
                            k_ = param.as_double();
                        }else if (param.get_name() == "max_speed") {
                            max_speed_ = param.as_double();
                        }
                    }
                    auto result = SetParametersResult();
                    result.successful = true;
                    return result;
                }
            );

在private:添加代码

        OnSetParametersCallbackHandle::SharedPtr parameters_callback_handle_;

2、在chapt4_ws目录下运行如下代码,构建功能包

colcon build

3、在chapt4_ws目录下,添加环境变量

source install/setup.bash

4、在chapt4_ws目录下,运行服务端节点

ros2 run demo_cpp_service turtle_control

在这里插入图片描述
此时节点已经可以收到参数设置的事件了

4.5.3节 修改其他节点的参数(提炼总结)

  • 通过C++实现请求服务的方式实现对节点参数的设置

1、修改src/demo_cpp_service/src/patrol_client.cpp文件,添加如下代码:
头文件添加:

#include "rcl_interfaces/msg/parameter.hpp"
#include "rcl_interfaces/msg/parameter_value.hpp"
#include "rcl_interfaces/msg/parameter_type.hpp"
#include "rcl_interfaces/srv/set_parameters.hpp"
using SetP = rcl_interfaces::srv::SetParameters;

class PatrolClient : public rclcpp::Node添加

std::shared_ptr<SetP::Response> call_set_parameters(rcl_interfaces::msg::Parameter &parameter)
  { 
    auto param_client = this -> create_client<SetP>("/turtle_controller/set_parameters");
    while(!param_client -> wait_for_service(std::chrono::seconds(1)))
    {
        if(!rclcpp::ok())
        {
            RCLCPP_ERROR(this->get_logger(), "Interrupted while waiting for the service. Exiting.");
            return nullptr;
        }
        RCLCPP_INFO(this->get_logger(), "service not available, waiting again...");
    }
    auto request = std::make_shared<SetP::Request>();
    request -> parameters.push_back(parameter);
    auto future = param_client -> async_send_request(request);
    rclcpp::spin_until_future_complete(this->get_node_base_interface(), future);
    auto response = future.get();
    return response;
  }

  void update_server_param_k(double k){
    auto param = rcl_interfaces::msg::Parameter();
    param.name = "k";
    auto param_value = rcl_interfaces::msg::ParameterValue();
    param_value.type = rcl_interfaces::msg::ParameterType::PARAMETER_DOUBLE;
    param_value.double_value = k;
    param.value = param_value;
    auto response = call_set_parameters(param);
    if(response == nullptr){
        RCLCPP_WARN(this->get_logger(), "Failed to update parameter k");
        return ;
    }else {
        for(auto result : response->results){
            if(result.successful){
                RCLCPP_INFO(this->get_logger(), "Successfully updated parameter k");
            }else{
                RCLCPP_WARN(this->get_logger(), "Failed to update parameter k");
            }
        }
    }

  }

在main函数添加,如下内容:

  node -> update_server_param_k(1.5);

2、在chapt4_ws目录下运行如下代码,构建功能包

colcon build

3、在chapt4_ws目录下,添加环境变量

source install/setup.bash

4、新建终端,在chapt4_ws目录下,运行服务端节点

ros2 run demo_cpp_service turtle_control

5、在chapt4_ws目录下,运行客户端节点

ros2 run demo_cpp_service patrol_client

在这里插入图片描述
至此管理节点参数全部学习完成

4.6节 使用launch启动脚本(提炼总结)

  • launch是ROS2中用于启动和管理节点与进程的工具,不需要每次启动都单独打开终端和输入命令,可以一次启动多个节点

4.6.1节 使用launch启动多个节点(提炼总结)

1、在src/demo_cpp_service/下创建launch文件夹,再创建文件demo.launch.py文件,编写如下代码:

import launch
import launch_ros

def generate_launch_description():
    action_node_turtle_control = launch_ros.actions.Node(
        package='demo_cpp_service',
        executable='turtle_control',
        output='screen',
    )
    action_node_patrol_client = launch_ros.actions.Node(
        package='demo_cpp_service',
        executable='patrol_client',
        output='log',
    )
    action_node_turtlesim_node = launch_ros.actions.Node(
        package='turtlesim',
        executable='turtlesim_node',
        output='both',
    )
    launch_description = launch.LaunchDescription([
        action_node_turtle_control,
        action_node_patrol_client,
        action_node_turtlesim_node
    ])
    return launch_description

2、在src/demo_cpp_service/CMakeLists.txt修改内容如下:

install(DIRECTORY launch DESTINATION share/${PROJECT_NAME})

3、在chapt4_ws目录下运行如下代码,构建功能包

colcon build

4、在chapt4_ws目录下,添加环境变量

source install/setup.bash

5、新建终端,在chapt4_ws目录下,运行launch节点

ros2 launch demo_cpp_service demo.launch.py

此时打开了海龟模拟器界面,可以看到海龟正常巡逻了

  • 在python功能包下创建launch文件的方法:
    1、在功能包下创建launch文件夹,并编写文件
    2、在setup.py文件中添加如下配置:
from glob import glob
    data_files=[
        ('share/' + package_name + '/launch', glob('launch/*.launch.py')),
    ]

4.6.2节 使用launch传递参数(提炼总结)

1、修改demo.launch.py,添加如下内容:
def generate_launch_description()添加如下内容:

    action_declare_arg_max_speed = launch.actions.DeclareLaunchArgument(
        'launch_max_speed',
        default_value='0.5',
    )

在action_node_turtle_control = launch_ros.actions.Node添加如下内容:

parameters[{'max_speed':launch.substitutions.LaunchConfiguration('launch_max_speed',default='2.0')}],

在launch_description = launch.LaunchDescription添加如下内容:

action_declare_arg_max_speed,

2、在chapt4_ws目录下运行如下代码,构建功能包

colcon build

3、在chapt4_ws目录下,添加环境变量

source install/setup.bash

4、在chapt4_ws目录下,运行launch节点

ros2 launch demo_cpp_service demo.launch.py

5、、在终端输入命令查看max_speed参数值

ros2 param get /turtle_controller max_speed

6、在chapt4_ws目录下,重新运行launch节点

ros2 launch demo_cpp_service demo.launch.py launch_max_speed:=3.0

从而实现从启动命令传递参数的值

4.6.3节 launch使用进阶(提炼总结)

  • ROS2 Launch 动作、添加和替换组件介绍:
    1、 Launch 动作(Actions)
    作用:告诉 ROS2 在启动时要做什么。
    常见动作
  • 启动节点Node):运行一个 ROS2 可执行程序(如 talkerlistener)。
  • 设置参数SetParameter):给节点传递参数(如 speed=2.0)。
  • 条件控制IfCondition):只有满足条件时才启动某个节点(如 if use_camera:=true)。
    2、添加组件(Adding Components)
    作用:在启动时动态增加新的节点或功能。
    常见方法
  • 直接添加:在 launch.py 里多写几个 Node
  • 导入其他 launch 文件:用 IncludeLaunchDescription 把另一个 launch 文件的内容合并进来(类似“复制粘贴”)。
    3、替换组件(Replacing Components)
    作用:修改或覆盖已有的节点配置(比如换参数、换节点)。
    常见方法
  • 覆盖参数:在 launch 文件里重新定义参数(后面的会覆盖前面的)。
  • 同名节点替换:如果两个节点名字相同,后启动的会替换先启动的。
  • YAML 参数覆盖:通过 YAML 文件动态修改参数。

4.7节 小结与点评(提炼总结)

  • 学习了服务通信
  • 学习了参数通信
  • 学习了launch工具
  • 动作通信在实践中几乎不会用到,所以暂不介绍

网站公告

今日签到

点亮在社区的每一天
去签到