写在最前面的话
为什么做该博客?该博客的特点是什么?
随着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 ¶meter)
{
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 可执行程序(如talker
、listener
)。 - 设置参数(
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工具
- 动作通信在实践中几乎不会用到,所以暂不介绍