> ## 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.

# Develop a pipeline application

This information demonstrates how to develop a pipeline application using the APIs provided by the QIR SDK. You can learn the basic workflow and write an example pipeline application by following the steps.

<Info>
  **Information**

  **Why do you need to use the QIR SDK to develop a pipeline application?**

  The normal development process requires extensive camera development experience and solid hardware knowledge to write camera-related code and handle image format conversion, while using the QIR SDK saves time and efforts.

  The following is a comparison between the normal development flow (without QIR SDK) and that uses the QIR SDK.

  * Using QIR SDK: Write a launch file to combine the existing nodes into a pipeline.
  * Without QIR SDK: Write camera-related code or handle image format conversion by yourselves.
</Info>

The following example demonstrates how to use the QIR SDK to develop a pipeline application.

## Pipeline application workflow

To write a pipeline application, you need to follow these workflow:

1. Define the objective of your application.
2. Check available APIs provided by the QIR SDK.
3. Decide the pipeline.

### Define the objective

Develop a sample that captures images from the device's built-in camera and publishes them in RGB888 format to a ROS 2 topic.

### Check the APIs

1. Check the APIs the QIR SDK offers, see [QIR SDK API list](./explore-the-available-apis). A first check finds that [qrb\_ros\_camera](./run-a-zero-copy-camera-with-qrb-ros-camera) is able to capture data from the device's built-in camera and publish it to the `/cam0_stream1` topic. However, this topic uses NV12 encoding format by default, while the application requires the RGB888 format.
2. Analyze other APIs that may be useful. As [qrb\_ros\_camera](./run-a-zero-copy-camera-with-qrb-ros-camera) doesn't output the image in the format as needed, a further check finds the following matching ROS node: [qrb\_ros\_colorspace\_convert](./convert-between-nv-12-and-rgb-888-with-qrb-ros-colorspace-convert) is able to convert image encoding from NV12 to RGB888.
3. Locate the key points for the implementation. `qrb_ros_camera` publishes to the `/cam0_stream1` topic, but the input for `qrb_ros_colorspace_convert` is `/image_raw`. Therefore, the example application needs to handle this conflict with additional processing.

### Decide the pipeline

The following information analyzes the pipeline application requirements in detail as an example solution.

**Pipeline flow**

This example uses the following pipeline flow based on the analysis:

1. Capture images from [qrb\_ros\_camera](./run-a-zero-copy-camera-with-qrb-ros-camera) and publish them to the `/cam0_stream1` topic.
2. Convert the images from NV12 to RGB888 format using [qrb\_ros\_colorspace\_convert](./convert-between-nv-12-and-rgb-888-with-qrb-ros-colorspace-convert) and publish the RGB888 format images to the `/image` topic.

## Example implementation

The following steps guide you through using the QIR SDK to write the pipeline based on the example solution and run it on the device, using the Qualcomm Dragonwing™ IQ-9075 Evaluation Kit (see [Get started with QIR SDK](./get-started-with-qir-sdk)) as an example.

### Set up the cross-compile environment

On the host computer, go to the artifacts directory and decompress the QIR SDK package:

<Note>
  **Note**

  The QIR SDK supports developing a pipeline application on these development kits for Qualcomm Linux:

  | **Development kit**               | **Machine name**      |
  | :-------------------------------- | :-------------------- |
  | 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
```

You have initialized the QIR SDK environment.

### Import the sample project

This example uses the template project provided in the QIR SDK package to develop the pipeline. This template helps you quickly create a Python-based ROS 2 package.

<Note>
  **Note**

  Alternatively, you can create a package on your IQ-9075 device with `ros2 pkg create --build-type ament_python --license Apache-2.0 <pkg-name> --dependencies [deps]`, and then push it to the host with the QIR SDK installed in the environment.
</Note>

To use the template project in the QIR SDK, go to the following directory:

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

### Customize the launch file

**Write the launch file**

1. Create the launch file that implements the following steps. This example uses two nodes `qrb_ros_camera` and `qrb_ros_colorspace_convert`. It remaps the `/cam0_stream1` topic of `qrb_ros_camera` to the `/image_raw` topic, and then uses `qrb_ros_colorspace_convert` to publish the RGB888 format images to the `/image` topic.

   <Note>
     **Note**

     For information about how to write a launch file, see [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. Update the `setup.py` file.

   ```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>

### Build and deploy

You can now compile the package and deploy it to the device.

<Accordion title="Try me">
  <Steps>
    <Step title="Build the workspace">
      ```bash theme={null}
      cd <qir_sdk_path>/qirp-samples/ai_vision/sample_template
      colcon build
      ```
    </Step>

    <Step title="Package and push the application to the device">
      ```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="Install the application on the device">
      ```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="Run the application">
      ```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="Verify the output">
      ```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>

Now you can launch the new pipeline using the `ros2 launch` command and see the camera data being converted to RGB888 format and published to the `/image` topic.

## Next steps

To build more advanced applications:

* Use additional QIR APIs such as sensor data streams, diagnostics, or control interfaces.
* Combine multiple modules to create hybrid applications (for example, autonomous navigation + health monitoring).
* Learn how to use the QIR SDK to develop AI pipeline applications. See [QIR SDK sample applications](./qir-sdk-sample-applications).
* Check [QIR SDK API list](./explore-the-available-apis) for a full list of available APIs.
