7. launch file กับ parameter

หุ่นจริงมี node สิบกว่าตัว เปิดทีละ terminal ไม่ไหว หัวข้อนี้แก้เรื่องนั้น

parameter — แยกค่าตั้งออกจากโค้ด

ใน หัวข้อ 6 ความเร็ว 0.2 ถูกเขียนตายอยู่ในโค้ด อยากเปลี่ยนต้องแก้ไฟล์ ทำให้เป็น parameter จะดีกว่า

class SpeedSender(Node):
    def __init__(self):
        super().__init__('speed_sender')
        # ประกาศพร้อมค่าเริ่มต้น
        self.declare_parameter('speed', 0.2)
        self.pub = self.create_publisher(Twist, '/cmd_vel', 10)
        self.timer = self.create_timer(0.1, self.tick)

    def tick(self):
        # อ่านทุกครั้งที่ใช้ เพื่อให้เปลี่ยนระหว่างรันได้
        speed = self.get_parameter('speed').value
        msg = Twist()
        msg.linear.x = speed
        self.pub.publish(msg)

ตอนรันกำหนดค่าได้เลย

ros2 run my_robot speed_sender --ros-args -p speed:=0.1

หรือเปลี่ยนระหว่างที่รันอยู่

ros2 param set /speed_sender speed 0.05

Tip

อ่านค่า parameter ทุกครั้งที่ใช้ อย่าอ่านครั้งเดียวใน __init__

ถ้าอ่านเก็บไว้ใน __init__ แล้วใช้ตัวแปรนั้นตลอด ros2 param set จะไม่มีผลอะไรเลย — ค่าเปลี่ยนจริงแต่โค้ดไม่ได้ไปอ่านใหม่

เจอบ่อยตอนจูนค่าแล้วงงว่าทำไมเปลี่ยนแล้วไม่เห็นผล

เก็บค่าลงไฟล์ YAML

พอค่าเยอะขึ้นควรแยกออกเป็นไฟล์ config/robot.yaml

speed_sender:
  ros__parameters:
    speed: 0.15

wheel_driver:
  ros__parameters:
    port: /dev/ttyUSB0
    baud: 115200

Warning

🔴 โครงสร้าง 2 ชั้นนี้ผิดไม่ได้ — ชื่อ node ชั้นนอก แล้ว ros__parameters ชั้นใน (ขีดล่าง 2 ตัว ระหว่าง ros กับ parameters)

ถ้าเขียนผิด ROS 2 จะโหลดไฟล์ผ่านโดยไม่ error แต่ค่าไม่ถูกใช้เลย node จะใช้ค่าเริ่มต้นแทนแบบเงียบ ๆ

ตรวจด้วย ros2 param get หลังเปิด node เสมอว่าค่าเข้าจริง

ชื่อชั้นนอกต้องตรงกับชื่อ node ที่ตั้งใน super().__init__('speed_sender') ไม่ใช่ชื่อไฟล์ และไม่ใช่ชื่อ package

launch file — เปิดหลาย node พร้อมกัน

สร้าง ~/ros2_ws/src/my_robot/launch/robot.launch.py

import os
from ament_index_python.packages import get_package_share_directory
from launch import LaunchDescription
from launch_ros.actions import Node


def generate_launch_description():
    config = os.path.join(
        get_package_share_directory('my_robot'),
        'config', 'robot.yaml')

    return LaunchDescription([
        Node(
            package='my_robot',
            executable='speed_sender',
            name='speed_sender',       # ต้องตรงกับชื่อใน YAML
            parameters=[config],
            output='screen',
        ),
        Node(
            package='my_robot',
            executable='speed_watcher',
            name='speed_watcher',
            output='screen',
        ),
    ])

บอก setup.py ให้ติดตั้งไฟล์เหล่านี้ด้วย

Danger

🔴 ขั้นนี้ลืมแล้วจะเจอ file not found แบบหาสาเหตุไม่เจอ

ไฟล์ launch กับ YAML อยู่ใน src/ แต่ตอนรันจริงระบบไปหาที่ install/ ถ้าไม่บอกให้คัดลอกไป มันจะไม่อยู่ที่นั่น

แก้ setup.py

import os
from glob import glob

setup(
    # ...
    data_files=[
        ('share/ament_index/resource_index/packages',
            ['resource/' + package_name]),
        ('share/' + package_name, ['package.xml']),
        (os.path.join('share', package_name, 'launch'),
            glob('launch/*.launch.py')),
        (os.path.join('share', package_name, 'config'),
            glob('config/*.yaml')),
    ],
)

Note

แก้ setup.py แล้วต้อง build ใหม่เสมอ แม้จะใช้ --symlink-install เพราะ --symlink-install ช่วยเฉพาะไฟล์ .py ที่ลงทะเบียนไว้แล้ว ไม่ได้ช่วยกับการเพิ่มไฟล์ใหม่หรือเปลี่ยนรายการติดตั้ง

รัน

cd ~/ros2_ws
colcon build --symlink-install --packages-select my_robot
source install/setup.bash
ros2 launch my_robot robot.launch.py

เปิดทั้ง 2 node ในคำสั่งเดียว กด Ctrl+C ครั้งเดียวปิดหมด

เปลี่ยนค่าตอนสั่ง launch ได้ด้วย

ros2 launch my_robot robot.launch.py speed:=0.05

ตรวจว่าค่าเข้าจริง

ros2 param get /speed_sender speed

Important

ทำข้อนี้ทุกครั้งหลังแก้ YAML ถ้าได้ 0.2 (ค่าเริ่มต้นในโค้ด) ทั้งที่ YAML เขียน 0.15 แปลว่าไฟล์ไม่ได้ถูกโหลด สาเหตุมี 3 อย่างเรียงตามที่เจอบ่อย

  1. ชื่อ node ใน YAML ไม่ตรงกับ name= ใน launch file

  2. ลืมใส่ config/*.yaml ใน data_files แล้วไม่ได้ build ใหม่

  3. สะกด ros__parameters ผิด (ขีดล่างตัวเดียว)


ก่อนหน้า: 6. publisher กับ subscriber · ต่อไป: 8. tf2 — กรอบพิกัด