Skip to content

在类中使用参数(C++)

目标: 使用 C++ 创建并运行一个带有 ROS 参数的类。

教程级别: 初级

预计时间: 20 分钟

在编写自定义节点(node)时,有时需要添加可以从 launch 文件设置的参数(parameter)。

本教程将介绍如何在 C++ 类中创建这些参数,以及如何通过 launch 文件来设置它们。

在之前的教程中,你学习了如何创建工作空间(workspace)和功能包(package),也了解了参数及其在 ROS 2 系统中的作用。

打开一个新终端并 source 你的 ROS 2 安装环境,以便 ros2 命令正常工作。

按照之前的说明创建一个名为 ros2_ws 的新工作空间。

注意,功能包应在 src 目录中创建,而非工作空间根目录。进入 ros2_ws/src,创建一个新功能包:

Terminal window
ros2 pkg create --build-type ament_cmake --license Apache-2.0 cpp_parameters --dependencies rclcpp

终端会返回一条消息,确认功能包 cpp_parameters 及其所有必要的文件和文件夹已创建。

--dependencies 参数会自动在 package.xml 和 CMakeLists.txt 中添加必要的依赖行。

由于在创建功能包时已使用 --dependencies 选项,因此无需手动在 package.xml 或 CMakeLists.txt 中添加依赖。不过,仍需照例在 package.xml 中填写描述、维护者姓名和邮箱以及许可证信息。

<description>C++ parameter tutorial</description>
<maintainer email="you@email.com">Your Name</maintainer>
<license>Apache-2.0</license>

在 ros2_ws/src/cpp_parameters/src 目录中,创建一个名为 cpp_parameters_node.cpp 的新文件,并粘贴以下代码:

#include <chrono>
#include <functional>
#include <string>
#include <rclcpp/rclcpp.hpp>
using namespace std::chrono_literals;
class MinimalParam : public rclcpp::Node
{
public:
MinimalParam()
: Node("minimal_param_node")
{
this->declare_parameter("my_parameter", "world");
auto timer_callback = [this](){
std::string my_param = this->get_parameter("my_parameter").as_string();
RCLCPP_INFO(this->get_logger(), "Hello %s!", my_param.c_str());
std::vector<rclcpp::Parameter> all_new_parameters{rclcpp::Parameter("my_parameter", "world")};
this->set_parameters(all_new_parameters);
};
timer_ = this->create_wall_timer(1000ms, timer_callback);
}
private:
rclcpp::TimerBase::SharedPtr timer_;
};
int main(int argc, char ** argv)
{
rclcpp::init(argc, argv);
rclcpp::spin(std::make_shared<MinimalParam>());
rclcpp::shutdown();
return 0;
}

顶部的 #include 语句用于引入功能包依赖。

接下来的代码创建了类和构造函数。构造函数的第一行声明了一个名为 my_parameter 的参数,默认值为 world。参数类型由默认值推断,因此在本例中会被设为字符串类型。随后,声明了一个名为 timer_callback 的 lambda 函数,它通过引用捕获当前对象 this,不接受输入参数并返回 void。timer_callback 函数的第一行从节点获取参数 my_parameter 并存入 my_param。然后 RCLCPP_INFO 函数记录一条日志。set_parameters 函数将参数 my_parameter 重置为默认字符串值 world,即使用户在外部修改了参数,也会恢复为原始值。最后,timer_ 以 1000ms 的周期初始化,使 timer_callback 函数每秒执行一次。

class MinimalParam : public rclcpp::Node
{
public:
MinimalParam()
: Node("minimal_param_node")
{
this->declare_parameter("my_parameter", "world");
auto timer_callback = [this](){
std::string my_param = this->get_parameter("my_parameter").as_string();
RCLCPP_INFO(this->get_logger(), "Hello %s!", my_param.c_str());
std::vector<rclcpp::Parameter> all_new_parameters{rclcpp::Parameter("my_parameter", "world")};
this->set_parameters(all_new_parameters);
};
timer_ = this->create_wall_timer(1000ms, timer_callback);
}

最后是 timer_ 的声明。

private:
rclcpp::TimerBase::SharedPtr timer_;

MinimalParam 之后是 main 函数。这里先初始化 ROS 2,构造 MinimalParam 类的实例,然后 rclcpp::spin 开始处理来自节点的数据。

int main(int argc, char ** argv)
{
rclcpp::init(argc, argv);
rclcpp::spin(std::make_shared<MinimalParam>());
rclcpp::shutdown();
return 0;
}
2.1.1(可选)添加 ParameterDescriptor
Section titled “2.1.1(可选)添加 ParameterDescriptor”

你也可以为参数设置描述符。描述符允许你指定参数的文字描述及其约束条件,例如设为只读、指定取值范围等。为此,需要将构造函数中的代码修改为:

// ...
class MinimalParam : public rclcpp::Node
{
public:
MinimalParam()
: Node("minimal_param_node")
{
auto param_desc = rcl_interfaces::msg::ParameterDescriptor{};
param_desc.description = "This parameter is mine!";
this->declare_parameter("my_parameter", "world", param_desc);
auto timer_callback = [this](){
std::string my_param = this->get_parameter("my_parameter").as_string();
RCLCPP_INFO(this->get_logger(), "Hello %s!", my_param.c_str());
std::vector<rclcpp::Parameter> all_new_parameters{rclcpp::Parameter("my_parameter", "world")};
this->set_parameters(all_new_parameters);
};
timer_ = this->create_wall_timer(1000ms, timer_callback);
}

其余代码保持不变。运行节点后,可以运行 ros2 param describe /minimal_param_node my_parameter 来查看类型和描述。

现在打开 CMakeLists.txt 文件,在依赖 find_package(rclcpp REQUIRED) 下方添加以下代码:

add_executable(minimal_param_node src/cpp_parameters_node.cpp)
target_link_libraries(minimal_param_node rclcpp::rclcpp)
install(TARGETS
minimal_param_node
DESTINATION lib/${PROJECT_NAME}
)

建议在工作空间(ros2_ws)的根目录下运行 rosdep,检查缺失的依赖:

Linux:

Terminal window
rosdep install -i --from-path src --rosdistro {DISTRO} -y

macOS:

rosdep 仅在 Linux 上运行,可直接跳到下一步。

Windows:

rosdep 仅在 Linux 上运行,可直接跳到下一步。

回到工作空间根目录 ros2_ws,构建你的新功能包:

Linux/macOS:

Terminal window
colcon build --packages-select cpp_parameters

Windows:

Terminal window
colcon build --merge-install --packages-select cpp_parameters

打开一个新终端,导航到 ros2_ws,并 source 环境配置文件:

Linux:

Terminal window
source install/setup.bash

macOS:

Terminal window
. install/setup.bash

Windows:

Terminal window
call install/setup.bat

现在运行节点,终端应该每秒输出以下消息:

Terminal window
$ ros2 run cpp_parameters minimal_param_node
[INFO] [minimal_param_node]: Hello world!

现在你可以看到参数的默认值了,但你可能希望自己设置它。有以下四种方法。

本节将运用之前参数教程中的知识,将其应用到你刚创建的节点上。

确保节点正在运行:

Terminal window
ros2 run cpp_parameters minimal_param_node

打开另一个终端,再次从 ros2_ws 中 source 环境配置文件,然后输入以下命令:

Terminal window
ros2 param list

你将看到自定义参数 my_parameter。要修改它,只需在控制台中运行以下命令:

Terminal window
ros2 param set /minimal_param_node my_parameter earth

如果输出 Set parameter successful,说明操作成功。切换到另一个终端,你应该会看到输出变为 [INFO] [minimal_param_node]: Hello earth!

你也可以在 launch 文件中设置参数,但首先需要添加一个 launch 目录。在 ros2_ws/src/cpp_parameters/ 目录中,创建一个名为 launch 的新目录。在其中创建一个名为 cpp_parameters_launch.py 的新文件:

from launch import LaunchDescription
from launch_ros.actions import Node
def generate_launch_description():
return LaunchDescription([
Node(
package='cpp_parameters',
executable='minimal_param_node',
name='custom_minimal_param_node',
output='screen',
emulate_tty=True,
parameters=[
{'my_parameter': 'earth'}
]
)
])

可以看到,我们在启动节点 minimal_param_node 时将 my_parameter 设置为 earth。通过添加以下两行,确保输出被打印到控制台。

output="screen",
emulate_tty=True,

现在打开 CMakeLists.txt 文件,在之前添加的代码下方,补充以下内容:

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

打开控制台,导航到工作空间根目录 ros2_ws,然后构建你的新功能包:

Linux/macOS:

Terminal window
colcon build --packages-select cpp_parameters

Windows:

Terminal window
colcon build --merge-install --packages-select cpp_parameters

然后在新终端中 source 环境配置文件:

Linux:

Terminal window
source install/setup.bash

macOS:

Terminal window
. install/setup.bash

Windows:

Terminal window
call install/setup.bat

现在用刚才创建的 launch 文件运行节点。首次运行时,终端应该返回以下消息:

Terminal window
$ ros2 launch cpp_parameters cpp_parameters_launch.py
[INFO] [custom_minimal_param_node]: Hello earth!

后续输出应该每秒显示 [INFO] [minimal_param_node]: Hello world!。

3.3 通过 launch 文件从 YAML 文件加载参数

Section titled “3.3 通过 launch 文件从 YAML 文件加载参数”

你可以选择在 launch 文件中加载独立的 YAML 文件,而不是直接在其中逐个列出参数及其值。将参数放在 YAML 文件中可以更方便地组织,例如分配到不同的命名空间。你可以在这里阅读更多相关信息。

注意:在 C++ 节点中声明、获取和设置参数值时,应该使用点作为参数命名空间和名称之间的分隔符。

3.4 在节点启动时通过传递 YAML 文件作为参数更改

Section titled “3.4 在节点启动时通过传递 YAML 文件作为参数更改”

请回到参数教程,回顾如何使用 CLI 在节点启动时加载参数文件。

你创建了一个带有自定义参数的节点,该参数可以通过 launch 文件或命令行设置。你还向功能包配置文件添加了依赖、可执行文件和 launch 文件,从而能够构建和运行它们,并观察参数的实际效果。

现在你已经有了一些自己的功能包和 ROS 2 系统,下一篇教程将介绍如何在遇到问题时排查环境和系统中的故障。

  • 有关使用 YAML 文件加载参数的更多详细信息,请参阅管理大型项目教程中的相关章节。
  • 如果你想了解如何监控和响应参数更改,请查看监控参数变化(C++)教程。