iT邦幫忙

2023 iThome 鐵人賽

DAY 3
0

開始之前先來複習一下ROSROS2Parameter的機制,會讓在Migrating的人更好理解,不然當初我寫的時候都一頭霧水。首先ROS2已經沒有Master Server,所以Parameter Server也一起消失了。這樣的好處是可以隨時啟動node不用先跑roscore

但是這也意味著Parameter必須依賴Node,必須在Node內被宣告後才能在使用。這也是為什麼ROS中我們可以直接用get_param來取得參數,但是ROS2必須要先declare_parameter然後再get_parameter才能取得參數。接下來我們來看看ROS2的Parameter怎麼用。

C++


  1. 首先我們建立一個新的c++ package,並且加入rclcpp的dependency:

    ros2 pkg create --build-type ament_cmake cpp_parameters --dependencies rclcpp
    
  2. 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");
    
        timer_ = this->create_wall_timer(
        1000ms, std::bind(&MinimalParam::timer_callback, this));
    }
    
    void timer_callback()
    {
        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);
    }
    
    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;
    }
    

    解析:

    class MinimalParam : public rclcpp::Node
    {
    public:
        MinimalParam()
        : Node("minimal_param_node")
        {
            this->declare_parameter("my_parameter", "world");
    
            timer_ = this->create_wall_timer(
            1000ms, std::bind(&MinimalParam::timer_callback, this));
        }
    

    首先來看Constructor。在ROS2中,必須要先declare_parameter才能使用get_parameter,所以先在這裡宣告一個my_parameter,並且給他預設值world。接著建立一個timer,每秒鐘會呼叫timer_callback

    void timer_callback()
    {
    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_callback中,我們先用get_parameter取得my_parameter的值,並且用RCLCPP_INFO印出來。接著建立一個std::vector,並且用set_parameters來設定my_parameter的值返回為world。這樣使用者在外部更改my_parameter的值,timer_callback就會將他改回world

    這邊用的set_parameters可以一次用來設定多個參數。當然也可以用set_parameter來設定單一參數。set_parameters的用法如下:

    this->set_parameter(rclcpp::Parameter("my_parameter", "world"));
    

    其餘的用法都和前面的範例一樣,smart pointer宣告node,rclcpp::initrclcpp::shutdown來啟動和關閉node,就不再贅述。

  3. CMakeLists.txtfind_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}
    )
    

    可以看到我們並沒有增新的dependency,因為parameter是rclcpp的一部分。

  4. Build:

    cd ~/ros2_ws
    # install dependencies (if any)
    rosdep install -i --from-path src --rosdistro foxy -y
    
    colcon build --packages-select cpp_parameters
    source install/setup.bash
    
    
  5. 執行:

    ros2 run cpp_parameters minimal_param_node
    

    就可以看到:

    [INFO] [1630549430.000000000] [minimal_param_node]: Hello world!
    
  6. 開一個新的terminal查看parameter:

    ros2 param list
    

    可以看到:

    my_parameter
    
  7. 接著將world換成其他字,像是there

    ros2 param set /minimal_param_node my_parameter there
    

    再回到第一個terminal,可以看到成功更改world:

    [INFO] [minimal_param_node]: Hello there!
    

Launch

當然也可以在launch file中更改parameter:

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": "there"}
            ]
        )
    ])

ros launch 的部分後面會再詳細介紹,這邊只要知道Node中的parameters=[]可以用來設定參數就好。

  1. 回到CMakeLists.txt中安裝launch:
    install(
        DIRECTORY launch
        DESTINATION share/${PROJECT_NAME}
    )
    
  2. Build with launch
    colcon build --symlink-install --packages-select cpp_parameters
    source install/setup.bash
    
    這邊用--symlink-install是因為我們要修改src底下得launch file,這樣可以讓install內的launch file也跟著改變。
  3. 執行
    ros2 launch cpp_parameters cpp_parameters_launch.py
    
    可以看到更改成功:
    [INFO] [minimal_param_node]: Hello there!
    

Python


大致上都和C++範例的架構一樣,不過Python的人會跳過XDD 這邊再說一次好了。

  1. ros2_ws/src底下新增一個python package:

    ros2 pkg create --build-type ament_python python_parameters --dependencies rclpy
    
  2. ros2_ws/src/python_parameters/python_parameters底下新增python_parameters_node.py:

    import rclpy
    import rclpy.node
    
    class MinimalParam(rclpy.node.Node):
        def __init__(self):
            super().__init__('minimal_param_node')
    
            self.declare_parameter('my_parameter', 'world')
    
            self.timer = self.create_timer(1, self.timer_callback)
    
        def timer_callback(self):
            my_param = self.get_parameter('my_parameter').get_parameter_value().string_value
    
            self.get_logger().info('Hello %s!' % my_param)
    
            my_new_param = rclpy.parameter.Parameter(
                'my_parameter',
                rclpy.Parameter.Type.STRING,
                'world'
            )
            all_new_parameters = [my_new_param]
            self.set_parameters(all_new_parameters)
    
    def main():
        rclpy.init()
        node = MinimalParam()
        rclpy.spin(node)
    
    if __name__ == '__main__':
        main()
    

    先看__init__的部分

    def __init__(self):
        super().__init__('minimal_param_node')
    
        self.declare_parameter('my_parameter', 'world')
    
        self.timer = self.create_timer(1, self.timer_callback)
    

    先宣告一個my_parameter,並且給他預設值world。接著建立一個timer,每秒鐘會呼叫timer_callback

    def timer_callback(self):
        my_param = self.get_parameter('my_parameter').get_parameter_value().string_value
    
        self.get_logger().info('Hello %s!' % my_param)
    
        my_new_param = rclpy.parameter.Parameter(
            'my_parameter',
            rclpy.Parameter.Type.STRING,
            'world'
        )
        all_new_parameters = [my_new_param]
        self.set_parameters(all_new_parameters)
    

    timer_callback中,我們先用get_parameter取得my_parameter的值,並且用self.get_logger().info印出來。接著建立一個rclpy.parameter.Parameter,並且用set_parameters來設定my_parameter的值返回為world。這樣使用者在外部更改my_parameter的值,timer_callback就會將他改回world

    注意這邊和C++不同的是,rclpy.parameter.Parameter必須要給他rclpy.Parameter.Type,這邊我們給他STRING

    其餘的部分和C++範例一樣,rclpy.initrclpy.spin來啟動和關閉node。

  3. 加入entry point到setup.py:

    entry_points={
        'console_scripts': [
            'minimal_param_node = python_parameters.python_parameters_node:main',
        ],
    },
    
  4. Build:

    cd ~/ros2_ws
    rosdep install -i --from-path src --rosdistro foxy -y
    
    colcon build --symlink-install --packages-select python_parameters
    source install/setup.bash
    
  5. 執行:

    ros2 run python_parameters minimal_param_node
    

    就可以看到:

    [INFO] [minimal_param_node]: Hello world!
    
  6. 開一個新的terminal查看parameter:

    ros2 param list
    

    可以看到:

    my_parameter
    
  7. 接著將world換成其他字,像是there

    ros2 param set /minimal_param_node my_parameter there
    

    再回到第一個terminal,可以看到成功更改world:

    [INFO] [minimal_param_node]: Hello there!
    

Launch

  1. Python也可以用launch file來更改parameter:
    from launch import LaunchDescription
    from launch_ros.actions import Node
    
    def generate_launch_description():
        return LaunchDescription([
            Node(
                package='python_parameters',
                executable='minimal_param_node',
                name='custom_minimal_param_node',
                output='screen',
                emulate_tty=True,
                parameters=[
                    {'my_parameter': 'there'}
                ]
            )
        ])
    
  2. 別忘了回到setup.py加入launch:
    import os
    from glob import glob
    # ...
    
    setup(
        # ...
        data_files=[
            # ...
            (os.path.join('share', package_name), glob('launch/*launch.[pxy][yma]*')),
            ]
        )
    
  3. Build with launch
    cd ~/ros2_ws
    colcon build --symlink-install --packages-select python_parameters
    source install/setup.bash
    
  4. 執行
    ros2 launch python_parameters python_parameters_launch.py
    
    可以看到更改成功:
    [INFO] [minimal_param_node]: Hello there!
    

ROS vs. ROS2


說明 ROS ROS2
Parameter Server
Accessibility 全域或namespace 僅Node內部
Parameter 取得 nh.getParam 1. node->declare_parameter2. node->get_parameter
Parameter 設定 nh.setParam 1. node->declare_parameter2. node->set_parameter
run 指令 rosrun pkg node _param:=value ros2 run pkg node --ros-args -p param:=value
Check Parameter rosparam list ros2 param list
Set Parameter rosparam set (/ns)/param value ros2 param set /node param value
Get Parameter rosparam get (/ns)/param ros2 param get /node param
Parameter Exists API nh.hasParam node->list_parameters

Reference



上一篇
Day18 ROS2 Parameters
下一篇
Day20 ROS2 Launch
系列文
ROS2 及 ROS Porting 自學筆記30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言