> ## Documentation Index
> Fetch the complete documentation index at: https://dragonwingdocs.qualcomm.com/llms.txt
> Use this file to discover all available pages before exploring further.

# 开发流水线应用

以下信息演示了如何使用 QIR SDK 提供的 API 开发流水线应用。你可以通过遵循这些步骤了解基本工作流并编写一个示例流水线应用。

<Info>
  **信息**

  **为什么需要使用 QIR SDK 来开发流水线应用？**

  常规开发流程需要丰富的摄像头开发经验和扎实的硬件知识来编写摄像头相关代码并处理图像格式转换，而使用 QIR SDK 可节省时间和精力。

  以下是常规开发流程（不使用 QIR SDK）与使用 QIR SDK 的对比。

  * 使用 QIR SDK：编写一个 launch 文件将现有节点组合成流水线。
  * 不使用 QIR SDK：自己编写摄像头相关代码或处理图像格式转换。
</Info>

以下示例演示了如何使用 QIR SDK 开发流水线应用。

## 流水线应用工作流

要编写流水线应用，你需要遵循以下工作流：

1. 定义应用的目标。
2. 检查 QIR SDK 提供的可用 API。
3. 决定流水线。

### 定义目标

开发一个从设备内置摄像头捕获图像并以 RGB888 格式发布到 ROS 2 topic 的示例。

### 检查 API

1. 检查 QIR SDK 提供的 API，参见 [QIR SDK API 列表](./explore-the-available-apis)。首先发现 [qrb\_ros\_camera](./run-a-zero-copy-camera-with-qrb-ros-camera) 能够从设备内置摄像头捕获数据并将其发布到 `/cam0_stream1` topic。但是，此 topic 默认使用 NV12 编码格式，而应用需要 RGB888 格式。
2. 分析其他可能有用的 API。由于 [qrb\_ros\_camera](./run-a-zero-copy-camera-with-qrb-ros-camera) 不输出所需格式的图像，进一步检查发现以下匹配的 ROS 节点：[qrb\_ros\_colorspace\_convert](./convert-between-nv-12-and-rgb-888-with-qrb-ros-colorspace-convert) 能够将图像编码从 NV12 转换为 RGB888。
3. 找出实现的关键点。`qrb_ros_camera` 发布到 `/cam0_stream1` topic，但 `qrb_ros_colorspace_convert` 的输入是 `/image_raw`。因此，示例应用需要通过额外处理来处理这个冲突。

### 决定流水线

以下信息详细分析了流水线应用需求作为示例解决方案。

**流水线流程**

根据分析，此示例使用以下流水线流程：

1. 从 [qrb\_ros\_camera](./run-a-zero-copy-camera-with-qrb-ros-camera) 捕获图像并将其发布到 `/cam0_stream1` topic。
2. 使用 [qrb\_ros\_colorspace\_convert](./convert-between-nv-12-and-rgb-888-with-qrb-ros-colorspace-convert) 将图像从 NV12 转换为 RGB888 格式，并将 RGB888 格式的图像发布到 `/image` topic。

## 示例实现

以下步骤引导你使用 QIR SDK 基于示例解决方案编写流水线并在设备上运行，以 Qualcomm Dragonwing™ IQ-9075 Evaluation Kit 为例（参见 [QIR SDK 快速入门](./get-started-with-qir-sdk)）。

### 设置交叉编译环境

在主机计算机上，进入制品目录并解压 QIR SDK 包：

<Note>
  **注意**

  QIR SDK 支持在这些 Qualcomm Linux 开发套件上开发流水线应用：

  | **开发套件**                          | **Machine 名称**        |
  | :-------------------------------- | :-------------------- |
  | Dragonwing IQ-9075 Evaluation Kit | `qcs9075-iq-9075-evk` |
</Note>

```bash theme={null}
cd <decompressed_workspace>/target/qcs9075-iq-9075-evk/qirpsdk_artifacts/qcs9075-iq-9075-evk
tar -zxvf qirp-sdk_<qirp_version>.tar.gz
cd qirp-sdk
source setup.sh
```

你已初始化 QIR SDK 环境。

### 导入示例项目

此示例使用 QIR SDK 包中提供的模板项目来开发流水线。此模板帮助你快速创建基于 Python 的 ROS 2 包。

<Note>
  **注意**

  或者，你可以在 IQ-9075 设备上使用 `ros2 pkg create --build-type ament_python --license Apache-2.0 <pkg-name> --dependencies [deps]` 创建一个包，然后将其推送到已在环境中安装 QIR SDK 的主机。
</Note>

要使用 QIR SDK 中的模板项目，请进入以下目录：

```bash theme={null}
cd <qir_sdk_path>/qirp-samples/ai_vision/sample_template
```

### 自定义 launch 文件

**编写 launch 文件**

1. 创建实现以下步骤的 launch 文件。此示例使用两个节点 `qrb_ros_camera` 和 `qrb_ros_colorspace_convert`。它将 `qrb_ros_camera` 的 `/cam0_stream1` topic 重映射到 `/image_raw` topic，然后使用 `qrb_ros_colorspace_convert` 将 RGB888 格式的图像发布到 `/image` topic。

   <Note>
     **注意**

     有关如何编写 launch 文件的信息，请参见 [ROS 2 documentation — Creating a launch file](https://docs.ros.org/en/jazzy/Tutorials/Intermediate/Launch/Creating-Launch-Files.html)。
   </Note>

   ```python theme={null}
   mkdir launch
   vim launch/rgb8_image_publisher_launch.py
   # Copy the following code to the file
   ```

   <Accordion title="rgb8_image_publisher_launch.py">
     ```python theme={null}
     # Copyright (c) 2025 Qualcomm Innovation Center, Inc. All rights reserved.
     # SPDX-License-Identifier: BSD-3-Clause-Clear
     import os
     import launch
     from launch import LaunchDescription
     from launch.actions import DeclareLaunchArgument
     from launch.substitutions import LaunchConfiguration
     from ament_index_python.packages import get_package_share_directory
     from launch_ros.actions import ComposableNodeContainer
     from launch_ros.descriptions import ComposableNode
     def generate_launch_description():
         # Get the path to the camera info configuration file
         camera_info_config_file_path = os.path.join(
             get_package_share_directory('qrb_ros_camera'),
             'config', 'camera_info_imx577.yaml'
         )
         # Define the composable node container to hold the camera node
         camera_container = ComposableNodeContainer(
             name="container",
             namespace='',
             package="rclcpp_components",
             executable="component_container",
             output='screen',
             composable_node_descriptions=[
                 ComposableNode(
                     package='qrb_ros_camera',
                     plugin='qrb_ros::camera::CameraNode',
                     name='camera_node',
                     parameters=[{
                         'camera_id': 0,
                         'stream_size': 1,
                         'stream_name': ["stream1"],
                         'stream1':{
                             'height':480,
                             'width':640,
                             'fps':30,
                         },
                         'camera_info_path': camera_info_config_file_path,
                     }],
                     remappings=[
                         ('/cam0_stream1', '/image_raw'),
                         ('/cam0_stream1_camera_info', '/qrb_ros_camera_info')
                     ],
                 )
             ]
         )
         # Declare launch arguments for conversion type and latency FPS test.
         # These arguments can be set when launching the file to customize behavior.
         # Default conversion type is set to 'nv12_to_rgb8' and latency FPS test
         # is enabled by default.
         conversion_type_arg = DeclareLaunchArgument(
             'conversion_type',
             default_value='nv12_to_rgb8',
             description='The type of conversion'
         )
         latency_fps_test_arg = DeclareLaunchArgument(
             'latency_fps_test',
             default_value='true',
             description='Enable or disable latency FPS test'
         )
         # Define the composable node container for the colorspace conversion node
         colorspace_convert_container = ComposableNodeContainer(
             name='component_colorconvert_container',
             namespace='',
             package='rclcpp_components',
             executable='component_container',
             composable_node_descriptions=[
                 ComposableNode(
                     package='qrb_ros_colorspace_convert',
                     plugin='qrb_ros::colorspace_convert::ColorspaceConvertNode',
                     parameters=[{
                         'conversion_type': LaunchConfiguration('conversion_type'),
                         'latency_fps_test': LaunchConfiguration('latency_fps_test'),
                     }],
                     extra_arguments=[{'use_intra_process_comms': True, 'log_level': 'INFO'}],
                 ),
             ],
             output='screen',
         )
         return LaunchDescription([
             camera_container,
             conversion_type_arg,
             latency_fps_test_arg,
             colorspace_convert_container,
         ])
     ```
   </Accordion>

2. 更新 `setup.py` 文件。

   ```python theme={null}
   vim setup.py
   # Copy the following code to the file
   ```

   <Accordion title="setup.py">
     ```python theme={null}
         from setuptools import find_packages, setup
         package_name = 'sample_template'
         setup(
             name=package_name,
             version='0.0.0',
             packages=find_packages(exclude=['test']),
             data_files=[
                 ('share/ament_index/resource_index/packages',
                     ['resource/' + package_name]),
                 ('share/' + package_name, ['package.xml']),
                 ('share/' + package_name, ['launch/' + "rgb8_image_publisher_launch.py"])
             ],
             install_requires=['setuptools'],
             zip_safe=True,
             maintainer='your_name',
             maintainer_email='your.email@example.com',
             description='Package description',
             license='License declaration',
             tests_require=['pytest'],
             entry_points={
                 'console_scripts': [
                 ],
             },
         )
     ```
   </Accordion>

### 构建和部署

现在你可以编译该包并将其部署到设备。

<Accordion title="试一试">
  <Steps>
    <Step title="构建工作区">
      ```bash theme={null}
      cd <qir_sdk_path>/qirp-samples/ai_vision/sample_template
      colcon build
      ```
    </Step>

    <Step title="打包并将应用推送到设备">
      ```bash theme={null}
      cd <qir_sdk_path>/qirp-samples/ai_vision/sample_template/install/sample_template
      tar -czvf sample_template.tar.gz lib share
      scp sample_template.tar.gz root@[ip-addr]:/opt/
      ```
    </Step>

    <Step title="在设备上安装应用">
      ```bash title="SSH Session" theme={null}
      # Install sample package
      tar --no-overwrite-dir --no-same-owner -zxf /opt/sample_template.tar.gz -C /opt/
      ```
    </Step>

    <Step title="运行应用">
      ```bash title="SSH Session" theme={null}
      # Configure according to qrb_ros_colorspace_convert
      # Set up Weston environment
      export XDG_RUNTIME_DIR=/dev/socket/weston/
      mkdir -p $XDG_RUNTIME_DIR
      export WAYLAND_DISPLAY=wayland-1
      # Set up shared memory
      export FASTRTPS_DEFAULT_PROFILES_FILE=/usr/ros/jazzy/share/qrb_ros_colorspace_convert/config/large_message_profile.xml
      # Launch the application
      export ROS_DOMAIN_ID=123
      source /usr/share/qirp-setup.sh
      ros2 launch sample_template rgb8_image_publisher_launch.py
      ```
    </Step>

    <Step title="验证输出">
      ```bash title="SSH Session" theme={null}
      export ROS_DOMAIN_ID=123
      source /usr/share/qirp-setup.sh
      ros2 topic echo /image_raw | grep "encoding"
      ros2 topic echo /image | grep "encoding"
      ```
    </Step>
  </Steps>
</Accordion>

现在你可以使用 `ros2 launch` 命令启动新的流水线，并看到摄像头数据被转换为 RGB888 格式并发布到 `/image` topic。

## 后续步骤

要构建更高级的应用：

* 使用其他 QIR API，例如传感器数据流、诊断或控制接口。
* 组合多个模块以创建混合应用（例如，自主导航 + 健康监控）。
* 学习如何使用 QIR SDK 开发 AI 流水线应用。参见 [QIR SDK 示例应用](./qir-sdk-sample-applications)。
* 查看 [QIR SDK API 列表](./explore-the-available-apis) 以获取可用 API 的完整列表。
