一個完整的簡單的launch文件配置過程
1.編寫launch文件
2.配置package.xml
3.配置setup.py(python包)
4.配置CMakeList(C++包)
5.編譯+運行
# 在 ROS 2 的 Python 啟動文件中,這些導入語句用于引入各類啟動模塊,以構建和配置節點啟動流程
from launch import LaunchDescription
from launch_ros.actions import Nodedef generate_launch_description():return LaunchDescription([Node(package='turtlesim',namespace='turtlesim1',executable='turtlesim_node',name='sim'),Node(package='turtlesim',namespace='turtlesim2',executable='turtlesim_node',name='sim'),Node(package='turtlesim',executable='mimic',name='mimic',remappings=[('/input/pose', '/turtlesim1/turtle1/pose'),('/output/cmd_vel', '/turtlesim2/turtle1/cmd_vel'),])])
package.xml加入以下內容:?
<exec_depend>ros2launch</exec_depend>
CMakeList:??to the end of the file (but before?ament_package()
).
# Install launch files.
install(DIRECTORYlaunchDESTINATION share/${PROJECT_NAME}/
)
package.list:
import os
from glob import glob
# Other imports ...package_name = 'py_launch_example'setup(# Other parameters ...data_files=[# ... Other data files# Include all launch files.(os.path.join('share', package_name, 'launch'), glob('launch/*'))]
)
?
替換表達式
from launch import LaunchDescription
from launch.actions import IncludeLaunchDescription
from launch.substitutions import PathJoinSubstitution
from launch_ros.substitutions import FindPackageSharedef generate_launch_description():colors = {'background_r': '200'}return LaunchDescription([# 使用PathJoinSubstitution和FindPackageShare動態構建啟動文件路徑IncludeLaunchDescription(PathJoinSubstitution([FindPackageShare('launch_tutorial'),'launch','example_substitutions.launch.py']),# 傳遞給被包含啟動文件的參數launch_arguments={'turtlesim_ns': 'turtlesim2','use_provided_red': 'True','new_background_r': colors['background_r'],}.items())])
在 Python 中,.items()
?是字典(dict
)對象的一個方法,用于將字典轉換為可迭代的鍵值對元組列表。
又如:
from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument, ExecuteProcess, TimerAction
from launch.conditions import IfCondition
from launch.substitutions import LaunchConfiguration, PythonExpression
from launch_ros.actions import Nodedef generate_launch_description():# turtlesim_ns = LaunchConfiguration('turtlesim_ns')use_provided_red = LaunchConfiguration('use_provided_red')new_background_r = LaunchConfiguration('new_background_r')return LaunchDescription([DeclareLaunchArgument('turtlesim_ns',default_value='turtlesim1'),DeclareLaunchArgument('use_provided_red',default_value='False'),DeclareLaunchArgument('new_background_r',default_value='200'),Node(package='turtlesim',namespace=turtlesim_ns,executable='turtlesim_node',name='sim'),ExecuteProcess(cmd=[['ros2 service call ',turtlesim_ns,'/spawn ','turtlesim/srv/Spawn ','"{x: 2, y: 2, theta: 0.2}"']],shell=True),ExecuteProcess(cmd=[['ros2 param set ',turtlesim_ns,'/sim background_r ','120']],shell=True),TimerAction(period=2.0,actions=[ExecuteProcess(condition=IfCondition(PythonExpression([new_background_r,' == 200',' and ',use_provided_red])),cmd=[['ros2 param set ',turtlesim_ns,'/sim background_r ',new_background_r]],shell=True),],)])
為什么需要 LaunchConfiguration?
簡單說,LaunchConfiguration
就像一個 “參數占位符”。當你在 Launch 文件中聲明了一個啟動參數(比如DeclareLaunchArgument('turtlesim_ns')
),LaunchConfiguration
可以引用這個參數的值,讓你在后續的節點、動作配置中動態使用它,而不用寫死具體數值。
比如你聲明了一個參數background_r
,默認值為 200,但用戶啟動時可能會通過命令行修改為 255。LaunchConfiguration('background_r')
就能自動獲取用戶輸入的最新值,無需修改 Launch 文件代碼。
命令行進程調用:
ExecuteProcess(cmd=[['ros2 param set ',turtlesim_ns,'/sim background_r ','120']],shell=True),
條件執行:
TimerAction(period=2.0,actions=[ExecuteProcess(condition=IfCondition(PythonExpression([new_background_r,' == 200',' and ',use_provided_red])),cmd=[['ros2 param set ',turtlesim_ns,'/sim background_r ',new_background_r]],shell=True),],)
格式:?
TimerAction(period = .... # 多少時間后actions =[ExecuteProcess(condition = IfCondition(...... # 具體條件),cmd = [ [........... # 命令行指令]],shell = True),],)
這段代碼的作用是:在啟動后 2 秒,檢查特定條件是否滿足,如果滿足則執行一條 ROS 2 命令修改 turtlesim 模擬器的背景紅色值。
ROS 2 中的事件處理器(Event Handlers)使用示例?
from launch import LaunchDescription
from launch.actions import (DeclareLaunchArgument,EmitEvent,ExecuteProcess,LogInfo,RegisterEventHandler,TimerAction
)
from launch.conditions import IfCondition
from launch.event_handlers import (OnExecutionComplete,OnProcessExit,OnProcessIO,OnProcessStart,OnShutdown
)
from launch.events import Shutdown
from launch.substitutions import (EnvironmentVariable,FindExecutable,LaunchConfiguration,LocalSubstitution,PythonExpression
)
from launch_ros.actions import Nodedef generate_launch_description():turtlesim_ns = LaunchConfiguration('turtlesim_ns')use_provided_red = LaunchConfiguration('use_provided_red')new_background_r = LaunchConfiguration('new_background_r')turtlesim_ns_launch_arg = DeclareLaunchArgument('turtlesim_ns',default_value='turtlesim1')use_provided_red_launch_arg = DeclareLaunchArgument('use_provided_red',default_value='False')new_background_r_launch_arg = DeclareLaunchArgument('new_background_r',default_value='200')turtlesim_node = Node(package='turtlesim',namespace=turtlesim_ns,executable='turtlesim_node',name='sim')spawn_turtle = ExecuteProcess(cmd=[[FindExecutable(name='ros2'),' service call ',turtlesim_ns,'/spawn ','turtlesim/srv/Spawn ','"{x: 2, y: 2, theta: 0.2}"']],shell=True)change_background_r = ExecuteProcess(cmd=[[FindExecutable(name='ros2'),' param set ',turtlesim_ns,'/sim background_r ','120']],shell=True)change_background_r_conditioned = ExecuteProcess(condition=IfCondition(PythonExpression([new_background_r,' == 200',' and ',use_provided_red])),cmd=[[FindExecutable(name='ros2'),' param set ',turtlesim_ns,'/sim background_r ',new_background_r]],shell=True)return LaunchDescription([turtlesim_ns_launch_arg,use_provided_red_launch_arg,new_background_r_launch_arg,turtlesim_node,RegisterEventHandler(OnProcessStart(target_action=turtlesim_node,on_start=[LogInfo(msg='Turtlesim started, spawning turtle'),spawn_turtle])),RegisterEventHandler(OnProcessIO(target_action=spawn_turtle,on_stdout=lambda event: LogInfo(msg='Spawn request says "{}"'.format(event.text.decode().strip())))),RegisterEventHandler(OnExecutionComplete(target_action=spawn_turtle,on_completion=[LogInfo(msg='Spawn finished'),change_background_r,TimerAction(period=2.0,actions=[change_background_r_conditioned],)])),RegisterEventHandler(OnProcessExit(target_action=turtlesim_node,on_exit=[LogInfo(msg=(EnvironmentVariable(name='USER'),' closed the turtlesim window')),EmitEvent(event=Shutdown(reason='Window closed'))])),RegisterEventHandler(OnShutdown(on_shutdown=[LogInfo(msg=['Launch was asked to shutdown: ', LocalSubstitution('event.reason')])])),])
?詳解:
這個啟動文件創建了一個 turtlesim 模擬器,并實現了以下功能:
- 啟動 turtlesim 節點后,自動生成一只新烏龜
- 捕獲生成烏龜的服務響應并打印日志
- 烏龜生成完成后,修改模擬器背景顏色
- 2 秒后,根據參數條件再次修改背景顏色
- 當 turtlesim 窗口關閉時,自動終止整個啟動過程
- 捕獲系統關閉事件并打印關閉原因
1.節點與命令定義
turtlesim_node = Node(package='turtlesim',namespace=turtlesim_ns,executable='turtlesim_node',name='sim'
)
spawn_turtle = ExecuteProcess(...) # 生成烏龜的服務調用
change_background_r = ExecuteProcess(...) # 修改背景顏色的命令
change_background_r_conditioned = ExecuteProcess(...) # 條件性修改背景顏色
這些組件定義了要執行的基本操作,但它們的執行時機由事件處理器控制。
2.事件處理器注冊
RegisterEventHandler(OnProcessStart(target_action=turtlesim_node,on_start=[LogInfo(...), spawn_turtle])
)
- OnProcessStart:當
turtlesim_node
啟動時,觸發生成烏龜的命令。
RegisterEventHandler(OnProcessIO(target_action=spawn_turtle,on_stdout=lambda event: LogInfo(...))
)
- OnProcessIO:捕獲
spawn_turtle
命令的標準輸出,提取服務響應信息。
當執行spawn_turtle
命令(即調用turtlesim/srv/Spawn
服務)時,服務響應會通過標準輸出返回。例如:
ros2 service call /turtlesim1/spawn turtlesim/srv/Spawn "{x: 2, y: 2, theta: 0.2}"
服務成功響應后,控制臺會打印:
[INFO] [launch]: Spawn request says "Created turtle [turtle2]"
這表明新烏龜已成功生成,并且名稱為turtle2
。
RegisterEventHandler(OnExecutionComplete(target_action=spawn_turtle,on_completion=[LogInfo(...), change_background_r, TimerAction(...)])
)
- OnExecutionComplete:當
spawn_turtle
命令完成后,修改背景顏色,并設置一個 2 秒后的延遲動作。
RegisterEventHandler(OnProcessExit(target_action=turtlesim_node,on_exit=[LogInfo(...), EmitEvent(event=Shutdown(...))])
)
- OnProcessExit:當
turtlesim_node
退出時,觸發系統關閉事件。
RegisterEventHandler(OnShutdown(on_shutdown=[LogInfo(...)])
)
- OnShutdown:捕獲系統關閉事件,打印關閉原因。
3.編譯執行:?
ros2 launch launch_tutorial example_event_handlers.launch.py turtlesim_ns:='turtlesim3' use_provided_red:='True' new_background_r:=200
管理大型工程
頂層管理:
from launch import LaunchDescription
from launch.actions import IncludeLaunchDescription
from launch.substitutions import PathJoinSubstitution
from launch_ros.substitutions import FindPackageSharedef generate_launch_description():launch_dir = PathJoinSubstitution([FindPackageShare('launch_tutorial'), 'launch'])return LaunchDescription([IncludeLaunchDescription(PathJoinSubstitution([launch_dir, 'turtlesim_world_1.launch.py'])),IncludeLaunchDescription(PathJoinSubstitution([launch_dir, 'turtlesim_world_2.launch.py'])),IncludeLaunchDescription(PathJoinSubstitution([launch_dir, 'broadcaster_listener.launch.py']),launch_arguments={'target_frame': 'carrot1'}.items()),IncludeLaunchDescription(PathJoinSubstitution([launch_dir, 'mimic.launch.py'])),IncludeLaunchDescription(PathJoinSubstitution([launch_dir, 'fixed_broadcaster.launch.py'])),IncludeLaunchDescription(PathJoinSubstitution([launch_dir, 'turtlesim_rviz.launch.py'])),])
在launch文件設置參數:
from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument
from launch.substitutions import LaunchConfiguration
from launch_ros.actions import Nodedef generate_launch_description():return LaunchDescription([DeclareLaunchArgument('background_r', default_value='0'),DeclareLaunchArgument('background_g', default_value='84'),DeclareLaunchArgument('background_b', default_value='122'),Node(package='turtlesim',executable='turtlesim_node',name='sim',parameters=[{'background_r': LaunchConfiguration('background_r'),'background_g': LaunchConfiguration('background_g'),'background_b': LaunchConfiguration('background_b'),}]),])
通過YAML文件設置參數:
from launch import LaunchDescription
from launch.substitutions import PathJoinSubstitution
from launch_ros.actions import Node
from launch_ros.substitutions import FindPackageSharedef generate_launch_description():return LaunchDescription([Node(package='turtlesim',executable='turtlesim_node',namespace='turtlesim2',name='sim',parameters=[PathJoinSubstitution([FindPackageShare('launch_tutorial'), 'config', 'turtlesim.yaml'])],),])
/turtlesim2/sim:ros__parameters:background_b: 255background_g: 86background_r: 150
整個/turtlesim2/sim
代表的是 “位于turtlesim2
命名空間下、名為sim
的turtlesim
節點”,其后的ros__parameters
則是該節點的具體參數配置(如背景色的 RGB 值)
命名空間 :
當啟動文件需要包含大量節點時,手動為每個節點單獨設置命名空間會變得繁瑣且易出錯。PushRosNamespace
?提供了一種更優雅的解決方案,讓你可以為一組節點批量設置命名空間。
首先remove the?namespace='turtlesim2'
?line from the?turtlesim_world_2.launch.py
?。然后按照以下修改:
from launch.actions import GroupAction
from launch_ros.actions import PushRosNamespace...GroupAction(actions=[PushROSNamespace('turtlesim2'),IncludeLaunchDescription(PathJoinSubstitution([launch_dir, 'turtlesim_world_2.launch.py'])),]),
那么在turtlesim_world_2.launch.py中的每一個節點都有一個turtlesim2前綴的命令空間。
節點復用
from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument
from launch.substitutions import LaunchConfigurationfrom launch_ros.actions import Nodedef generate_launch_description():return LaunchDescription([DeclareLaunchArgument('target_frame', default_value='turtle1',description='Target frame name.',),Node(package='turtle_tf2_py',executable='turtle_tf2_broadcaster',name='broadcaster1',parameters=[{'turtlename': 'turtle1'}],),Node(package='turtle_tf2_py',executable='turtle_tf2_broadcaster',name='broadcaster2',parameters=[{'turtlename': 'turtle2'}],),Node(package='turtle_tf2_py',executable='turtle_tf2_listener',name='listener',parameters=[{'target_frame': LaunchConfiguration('target_frame')}],),])
?話題重映射
from launch import LaunchDescription
from launch_ros.actions import Nodedef generate_launch_description():return LaunchDescription([Node(package='turtlesim',executable='mimic',name='mimic',remappings=[('/input/pose', '/turtle2/pose'),('/output/cmd_vel', '/turtlesim2/turtle1/cmd_vel'),])])
?啟動rviz,并按照文件配置:
from launch import LaunchDescription
from launch.substitutions import PathJoinSubstitution
from launch_ros.actions import Node
from launch_ros.substitutions import FindPackageSharedef generate_launch_description():return LaunchDescription([Node(package='rviz2',executable='rviz2',name='rviz2',arguments=['-d', PathJoinSubstitution([FindPackageShare('turtle_tf2_py'), 'rviz', 'turtle_rviz.rviz'])],),])
動態配置節點名:
from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument
from launch.substitutions import EnvironmentVariable, LaunchConfiguration
from launch_ros.actions import Nodedef generate_launch_description():return LaunchDescription([DeclareLaunchArgument('node_prefix',default_value=[EnvironmentVariable('USER'), '_'],description='prefix for node name'),Node(package='turtle_tf2_py',executable='fixed_frame_tf2_broadcaster',name=[LaunchConfiguration('node_prefix'), 'fixed_broadcaster'],),])
- 聲明一個啟動參數
node_prefix
,默認值為當前系統用戶的用戶名加上下劃線(例如:doubao_) - 啟動一個節點,節點名稱由
node_prefix
和固定后綴fixed_broadcaster
組成(例如:doubao_fixed_broadcaster)
更新setup.py:
import os
from glob import glob
from setuptools import setup
...data_files=[...(os.path.join('share', package_name, 'launch'),glob('launch/*')),(os.path.join('share', package_name, 'config'),glob('config/*.yaml')),(os.path.join('share', package_name, 'rviz'),glob('config/*.rviz')),],