Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding the ARSPointCloudFilter node in the Radar ROS2 Pipeline #19

Open
wants to merge 53 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
96f7201
added Dockerfile and profile for radar_driver
rijin113 Jan 26, 2023
7972a93
configured radar dockerfile and profile
rijin113 Jan 28, 2023
9b1b56b
changed radar dockerfile
Jan 28, 2023
c474d17
added basefiles for the radar_pointcloud_filter node
Jan 28, 2023
c6a21e3
updated files for RadarPointCloudfilter Node
Jan 29, 2023
fbe1242
updated files for radar_pointcloud_filter node
Jan 30, 2023
c877695
updated files for radar_pointcloud_filter node
Jan 31, 2023
9d7e186
added new ROS nodes and updated files for point cloud filters
Feb 6, 2023
3dbd521
updated radar custom message types and implementation of pointcloud f…
Feb 7, 2023
1de1e58
adde code ffor range, snr and angle filtrs and updated docker files
Feb 8, 2023
8c851b1
fixed ros errors
Feb 9, 2023
faacc32
modified ros launch file and updated code structure
Feb 10, 2023
29f3cb6
modified filter code and updated code structure
Feb 11, 2023
41c4de3
creating a generic test filter
Feb 12, 2023
b2aaae3
updated radarpointcloud filter node
Feb 13, 2023
58bbaf8
updated pointcloud filter node files
Feb 14, 2023
a0632c3
updated pointcloud filter node ROS files
Feb 15, 2023
3850a0f
resolved node build errors
Feb 15, 2023
4fb221f
updated radar left and right unfiltered callbacks
Feb 15, 2023
f43323b
updated pointcloud filter ROS node
Feb 19, 2023
653d751
implementing near and far scan mode filters
Feb 21, 2023
6da6ef4
created near scan and far scan filters
Feb 22, 2023
fe75cbc
updated near and far scan filters structure
Feb 23, 2023
0e2931e
wrote pseudocode for double buffer
Feb 25, 2023
5d9724b
added double buffer algorithm for near + far scan filter
Feb 26, 2023
380e93f
updated code structure and started testing
Feb 28, 2023
d22c1b2
updated code structure and started testing
Feb 28, 2023
d63f61b
changed near and far filter implementation; added counts
Mar 2, 2023
738b44e
updated near, far scan and double buffer implementation
Mar 3, 2023
6d2b65b
updated near + far scan filters (double buffer algo)
Mar 6, 2023
4fc2b8a
added new implementation for near, far scan and double buffer; added …
Mar 7, 2023
7ccec61
updated filter implementation and unit tests for common scan filter
Mar 8, 2023
e770d9f
Updated unit tests for common scan filters
Mar 9, 2023
b71b4e8
updated unit tests for common scan filter on the pointcloudfilter ROS…
Mar 10, 2023
00dd5dd
Updated unit tests for common scan filter
Mar 11, 2023
51f5fce
updated tests for the ARSPointCloudFilter ROS Node
Mar 12, 2023
db9f34c
added more tests for double buffer algo
Mar 13, 2023
e5596dc
updated unit tests and reorganized code structure
Mar 14, 2023
623ff92
Saving code structure before major changes
Mar 14, 2023
6c5e527
updated unit tests and filter implementation
Mar 16, 2023
982a25c
removed log statements, and changed files based on the linter
Mar 17, 2023
e0c57fe
updated node callbacks and structure
Mar 17, 2023
6613e88
updated code structure
Mar 18, 2023
21041e2
updated code structure again
Mar 18, 2023
759b1f3
updated comments
Mar 20, 2023
c982b1b
added linter changes
Mar 21, 2023
7b4672e
all linter suggestions are added and tests are passing
Mar 22, 2023
029458e
deleted files for CarlaPointCloudFilternode
Mar 22, 2023
39a4a82
Merge branch 'main' into radar_pointcloud_filter
CheranMahalingam Mar 22, 2023
91d281a
renamed radar to continental
Mar 23, 2023
fbf4e39
Merge branch 'radar_pointcloud_filter' of github.com:WATonomous/wato_…
Mar 23, 2023
7e99420
renamed ROS node to continental driver
Mar 23, 2023
3b07ebb
added variables for packet capacities
May 2, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions dev_config.sh
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ SAMPLES_CPP_AGGREGATOR_IMAGE=${SAMPLES_CPP_AGGREGATOR_IMAGE:-"git.uwaterloo.ca:5
SAMPLES_CPP_PRODUCER_IMAGE=${SAMPLES_CPP_PRODUCER_IMAGE:-"git.uwaterloo.ca:5050/watonomous/wato_monorepo/samples_cpp_producer"}
SAMPLES_CPP_TRANSFORMER_IMAGE=${SAMPLES_CPP_TRANSFORMER_IMAGE:-"git.uwaterloo.ca:5050/watonomous/wato_monorepo/samples_cpp_transformer"}

# Sensor Interfacing
CONTINENTAL_DRIVER_IMAGE=${CONTINENTAL_DRIVER_IMAGE:-"git.uwaterloo.ca:5050/watonomous/wato_monorepo/continental_driver"}

# Infrastructure
INFRASTRUCTURE_VIS_TOOLS_VNC_IMAGE=${INFRASTRUCTURE_VIS_TOOLS_VNC_IMAGE:-"git.uwaterloo.ca:5050/watonomous/wato_monorepo/infrastructure_vis_tools_vnc"}
INFRASTRUCTURE_DATA_STREAM_IMAGE=${DATA_STREAM_IMAGE:-"git.uwaterloo.ca:5050/watonomous/wato_monorepo/infrastructure_data_stream"}
Expand Down Expand Up @@ -96,6 +99,9 @@ echo "SAMPLES_CPP_PRODUCER_IMAGE=$SAMPLES_CPP_PRODUCER_IMAGE" >> "$PROFILES_DIR/
echo "SAMPLES_CPP_TRANSFORMER_IMAGE=$SAMPLES_CPP_TRANSFORMER_IMAGE" >> "$PROFILES_DIR/.env"
echo "INFRASTRUCTURE_DATA_STREAM_IMAGE=$INFRASTRUCTURE_DATA_STREAM_IMAGE" >> "$PROFILES_DIR/.env"


echo "CONTINENTAL_DRIVER_IMAGE=$CONTINENTAL_DRIVER_IMAGE" >> "$PROFILES_DIR/.env"

echo "TAG=$TAG" >> "$PROFILES_DIR/.env"
echo "TARGET_STAGE=$TARGET_STAGE" >> "$PROFILES_DIR/.env"
echo "CACHE_FROM_TAG=$CACHE_FROM_TAG" >> "$PROFILES_DIR/.env"
Expand Down
43 changes: 43 additions & 0 deletions docker/continental_driver/continental_driver.Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
FROM ros:humble AS base

RUN apt-get update && apt-get install -y curl && \
rm -rf /var/lib/apt/lists/*

# Add a docker user so we that created files in the docker container are owned by a non-root user
RUN addgroup --gid 1000 docker && \
adduser --uid 1000 --ingroup docker --home /home/docker --shell /bin/bash --disabled-password --gecos "" docker && \
echo "docker ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers.d/nopasswd

# Remap the docker user and group to be the same uid and group as the host user.
# Any created files by the docker container will be owned by the host user.
RUN USER=docker && \
GROUP=docker && \
curl -SsL https://github.com/boxboat/fixuid/releases/download/v0.4/fixuid-0.4-linux-amd64.tar.gz | tar -C /usr/local/bin -xzf - && \
chown root:root /usr/local/bin/fixuid && \
chmod 4755 /usr/local/bin/fixuid && \
mkdir -p /etc/fixuid && \
printf "user: $USER\ngroup: $GROUP\npaths:\n - /home/docker/" > /etc/fixuid/config.yml

USER docker:docker

RUN mkdir -p ~/ament_ws/src
WORKDIR /home/docker/ament_ws/src

COPY src/sensor_interfacing/continental_driver continental_driver
COPY src/wato_msgs/radar_msgs radar_msgs

WORKDIR /home/docker/ament_ws
RUN . /opt/ros/$ROS_DISTRO/setup.sh && \
rosdep update && \
rosdep install -i --from-path src --rosdistro $ROS_DISTRO -y && \
colcon build \
--cmake-args -DCMAKE_BUILD_TYPE=Release

COPY docker/wato_ros_entrypoint.sh /home/docker/wato_ros_entrypoint.sh
COPY docker/.bashrc /home/docker/.bashrc

RUN sudo chmod +x ~/wato_ros_entrypoint.sh

ENTRYPOINT ["/usr/local/bin/fixuid", "-q", "/home/docker/wato_ros_entrypoint.sh"]

CMD ["tail", "-f", "/dev/null"]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Command should run the relevant ROS node or launch file

13 changes: 13 additions & 0 deletions profiles/docker-compose.sensors.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
version: "3.8"
services:
continental_driver:
build:
context: ..
dockerfile: docker/continental_driver/continental_driver.Dockerfile
cache_from:
- "${CONTINENTAL_DRIVER_IMAGE:?}:${CACHE_FROM_TAG}"
- "${CONTINENTAL_DRIVER_IMAGE:?}:develop"
image: "${CONTINENTAL_DRIVER_IMAGE:?}:${TAG}"
user: ${FIXUID:?}:${FIXGID:?}
volumes:
- ../src/sensor_interfacing/continental_driver:/home/docker/ament_ws/src/continental_driver
77 changes: 77 additions & 0 deletions src/sensor_interfacing/continental_driver/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
cmake_minimum_required(VERSION 3.10)
project(continental_pointcloud_filter)

# Set compiler to use C++ 17 standard
rijin113 marked this conversation as resolved.
Show resolved Hide resolved
if(NOT CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD 17)
endif()

if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wall -Wextra -Wpedantic)
endif()

# Search for dependencies required for building this package
find_package(ament_cmake REQUIRED) # ROS2 build tool
find_package(rclcpp REQUIRED) # ROS2 C++ package
find_package(radar_msgs REQUIRED) # Custom package containing ROS2 messages

# Compiles source files into a library
# A library is not executed, instead other executables can link
# against it to access defined methods and classes.
# We build a library so that the methods defined can be used by
# both the unit test and ROS2 node executables.
add_library(continentalpointcloudfilter_lib
rijin113 marked this conversation as resolved.
Show resolved Hide resolved
src/continental_pointcloud_filter.cpp)
# Indicate to compiler where to search for header files
target_include_directories(continentalpointcloudfilter_lib
PUBLIC include)
# Add ROS2 dependencies required by package
ament_target_dependencies(continentalpointcloudfilter_lib rclcpp radar_msgs)

# By default tests are built. This can be overridden by modifying the
# colcon build command to include the -DBUILD_TESTING=OFF flag.
option(BUILD_TESTING "Build tests" ON)
if(BUILD_TESTING)
# Search for dependencies required for building tests + linting
find_package(ament_cmake_gtest REQUIRED)
find_package(ament_lint_auto REQUIRED)
find_package(ament_lint_common REQUIRED)

# Remove the default C++ and copyright linter
list(APPEND AMENT_LINT_AUTO_EXCLUDE
ament_cmake_cpplint
ament_cmake_copyright)

# Reinstall cpplint ignoring copyright errors
ament_cpplint(FILTERS "-legal/copyright")

ament_lint_auto_find_test_dependencies()

# Create test executable from source files
ament_add_gtest(continental_pointcloud_filter_test test/continental_pointcloud_filter_test.cpp)
# Link to the previously built library to access ContinentalPointCloudFilter classes and methods
target_link_libraries(continental_pointcloud_filter_test continentalpointcloudfilter_lib)

# Copy executable to installation location
install(TARGETS
continental_pointcloud_filter_test
DESTINATION lib/${PROJECT_NAME})
endif()

# Create ROS2 node executable from source files
add_executable(continental_pointcloud_filter_node src/continental_pointcloud_filter_node.cpp)
# Link to the previously built library to access Aggregator classes and methods
target_link_libraries(continental_pointcloud_filter_node continentalpointcloudfilter_lib)

# Copy executable to installation location
install(TARGETS
continental_pointcloud_filter_node
DESTINATION lib/${PROJECT_NAME})

# Copy launch files to installation location
install(DIRECTORY
launch
config
DESTINATION share/${PROJECT_NAME})

ament_package()
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
continental_point_cloud_filter:
rijin113 marked this conversation as resolved.
Show resolved Hide resolved
rijin113 marked this conversation as resolved.
Show resolved Hide resolved
ros__parameters:
filter_mode: "continental"
vrel_rad: -99999.99
el_ang: -99999.99
rcs0: -99999.99
snr: -99999.99
range: -99999.99
az_ang0: -99999.99
scan_mode: "near"
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
#ifndef CONTINENTAL_POINTCLOUD_FILTER_HPP_
#define CONTINENTAL_POINTCLOUD_FILTER_HPP_

#include <string>
#include "rclcpp/rclcpp.hpp"

#include "radar_msgs/msg/radar_packet.hpp"
#include "radar_msgs/msg/radar_detection.hpp"

namespace filtering
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This namespace is generic, it could apply to any other sensor driver or perception. It could be renamed to continental_driver. Some ideas for how some companies standardize namespacing,
image

{

/**
* @brief The ContinentalPointCloudFilter is responsible for filtering, and publishing radar detections
* coming from the ARS430 radar sensor in the Radar ROS2 pipeline. These incoming
* detections are first organized and transformed into custom ROS messages/packets prior
* to being sent into this node. Note each message has detections of two types.
* These detections can be from a near scan or a far scan.
*
* The internal logic for this node is divided into three sections or "modes".
*
* Mode 1. The node recieves, filters and publishes only NEAR scan radar detections.
* These are packets with the event id 3, 4 or 5.
*
* Mode 2. The node recieves, filters and publishes only FAR scan radar detections
* These are packets with the event id 1 or 2.
*
* Mode 3. The node recieves, filters and publishes BOTH NEAR and FAR scan radar detections.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't need to go into detail about how near + far works since it should be abstracted away for the user, just mention that Mode 3 applies to all detections.

* In this case, it is much more complex to handle detections from both NEAR and FAR scan
* especially if they are also from two seperate scans. To solve this, the node uses the double
* buffer algorithm.
*/
class ContinentalPointCloudFilter
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: For consistency since everywhere else the variable is pointcloud_... then here it should be ContinentalPointcloudFilter.

{
public:
ContinentalPointCloudFilter();
typedef struct
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comments as struct from below.

{
std::string scan_mode;
double vrel_rad_param;
double el_ang_param;
double rcs0_param;
double snr_param;
double range_param;
double az_ang0_param;
} filter_parameters;

enum scan_type
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

make this an enum class for C++ code since it makes it less likely for namespacing and implicit type conversion issues to happen in the future.

{
NEAR,
FAR
};

/**
* @brief The following is the internal logic of the common_scan_filter for near and far scan modes.
* This filter works by checking the timestamps of incoming messages. If messages share the same
* timestamp, this means that they are part of the same scan. Therefore, all the detections from
* that incoming message are filtered and appended to a buffer packet. This process continues
* until the packet count is at maxiumum capacity or when there is a timestamp change indicating
* that a new scan has begun and the old buffer packet is ready to be published.
* Special case to consider: If the scan starts in the middle such that we don't have a
* complete number of packets (18 or 12) collected, then the code discards these detections
* and starts from scratch with a new scan.
*/
bool common_scan_filter(
const radar_msgs::msg::RadarPacket::SharedPtr unfiltered_continental,
const filter_parameters & parameters,
radar_msgs::msg::RadarPacket & publish_packet);

/**
* @brief The following is the internal logic of the near_far_scan_filter
* for the "nearfar" mode. This is when the double buffer algorithm is utilized.
* Please refer to the figure in the WATO drive for more information
* on the algorithm.
* Special cases to consider:
* 1. If the scan started in the middle such that there is an incomplete packets of
* near scans but a full number of far scans. This means that we will have
* less than 30 packets when publishing. Therefore, the program discards this
* incomplete packet and starts from scratch.
* 2. If the scan started in the middle, and we are only receiving far scan packets
* for that scan (no near scan packets were collected). Then the program discards
* each far scan packet that is recieved from that scan.
*/
bool near_far_scan_filter(
const radar_msgs::msg::RadarPacket::SharedPtr unfiltered_continental,
const filter_parameters & parameters,
radar_msgs::msg::RadarPacket & publish_packet);

/**
* @brief Checks the Event ID of a message and returns which scan it is (NEAR OR FAR).
*/
scan_type check_scan_type(const radar_msgs::msg::RadarPacket::SharedPtr unfiltered_continental);

/**
* @brief Resets all scan states (timestamp, packet count, and publish status).
*/
void reset_scan_states();

private:
/**
* @brief Pointfilter() filters an incoming radar packet based on set thresholds.
*/
radar_msgs::msg::RadarPacket point_filter(
const radar_msgs::msg::RadarPacket::SharedPtr unfiltered_continental,
double snr_threshold,
double AzAng0_threshold,
double range_threshold,
double vrel_rad_threshold,
double el_ang_threshold,
double rcs_threshold);

/**
* @brief Variables below are used for all the filters (Common scan filter & NearFarScan Filter).
*/
int near_scan_capacity_;
rijin113 marked this conversation as resolved.
Show resolved Hide resolved
int far_scan_capacity_;
unsigned int default_timestamp_;
typedef struct
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typedef not needed and naming convention should follow class style, declare struct as,

struct ScanParams
{
  ...
}

Also I would put all structs near the top of the class declaration so that it can easily be found.

{
unsigned int timestamp_;
int packet_count_;
bool publish_status_;
} scan_params;

/**
* @brief Variables only used for common scan filter.
*/
scan_params near_scan_single_;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: near_scan_single_ sounds redundant since a near scan is one scan, try near_scan_ instead and rename the array to near_scane_buffer_.

scan_params far_scan_single_;
radar_msgs::msg::RadarPacket buffer_packet_;

/**
* @brief Variables only used for Near Far Scan Filter (Double Buffer Algorithm).
*/
int buffer_index_;
std::array<radar_msgs::msg::RadarPacket, 2> near_far_buffer_packets_;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: near_far sounds redundant, you could prefix it as all.

std::array<scan_params, 2> near_scan_;
std::array<scan_params, 2> far_scan_;
};

} // namespace filtering

#endif // CONTINENTAL_POINTCLOUD_FILTER_HPP_
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#ifndef CONTINENTAL_POINTCLOUD_FILTER_NODE_HPP_
#define CONTINENTAL_POINTCLOUD_FILTER_NODE_HPP_

#include "rclcpp/rclcpp.hpp"

#include "radar_msgs/msg/radar_packet.hpp"
#include "radar_msgs/msg/radar_detection.hpp"

#include "continental_pointcloud_filter.hpp"

/**
* @brief Implementation of a ROS2 Point Cloud Filter node that listens to "unfilteredRadarLeft"
* and "unfilteredRadarRight" topics and publishes filtered radar data to "processed" radar topic.
*/
class ContinentalPointCloudFilterNode : public rclcpp::Node
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Similar to above use Pointcloud for consistency

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you clarify this again please?

{
public:
ContinentalPointCloudFilterNode();

private:
filtering::ContinentalPointCloudFilter::filter_parameters parameters;
rijin113 marked this conversation as resolved.
Show resolved Hide resolved

/**
* @brief A ROS2 subscription node callback used to unpack raw ARS radar data from the "unfilteredRadarRight"
* topic.
*
* @param msg a raw message from the "unfilteredRadarRight" topic
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make sure the topic name you mention here is the one you are actually listening to.

*/
void unfiltered_continental_radar_right_callback(
const radar_msgs::msg::RadarPacket::SharedPtr msg);

/**
* @brief A ROS2 subscription node callback used to unpack raw ARS radar data from the "unfilteredRadarLeft"
* topic.
*
* @param msg a raw message from the "unfilteredRadarLeft" topic
*/
void unfiltered_continental_radar_left_callback(
const radar_msgs::msg::RadarPacket::SharedPtr msg);

// ROS2 Subscriber listening to "unfilteredRadarLeft" topic.
rclcpp::Subscription<radar_msgs::msg::RadarPacket>::SharedPtr raw_left_sub_;

// ROS2 Subscriber listening to "unfilteredRadarRight" topic.
rclcpp::Subscription<radar_msgs::msg::RadarPacket>::SharedPtr raw_right_sub_;

// ROS2 Publisher that sends filtered messages from left and right radar to the "processed" topic.
rclcpp::Publisher<radar_msgs::msg::RadarPacket>::SharedPtr left_right_pub_;
rijin113 marked this conversation as resolved.
Show resolved Hide resolved

// An object containing methods for near and far scan filters.
filtering::ContinentalPointCloudFilter pointcloudfilter_;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rename to pointcloud_filter for consistency.

};

#endif // CONTINENTAL_POINTCLOUD_FILTER_NODE_HPP_
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
from launch import LaunchDescription
from launch_ros.actions import Node
from launch.actions import DeclareLaunchArgument
from launch.substitutions import LaunchConfiguration
from launch.conditions import LaunchConfigurationEquals


def generate_launch_description():
"""Launch ContinentalPointCloudFilter node."""
continental_pointcloud_filter_param = DeclareLaunchArgument(
'filter_mode', default_value='continental')

continental_pointcloud_filter_node = Node(
LaunchConfiguration('filter_mode'),
condition=LaunchConfigurationEquals('filter_mode', 'continental'),
package='continental_pointcloud_filter',
executable='continental_pointcloud_filter_node',
parameters=[
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't pass parameters directly to the node, pass down the parameter file instead.

{'vrel_rad': -99999.99},
{'el_ang': -99999.99},
{'rcs0': -99999.99},
{'snr': -99999.99},
{'range': -99999.99},
{'az_ang0': -99999.99},
{'scan_mode': 'near'}
]
)
return LaunchDescription([
continental_pointcloud_filter_param,
continental_pointcloud_filter_node
])
Loading