Study/[ROS2] ROS2 Basic

ROS2 with C++ - Node Parameters

soohwan_justin 2023. 8. 3. 22:05

본 포스트는 ROS1에 대한 전반적인 내용 및 ROS2 Python에 대한 지식이 있다는 가정 하에 작성되었으며, The Construct Youtube 영상을 참고하였습니다.

또한, 시뮬레이션 환경은 turtlebot3 waffle_pi를 사용합니다 : https://github.com/ROBOTIS-GIT/turtlebot3_simulations

 

 

ROS2에서 파라미터들은 각각 노드마다 따로 갖고있습니다(roscore가 없으므로). 따라서 노드가 죽으면 관련 파라미터들은 전부 사라집니다. 파라미터는 노드가 시작될 때, 혹은 동작중일때 불러올 수 있습니다.

 

1. create a demo code

1. 패키지 생성

$ ros2 pkg create --build-type ament_cmake parameter_tests_cpp --dependencies rclcpp geometry_msgs

 

2. cpp 파일 생성

parameter_tests_node.cpp

#include <cmath>
#include <rclcpp/rclcpp.hpp>
#include <chrono>
#include <functional>
#include <geometry_msgs/msg/twist.hpp>

using namespace std::chrono_literals;

class VelParam: public rclcpp::Node
{
  public:
    VelParam()
      : Node("param_vel_node")
    {
      auto param_desc = rcl_interfaces::msg::ParameterDescriptor{};
      param_desc.description = "Sets the velocity (in m/s) of the robot.";
      this->declare_parameter<std::double_t>("velocity", 0.0, param_desc);
      timer_ = this->create_wall_timer(
      1000ms, std::bind(&VelParam::timer_callback, this));
      publisher_ = this->create_publisher<geometry_msgs::msg::Twist>("cmd_vel", 10);
    }
    void timer_callback()
    {
      this->get_parameter("velocity", vel_parameter_);
      RCLCPP_INFO(this->get_logger(), "Velocity parameter is: %f", vel_parameter_);
      auto message = geometry_msgs::msg::Twist();
      message.linear.x = vel_parameter_;
      publisher_->publish(message);
    }
  private:
    std::double_t vel_parameter_;
    rclcpp::TimerBase::SharedPtr timer_;
    rclcpp::Publisher<geometry_msgs::msg::Twist>::SharedPtr publisher_;
};

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

 

3. CMakeLists.txt파일 수정

아래 부분을 추가합니다

add_executable(param_vel_node src/parameter_tests_node.cpp)
ament_target_dependencies(param_vel_node rclcpp geometry_msgs)

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

 

4. 패키지 빌드 후, 실행

$ ros2 run parameter_tests param_vel_node

 

 

2. command line에서 파라미터 상호작용 해보기

이전 코드 실행 후, 아래 결과 확인

$ ros2 param list

 

 

다음 명령으로 특정 node의 파라미터만 확인할 수도 있습니다.

$ ros2 param list /param_vel_node

 

다음 명령으로 파라미터에 대한 설명을 확인할 수 있습니다.

$ ros2 param describe /param_vel_node velocity

 

다음 명령으로 현재 파라미터 값을 확인할 수 있습니다.

$ ros2 param get /param_vel_node velocity

 

다음 명령으로 파라미터 값을 설정하면, 로봇이 움직이는 것을 확인할 수 있습니다.

$ ros2 param set /param_vel_node velocity 0.1

 

 

YAML 파일 사용하기

다음 커맨드를 사용해서 현재 사용중인 파라미터를 저장할 수 있습니다.

$ ros2 param dump /param_vel_node > param_vel_node.yaml

 

파일 확인 결과:

 

 

다음 명령으로 파라미터를 불러올 수 있습니다.

$ ros2 param load /param_vel_node param_vel_node.yaml

 

 ros2 run 명령으로 node를 실행할 때, 파라미터를 같이 불러올 수 있습니다.

$ ros2 run parameter_tests param_vel --ros-args --params-file /home/user/param_vel_node.yaml

 

다음 명령으로는 시작하면서 파라미터 값을 설정해줄 수 있습니다.

$ ros2 run parameter_tests param_vel --ros-args -p velocity:=0.1

 

 

2. launch 파일에서 파라미터 설정하기

test_parameters.launch.py

from launch import LaunchDescription
from launch_ros.actions import Node


def generate_launch_description():
    return LaunchDescription([
        Node(
            package='parameter_tests',
            executable='param_vel',
            name='param_vel_node',
            output='screen',
            emulate_tty=True,
            parameters=[
                {'velocity': 0.2}
            ]
        )
    ])

 

 CMakeLists.txt에 다음 내용을 추가합니다.

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

 

빌드 후, 실행해보면 로봇이 움직이는 것을 확인할 수 있습니다

 

 

 

 

3. Parameter Callbacks

앞에서 보셨다시피, ROS2에서는 아무때나 파라미터를 바꿀 수 있습니다. 이렇게 파라미터가 바뀔 때, node에서 callback을 호출할 수 있습니다.

 

1. cpp파일을 만듭니다

parameter_tests_callback.cpp

#include <cmath>
#include <rclcpp/rclcpp.hpp>
#include <chrono>
#include <string>
#include <functional>
#include <geometry_msgs/msg/twist.hpp>
#include <rcl_interfaces/msg/set_parameters_result.hpp>

using namespace std::chrono_literals;

class VelParam: public rclcpp::Node
{
  public:
    rcl_interfaces::msg::SetParametersResult parametersCallback(
        const std::vector<rclcpp::Parameter> &parameters)
    {
        rcl_interfaces::msg::SetParametersResult result;
        result.successful = false;
        result.reason = "";
        for (const auto &parameter : parameters)
        {
            if (parameter.get_name() == "velocity" &&
                parameter.as_double() > 0.2)
            {
                RCLCPP_INFO(this->get_logger(), "Parameter 'velocity' not changed!");
                result.reason = "Parameter 'velocity' cannot be higher than 0.2";
            }
            else
            {
                RCLCPP_INFO(this->get_logger(), "Parameter 'velocity' changed!");
                result.successful = true;
                result.reason = "Parameter 'velocity' is lower than 0.2";
            }
        }
        return result;
    }
    
    VelParam()
      : Node("param_vel_node")
    {
      this->declare_parameter<std::double_t>("velocity", 0.0);
      timer_ = this->create_wall_timer(1000ms, std::bind(&VelParam::timer_callback, this));
      publisher_ = this->create_publisher<geometry_msgs::msg::Twist>("cmd_vel", 10);
      callback_handle_ = this->add_on_set_parameters_callback(
            std::bind(&VelParam::parametersCallback, this, std::placeholders::_1));
    }
    void timer_callback()
    {
      this->get_parameter("velocity", vel_parameter_);
      RCLCPP_INFO(this->get_logger(), "Velocity parameter is: %f", vel_parameter_);
      auto message = geometry_msgs::msg::Twist();
      message.linear.x = vel_parameter_;
      publisher_->publish(message);
    }
  private:
    std::double_t vel_parameter_;
    rclcpp::TimerBase::SharedPtr timer_;
    rclcpp::Publisher<geometry_msgs::msg::Twist>::SharedPtr publisher_;
    OnSetParametersCallbackHandle::SharedPtr callback_handle_;
};

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

 

2. CMakeLists.txt에 아래 내용을 추가합니다

add_executable(param_vel_node2 src/parameter_tests_callback.cpp)
ament_target_dependencies(param_vel_node2 rclcpp geometry_msgs rcl_interfaces)

install(TARGETS
  param_vel_node
  param_vel_node2
  DESTINATION lib/${PROJECT_NAME}
)

 

빌드 후, 실행하고 파라미터를 바꿔봅니다

$ ros2 param set /param_vel_node velocity 0.2