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

【Java Web学习 | 第15篇】jQuery(万字长文警告)

【Java Web学习 | 第15篇】jQuery(万字长文警告)

🌈个人主页: Hygge_Code🔥热门专栏:从0开始学习Java | Linux学习| 计算机网络💫个人格言: “既然选择了远方,便不顾风雨兼程” 文章目录 * 从零开始学 jQuery * jQuery 核心知识🥝 * 一、jQuery 简介:为什么选择它? * 1. 核心用途 * 2. 核心优势 * 3. 下载与引入 * 二、jQuery 语法:基础与选择器 * 1. 常用选择器 * 2. ready 方法:确保文档加载完成 * 三、DOM 元素操作:内容、属性、样式 * 1. 操作元素内容 * 2. 操作元素属性 * 3. 操作元素样式 * (1)操作宽度与高度 * (2)

中文文本分类新选择|AI万能分类器集成WebUI开箱即用

中文文本分类新选择|AI万能分类器集成WebUI开箱即用 在自然语言处理(NLP)的实际应用中,文本分类是构建智能客服、舆情监控、工单系统、内容推荐等场景的核心能力。传统方法往往依赖大量标注数据进行模型训练,成本高、周期长。而随着预训练语言模型的发展,零样本学习(Zero-Shot Learning) 正在改变这一局面。 本文将介绍一款基于 StructBERT 零样本分类模型 的 AI 万能分类器镜像——无需训练、支持自定义标签、集成可视化 WebUI,真正实现“开箱即用”的中文文本智能打标解决方案。 🌟 为什么需要“零样本”文本分类? 在实际业务中,我们常常面临这样的问题: * 新的分类需求不断出现(如新增投诉类型) * 标注数据稀缺或成本过高 * 快速验证分类逻辑是否可行 * 希望快速搭建原型系统进行演示 传统的监督学习流程:收集数据 → 清洗标注 → 训练模型 → 部署上线,动辄数周甚至数月。而零样本分类技术让我们可以跳过训练阶段,在推理时直接定义类别标签,由模型根据语义理解自动判断归属。 💡 零样本

DeerFlow基础教程:DeerFlow控制台UI与Web UI双模式使用详解

DeerFlow基础教程:DeerFlow控制台UI与Web UI双模式使用详解 1. DeerFlow是什么:你的个人深度研究助理 你有没有过这样的经历?想快速了解一个新领域,比如“量子计算在金融建模中的最新应用”,但搜了一堆资料,发现信息零散、真假难辨、专业门槛高,最后只能放弃?或者需要为一次重要汇报准备一份结构清晰、数据扎实、有深度分析的行业报告,却卡在资料收集和逻辑梳理上,耗时又费力? DeerFlow就是为解决这类问题而生的。它不是另一个聊天机器人,而是一个能真正帮你“做研究”的AI助手——你的个人深度研究助理。 它不满足于简单回答问题,而是主动调用搜索引擎获取最新信息、运行Python代码验证假设、调用专业工具分析数据、整合多源内容生成结构化报告,甚至能把研究成果变成一段自然流畅的播客音频。整个过程就像你请了一支由研究员、数据分析师、文案编辑和播客制作人组成的虚拟团队,在后台协同工作。 更关键的是,它把这一切复杂能力,封装成了两种你随时可以打开、上手就用的界面:一个简洁高效的命令行控制台(Console UI),适合喜欢直接、快速、可复现操作的用户;还有一个功能

【前端实战】Axios 错误处理的设计与进阶封装,实现网络层面的数据与状态解耦

【前端实战】Axios 错误处理的设计与进阶封装,实现网络层面的数据与状态解耦

目录 【前端实战】Axios 错误处理的设计与进阶封装,实现网络层面的数据与状态解耦 一、为什么网络错误处理一定要下沉到 Axios 层 二、Axios 拦截器 interceptors 1、拦截器的基础应用 2、错误分级和策略映射的设计 3、错误对象标准化 三、结语         作者:watermelo37         ZEEKLOG优质创作者、华为云云享专家、阿里云专家博主、腾讯云“创作之星”特邀作者、火山KOL、支付宝合作作者,全平台博客昵称watermelo37。         一个假装是giser的coder,做不只专注于业务逻辑的前端工程师,Java、Docker、Python、LLM均有涉猎。 --------------------------------------------------------------------- 温柔地对待温柔的人,包容的三观就是最大的温柔。 --------------------------------------------------------------------- 【前