【智能系统项目开发与学习记录】bringup功能包详解

前言

本项目机器人平台为幻尔智能ROS2机器人,本节开始会逐步拆解学习其示例代码,侵权即删。

一、bringup 包核心定位

在 ROS2 生态中,bringup 包是系统启动包,核心作用是「一键启动机器人全系统的核心功能」,避免手动逐个启动控制器、传感器、应用节点等繁琐操作。

1.1 bringup 包的典型内容

  • 总启动文件(.launch.py):统筹启动所有子模块(相机、雷达、控制器等)
  • 自检节点(如 startup_check.py):启动前检查硬件设备(麦克风、网卡等)是否正常
  • 配置文件:定义硬件参数、启动逻辑(如环境变量判断)
  • 依赖管理文件(package.xmlsetup.py):确保启动所需的依赖包已安装

二、核心文件逐行拆解

2.1 包身份与依赖配置:package.xml

package.xml 是 ROS2 包的「身份证」,告诉系统包的名字、依赖、维护者等信息,必须与 setup.py 同步

2.1.1 文件结构与关键标签
XML 复制代码
<?xml version="1.0"?>
<!-- XML 格式校验:确保文件符合 ROS2 规范 -->
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>

<!-- 根标签:format="3" 是 ROS2 主流格式 -->
<package format="3">
  <!-- 1. 基础身份信息 -->
  <name>bringup</name>          <!-- 包名:必须唯一,与 setup.py 一致 -->
  <version>0.0.0</version>       <!-- 版本号:如 1.0.0(正式版)、0.1.0(测试版) -->
  <description>一键启动机器人控制器、雷达、相机等核心功能</description>  <!-- 功能描述(替换 TODO) -->
  <maintainer email="1270161395@qq.com">ubuntu</maintainer>  <!-- 维护者:名字+邮箱 -->
  <license>Apache-2.0</license>  <!-- 许可证:ROS2 常用 Apache-2.0(开源可商用) -->

  <!-- 2. 运行依赖:启动包必须的依赖 -->
  <depend>rclpy</depend>        <!-- Python 节点依赖:rclpy 是 ROS2 Python 核心库 -->
  <depend>controller</depend>    <!-- 依赖控制器包(示例:根据实际需求添加) -->
  <depend>peripherals</depend>   <!-- 依赖外设包(相机、雷达等) -->

  <!-- 3. 测试依赖:仅代码测试时需要 -->
  <test_depend>ament_copyright</test_depend>  <!-- 检查版权声明 -->
  <test_depend>ament_flake8</test_depend>     <!-- 检查 Python 代码格式 -->
  <test_depend>python3-pytest</test_depend>   <!-- 运行 Python 测试用例 -->

  <!-- 4. 编译类型导出:告诉系统如何编译该包 -->
  <export>
    <build_type>ament_python</build_type>  <!-- Python 包用 ament_python;C++ 包用 ament_cmake -->
  </export>
</package>
2.1.2 关键注意事项
  • 包名(<name>)必须与 setup.py 中的 package_name 完全一致,否则编译失败
  • 运行依赖(<depend>)要写全:缺少依赖会导致启动时提示「找不到某个包」
  • 许可证(<license>)不可省略:避免开源法律风险,优先选 Apache-2.0

2.2 安装与可执行配置:setup.py

setup.py 是 Python 打包工具的配置文件,负责:① 安装 Python 代码和资源文件;② 生成终端可执行命令(如 startup_check)。

2.2.1 完整代码与注释
python 复制代码
import os                           # 处理文件路径
from glob import glob               # 查找匹配文件(如所有 launch 文件)
from setuptools import find_packages, setup  # Python 打包核心工具

package_name = 'bringup'  # 包名:必须与 package.xml 一致

setup(
    name=package_name,
    version='0.0.0',                # 版本号:与 package.xml 同步
    # 1. 自动查找包内所有 Python 模块(排除 test 测试目录)
    packages=find_packages(exclude=['test']),
    
    # 2. 安装非 Python 资源文件(launch、config 等)
    data_files=[
        # 2.1 注册包到 ROS2 索引(必须)
        ('share/ament_index/resource_index/packages', ['resource/' + package_name]),
        # 2.2 安装 package.xml 到系统目录
        ('share/' + package_name, ['package.xml']),
        # 2.3 安装所有 launch 文件到 share/bringup/launch 目录
        (os.path.join('share', package_name, 'launch'), glob(os.path.join('launch', '*.*'))),
        # (可选)安装配置文件:如 config 目录下的参数文件
        # (os.path.join('share', package_name, 'config'), glob(os.path.join('config', '*.*'))),
    ],
    
    # 3. 安装依赖:Python 打包工具必须
    install_requires=['setuptools'],
    zip_safe=True,  # ROS2 推荐 True:支持压缩安装
    
    # 4. 包元数据:与 package.xml 同步
    maintainer='ubuntu',
    maintainer_email='1270161395@qq.com',
    description='一键启动机器人核心功能',
    license='Apache-2.0',
    tests_require=['pytest'],  # 测试依赖
    
    # 5. 生成终端可执行命令(核心!)
    entry_points={
        'console_scripts': [
            # 格式:终端命令 = 模块路径:函数名
            # 示例1:启动自检节点:ros2 run bringup startup_check
            'startup_check = bringup.startup_check:main',
            # 示例2:启动总 launch 文件(可选):ros2 run bringup start_all
            # 'start_all = bringup.script.start_all:main',
        ],
    },
)
2.2.2 核心功能解析
  • data_files 资源安装 :确保 launchconfig 等文件夹被安装到系统目录(install/bringup/share/bringup/),否则启动时找不到 launch 文件。
  • entry_points 可执行命令 :定义后,可通过 ros2 run bringup 命令名 运行节点(如 ros2 run bringup startup_check),本质是映射到 Python 模块的 main 函数。

2.3 系统总启动文件:bringup_launch.py

这是 bringup 包的「核心大脑」,负责根据环境变量判断路径、启动所有子模块(控制器、相机、雷达等)。

2.3.1 完整代码与注释
python 复制代码
import os
from ament_index_python.packages import get_package_share_directory  # 获取包路径

# 导入 Launch 核心类
from launch_ros.actions import Node
from launch.actions import ExecuteProcess, IncludeLaunchDescription, OpaqueFunction
from launch import LaunchDescription, LaunchService
from launch.launch_description_sources import PythonLaunchDescriptionSource

def launch_setup(context):
    """根据环境变量配置启动路径,返回所有要启动的节点/launch"""
    # 1. 读取环境变量:判断是「编译安装版」还是「源码版」
    compiled = os.environ.get('need_compile', 'False')  # 默认为 False(源码版)

    if compiled == 'True':
        # 编译安装版:从系统安装目录获取包路径
        controller_path = get_package_share_directory('controller')
        peripherals_path = get_package_share_directory('peripherals')
        app_path = get_package_share_directory('app')
    else:
        # 源码版:直接指定源码路径(开发调试用)
        controller_path = '/home/ubuntu/ros2_ws/src/driver/controller'
        peripherals_path = '/home/ubuntu/ros2_ws/src/peripherals'
        app_path = '/home/ubuntu/ros2_ws/src/app'

    # 2. 启动子模块:包含其他包的 launch 文件
    # 2.1 启动控制器(如电机控制)
    controller_launch = IncludeLaunchDescription(
        PythonLaunchDescriptionSource(
            os.path.join(controller_path, 'launch/controller.launch.py')
        )
    )

    # 2.2 启动外设(深度相机 + 雷达)
    depth_camera_launch = IncludeLaunchDescription(
        PythonLaunchDescriptionSource(
            os.path.join(peripherals_path, 'launch/depth_camera.launch.py')
        )
    )
    lidar_launch = IncludeLaunchDescription(
        PythonLaunchDescriptionSource(
            os.path.join(peripherals_path, 'launch/lidar.launch.py')
        )
    )

    # 2.3 启动 Web 相关服务(rosbridge 用于 Web 通信,web_video 用于视频流)
    rosbridge_launch = ExecuteProcess(
        cmd=['ros2', 'launch', 'rosbridge_server', 'rosbridge_websocket_launch.xml'],
        output='screen'  # 输出日志到终端
    )
    web_video_node = Node(
        package='web_video_server',
        executable='web_video_server',
        output='screen'
    )

    # 2.4 启动自检节点(来自 bringup 包)
    startup_check_node = Node(
        package='bringup',
        executable='startup_check',  # 对应 setup.py 中定义的命令
        output='screen'
    )

    # 3. 返回所有要启动的组件(顺序即启动顺序)
    return [
        startup_check_node,    # 1. 先自检
        controller_launch,     # 2. 启动控制器
        depth_camera_launch,   # 3. 启动相机
        lidar_launch,          # 4. 启动雷达
        rosbridge_launch,      # 5. 启动 Web 服务
        web_video_node         # 6. 启动视频流服务
    ]

def generate_launch_description():
    """ROS2 launch 标准入口:返回 LaunchDescription 对象"""
    return LaunchDescription([
        OpaqueFunction(function=launch_setup)  # 包装 launch_setup,支持 Python 逻辑
    ])

if __name__ == '__main__':
    # 直接运行该文件时启动(非必需,通常用 ros2 launch 命令)
    ld = generate_launch_description()
    ls = LaunchService()
    ls.include_launch_description(ld)
    ls.run()
2.3.2 关键功能拆解
  1. 环境变量判断(compiled :解决「开发时用源码路径,安装后用系统路径」的问题,通过 os.environ.get('need_compile') 读取环境变量,启动前可通过 export need_compile=True 切换模式。

  2. IncludeLaunchDescription :用于「嵌套启动其他包的 launch 文件」,避免在一个文件中写所有启动逻辑,模块化更强(如单独启动相机的 depth_camera.launch.py)。

  3. Node vs ExecuteProcess

    • Node:启动 ROS2 节点(需指定 packageexecutable);
    • ExecuteProcess:执行系统命令(如 ros2 launch rosbridge_server ...)。

2.4 系统自检节点:startup_check.py

自检节点是启动前的「硬件检查员」,负责检查设备(麦克风、网卡)是否正常,并用 ROS2 消息控制蜂鸣器 / OLED 显示信息。

2.4.1 核心功能与代码
python 复制代码
#!/usr/bin/env python3
import os
import time
import rclpy
import psutil
import threading
from ros_robot_controller_msgs.msg import BuzzerState, OLEDState  # 自定义消息

def check_mic():
    """检查麦克风设备是否存在,存在则启动语音识别"""
    # 查看 /dev 目录下是否有 ring_mic 设备
    mic_exists = os.popen('ls /dev/ | grep ring_mic').read() == 'ring_mic\n'
    if mic_exists:
        os.system("ros2 launch xf_mic_asr_offline startup_test.launch.py")

def get_wifi_info():
    """获取 Wi-Fi SSID(从设备序列号生成)和 IP 地址"""
    # 1. 从设备树读取序列号(树莓派/嵌入式设备常用)
    with open("/proc/device-tree/serial-number", 'r') as f:
        serial = f.readlines()[0][-10:-1]  # 截取后10位序列号
    ssid = f'HW-{serial[:8]}'  # 生成 SSID(如 HW-12345678)

    # 2. 获取 wlan0 的 IP 地址
    ip = '0.0.0.0'
    for iface, addrs in psutil.net_if_addrs().items():
        if 'wlan0' in iface:
            for addr in addrs:
                if addr.family == 2:  # 2 代表 IPv4 地址
                    ip = addr.address
                    break
    return ssid, ip

def main():
    # 1. 启动麦克风检查线程(不阻塞主逻辑)
    threading.Thread(target=check_mic, daemon=False).start()

    # 2. 初始化 ROS2 节点
    rclpy.init()
    node = rclpy.create_node('startup_check')

    # 3. 创建发布器:控制蜂鸣器和 OLED
    buzzer_pub = node.create_publisher(BuzzerState, '/ros_robot_controller/set_buzzer', 10)
    oled_pub = node.create_publisher(OLEDState, '/ros_robot_controller/set_oled', 10)

    # 4. 等待 5 秒(确保其他节点启动完成)
    time.sleep(5)

    # 5. 控制蜂鸣器响一声(频率1900Hz,响0.2秒)
    buzzer_msg = BuzzerState()
    buzzer_msg.freq = 1900
    buzzer_msg.on_time = 0.2
    buzzer_msg.off_time = 0.01
    buzzer_msg.repeat = 1
    buzzer_pub.publish(buzzer_msg)

    # 6. 在 OLED 显示 Wi-Fi 信息(第一行 SSID,第二行 IP)
    ssid, ip = get_wifi_info()
    oled_msg1 = OLEDState()
    oled_msg1.index = 1  # 第一行
    oled_msg1.text = f'SSID: {ssid}'
    oled_pub.publish(oled_msg1)

    time.sleep(0.2)  # 短暂延时避免消息拥堵
    oled_msg2 = OLEDState()
    oled_msg2.index = 2  # 第二行
    oled_msg2.text = f'IP: {ip}'
    oled_pub.publish(oled_msg2)

    # 7. 保持节点运行
    rclpy.spin(node)
    rclpy.shutdown()

if __name__ == '__main__':
    main()

三、实际应用流程(学习 / 复习重点)

3.1 编译 bringup 包

  1. 进入 ROS2 工作空间(如 ros2_ws):

    复制代码
    cd ~/ros2_ws
  2. 编译(仅编译 bringup 包,速度更快):

    复制代码
    colcon build --packages-select bringup
  3. 加载环境变量(每次新终端都需执行):

    复制代码
    source install/setup.bash

3.2 启动 bringup 系统

  1. (可选)设置环境变量(切换编译 / 源码模式):

    复制代码
    export need_compile=False  # 源码模式(开发用)
    # export need_compile=True  # 编译安装模式(部署用)
  2. 启动总 launch 文件:

    复制代码
    ros2 launch bringup bringup_launch.py

3.3 验证启动结果

  1. 查看所有启动的节点:

    复制代码
    ros2 node list

    应包含 startup_checkcontroller_nodedepth_camera_node 等。

  2. 查看话题是否正常发布:

    复制代码
    ros2 topic list

    应包含 /ros_robot_controller/set_buzzer(蜂鸣器控制)、/camera/image_raw(相机图像)等。

  3. 检查机器人模型(若有):启动 RViz 并加载模型:

    复制代码
    rviz2
    • 添加 RobotModel 插件;
    • 设置 Fixed Framebase_link
    • 若能看到模型,说明启动成功。

四、常见问题排查(复习 / 应用必备)

4.1 launch 文件启动失败

  • 问题 1:找不到某个 launch 文件 排查:检查 setup.pydata_files 是否包含 launch 目录,确保 launch 文件已安装到 install/bringup/share/bringup/launch

  • 问题 2:环境变量 need_compile 未设置 解决:启动前执行 export need_compile=False(源码模式)或 True(编译模式)。

4.2 节点无法运行(ros2 run bringup startup_check 报错)

  • 问题 1:startup_check 命令不存在 排查:检查 setup.pyentry_points 是否正确定义 startup_check = bringup.startup_check:main,重新编译后重试。

  • 问题 2:缺少自定义消息包 报错:from ros_robot_controller_msgs.msg import ... ImportError解决:在 package.xml 中添加依赖 <depend>ros_robot_controller_msgs</depend>,重新编译。

五、总结:bringup 包学习要点

  1. 核心定位:一键启动全系统,模块化管理子模块(控制器、传感器等)。
  2. 文件分工
    • package.xml:管依赖和身份;
    • setup.py:管安装和可执行命令;
    • *.launch.py:管启动逻辑;
    • startup_check.py:管硬件自检。
  3. 应用关键 :编译后必须 source 环境变量,启动时注意环境变量 need_compile 的模式切换。
  4. 排查思路 :先看节点是否启动(ros2 node list),再看话题是否正常(ros2 topic list),最后定位具体模块问题。
相关推荐
半夏知半秋3 小时前
skynet.newservice接口分析
笔记·后端·学习·安全架构
我的xiaodoujiao3 小时前
从 0 到 1 搭建 Python 语言 Web UI自动化测试学习系列 15--二次开发--封装公共方法 3
python·学习·测试工具
立志成为大牛的小牛3 小时前
数据结构——十四、构造二叉树(王道408)
数据结构·笔记·学习·程序人生·考研
RanceGru3 小时前
LLM学习笔记5——本地部署ComfyUI和Wan2.1-T2V-1.3B文生视频模型
笔记·学习·stable diffusion·transformer
molong9314 小时前
Activity/Service/Broadcast/ContentProvider 生命周期交互
android·学习·交互
楼田莉子4 小时前
python学习:爬虫+项目测试
后端·爬虫·python·学习
嘉年华-cocos4 小时前
高中3500个单词, 纯粹数据版, 助力背诵, 按相似度+从短到长 排序
学习·英语·背单词·音标·记单词
东风西巷4 小时前
draw.io(免费流程图制作工具) 中文绿色版
学习·电脑·流程图·软件需求·draw.io
siliconstorm.ai5 小时前
阿里下场造“机器人”:从通义千问到具身智能,中国AI正走向“实体化”阶段
人工智能·自然语言处理·chatgpt·机器人·云计算