参数服务器 server and client
ROS2 参数服务器和客户端实现利用了 ROS2 内置的参数服务机制,具有以下特点:
服务器节点(param_server_node):
- 初始化并维护参数存储
- 定期打印当前所有参数状态
- 支持客户端的参数查询、设置和删除操作
客户端节点(param_client_node):
- 使用
SyncParametersClient
与参数服务器交互 - 提供参数的获取、设置、更新和删除示例
- 等待服务器可用后再执行操作
- 使用
项目结构:
- 包含完整的 CMakeLists.txt 和 package.xml 配置文件
- 符合 ROS2 的包结构规范
使用方法
创建 ROS2 工作空间并构建包:
bash
mkdir -p ~/ros2_ws/src
cd ~/ros2_ws/src
# 创建包
ros2 pkg create --build-type ament_cmake parameter_server_example
# 将上述代码文件放入相应位置
cd ~/ros2_ws
colcon build
source install/setup.bash
运行参数服务器:
bash
ros2 run parameter_server_example param_server
在另一个终端运行参数客户端:
bash
source ~/ros2_ws/install/setup.bash
ros2 run parameter_server_example param_client
客户端会执行一系列参数操作并输出结果,服务器则会每 10 秒打印一次当前所有参数的状态。
这种实现充分利用了 ROS2 的参数系统,比直接使用套接字实现的参数服务器更符合 ROS2 的生态系统和设计理念。
param_server_node.cpp
#include "rclcpp/rclcpp.hpp"
#include <unordered_map>
#include <string>
class ParamServerNode : public rclcpp::Node
{
public:
ParamServerNode() : Node("param_server_node")
{
// 初始化一些默认参数
declare_parameter("max_connections", 10);
declare_parameter("timeout", 5.0);
declare_parameter("enabled", true);
declare_parameter("server_name", "main_server");
RCLCPP_INFO(get_logger(), "参数服务器节点已启动");
// 创建一个定时器用于周期性打印当前参数状态
timer_ = this->create_wall_timer(
std::chrono::seconds(10),
std::bind(&ParamServerNode::print_parameters, this)
);
}
private:
void print_parameters()
{
RCLCPP_INFO(get_logger(), "\n当前参数列表:");
// 获取所有参数名称
auto param_names = get_parameter_names();
for (const auto& name : param_names) {
rclcpp::Parameter param;
if (get_parameter(name, param)) {
RCLCPP_INFO(get_logger(), "%s: %s",
name.c_str(),
param.value_to_string().c_str());
}
}
}
rclcpp::TimerBase::SharedPtr timer_;
};
int main(int argc, char * argv[])
{
rclcpp::init(argc, argv);
rclcpp::spin(std::make_shared<ParamServerNode>());
rclcpp::shutdown();
return 0;
}
param_client_node.cpp
#include "rclcpp/rclcpp.hpp"
#include <chrono>
#include <thread>
class ParamClientNode : public rclcpp::Node
{
public:
ParamClientNode() : Node("param_client_node")
{
RCLCPP_INFO(get_logger(), "参数客户端节点已启动");
// 创建参数客户端
param_client_ = std::make_shared<rclcpp::SyncParametersClient>(this, "param_server_node");
// 等待参数服务器可用
while (!param_client_->wait_for_service(std::chrono::seconds(1))) {
if (!rclcpp::ok()) {
RCLCPP_ERROR(get_logger(), "等待参数服务器时被中断");
return;
}
RCLCPP_INFO(get_logger(), "等待参数服务器...");
}
// 执行参数操作示例
perform_parameter_operations();
}
private:
void perform_parameter_operations()
{
// 获取参数
get_parameters_example();
// 设置新参数
set_parameters_example();
// 更新已有参数
update_parameters_example();
// 删除参数
delete_parameters_example();
// 再次获取参数以显示变化
get_parameters_example();
}
void get_parameters_example()
{
RCLCPP_INFO(get_logger(), "\n--- 获取参数示例 ---");
// 获取单个参数
auto max_connections = param_client_->get_parameter<int>("max_connections");
RCLCPP_INFO(get_logger(), "max_connections: %d", max_connections);
// 批量获取参数
std::vector<std::string> param_names = {"timeout", "enabled", "server_name"};
auto params = param_client_->get_parameters(param_names);
for (const auto& param : params) {
RCLCPP_INFO(get_logger(), "%s: %s",
param.get_name().c_str(),
param.value_to_string().c_str());
}
}
void set_parameters_example()
{
RCLCPP_INFO(get_logger(), "\n--- 设置新参数示例 ---");
std::vector<rclcpp::Parameter> params;
params.push_back(rclcpp::Parameter("buffer_size", 1024));
params.push_back(rclcpp::Parameter("log_level", "info"));
params.push_back(rclcpp::Parameter("timeout_ms", 5000));
auto results = param_client_->set_parameters(params);
for (size_t i = 0; i < results.size(); ++i) {
if (results[i].successful) {
RCLCPP_INFO(get_logger(), "成功设置参数: %s", params[i].get_name().c_str());
} else {
RCLCPP_ERROR(get_logger(), "设置参数失败: %s", params[i].get_name().c_str());
}
}
}
void update_parameters_example()
{
RCLCPP_INFO(get_logger(), "\n--- 更新参数示例 ---");
std::vector<rclcpp::Parameter> params;
params.push_back(rclcpp::Parameter("max_connections", 20));
params.push_back(rclcpp::Parameter("timeout", 10.0));
params.push_back(rclcpp::Parameter("enabled", false));
auto results = param_client_->set_parameters(params);
for (size_t i = 0; i < results.size(); ++i) {
if (results[i].successful) {
RCLCPP_INFO(get_logger(), "成功更新参数: %s", params[i].get_name().c_str());
} else {
RCLCPP_ERROR(get_logger(), "更新参数失败: %s", params[i].get_name().c_str());
}
}
}
void delete_parameters_example()
{
RCLCPP_INFO(get_logger(), "\n--- 删除参数示例 ---");
std::vector<std::string> params_to_delete = {"server_name", "timeout_ms"};
auto results = param_client_->delete_parameters(params_to_delete);
for (size_t i = 0; i < results.size(); ++i) {
if (results[i].successful) {
RCLCPP_INFO(get_logger(), "成功删除参数: %s", params_to_delete[i].c_str());
} else {
RCLCPP_ERROR(get_logger(), "删除参数失败: %s", params_to_delete[i].c_str());
}
}
}
rclcpp::SyncParametersClient::SharedPtr param_client_;
};
int main(int argc, char * argv[])
{
rclcpp::init(argc, argv);
rclcpp::spin(std::make_shared<ParamClientNode>());
rclcpp::shutdown();
return 0;
}
CmakeList.txt
cmake_minimum_required(VERSION 3.5)
project(parameter_server_example)
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wall -Wextra -Wpedantic)
endif()
# 查找依赖
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
# 声明服务器节点
add_executable(param_server src/param_server_node.cpp)
ament_target_dependencies(param_server rclcpp)
# 声明客户端节点
add_executable(param_client src/param_client_node.cpp)
ament_target_dependencies(param_client rclcpp)
# 安装目标
install(TARGETS
param_server
param_client
DESTINATION lib/${PROJECT_NAME})
ament_package()
packages.xml
<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
<name>parameter_server_example</name>
<version>0.0.0</version>
<description>ROS2参数服务器和客户端示例</description>
<maintainer email="your@email.com">Your Name</maintainer>
<license>Apache-2.0</license>
<buildtool_depend>ament_cmake</buildtool_depend>
<depend>rclcpp</depend>
<build_depend>ament_cmake</build_depend>
<exec_depend>rclcpp</exec_depend>
<build_depend>ament_cmake</build_depend>
<exec_depend>rclcpp</exec_depend>
<export>
<build_type>ament_cmake</build_type>
</export>
</package>