【ROS2】初级:客户端-使用类中的参数(C++)

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

 教程级别:初学者

 时间:20 分钟

 目录

  •  背景

  •  先决条件

  •  任务

    • 1. 创建一个包

    • 2. 编写 C++ 节点

    • 3. 建立并运行

  •  摘要

  •  下一步

背景

在创建您自己的节点时,有时需要添加可以从启动文件设置的参数

这个教程将向您展示如何在 C++类中创建这些参数,以及如何在启动文件中设置它们。

先决条件

在之前的教程中,您学习了如何创建工作空间和创建软件包。您还了解了参数及其在 ROS 2 系统中的功能。

任务

1. 创建一个包

打开一个新的终端并且初始化您的 ROS 2 安装,这样 ros2 命令就会生效。

按照这些指示创建一个名为 ros2_ws 的新工作区。

请记住,包应该在 src 目录中创建,而不是在工作区的根目录中。导航到 ros2_ws/src 并创建一个新包:

cxy@ubuntu2404-cxy:~/ros2_ws/src$ ros2 pkg create --build-type ament_cmake --license Apache-2.0 cpp_parameters --dependencies rclcpp
going to create a new package
package name: cpp_parameters
destination directory: /home/cxy/ros2_ws/src
package format: 3
version: 0.0.0
description: TODO: Package description
maintainer: ['cxy <cxy@todo.todo>']
licenses: ['Apache-2.0']
build type: ament_cmake
dependencies: ['rclcpp']
creating folder ./cpp_parameters
creating ./cpp_parameters/package.xml
creating source and include folder
creating folder ./cpp_parameters/src
creating folder ./cpp_parameters/include/cpp_parameters
creating ./cpp_parameters/CMakeLists.txt

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

--dependencies `参数将自动向` package.xml `和` CMakeLists.txt `添加必要的依赖行。

 1.1 更新 package.xml 

因为在创建包时您使用了 --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>

2. 编写 C++ 节点

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

#include <chrono> // 包含chrono库,用于处理时间
#include <functional> // 包含functional库,用于使用函数对象
#include <string> // 包含string库,用于使用字符串类型


#include <rclcpp/rclcpp.hpp> // 包含rclcpp库,用于ROS 2节点的创建和管理


using namespace std::chrono_literals; // 使用std::chrono_literals命名空间,允许使用后缀ms表示毫秒


class MinimalParam : public rclcpp::Node // 定义一个MinimalParam类,继承自rclcpp::Node类
{
public:
  MinimalParam()
  : Node("minimal_param_node") // 构造函数,初始化节点名为"minimal_param_node"
  {
    this->declare_parameter("my_parameter", "world"); // 声明一个参数"my_parameter",默认值为"world"


    auto timer_callback = [this](){ // 定义一个定时器回调函数
      std::string my_param = this->get_parameter("my_parameter").as_string(); // 获取参数"my_parameter"的值,并转换为字符串


      RCLCPP_INFO(this->get_logger(), "Hello %s!", my_param.c_str()); // 使用节点的日志记录器输出信息


      std::vector<rclcpp::Parameter> all_new_parameters{rclcpp::Parameter("my_parameter", "world")}; // 创建一个新的参数向量,并重新设置"my_parameter"
      this->set_parameters(all_new_parameters); // 设置新的参数向量
    };
    timer_ = this->create_wall_timer(1000ms, timer_callback); // 创建一个定时器,每1000毫秒调用一次timer_callback函数
  }


private:
  rclcpp::TimerBase::SharedPtr timer_; // 定义一个定时器指针成员变量
};


int main(int argc, char ** argv) // 主函数
{
  rclcpp::init(argc, argv); // 初始化ROS 2
  rclcpp::spin(std::make_shared<MinimalParam>()); // 创建MinimalParam类的实例,并使节点开始运行
  rclcpp::shutdown(); // 关闭ROS 2
  return 0; // 返回0,表示程序正常结束
}
检查代码 2.1

顶部的 #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(可选)添加参数描述符 

您可以选择为参数设置一个描述符。描述符允许您指定参数及其约束的文本描述,例如使其只读,指定范围等。为此,构造函数中的代码必须更改为:

class MinimalParam : public rclcpp::Node // 定义一个MinimalParam类,继承自rclcpp::Node类
{
public:
  MinimalParam()
  : Node("minimal_param_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); // 声明一个带描述的参数"my_parameter",默认值为"world"


    auto timer_callback = [this](){ // 定义一个定时器回调函数
      std::string my_param = this->get_parameter("my_parameter").as_string(); // 获取参数"my_parameter"的值,并转换为字符串


      RCLCPP_INFO(this->get_logger(), "Hello %s!", my_param.c_str()); // 使用节点的日志记录器输出信息


      std::vector<rclcpp::Parameter> all_new_parameters{rclcpp::Parameter("my_parameter", "world")}; // 创建一个新的参数向量,并重新设置"my_parameter"
      this->set_parameters(all_new_parameters); // 设置新的参数向量
    };
    timer_ = this->create_wall_timer(1000ms, timer_callback); // 创建一个定时器,每1000毫秒调用一次timer_callback函数
  }

代码的其余部分保持不变。一旦运行了节点,你就可以运行 ros2 param describe /minimal_param_node my_parameter 来查看类型和描述。

 2.2 添加可执行文件

现在打开 CMakeLists.txt 文件。在依赖项 find_package(rclcpp REQUIRED) 下面添加以下代码行。

add_executable(minimal_param_node src/cpp_parameters_node.cpp)
ament_target_dependencies(minimal_param_node rclcpp)


install(TARGETS
    minimal_param_node
  DESTINATION lib/${PROJECT_NAME}
)

3. 构建并运行

在工作区的根目录运行 rosdep ( ros2_ws )以检查构建前缺失的依赖项是一个好习惯:

rosdep install -i --from-path src --rosdistro jazzy -y

返回到您的工作区根目录, ros2_ws ,然后构建您的新包:

cxy@ubuntu2404-cxy:~/ros2_ws$ colcon build --packages-select cpp_parameters
Starting >>> cpp_parameters
Finished <<< cpp_parameters [7.02s]                     


Summary: 1 package finished [7.76s]

打开一个新的终端,导航到 ros2_ws ,并且导入设置文件:

source install/setup.bash

现在运行节点:

cxy@ubuntu2404-cxy:~/ros2_ws$ ros2 run cpp_parameters minimal_param_node
[INFO] [1720323417.584053341] [minimal_param_node]: Hello world!
[INFO] [1720323418.584192088] [minimal_param_node]: Hello world!
[INFO] [1720323419.584106135] [minimal_param_node]: Hello world!

现在您可以看到参数的默认值,但您希望能够自己设置它。有两种方法可以做到这一点。

3.1 通过控制台进行更改 

这部分将使用你从关于参数的教程中获得的知识 https://docs.ros.org/en/jazzy/Tutorials/Beginner-CLI-Tools/Understanding-ROS2-Parameters/Understanding-ROS2-Parameters.html ,并将其应用到你刚刚创建的节点上。

确保节点正在运行:

ros2 run cpp_parameters minimal_param_node

打开另一个终端,再次从 ros2_ws 内部获取设置文件,并输入以下行:

cxy@ubuntu2404-cxy:~/ros2_ws$ ros2 param list
/minimal_param_node:
  my_parameter
  qos_overrides./parameter_events.publisher.depth
  qos_overrides./parameter_events.publisher.durability
  qos_overrides./parameter_events.publisher.history
  qos_overrides./parameter_events.publisher.reliability
  start_type_description_service
  use_sim_time

在那里,您将看到自定义参数 my_parameter 。要更改它,只需在控制台中运行以下行:

ros2 param set /minimal_param_node my_parameter earth

c5fb592c271beda1bb4ad63e304314c0.png

3.2 通过启动文件进行更改

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

# 导入LaunchDescription类,用于描述launch文件内容
from launch import LaunchDescription
# 导入Node类,用于描述一个ROS节点的启动配置
from launch_ros.actions import Node


# 定义一个函数generate_launch_description,这个函数将会返回LaunchDescription对象
def generate_launch_description():
    # 创建LaunchDescription对象实例,并向其中添加一个Node对象
    return LaunchDescription([
        Node(
            # 指定节点所属的包名
            package="cpp_parameters",
            # 指定节点可执行文件的名称
            executable="minimal_param_node",
            # 为节点设置一个名称
            name="custom_minimal_param_node",
            # 设置节点的输出选项,"screen"表示将输出打印到屏幕
            output="screen",
            # 设置emulate_tty为True,以模拟TTY输出(使得输出更易读)
            emulate_tty=True,
            # 设置节点的参数,这里定义了一个名为"my_parameter"的参数,其值为"earth"
            parameters=[
                {"my_parameter": "earth"}
            ]
        )
    ])

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

output="screen",
emulate_tty=True,

现在打开 CMakeLists.txt 文件。在您之前添加的那些行下面,添加以下代码行。

install(
  DIRECTORY launch
  DESTINATION share/${PROJECT_NAME}
)
# 指定CMake的最低版本要求
cmake_minimum_required(VERSION 3.8)
# 定义项目名称
project(cpp_parameters)


# 如果使用的是GNU编译器或Clang编译器,添加编译选项
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
  add_compile_options(-Wall -Wextra -Wpedantic)
endif()


# 查找依赖包
# ament_cmake是ROS2中用于构建和测试ROS包的工具
find_package(ament_cmake REQUIRED)
# rclcpp是ROS2中C++客户端库的包
find_package(rclcpp REQUIRED)


# 添加一个可执行文件目标,指定源文件为src/cpp_parameters_node.cpp
add_executable(minimal_param_node src/cpp_parameters_node.cpp)
# 将rclcpp作为目标依赖添加到minimal_param_node目标上
ament_target_dependencies(minimal_param_node rclcpp)


# 安装目标文件,将minimal_param_node可执行文件安装到lib/${PROJECT_NAME}目录下
install(TARGETS
    minimal_param_node
  DESTINATION lib/${PROJECT_NAME}
)


# 安装launch目录,将其安装到share/${PROJECT_NAME}目录下
install(
  DIRECTORY launch
  DESTINATION share/${PROJECT_NAME}
)


# 如果构建测试被启用,则查找ament_lint_auto包,并设置一些lint选项
if(BUILD_TESTING)
  find_package(ament_lint_auto REQUIRED)
  # 跳过版权检查linter(当所有源文件添加了版权和许可时,取消注释此行)
  set(ament_cmake_cpplint_FOUND TRUE)
  # 跳过cpplint检查(只在git仓库中有效,当包在git仓库中且所有源文件添加了版权和许可时,取消注释此行)
  set(ament_cmake_cpplint_FOUND TRUE)
  # 查找并添加lint测试依赖项
  ament_lint_auto_find_test_dependencies()
endif()


# 调用ament_package()函数,处理安装和测试等工作
ament_package()

打开控制台并导航到您的工作区的根目录, ros2_ws ,然后构建您的新包:

cxy@ubuntu2404-cxy:~/ros2_ws$ colcon build --packages-select cpp_parameters
Starting >>> cpp_parameters
Finished <<< cpp_parameters [1.53s]                  


Summary: 1 package finished [1.76s]

然后在新终端中导入设置文件:

source install/setup.bash

现在使用我们刚刚创建的启动文件运行节点:

ros2 launch cpp_parameters cpp_parameters_launch.py

终端应该在第一次返回以下消息:

cxy@ubuntu2404-cxy:~/ros2_ws$ source install/setup.bash
cxy@ubuntu2404-cxy:~/ros2_ws$ ros2 launch cpp_parameters cpp_parameters_launch.py
[INFO] [launch]: All log files can be found below /home/cxy/.ros/log/2024-07-07-11-49-18-433664-ubuntu2404-cxy-19434
[INFO] [launch]: Default logging verbosity is set to INFO
[INFO] [minimal_param_node-1]: process started with pid [19437]
[minimal_param_node-1] [INFO] [1720324159.753973339] [custom_minimal_param_node]: Hello earth!
[minimal_param_node-1] [INFO] [1720324160.753977739] [custom_minimal_param_node]: Hello world!

进一步的输出应该每秒显示 [INFO] [minimal_param_node]: Hello world! 。

 摘要

您创建了一个带有自定义参数的节点,该参数可以从启动文件或命令行中设置。您将依赖项、可执行文件和启动文件添加到包配置文件中,以便您可以构建并运行它们,并看到参数在实际中的应用。

 下一步

现在您已经拥有了一些包和自己的 ROS 2 系统,下一个教程 https://docs.ros.org/en/jazzy/Tutorials/Beginner-Client-Libraries/Getting-Started-With-Ros2doctor.html 将向您展示如何在遇到问题时检查环境和系统中的问题。

  • 9
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 这是一个关于ROS Noetic软件包依赖关系的问题。其中,下列软件包的依赖关系尚不足够满足要求,无法安装: ros-noetic-desktop-full: 依赖于 ros-noetic-desktop,但它不会被安装。 依赖于 ros-noetic-perception,但它不会被安装。 依赖于 ros-noetic-simulators,但它不会被安装。 依赖于 ros-noetic-urdf-sim-tu,但它不会被安装。 ### 回答2: 这个错误提示是说明在安装 ros-noetic-desktop-full 软件包时,发现它需要依赖一些其他的软件包,但是这些软件包未被安装。其中,ros-noetic-desktop、ros-noetic-perception、ros-noetic-simulators 和 ros-noetic-urdf-sim-tu 是四个未满足依赖关系的软件包。 这个错误提示一般是由于软件源的问题所导致的。在安装软件包时,系统会从软件源中查找该软件包以及它所需的依赖关系。如果软件源中不存在某个软件包的依赖关系,则会提示这个错误信息。 要解决这个问题,可以尝试以下几个方法: 1. 更新软件源:可通过修改软件源配置文件或使用软件源管理工具来更新软件源。更新后再次尝试安装软件包,看是否能够解决依赖关系问题。 2. 手动安装依赖关系:如果更新软件源后仍然无法解决依赖关系问题,可以尝试手动安装依赖关系。按照依赖关系的提示,逐个安装这四个软件包。安装完成后再次尝试安装 ros-noetic-desktop-full 软件包,看是否能够正常安装。 3. 使用 aptitude 命令安装:aptitude 命令可以自动处理依赖关系,可能会更好地解决这个问题。可以通过运行以下命令安装 ros-noetic-desktop-full 软件包: sudo aptitude install ros-noetic-desktop-full 以上是我的回答,希望能对你有所帮助。如果你还有其他问题,请随时回复。 ### 回答3: 这个问题意味着在安装 ros-noetic-desktop-full 软件包时,计算机无法满足所有需要的依赖关系。这些依赖关系包括 ros-noetic-desktop、ros-noetic-perception、ros-noetic-simulators 和 ros-noetic-urdf-sim-tu。 在解决这个问题之前,我们需要了解什么是依赖关系。在软件工程中,依赖关系指的是一个软件包需要另一个软件包才能正常运行的情况。例如,在 ROS 中,ros-noetic-desktop-full 需要依赖其他的软件包才能提供完整的功能。 为了解决这个问题,我们可以使用以下方法: 1. 更新软件包源列表。我们可以更新软件包源列表,这有助于计算机查找所需的软件包。在 Ubuntu 系统中,我们可以使用以下命令更新软件包源列表:sudo apt-get update。 2. 安装依赖关系。我们可以尝试单独安装缺失的依赖关系。在 ROS 中,我们可以使用以下命令安装缺失的软件包:sudo apt-get install ros-noetic-desktop ros-noetic-perception ros-noetic-simulators ros-noetic-urdf-sim-tu。 3. 检查软件包仓库。某些情况下,软件包源可能已经过时或不再受支持。我们可以检查软件包仓库,查看软件包是否可用。在 Ubuntu 系统中,我们可以使用以下命令查看软件包仓库:apt-cache search ros-noetic-desktop-full。 总之,无法满足依赖关系的问题是常见的,在解决这个问题之前,我们需要了解依赖关系的概念,并掌握一些解决方法。在 ROS 中,我们可以使用更新软件包源列表、安装依赖关系和检查软件包仓库等方法解决问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值