Repository: tier4/aip_launcher
Pull request #348: feat: acceleration and transport layer
Author: login: knzo25
Labels: name: tag:run-build-and-test-differential
name: backport beta/v0.44
Created: 2024-11-25T08:21:58Z | Merged: 2025-04-28T00:45:49Z

## Description
This is part of the series of PR related to https://github.com/autowarefoundation/autoware.universe/issues/9722

List of PRs:
 - https://github.com/autowarefoundation/autoware.universe/pull/9454 (pointcloud preprocessing)
 - https://github.com/autowarefoundation/autoware.universe/pull/10298 (concatenation - template refactor)
 - https://github.com/autowarefoundation/autoware.universe/pull/10300 (concatenation - cuda version)
 - https://github.com/autowarefoundation/autoware.universe/pull/9453 (centerpoint)
 - transfusion (TODO)
 - https://github.com/autowarefoundation/autoware.universe/pull/9542 (OGM - the first implementation will be independent of the blackboard to ease the transition)
 - https://github.com/tier4/aip_launcher/pull/348 (aip_launcher)
 - https://github.com/autowarefoundation/sample_sensor_kit_launch/pull/111 (sample_sensor_kit_launch)

Depending on your machine and how many nodes are in a container, the following branch may also be required:
https://github.com/knzo25/launch_ros/tree/fix/load_composable_node
There seems to be a but in ROS where if you send too many services at once some will be lost and `ros_launch` can not handle that.

## Related links

**Parent Issue:**

- Link

<!-- ⬇️🟢
**Private Links:**

- [CompanyName internal link]()
⬆️🟢 -->

## How was this PR tested?
The sensing/perception pipeline was tested until centerpoint for TIER IV's taxi using the logging simulator.

## Notes for reviewers
The main branch that I used for development is [`feat/cuda_acceleration_and_transport_layer2`](https://github.com/knzo25/autoware.universe/tree/feat/cuda_acceleration_and_transport_layer2).
However, the changes were too big so I split the PRs. That being said, development, if any will still be on that branch (and then cherrypicked to the respective PRs), and the review changes will be cherrypicked into the development branch.

## Interface changes

An additional topic is added to perform type negotiation:
Example: `input/pointcloud` -> `input/pointcloud` and `input/pointcloud/cuda`

<!-- ⬇️🔴

### Topic changes

#### Additions and removals

| Change type   | Topic Type      | Topic Name    | Message Type        | Description       |
|:--------------|:----------------|:--------------|:--------------------|:------------------|
| Added/Removed | Pub/Sub/Srv/Cli | `/topic_name` | `std_msgs/String`   | Topic description |

#### Modifications

| Version | Topic Type      | Topic Name        | Message Type        | Description       |
|:--------|:----------------|:------------------|:--------------------|:------------------|
| Old     | Pub/Sub/Srv/Cli | `/old_topic_name` | `sensor_msgs/Image` | Topic description |
| New     | Pub/Sub/Srv/Cli | `/new_topic_name` | `sensor_msgs/Image` | Topic description |

### ROS Parameter Changes

#### Additions and removals

| Change type   | Parameter Name | Type     | Default Value | Description       |
|:--------------|:---------------|:---------|:--------------|:------------------|
| Added/Removed | `param_name`   | `double` | `1.0`         | Param description |

#### Modifications

| Version | Parameter Name   | Type     | Default Value | Description       |
|:--------|:-----------------|:---------|:--------------|:------------------|
| Old     | `old_param_name` | `double` | `1.0`         | Param description |
| New     | `new_param_name` | `double` | `1.0`         | Param description |

🔴⬆️ -->

## Effects on system behavior

Enabling this preprocessing in the launchers should provide a much reduced latency and cpu usage (at the cost of a higher GPU usage)



Diff:
diff --git a/aip_common_sensor_launch/launch/hesai_OT128.launch.xml b/aip_common_sensor_launch/launch/hesai_OT128.launch.xml
index fe5d8bac..493afa95 100644
--- a/aip_common_sensor_launch/launch/hesai_OT128.launch.xml
+++ b/aip_common_sensor_launch/launch/hesai_OT128.launch.xml
@@ -14,7 +14,9 @@
   <arg name="cloud_max_angle" default="360"/>
   <arg name="dual_return_distance_threshold" default="0.1"/>
   <arg name="vehicle_mirror_param_file"/>
-  <arg name="container_name" default="hesai_node_container"/>
+  <arg name="container_name" default="pointcloud_container"/>
+  <arg name="use_shared_container" default="false"/>
+  <arg name="use_cuda_preprocessor" default="false"/>
   <arg name="udp_only" default="false"/>
 
   <include file="$(find-pkg-share aip_common_sensor_launch)/launch/nebula_node_container.launch.py">
@@ -31,6 +33,8 @@
     <arg name="dual_return_distance_threshold" value="$(var dual_return_distance_threshold)"/>
     <arg name="vehicle_mirror_param_file" value="$(var vehicle_mirror_param_file)"/>
     <arg name="container_name" value="$(var container_name)"/>
+    <arg name="use_shared_container" value="$(var use_shared_container)"/>
+    <arg name="use_cuda_preprocessor" value="$(var use_cuda_preprocessor)"/>
     <arg name="use_intra_process" value="true"/>
     <arg name="use_multithread" value="true"/>
     <arg name="ptp_profile" value="automotive"/>
diff --git a/aip_common_sensor_launch/launch/hesai_XT32.launch.xml b/aip_common_sensor_launch/launch/hesai_XT32.launch.xml
index f8705a48..f1b26701 100644
--- a/aip_common_sensor_launch/launch/hesai_XT32.launch.xml
+++ b/aip_common_sensor_launch/launch/hesai_XT32.launch.xml
@@ -15,7 +15,9 @@
   <arg name="max_range" default="120.0"/>
   <arg name="dual_return_distance_threshold" default="0.1"/>
   <arg name="vehicle_mirror_param_file"/>
-  <arg name="container_name" default="hesai_node_container"/>
+  <arg name="container_name" default="pointcloud_container"/>
+  <arg name="use_shared_container" default="false"/>
+  <arg name="use_cuda_preprocessor" default="false"/>
   <arg name="udp_only" default="false"/>
 
   <include file="$(find-pkg-share aip_common_sensor_launch)/launch/nebula_node_container.launch.py">
@@ -33,6 +35,8 @@
     <arg name="dual_return_distance_threshold" value="$(var dual_return_distance_threshold)"/>
     <arg name="vehicle_mirror_param_file" value="$(var vehicle_mirror_param_file)"/>
     <arg name="container_name" value="$(var container_name)"/>
+    <arg name="use_shared_container" value="$(var use_shared_container)"/>
+    <arg name="use_cuda_preprocessor" value="$(var use_cuda_preprocessor)"/>
     <arg name="use_intra_process" value="true"/>
     <arg name="use_multithread" value="true"/>
     <arg name="ptp_profile" value="automotive"/>
diff --git a/aip_common_sensor_launch/launch/nebula_node_container.launch.py b/aip_common_sensor_launch/launch/nebula_node_container.launch.py
index 53fec88d..4c374b26 100644
--- a/aip_common_sensor_launch/launch/nebula_node_container.launch.py
+++ b/aip_common_sensor_launch/launch/nebula_node_container.launch.py
@@ -1,4 +1,4 @@
-# Copyright 2023 Tier IV, Inc. All rights reserved.
+# Copyright 2025 Tier IV, Inc. All rights reserved.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -57,24 +57,32 @@ def get_vehicle_info(context):
     return p
 
 
-def get_vehicle_mirror_info(context):
-    path = LaunchConfiguration("vehicle_mirror_param_file").perform(context)
-    with open(path, "r") as f:
-        p = yaml.safe_load(f)["/**"]["ros__parameters"]
-    return p
+def load_composable_node_param(context, param_path):
+    with open(LaunchConfiguration(param_path).perform(context), "r") as f:
+        return yaml.safe_load(f)["/**"]["ros__parameters"]
 
 
-def launch_setup(context, *args, **kwargs):
-    def load_composable_node_param(param_path):
-        with open(LaunchConfiguration(param_path).perform(context), "r") as f:
-            return yaml.safe_load(f)["/**"]["ros__parameters"]
+def create_parameter_dict(*args):
+    result = {}
+    for x in args:
+        result[x] = LaunchConfiguration(x)
+    return result
+
+
+def make_common_nodes(context):
+    if UnlessCondition(LaunchConfiguration("use_shared_container")).evaluate(context):
+        return [
+            ComposableNode(
+                package="autoware_glog_component",
+                plugin="autoware::glog_component::GlogComponent",
+                name="glog_component",
+            )
+        ]
+
+    return []
 
-    def create_parameter_dict(*args):
-        result = {}
-        for x in args:
-            result[x] = LaunchConfiguration(x)
-        return result
 
+def make_nebula_nodes(context):
     # Model and make
     sensor_model = LaunchConfiguration("sensor_model").perform(context)
     sensor_make, sensor_extension = get_lidar_make(sensor_model)
@@ -94,27 +102,7 @@ def create_parameter_dict(*args):
     else:  # Robosense
         sensor_calib_fp = ""
 
-    # Pointcloud preprocessor parameters
-    distortion_corrector_node_param = ParameterFile(
-        param_file=LaunchConfiguration("distortion_correction_node_param_path").perform(context),
-        allow_substs=True,
-    )
-    ring_outlier_filter_node_param = ParameterFile(
-        param_file=LaunchConfiguration("ring_outlier_filter_node_param_path").perform(context),
-        allow_substs=True,
-    )
-
-    nodes = []
-
-    nodes.append(
-        ComposableNode(
-            package="autoware_glog_component",
-            plugin="autoware::glog_component::GlogComponent",
-            name="glog_component",
-        )
-    )
-
-    nodes.append(
+    return [
         ComposableNode(
             package="nebula_ros",
             plugin=sensor_make + "RosWrapper",
@@ -163,6 +151,86 @@ def create_parameter_dict(*args):
             ],
             extra_arguments=[{"use_intra_process_comms": LaunchConfiguration("use_intra_process")}],
         )
+    ]
+
+
+def make_cuda_preprocessor_nodes(context):
+    # Vehicle parameters
+    vehicle_info = get_vehicle_info(context)
+    mirror_info = load_composable_node_param(context, "vehicle_mirror_param_file")
+
+    # Pointcloud preprocessor parameters
+    distortion_corrector_node_param = ParameterFile(
+        param_file=LaunchConfiguration("distortion_correction_node_param_path").perform(context),
+        allow_substs=True,
+    )
+    ring_outlier_filter_node_param = ParameterFile(
+        param_file=LaunchConfiguration("ring_outlier_filter_node_param_path").perform(context),
+        allow_substs=True,
+    )
+
+    preprocessor_parameters = {}
+    preprocessor_parameters["crop_box.min_x"] = [
+        vehicle_info["min_longitudinal_offset"],
+        mirror_info["min_longitudinal_offset"],
+    ]
+    preprocessor_parameters["crop_box.max_x"] = [
+        vehicle_info["max_longitudinal_offset"],
+        mirror_info["max_longitudinal_offset"],
+    ]
+    preprocessor_parameters["crop_box.min_y"] = [
+        vehicle_info["min_lateral_offset"],
+        mirror_info["min_lateral_offset"],
+    ]
+    preprocessor_parameters["crop_box.max_y"] = [
+        vehicle_info["max_lateral_offset"],
+        mirror_info["max_lateral_offset"],
+    ]
+    preprocessor_parameters["crop_box.min_z"] = [
+        vehicle_info["min_height_offset"],
+        mirror_info["min_height_offset"],
+    ]
+    preprocessor_parameters["crop_box.max_z"] = [
+        vehicle_info["max_height_offset"],
+        mirror_info["max_height_offset"],
+    ]
+
+    return [
+        ComposableNode(
+            package="autoware_cuda_pointcloud_preprocessor",
+            plugin="autoware::cuda_pointcloud_preprocessor::CudaPointcloudPreprocessorNode",
+            name="cuda_pointcloud_preprocessor_node",
+            parameters=[
+                preprocessor_parameters,
+                distortion_corrector_node_param,
+                ring_outlier_filter_node_param,
+            ],
+            remappings=[
+                ("~/input/pointcloud", "pointcloud_raw_ex"),
+                ("~/input/twist", "/sensing/vehicle_velocity_converter/twist_with_covariance"),
+                ("~/input/imu", "/sensing/imu/imu_data"),
+                ("~/output/pointcloud", "pointcloud_before_sync"),
+                ("~/output/pointcloud/cuda", "pointcloud_before_sync/cuda"),
+            ],
+            # The whole node can not set use_intra_process due to type negotiation internal topics
+            # extra_arguments=[{"use_intra_process_comms": LaunchConfiguration("use_intra_process")}],
+        )
+    ]
+
+
+def make_preprocessor_nodes(context):
+    # Vehicle parameters
+    vehicle_info = get_vehicle_info(context)
+    mirror_info = load_composable_node_param(context, "vehicle_mirror_param_file")
+
+    # Pointcloud preprocessor parameters
+    distortion_corrector_node_param = ParameterFile(
+        param_file=LaunchConfiguration("distortion_correction_node_param_path").perform(context),
+        allow_substs=True,
+    )
+    ring_outlier_filter_node_param = ParameterFile(
+        param_file=LaunchConfiguration("ring_outlier_filter_node_param_path").perform(context),
+        allow_substs=True,
     )
 
     cropbox_parameters = create_parameter_dict("input_frame", "output_frame")
@@ -176,6 +244,8 @@ def create_parameter_dict(*args):
     cropbox_parameters["min_z"] = vehicle_info["min_height_offset"]
     cropbox_parameters["max_z"] = vehicle_info["max_height_offset"]
 
+    nodes = []
+
     nodes.append(
         ComposableNode(
             package="autoware_pointcloud_preprocessor",
@@ -190,7 +260,6 @@ def create_parameter_dict(*args):
         )
     )
 
-    mirror_info = get_vehicle_mirror_info(context)
     cropbox_parameters["min_x"] = mirror_info["min_longitudinal_offset"]
     cropbox_parameters["max_x"] = mirror_info["max_longitudinal_offset"]
     cropbox_parameters["min_y"] = mirror_info["min_lateral_offset"]
@@ -249,6 +318,58 @@ def create_parameter_dict(*args):
         )
     )
 
+    return nodes
+
+
+def make_blockage_diag_nodes(context):
+    return [
+        ComposableNode(
+            package="autoware_pointcloud_preprocessor",
+            plugin="autoware::pointcloud_preprocessor::BlockageDiagComponent",
+            name="blockage_diag",
+            remappings=[
+                ("input", "pointcloud_raw_ex"),
+                ("output", "blockage_diag/pointcloud"),
+            ],
+            parameters=[
+                {
+                    "angle_range": [
+                        float(context.perform_substitution(LaunchConfiguration("cloud_min_angle"))),
+                        float(context.perform_substitution(LaunchConfiguration("cloud_max_angle"))),
+                    ],
+                    "horizontal_ring_id": LaunchConfiguration("horizontal_ring_id"),
+                    "vertical_bins": LaunchConfiguration("vertical_bins"),
+                    "is_channel_order_top2down": LaunchConfiguration("is_channel_order_top2down"),
+                    "max_distance_range": LaunchConfiguration("max_range"),
+                    "horizontal_resolution": LaunchConfiguration("horizontal_resolution"),
+                }
+            ]
+            + [load_composable_node_param(context, "blockage_diagnostics_param_file")],
+            extra_arguments=[{"use_intra_process_comms": LaunchConfiguration("use_intra_process")}],
+        )
+    ]
+
+
+def launch_setup(context, *args, **kwargs):
+    # Check that the cuda preprocessor is only used with a shared container
+    if IfCondition(LaunchConfiguration("use_cuda_preprocessor")).evaluate(context):
+        assert IfCondition(LaunchConfiguration("use_shared_container")).evaluate(
+            context
+        ), "The cuda preprocessor should only be used with a shared container."
+
+    nodes = []
+
+    nodes.extend(make_common_nodes(context))
+    nodes.extend(make_nebula_nodes(context))
+
+    if IfCondition(LaunchConfiguration("use_cuda_preprocessor")).evaluate(context):
+        nodes.extend(make_cuda_preprocessor_nodes(context))
+    else:
+        nodes.extend(make_preprocessor_nodes(context))
+
+    if IfCondition(LaunchConfiguration("enable_blockage_diag")).evaluate(context):
+        nodes.extend(make_blockage_diag_nodes(context))
+
     # set container to run all required components in the same process
     container = ComposableNodeContainer(
         name=LaunchConfiguration("container_name"),
@@ -257,40 +378,16 @@ def create_parameter_dict(*args):
         executable=LaunchConfiguration("container_executable"),
         composable_node_descriptions=nodes,
         output="both",
+        condition=UnlessCondition(LaunchConfiguration("use_shared_container")),
     )
 
-    blockage_diag_component = ComposableNode(
-        package="autoware_pointcloud_preprocessor",
-        plugin="autoware::pointcloud_preprocessor::BlockageDiagComponent",
-        name="blockage_diag",
-        remappings=[
-            ("input", "pointcloud_raw_ex"),
-            ("output", "blockage_diag/pointcloud"),
-        ],
-        parameters=[
-            {
-                "angle_range": [
-                    float(context.perform_substitution(LaunchConfiguration("cloud_min_angle"))),
-                    float(context.perform_substitution(LaunchConfiguration("cloud_max_angle"))),
-                ],
-                "horizontal_ring_id": LaunchConfiguration("horizontal_ring_id"),
-                "vertical_bins": LaunchConfiguration("vertical_bins"),
-                "is_channel_order_top2down": LaunchConfiguration("is_channel_order_top2down"),
-                "max_distance_range": LaunchConfiguration("max_range"),
-                "horizontal_resolution": LaunchConfiguration("horizontal_resolution"),
-            }
-        ]
-        + [load_composable_node_param("blockage_diagnostics_param_file")],
-        extra_arguments=[{"use_intra_process_comms": LaunchConfiguration("use_intra_process")}],
-    )
-
-    blockage_diag_loader = LoadComposableNodes(
-        composable_node_descriptions=[blockage_diag_component],
-        target_container=container,
-        condition=IfCondition(LaunchConfiguration("enable_blockage_diag")),
+    load_composable_nodes = LoadComposableNodes(
+        composable_node_descriptions=nodes,
+        target_container=LaunchConfiguration("container_name"),
+        condition=IfCondition(LaunchConfiguration("use_shared_container")),
     )
 
-    return [container, blockage_diag_loader]
+    return [container, load_composable_nodes]
 
 
 def generate_launch_description():
@@ -336,7 +433,21 @@ def add_launch_arg(name: str, default_value=None, description=None):
     add_launch_arg("advanced_diagnostics", "false")
     add_launch_arg("use_multithread", "False", "use multithread")
     add_launch_arg("use_intra_process", "False", "use ROS 2 component container communication")
-    add_launch_arg("lidar_container_name", "nebula_node_container")
+    add_launch_arg(
+        "lidar_container_name",
+        "nebula_node_container",
+        "Name of the new container to be created when use_shared_container is false",
+    )
+    add_launch_arg(
+        "use_shared_container",
+        "False",
+        "Whether to use a new container for this lidar or use an existing one",
+    )
+    add_launch_arg(
+        "use_cuda_preprocessor",
+        "False",
+        "Use the cuda implementation of the pointcloud preprocessor. Requires use_shared_container to be enabled",
+    )
     add_launch_arg("ptp_profile", "1588v2")
     add_launch_arg("ptp_transport_type", "L2")
     add_launch_arg("ptp_switch_type", "TSN")
diff --git a/aip_common_sensor_launch/launch/velodyne_VLP16.launch.xml b/aip_common_sensor_launch/launch/velodyne_VLP16.launch.xml
index ef1fcacb..81991a6f 100644
--- a/aip_common_sensor_launch/launch/velodyne_VLP16.launch.xml
+++ b/aip_common_sensor_launch/launch/velodyne_VLP16.launch.xml
@@ -15,7 +15,9 @@
   <arg name="cloud_min_angle" default="0"/>
   <arg name="cloud_max_angle" default="360"/>
   <arg name="vehicle_mirror_param_file"/>
-  <arg name="container_name" default="velodyne_node_container"/>
+  <arg name="container_name" default="pointcloud_container"/>
+  <arg name="use_shared_container" default="false"/>
+  <arg name="use_cuda_preprocessor" default="false"/>
   <arg name="vertical_bins" default="16"/>
   <arg name="horizontal_ring_id" default="0"/>
   <arg name="is_channel_order_top2down" default="false"/>
@@ -40,6 +42,8 @@
     <arg name="use_intra_process" value="true"/>
     <arg name="use_multithread" value="true"/>
     <arg name="container_name" value="$(var container_name)"/>
+    <arg name="use_shared_container" value="$(var use_shared_container)"/>
+    <arg name="use_cuda_preprocessor" value="$(var use_cuda_preprocessor)"/>
     <arg name="vertical_bins" value="$(var vertical_bins)"/>
     <arg name="horizontal_ring_id" value="$(var horizontal_ring_id)"/>
     <arg name="is_channel_order_top2down" value="$(var is_channel_order_top2down)"/>
diff --git a/aip_common_sensor_launch/launch/velodyne_VLP32C.launch.xml b/aip_common_sensor_launch/launch/velodyne_VLP32C.launch.xml
index 142e8525..169bdc0e 100644
--- a/aip_common_sensor_launch/launch/velodyne_VLP32C.launch.xml
+++ b/aip_common_sensor_launch/launch/velodyne_VLP32C.launch.xml
@@ -15,7 +15,9 @@
   <arg name="cloud_min_angle" default="0"/>
   <arg name="cloud_max_angle" default="360"/>
   <arg name="vehicle_mirror_param_file"/>
-  <arg name="container_name" default="velodyne_node_container"/>
+  <arg name="container_name" default="pointcloud_container"/>
+  <arg name="use_shared_container" default="false"/>
+  <arg name="use_cuda_preprocessor" default="false"/>
   <arg name="vertical_bins" default="32"/>
   <arg name="horizontal_ring_id" default="0"/>
   <arg name="is_channel_order_top2down" default="false"/>
@@ -40,6 +42,8 @@
     <arg name="use_intra_process" value="false"/>
     <arg name="use_multithread" value="false"/>
     <arg name="container_name" value="$(var container_name)"/>
+    <arg name="use_shared_container" value="$(var use_shared_container)"/>
+    <arg name="use_cuda_preprocessor" value="$(var use_cuda_preprocessor)"/>
     <arg name="vertical_bins" value="$(var vertical_bins)"/>
     <arg name="horizontal_ring_id" value="$(var horizontal_ring_id)"/>
     <arg name="is_channel_order_top2down" value="$(var is_channel_order_top2down)"/>
diff --git a/aip_common_sensor_launch/launch/velodyne_VLS128.launch.xml b/aip_common_sensor_launch/launch/velodyne_VLS128.launch.xml
index 524e4427..6164cb3a 100644
--- a/aip_common_sensor_launch/launch/velodyne_VLS128.launch.xml
+++ b/aip_common_sensor_launch/launch/velodyne_VLS128.launch.xml
@@ -15,7 +15,9 @@
   <arg name="cloud_min_angle" default="0"/>
   <arg name="cloud_max_angle" default="360"/>
   <arg name="vehicle_mirror_param_file"/>
-  <arg name="container_name" default="velodyne_node_container"/>
+  <arg name="container_name" default="pointcloud_container"/>
+  <arg name="use_shared_container" default="false"/>
+  <arg name="use_cuda_preprocessor" default="false"/>
   <arg name="vertical_bins" default="128"/>
   <arg name="horizontal_ring_id" default="64"/>
   <arg name="is_channel_order_top2down" default="false"/>
@@ -40,6 +42,8 @@
     <arg name="use_intra_process" value="true"/>
     <arg name="use_multithread" value="true"/>
     <arg name="container_name" value="$(var container_name)"/>
+    <arg name="use_shared_container" value="$(var use_shared_container)"/>
+    <arg name="use_cuda_preprocessor" value="$(var use_cuda_preprocessor)"/>
     <arg name="vertical_bins" value="$(var vertical_bins)"/>
     <arg name="horizontal_ring_id" value="$(var horizontal_ring_id)"/>
     <arg name="is_channel_order_top2down" value="$(var is_channel_order_top2down)"/>
diff --git a/aip_xx1_launch/launch/lidar.launch.xml b/aip_xx1_launch/launch/lidar.launch.xml
index c4ae4d1a..821c54d8 100644
--- a/aip_xx1_launch/launch/lidar.launch.xml
+++ b/aip_xx1_launch/launch/lidar.launch.xml
@@ -6,6 +6,8 @@
   <arg name="vehicle_id" default="$(env VEHICLE_ID default)"/>
   <arg name="vehicle_mirror_param_file"/>
   <arg name="pointcloud_container_name" default="pointcloud_container"/>
+  <arg name="use_shared_container" default="false"/>
+  <arg name="use_cuda_preprocessor" default="false"/>
   <arg name="enable_blockage_diag" default="true"/>
   <arg name="udp_only" default="false"/>
 
@@ -24,7 +26,9 @@
         <arg name="launch_driver" value="$(var launch_driver)"/>
         <arg name="udp_only" value="$(var udp_only)"/>
         <arg name="vehicle_mirror_param_file" value="$(var vehicle_mirror_param_file)"/>
-        <arg name="container_name" value="pointcloud_container"/>
+        <arg name="container_name" value="$(var pointcloud_container_name)"/>
+        <arg name="use_shared_container" value="$(var use_shared_container)"/>
+        <arg name="use_cuda_preprocessor" value="$(var use_cuda_preprocessor)"/>
         <arg name="vertical_bins" value="128"/>
         <arg name="horizontal_ring_id" value="64"/>
         <arg name="is_channel_order_top2down" value="false"/>
@@ -47,7 +51,9 @@
         <arg name="launch_driver" value="$(var launch_driver)"/>
         <arg name="udp_only" value="$(var udp_only)"/>
         <arg name="vehicle_mirror_param_file" value="$(var vehicle_mirror_param_file)"/>
-        <arg name="container_name" value="pointcloud_container"/>
+        <arg name="container_name" value="$(var pointcloud_container_name)"/>
+        <arg name="use_shared_container" value="$(var use_shared_container)"/>
+        <arg name="use_cuda_preprocessor" value="$(var use_cuda_preprocessor)"/>
         <arg name="vertical_bins" value="16"/>
         <arg name="horizontal_ring_id" value="0"/>
         <arg name="is_channel_order_top2down" value="false"/>
@@ -70,7 +76,9 @@
         <arg name="launch_driver" value="$(var launch_driver)"/>
         <arg name="udp_only" value="$(var udp_only)"/>
         <arg name="vehicle_mirror_param_file" value="$(var vehicle_mirror_param_file)"/>
-        <arg name="container_name" value="pointcloud_container"/>
+        <arg name="container_name" value="$(var pointcloud_container_name)"/>
+        <arg name="use_shared_container" value="$(var use_shared_container)"/>
+        <arg name="use_cuda_preprocessor" value="$(var use_cuda_preprocessor)"/>
         <arg name="vertical_bins" value="16"/>
         <arg name="horizontal_ring_id" value="0"/>
         <arg name="is_channel_order_top2down" value="false"/>
@@ -93,7 +101,9 @@
         <arg name="launch_driver" value="$(var launch_driver)"/>
         <arg name="udp_only" value="$(var udp_only)"/>
         <arg name="vehicle_mirror_param_file" value="$(var vehicle_mirror_param_file)"/>
-        <arg name="container_name" value="pointcloud_container"/>
+        <arg name="container_name" value="$(var pointcloud_container_name)"/>
+        <arg name="use_shared_container" value="$(var use_shared_container)"/>
+        <arg name="use_cuda_preprocessor" value="$(var use_cuda_preprocessor)"/>
         <arg name="vertical_bins" value="16"/>
         <arg name="horizontal_ring_id" value="0"/>
         <arg name="is_channel_order_top2down" value="false"/>
@@ -126,6 +136,7 @@
       <arg name="base_frame" value="base_link"/>
       <arg name="use_intra_process" value="true"/>
       <arg name="use_multithread" value="true"/>
+      <arg name="use_cuda_preprocessor" value="$(var use_cuda_preprocessor)"/>
       <arg name="pointcloud_container_name" value="$(var pointcloud_container_name)"/>
     </include>
 
diff --git a/aip_xx1_launch/launch/pointcloud_preprocessor.launch.py b/aip_xx1_launch/launch/pointcloud_preprocessor.launch.py
index d1fa63a1..53d62f2c 100644
--- a/aip_xx1_launch/launch/pointcloud_preprocessor.launch.py
+++ b/aip_xx1_launch/launch/pointcloud_preprocessor.launch.py
@@ -37,16 +37,34 @@ def launch_setup(context, *args, **kwargs):
     )
 
     # set concat filter as a component
+    concat_remappings = [
+        ("~/input/twist", "/sensing/vehicle_velocity_converter/twist_with_covariance"),
+        ("output", "concatenated/pointcloud"),
+    ]
+    concat_extra_arguments = []
+
+    if IfCondition(LaunchConfiguration("use_cuda_preprocessor")).evaluate(context):
+        concat_package = "autoware_cuda_pointcloud_preprocessor"
+        concat_plugin = "autoware::cuda_pointcloud_preprocessor::CudaPointCloudConcatenateDataSynchronizerComponent"
+        # NOTE(knzo25): when using  the cuda blackboard, this setting can not be made global
+        # extra_arguments=[{"use_intra_process_comms": LaunchConfiguration("use_intra_process")}],
+        concat_remappings.append(("output/cuda", "concatenated/pointcloud/cuda"))
+    else:
+        concat_package = "autoware_pointcloud_preprocessor"
+        concat_plugin = (
+            "autoware::pointcloud_preprocessor::PointCloudConcatenateDataSynchronizerComponent"
+        )
+        concat_extra_arguments.append(
+            {"use_intra_process_comms": LaunchConfiguration("use_intra_process")}
+        )
+
     concat_component = ComposableNode(
-        package="autoware_pointcloud_preprocessor",
-        plugin="autoware::pointcloud_preprocessor::PointCloudConcatenateDataSynchronizerComponent",
+        package=concat_package,
+        plugin=concat_plugin,
         name="concatenate_data",
-        remappings=[
-            ("~/input/twist", "/sensing/vehicle_velocity_converter/twist_with_covariance"),
-            ("output", "concatenated/pointcloud"),
-        ],
+        remappings=concat_remappings,
         parameters=[concatenate_and_time_sync_node_param],
-        extra_arguments=[{"use_intra_process_comms": LaunchConfiguration("use_intra_process")}],
+        extra_arguments=concat_extra_arguments,
     )
 
     # load concat or passthrough filter
@@ -69,6 +87,7 @@ def add_launch_arg(name: str, default_value=None):
 
     add_launch_arg("use_multithread", "False")
     add_launch_arg("use_intra_process", "False")
+    add_launch_arg("use_cuda_preprocessor", "True")
     add_launch_arg("pointcloud_container_name", "pointcloud_container")
     add_launch_arg(
         "concatenate_and_time_sync_node_param_path",
diff --git a/aip_xx1_launch/package.xml b/aip_xx1_launch/package.xml
index 7ff7f7ec..d88942e6 100644
--- a/aip_xx1_launch/package.xml
+++ b/aip_xx1_launch/package.xml
@@ -11,6 +11,7 @@
   <buildtool_depend>ament_cmake_auto</buildtool_depend>
 
   <exec_depend>aip_common_sensor_launch</exec_depend>
+  <exec_depend>autoware_cuda_pointcloud_preprocessor</exec_depend>
   <exec_depend>autoware_glog_component</exec_depend>
   <exec_depend>autoware_gnss_poser</exec_depend>
   <exec_depend>autoware_imu_corrector</exec_depend>


Reviews:
user: login: mojomex
body: Looking good! I left a few comments on readability/maintainability but that's all I think.
Is there a reason that x2(_gen2) is not covered?
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-06T01:51:05Z
user: login: knzo25
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-02T04:20:31Z
user: login: knzo25
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-02T04:30:09Z
user: login: knzo25
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-07T01:07:16Z
user: login: mojomex
body: I'd like the two container_name params to be merged but otherwise LGTM
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-22T07:31:21Z
user: login: knzo25
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-23T04:40:20Z
user: login: mojomex
body: [xx1-cuda.webm](https://github.com/user-attachments/assets/dd7bf091-4ab5-4ae4-a405-5733bd0cd454)

Works up until concat. I had to shoehorn the new autoware_universe into pilot-auto.xx1 so I could not test everything (at least autoware_tensorrt_plugins did not build successfully).

Since this is not this PR's fault, I'd say LGTM.
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-25T13:57:00Z

Review comments:
diff_hunk: @@ -37,17 +37,32 @@ def launch_setup(context, *args, **kwargs):
     )
 
     # set concat filter as a component
-    concat_component = ComposableNode(
-        package="autoware_pointcloud_preprocessor",
-        plugin="autoware::pointcloud_preprocessor::PointCloudConcatenateDataSynchronizerComponent",
-        name="concatenate_data",
-        remappings=[
-            ("~/input/twist", "/sensing/vehicle_velocity_converter/twist_with_covariance"),
-            ("output", "concatenated/pointcloud"),
-        ],
-        parameters=[concatenate_and_time_sync_node_param],
-        extra_arguments=[{"use_intra_process_comms": LaunchConfiguration("use_intra_process")}],
-    )
+    if LaunchConfiguration("use_cuda_preprocessor").perform(context):
+        concat_component = ComposableNode(
+            package="autoware_cuda_pointcloud_preprocessor",
+            plugin="autoware::cuda_pointcloud_preprocessor::CudaPointCloudConcatenateDataSynchronizerComponent",
+            name="concatenate_data",
+            remappings=[
+                ("~/input/twist", "/sensing/vehicle_velocity_converter/twist_with_covariance"),
+                ("output", "concatenated/pointcloud"),
+                ("output/cuda", "concatenated/pointcloud/cuda"),
+            ],
+            parameters=[concatenate_and_time_sync_node_param],
+            # NOTE(knzo25): when using  the cuda blackboard, this setting can not be made global
+            # extra_arguments=[{"use_intra_process_comms": LaunchConfiguration("use_intra_process")}],
+        )
+    else:
+        concat_component = ComposableNode(
+            package="autoware_pointcloud_preprocessor",
+            plugin="autoware::pointcloud_preprocessor::PointCloudConcatenateDataSynchronizerComponent",
+            name="concatenate_data",
+            remappings=[
+                ("~/input/twist", "/sensing/vehicle_velocity_converter/twist_with_covariance"),
+                ("output", "concatenated/pointcloud"),
+            ],
+            parameters=[concatenate_and_time_sync_node_param],
+            extra_arguments=[{"use_intra_process_comms": LaunchConfiguration("use_intra_process")}],
+        )
path: aip_xx1_launch/launch/pointcloud_preprocessor.launch.py
user: login: mojomex
body: The two branches differ only in their package, plugin and extra_params. The rest of the code could be merged into one to prevent future contributors from only modifying the one they're concerned about
created_at: 2025-03-06T01:10:00Z
author_association: CONTRIBUTOR
path: common_sensor_launch/launch/nebula_node_container.launch.py
user: login: mojomex
body: This was already the case before this PR, but this launch file is spaghetti code, making it very hard to understand at a glance what is going on.

Especially with the kind of multiplexed code of CUDA/non-CUDA, I'd suggest factoring out into a few functions like these:

```python
def make_cuda_pointcloud_preprocessor(distortion_corrector_params, ring_outlier_params, vehicle_info, mirror_info) -> ComposableNode:
  ...

def make_vehicle_cropbox_filter(vehicle_info) ->ComposableNode:
  ...

```
created_at: 2025-03-06T01:40:25Z
author_association: CONTRIBUTOR
diff_hunk: @@ -333,6 +406,9 @@ def add_launch_arg(name: str, default_value=None, description=None):
     add_launch_arg("use_multithread", "False", "use multithread")
     add_launch_arg("use_intra_process", "False", "use ROS 2 component container communication")
     add_launch_arg("lidar_container_name", "nebula_node_container")
+    add_launch_arg("pointcloud_container_name", "pointcloud_container")
+    add_launch_arg("use_pointcloud_container", "False")
+    add_launch_arg("use_cuda_preprocessor", "False")
     add_launch_arg("ptp_profile", "1588v2")
path: common_sensor_launch/launch/nebula_node_container.launch.py
user: login: mojomex
body: `lidar_container_name`, `pointcloud_container_name` and `use_pointcloud_container` could be confusing for users, how about for example `container_name` and `use_shared_container`, with the appropriate `description=`?
created_at: 2025-03-06T01:48:42Z
author_association: CONTRIBUTOR
diff_hunk: @@ -333,6 +406,9 @@ def add_launch_arg(name: str, default_value=None, description=None):
     add_launch_arg("use_multithread", "False", "use multithread")
     add_launch_arg("use_intra_process", "False", "use ROS 2 component container communication")
     add_launch_arg("lidar_container_name", "nebula_node_container")
+    add_launch_arg("pointcloud_container_name", "pointcloud_container")
+    add_launch_arg("use_pointcloud_container", "False")
+    add_launch_arg("use_cuda_preprocessor", "False")
     add_launch_arg("ptp_profile", "1588v2")
path: common_sensor_launch/launch/nebula_node_container.launch.py
user: login: mojomex
body: Also, are users always expected to use a shared container when using CUDA?
created_at: 2025-03-06T01:49:38Z
author_association: CONTRIBUTOR
diff_hunk: @@ -37,17 +37,32 @@ def launch_setup(context, *args, **kwargs):
     )
 
     # set concat filter as a component
-    concat_component = ComposableNode(
-        package="autoware_pointcloud_preprocessor",
-        plugin="autoware::pointcloud_preprocessor::PointCloudConcatenateDataSynchronizerComponent",
-        name="concatenate_data",
-        remappings=[
-            ("~/input/twist", "/sensing/vehicle_velocity_converter/twist_with_covariance"),
-            ("output", "concatenated/pointcloud"),
-        ],
-        parameters=[concatenate_and_time_sync_node_param],
-        extra_arguments=[{"use_intra_process_comms": LaunchConfiguration("use_intra_process")}],
-    )
+    if LaunchConfiguration("use_cuda_preprocessor").perform(context):
+        concat_component = ComposableNode(
+            package="autoware_cuda_pointcloud_preprocessor",
+            plugin="autoware::cuda_pointcloud_preprocessor::CudaPointCloudConcatenateDataSynchronizerComponent",
+            name="concatenate_data",
+            remappings=[
+                ("~/input/twist", "/sensing/vehicle_velocity_converter/twist_with_covariance"),
+                ("output", "concatenated/pointcloud"),
+                ("output/cuda", "concatenated/pointcloud/cuda"),
+            ],
+            parameters=[concatenate_and_time_sync_node_param],
+            # NOTE(knzo25): when using  the cuda blackboard, this setting can not be made global
+            # extra_arguments=[{"use_intra_process_comms": LaunchConfiguration("use_intra_process")}],
+        )
+    else:
+        concat_component = ComposableNode(
+            package="autoware_pointcloud_preprocessor",
+            plugin="autoware::pointcloud_preprocessor::PointCloudConcatenateDataSynchronizerComponent",
+            name="concatenate_data",
+            remappings=[
+                ("~/input/twist", "/sensing/vehicle_velocity_converter/twist_with_covariance"),
+                ("output", "concatenated/pointcloud"),
+            ],
+            parameters=[concatenate_and_time_sync_node_param],
+            extra_arguments=[{"use_intra_process_comms": LaunchConfiguration("use_intra_process")}],
+        )
path: aip_xx1_launch/launch/pointcloud_preprocessor.launch.py
user: login: knzo25
body: Addressed in [bfcf9a2](https://github.com/tier4/aip_launcher/pull/348/commits/bfcf9a2a563dadc3ef4d6c96e4dc60049314c392)
created_at: 2025-04-02T04:20:30Z
author_association: CONTRIBUTOR
diff_hunk: @@ -333,6 +406,9 @@ def add_launch_arg(name: str, default_value=None, description=None):
     add_launch_arg("use_multithread", "False", "use multithread")
     add_launch_arg("use_intra_process", "False", "use ROS 2 component container communication")
     add_launch_arg("lidar_container_name", "nebula_node_container")
+    add_launch_arg("pointcloud_container_name", "pointcloud_container")
+    add_launch_arg("use_pointcloud_container", "False")
+    add_launch_arg("use_cuda_preprocessor", "False")
     add_launch_arg("ptp_profile", "1588v2")
path: common_sensor_launch/launch/nebula_node_container.launch.py
user: login: knzo25
body: Addressed in [411c39f](https://github.com/tier4/aip_launcher/pull/348/commits/411c39feaff2c906c06e60c194489a16a9adeeb2), 
315d2a4, and [f79d487](https://github.com/tier4/aip_launcher/pull/348/commits/f79d4875c315725de3562029a4904b548ac9b547).

I made it so that it was required (otherwise performance will not be good and I prefer it not working than working bad without the user realizing)


created_at: 2025-04-02T04:30:08Z
author_association: CONTRIBUTOR
path: common_sensor_launch/launch/nebula_node_container.launch.py
user: login: knzo25
body: Addressed in [00d1a0c](https://github.com/tier4/aip_launcher/pull/348/commits/00d1a0c92eff6fd7c74cbf220c341a6eea8ad689)
created_at: 2025-04-07T01:07:16Z
author_association: CONTRIBUTOR
diff_hunk: @@ -336,7 +433,26 @@ def add_launch_arg(name: str, default_value=None, description=None):
     add_launch_arg("advanced_diagnostics", "false")
     add_launch_arg("use_multithread", "False", "use multithread")
     add_launch_arg("use_intra_process", "False", "use ROS 2 component container communication")
-    add_launch_arg("lidar_container_name", "nebula_node_container")
+    add_launch_arg(
+        "lidar_container_name",
+        "nebula_node_container",
+        "Name of the new container to be created when use_shared_container is false",
+    )
+    add_launch_arg(
+        "shared_container_name",
+        "pointcloud_container",
+        "Name of the existing container to be used when use_shared_container is true",
+    )
path: aip_common_sensor_launch/launch/nebula_node_container.launch.py
user: login: mojomex
body: These two should probably be combined for clarity. They can never validly be set at the same time and the shared container vs. new container aspect is already handled by the use_shared_container flag.
created_at: 2025-04-22T07:30:53Z
author_association: CONTRIBUTOR
diff_hunk: @@ -336,7 +433,26 @@ def add_launch_arg(name: str, default_value=None, description=None):
     add_launch_arg("advanced_diagnostics", "false")
     add_launch_arg("use_multithread", "False", "use multithread")
     add_launch_arg("use_intra_process", "False", "use ROS 2 component container communication")
-    add_launch_arg("lidar_container_name", "nebula_node_container")
+    add_launch_arg(
+        "lidar_container_name",
+        "nebula_node_container",
+        "Name of the new container to be created when use_shared_container is false",
+    )
+    add_launch_arg(
+        "shared_container_name",
+        "pointcloud_container",
+        "Name of the existing container to be used when use_shared_container is true",
+    )
path: aip_common_sensor_launch/launch/nebula_node_container.launch.py
user: login: knzo25
body: Addressed in [fc2b7fd](https://github.com/tier4/aip_launcher/pull/348/commits/fc2b7fd2992092e5b43812fb42015bea96ec9492)
The consequence is that all containers will be called `pointcloud_container` which will need tooling that relied on this to be updated (not sure if the container name was ever used though)
created_at: 2025-04-23T04:40:19Z
author_association: CONTRIBUTOR

PR comments:
user: login: knzo25
created_at: 2024-11-25T09:44:55Z
body: Note: this PR will be kept as a draft until https://github.com/tier4/aip_launcher/pull/337 it merged, at which point this PR will be reabsed
author_association: CONTRIBUTOR
user: login: knzo25
created_at: 2025-02-05T09:57:45Z
body: The concatenated PR was merged, so after solving the conflicts and some recent changes, I am opening this PR :pray: 
author_association: CONTRIBUTOR
user: login: knzo25
created_at: 2025-04-01T23:35:11Z
body: @mojomex 
Thanks for the review. X2 Gen2 was not covered on purpose since it is very difficult to maintain a compatible version of all the PRs (XX1 is better in this sense). As soon as the universe PRs get merged and I can get an easy way to port and test it on X2 Gen2 I will sent the proper PR here
author_association: CONTRIBUTOR
user: login: knzo25
created_at: 2025-04-07T01:08:02Z
body: @mojomex 
Addressed all comments. I can not ask for a review since the previous comments were not added in the context of a `require changes` it seems
author_association: CONTRIBUTOR
user: login: knzo25
created_at: 2025-04-23T04:42:25Z
body: @mojomex 
Addressed the last comment.
Since we are close to merging this, I left the cuda acceleration and shared containers as disabled.
To enable them, you just need to change the following settings:

```xml
<arg name="use_shared_container" default="false"/>
<arg name="use_cuda_preprocessor" default="false"/>
```
to

```xml
<arg name="use_shared_container" default="true"/>
<arg name="use_cuda_preprocessor" default="true"/>
```

in `aip_xx1_launch/launch/lidar.launch.xml`
author_association: CONTRIBUTOR