gazebo加载机器人与环境launch文件分析

gazebo加载机器人与环境launch文件分析

文章目录

launch文件源码

import launch import launch_ros from ament_index_python.packages import get_package_share_directory from launch.launch_description_sources import PythonLaunchDescriptionSource defgenerate_launch_description():# 获取默认路径 robot_name_in_model ="fishbot" urdf_tutorial_path = get_package_share_directory('fishbot_description') default_model_path = urdf_tutorial_path +'/urdf/fishbot/fishbot.urdf.xacro' default_world_path = urdf_tutorial_path +'/world/custom_room.world'# 为 Launch 声明参数 action_declare_arg_mode_path = launch.actions.DeclareLaunchArgument( name='model', default_value=str(default_model_path), description='URDF 的绝对路径')# 获取文件内容生成新的参数 robot_description = launch_ros.parameter_descriptions.ParameterValue( launch.substitutions.Command(['xacro ', launch.substitutions.LaunchConfiguration('model')]), value_type=str) robot_state_publisher_node = launch_ros.actions.Node( package='robot_state_publisher', executable='robot_state_publisher', parameters=[{'robot_description': robot_description}])# 通过 IncludeLaunchDescription 包含另外一个 launch 文件 launch_gazebo = launch.actions.IncludeLaunchDescription( PythonLaunchDescriptionSource([get_package_share_directory('gazebo_ros'),'/launch','/gazebo.launch.py']),# 传递参数 launch_arguments=[('world', default_world_path),('verbose','true')])# 请求 Gazebo 加载机器人 spawn_entity_node = launch_ros.actions.Node( package='gazebo_ros', executable='spawn_entity.py', arguments=['-topic','/robot_description','-entity', robot_name_in_model,])# 加载并激活 fishbot_joint_state_broadcaster 控制器 load_joint_state_controller = launch.actions.ExecuteProcess( cmd=['ros2','control','load_controller','--set-state','active','fishbot_joint_state_broadcaster'], output='screen')# 加载并激活 fishbot_effort_controller 控制器 load_fishbot_effort_controller = launch.actions.ExecuteProcess( cmd=['ros2','control','load_controller','--set-state','active','fishbot_effort_controller'], output='screen') load_fishbot_diff_drive_controller = launch.actions.ExecuteProcess( cmd=['ros2','control','load_controller','--set-state','active','fishbot_diff_drive_controller'], output='screen')return launch.LaunchDescription([ action_declare_arg_mode_path, robot_state_publisher_node, launch_gazebo, spawn_entity_node,# 事件动作,当加载机器人结束后执行  launch.actions.RegisterEventHandler( event_handler=launch.event_handlers.OnProcessExit( target_action=spawn_entity_node, on_exit=[load_joint_state_controller],)),# 事件动作,load_fishbot_diff_drive_controller launch.actions.RegisterEventHandler( event_handler=launch.event_handlers.OnProcessExit( target_action=load_joint_state_controller, on_exit=[load_fishbot_diff_drive_controller],)),])

代码分析

第一段:定义路径常量(机器人模型&仿真世界)
 # 获取默认路径 robot_name_in_model = "fishbot" urdf_tutorial_path = get_package_share_directory('fishbot_description') default_model_path = urdf_tutorial_path + '/urdf/fishbot/fishbot.urdf.xacro' default_world_path = urdf_tutorial_path + '/world/custom_room.world' 
  1. robot_name_in_model = "fishbot":定义机器人在Gazebo中的实体名称,后续在Gazebo中生成机器人时,会用这个名称标识该机器人实体。
  2. get_package_share_directory('fishbot_description'):ROS 2的工具函数,作用是获取fishbot_description包的share目录的绝对路径share目录是ROS 2包中存放配置文件、模型、世界文件等资源的标准目录),避免硬编码路径(不同环境下包的安装路径不同,硬编码会导致路径错误)。
  3. default_model_path:拼接得到机器人模型文件(fishbot.urdf.xacro)的绝对路径,注意文件格式是.urdf.xacro(不是纯.urdf),说明这是一个支持宏定义、参数化的URDF文件,需要先通过xacro工具解析为纯URDF格式才能被ROS 2识别。
  4. default_world_path:拼接得到Gazebo仿真世界文件(custom_room.world)的绝对路径,这个文件定义了Gazebo中的仿真环境(如墙壁、地面、灯光等)。

第二段:声明Launch参数(模型路径)
 # 为 Launch 声明参数 action_declare_arg_mode_path = launch.actions.DeclareLaunchArgument( name='model', default_value=str(default_model_path), description='URDF 的绝对路径') 
  1. 这是ROS 2 Launch的参数声明动作(DeclareLaunchArgument),作用是为当前Launch文件定义一个可外部传入的参数model
  2. 关键属性说明:
    • name='model':参数名,外部启动该Launch文件时,可以通过ros2 launch 包名 该launch文件名 model:=自定义模型路径来覆盖默认值。
    • default_value=str(default_model_path):参数默认值,即第一段中拼接的fishbot.urdf.xacro路径,不传入自定义参数时,使用该默认模型。
    • description:参数描述,用于说明该参数的作用,提高脚本可读性。
  3. 核心目的:提高Launch文件的灵活性,无需修改Launch文件代码,即可切换不同的机器人模型。

第三段:生成机器人描述(解析Xacro为URDF)
 # 获取文件内容生成新的参数 robot_description = launch_ros.parameter_descriptions.ParameterValue( launch.substitutions.Command( ['xacro ', launch.substitutions.LaunchConfiguration('model')]), value_type=str) 

这是整个Launch文件的核心步骤之一,作用是解析Xacro文件,生成可供ROS 2使用的机器人描述(robot_description),拆解如下:

  1. launch.substitutions.LaunchConfiguration('model'):获取第二段声明的model参数的值(默认值或外部传入值),即Xacro文件的路径。
  2. launch.substitutions.Command([...]):ROS 2的替换器,作用是在Launch文件执行时,运行一个系统命令,并获取命令的输出结果。这里的命令是xacro 【Xacro文件路径】,对应终端中的xacro fishbot.urdf.xacro命令,用于将Xacro文件解析为纯URDF格式的字符串。
  3. launch_ros.parameter_descriptions.ParameterValue(...):将命令执行的输出(URDF字符串)封装为ROS 2的参数值,指定value_type=str(确保输出是字符串格式,符合robot_description的要求)。
  4. 最终结果:robot_description变量中存储了完整的、解析后的URDF机器人描述字符串,后续会传递给robot_state_publisher节点。

第四段:启动机器人状态发布节点(robot_state_publisher)
 robot_state_publisher_node = launch_ros.actions.Node( package='robot_state_publisher', executable='robot_state_publisher', parameters=[{'robot_description': robot_description}] ) 
  1. launch_ros.actions.Node:ROS 2 Launch中用于启动一个ROS 2节点的动作。
  2. 节点配置说明:
    • package='robot_state_publisher':节点所属的ROS 2包(核心功能包,无需自定义)。
    • executable='robot_state_publisher':要运行的节点可执行文件名称。
    • parameters=[{'robot_description': robot_description}]:向节点传递参数,这里将第三段生成的robot_description(URDF字符串)作为参数传递给该节点。
  3. robot_state_publisher节点的核心作用:
    • 解析robot_description中的URDF模型,获取机器人的关节树结构。
    • 发布机器人的TF/TF2变换(坐标变换)(如从基坐标系base_link到各个关节、传感器坐标系的变换),这是ROS 2中导航、感知、控制的基础(其他节点需要通过TF变换获取各部件的空间位置关系)。

第五段:包含并启动Gazebo仿真环境
 # 通过 IncludeLaunchDescription 包含另外一个 launch 文件 launch_gazebo = launch.actions.IncludeLaunchDescription( PythonLaunchDescriptionSource([get_package_share_directory( 'gazebo_ros'), '/launch', '/gazebo.launch.py']), # 传递参数 launch_arguments=[('world', default_world_path),('verbose','true')] ) 
  1. launch.actions.IncludeLaunchDescription:ROS 2 Launch的文件包含动作,作用是在当前Launch文件中嵌套执行另一个Launch文件,避免重复编写启动Gazebo的代码(gazebo_ros包已经提供了成熟的gazebo.launch.py)。
  2. PythonLaunchDescriptionSource([...]):指定要包含的Launch文件的路径,这里通过get_package_share_directory('gazebo_ros')获取gazebo_ros包的路径,拼接得到gazebo.launch.py(Gazebo的核心启动文件)。
  3. launch_arguments=[('world', default_world_path),('verbose','true')]:向被包含的gazebo.launch.py传递参数:
    • world:指定Gazebo加载的仿真世界文件,即第一段中的custom_room.world
    • verbose='true':开启Gazebo的详细输出模式,便于调试(在终端中打印更多Gazebo的运行日志)。
  4. 执行结果:启动Gazebo仿真器,并加载custom_room.world定义的自定义环境,等待生成机器人实体。

第六段:在Gazebo中生成机器人实体(spawn_entity.py
 # 请求 Gazebo 加载机器人 spawn_entity_node = launch_ros.actions.Node( package='gazebo_ros', executable='spawn_entity.py', arguments=['-topic', '/robot_description', '-entity', robot_name_in_model, ]) 
  1. 启动gazebo_ros包中的spawn_entity.py脚本节点,作用是将机器人模型发布到Gazebo仿真环境中,生成可交互的机器人实体
  2. arguments:向该节点传递命令行参数(对应终端运行ros2 run gazebo_ros spawn_entity.py -h看到的参数):
    • -topic /robot_description:指定从/robot_description话题中获取机器人的URDF描述(robot_state_publisher节点会发布该话题)。
    • -entity robot_name_in_model:指定机器人在Gazebo中的实体名称(即第一段定义的fishbot)。
  3. 执行结果:Gazebo中会出现fishbot机器人模型,机器人被成功加载到仿真环境中,但此时还无法进行控制(需要激活控制器)。

第七段:定义控制器加载进程(3个控制器)
 # 加载并激活 fishbot_joint_state_broadcaster 控制器 load_joint_state_controller = launch.actions.ExecuteProcess( cmd=['ros2', 'control', 'load_controller', '--set-state', 'active', 'fishbot_joint_state_broadcaster'], output='screen' ) # 加载并激活 fishbot_effort_controller 控制器 load_fishbot_effort_controller = launch.actions.ExecuteProcess( cmd=['ros2', 'control', 'load_controller', '--set-state', 'active','fishbot_effort_controller'], output='screen') # 加载并激活 fishbot_diff_drive_controller 控制器 load_fishbot_diff_drive_controller = launch.actions.ExecuteProcess( cmd=['ros2', 'control', 'load_controller', '--set-state', 'active','fishbot_diff_drive_controller'], output='screen') 
  1. launch.actions.ExecuteProcess:ROS 2 Launch中用于**运行一个系统终端命令(非ROS 2节点)**的动作,这里用于执行ros2 control相关命令,加载并激活机器人控制器。
  2. 核心命令:ros2 control load_controller --set-state active 【控制器名称】,这是ROS 2控制框架(ros2_control)的终端命令,作用是从机器人的控制器配置中加载指定名称的控制器,并直接将其设置为激活状态(active)(控制器默认加载后是未激活状态,需要手动或自动激活才能工作)。
  3. 三个控制器的作用说明:
    • fishbot_joint_state_broadcaster关节状态广播器(核心必备),用于采集机器人所有关节的状态(位置、速度、力矩),并发布到/joint_states话题中,供robot_state_publisher、RViz等节点使用。
    • fishbot_effort_controller力矩控制器,用于向机器人关节发送力矩指令,控制关节运动(该Launch文件中未实际执行激活,仅定义了进程)。
    • fishbot_diff_drive_controller差速驱动控制器(核心运动控制器),用于实现机器人的差速驱动(左右轮转速控制,完成前进、转弯等动作),是移动机器人运动控制的核心。
  4. output='screen':将命令执行的输出日志打印到终端,便于调试(查看控制器是否加载成功、有无报错)。

第八段:返回Launch描述(组装所有动作,定义执行逻辑)
 return launch.LaunchDescription([ action_declare_arg_mode_path, robot_state_publisher_node, launch_gazebo, spawn_entity_node, # 事件动作,当加载机器人结束后执行 launch.actions.RegisterEventHandler( event_handler=launch.event_handlers.OnProcessExit( target_action=spawn_entity_node, on_exit=[load_joint_state_controller],) ), # 事件动作,load_fishbot_diff_drive_controller launch.actions.RegisterEventHandler( event_handler=launch.event_handlers.OnProcessExit( target_action=load_joint_state_controller, on_exit=[load_fishbot_diff_drive_controller],) ), ]) 

这是Launch文件的最终返回结果launch.LaunchDescription()接收一个动作列表,定义了所有要执行的动作和执行逻辑,核心分为两部分:顺序执行的基础动作事件驱动的有序控制器激活

  1. 基础动作(按列表顺序执行,无依赖):
    • action_declare_arg_mode_path:先声明model参数。
    • robot_state_publisher_node:启动机器人状态发布节点。
    • launch_gazebo:启动Gazebo仿真环境。
    • spawn_entity_node:在Gazebo中生成机器人实体。
  2. 事件驱动动作(RegisterEventHandler + OnProcessExit):这是该Launch文件的亮点逻辑,解决了「控制器必须在机器人加载完成后才能激活」的依赖问题(如果直接顺序执行,可能出现机器人还没加载好,控制器就开始加载,导致加载失败)。
    • OnProcessExit:事件处理器,作用是监听指定目标动作(target_action)的进程退出事件(即目标动作执行完成、进程正常退出)。
    • 第一个事件监听:
      • target_action=spawn_entity_node:监听spawn_entity_node(机器人生成)的进程退出。
      • on_exit=[load_joint_state_controller]:当机器人生成完成后,自动执行load_joint_state_controller(激活关节状态广播器)。
    • 第二个事件监听:
      • target_action=load_joint_state_controller:监听关节状态广播器的加载完成事件。
      • on_exit=[load_fishbot_diff_drive_controller]:当关节状态广播器激活完成后,自动执行load_fishbot_diff_drive_controller(激活差速驱动控制器)。
    • 最终控制器激活顺序(严格依赖,不会出错):机器人生成完成激活关节状态广播器激活差速驱动控制器
  3. 注意:load_fishbot_effort_controller(力矩控制器)仅定义了进程,但未在LaunchDescription中添加执行逻辑,因此该控制器不会被自动激活(如需激活,可在第二个事件的on_exit中添加该进程,或新增一个事件监听)。

三、补充说明(新手必看)

  1. 依赖条件:运行该Launch文件前,必须确保fishbot_description包已编译(colcon build --packages-select fishbot_description),且已执行source install/setup.bash
  2. 常见报错:
    • 路径错误:get_package_share_directory找不到fishbot_description,大概率是包名拼写错误或未编译。
    • Xacro解析错误:终端提示xacro命令错误,大概率是fishbot.urdf.xacro文件语法错误,或缺少依赖的宏定义。
    • 控制器加载失败:大概率是fishbot的URDF文件中未配置对应的ros2_control控制器(缺少<ros2_control>标签),或控制器名称拼写错误。
  3. 扩展方向:如需添加RViz可视化,可在LaunchDescription中新增rviz2节点,指定配置文件路径。

四、总结

  1. 该Launch文件的核心是一站式启动fishbot机器人的Gazebo仿真环境,并通过事件驱动实现控制器的有序自动激活,避免了手动执行多个终端命令的繁琐。
  2. 关键技术点包括:Xacro文件解析、Launch参数声明、嵌套Launch文件、事件驱动(OnProcessExit)、ros2_control控制器加载。
  3. 核心执行流程:声明参数→启动状态发布节点→启动Gazebo→生成机器人→按依赖激活控制器(关节状态广播器→差速驱动控制器)。

Read more

AI编程工具深度对比:Cursor、Copilot、Trae与Claude Code,2025年开发者该如何选择?

2025年,AI编程助手已从新奇技术演变为生产力核心,但面对众多选择,开发者如何才能找到最适合自己的智能编程伙伴? 一、四大AI编程工具的核心定位与市场格局 2025年的AI编程工具市场已经形成了明显的分层格局。根据最新的开发者使用数据,这些工具不再仅仅是代码补全助手,而是朝着专业化、场景化方向发展。

专科生必看!10个高效降aigc工具推荐,轻松过审!

专科生必看!10个高效降aigc工具推荐,轻松过审!

专科生必看!10个高效降aigc工具推荐,轻松过审! AI降重工具:让论文更自然,更安心 在当前的学术环境中,越来越多的高校和机构开始采用AIGC检测系统来评估论文的原创性。对于专科生来说,如何有效降低AI痕迹、提升论文的通过率成为了一个亟需解决的问题。而AI降重工具的出现,正好为这一难题提供了全新的解决方案。 这些工具不仅能够精准识别并去除AI生成内容的痕迹,还能在保持原意不变的前提下,对文本进行语义优化和结构调整,使论文更加自然流畅。同时,它们还具备强大的查重功能,帮助学生在提交前预判可能存在的重复问题,从而有针对性地进行修改。 面对众多选择,如何挑选一款真正适合自己的AI降重工具呢?下面是一份详细的对比表格,涵盖了主流工具的功能、适用场景以及使用效果,供你参考。 工具名称主要功能适用场景千笔强力去除AI痕迹、保语义降重AI率过高急需降重云笔AI多模式降重初稿快速处理锐智 AI综合查重与降重定稿前自查文途AI操作简单片段修改降重鸟同义词替换小幅度修改笔杆在线写作辅助辅助润色维普官方查重最终检测万方数据库查重数据对比Turnitin国际通用检测留学生降重ChatGPT

微软 Copilot Cowork 深度解析:用 Kotlin + 147API 手搓一个 AI Agent

微软 Copilot Cowork 深度解析:用 Kotlin + 147API 手搓一个 AI Agent

微软最近发布的 Copilot Cowork 在技术圈炸开了锅。它变了。它不再是那个只会补全代码的插件,而是变成了你的 “Coworker”(同事)。基于 Anthropic 的 Claude 构建,它现在能像真人一样处理复杂任务。 作为开发者,我们不仅要会用,更要懂得背后的原理。今天我们就来拆解一下 Copilot Cowork 的核心逻辑,并教你如何利用 Kotlin 和 147API 构建一个属于自己的简易 AI Agent。 从 Chatbot 到 Agent 传统的 Copilot 就像一个实习生,你给它一个指令,它执行一个动作。而 Copilot Cowork 更像是一个成熟的合作伙伴。它具备了 感知(Perception)、规划(Planning) 和 执行(Execution)