Crate ros_pointcloud2

source ·
Expand description

A PointCloud2 message conversion library.

The library provides the PointCloud2Msg type, which implements conversions to and from Vec and (parallel) iterators.

Vector conversions are optimized for direct copy. They are useful when you just move similar data around. They are usually a good default.

You can use the iterator functions for more control over the conversion process.

These feature predictable performance but they do not scale well with large clouds. Learn more about that in the performance section of the repository. The iterators are useful when your conversions are more complex than a simple copy or you the cloud is small enough.

When the cloud is getting larger or you are doing a lot of processing per point, switch to the parallel iterators.

For ROS interoperability, there are integrations avialable with feature flags. If you miss a message type, please open an issue or a PR. See the ros module for more information on how to integrate more libraries.

Common point types like PointXYZ or PointXYZI are provided. See the full list here. You can easily add any additional custom type. See custom_enum_field_filter.rs for an example.

§Minimal Example

use ros_pointcloud2::prelude::*;

let cloud_points = vec![
    PointXYZI::new(9.6, 42.0, -6.2, 0.1),
    PointXYZI::new(46.0, 5.47, 0.5, 0.1),
];
let cloud_copy = cloud_points.clone(); // For equality test later.
     
let out_msg = PointCloud2Msg::try_from_iter(cloud_points).unwrap();
     
// Convert to your ROS crate message type.
// let msg: r2r::sensor_msgs::msg::PointCloud2 = in_msg.into();
// Publish ...

// ... now incoming from a topic.
// let in_msg: PointCloud2Msg = msg.into();
let in_msg = out_msg;
     
let processed_cloud = in_msg.try_into_iter().unwrap()
    .map(|point: PointXYZ| { // Define the data you want from the point.
        // Some logic here.
        PointXYZI::new(point.x, point.y, point.z, 0.1)
    }).collect::<Vec<_>>();

assert_eq!(processed_cloud, cloud_copy);

§Features

  • r2r_msg — Integration for the ROS2 library r2r.
  • rosrust_msg — Integration with the rosrust library for ROS1 message types.
  • (rclrs_msg) — Integration for ROS2 rclrs but it currently needs this workaround.
  • derive (enabled by default) — Enables the _vec functions and offers helpful custom point derive macros for the PointConvertible trait.
  • rayon — Parallel iterator support for _par_iter functions. PointCloud2Msg::try_from_par_iter additionally needs the ‘derive’ feature.
  • nalgebra — Predefined points offer a nalgebra typed getter for coordinates (e.g. xyz).
  • std (enabled by default) — Use the standard library. no_std only works standalone or with the ‘nalgebra’ feature.

§Custom Points

Implement PointConvertible for your point with the derive feature or manually.

use ros_pointcloud2::prelude::*;

#[cfg_attr(feature = "derive", derive(PointConvertible, TypeLayout))]
#[derive(Clone, Debug, PartialEq, Copy, Default)]
pub struct MyPointXYZI {
    pub x: f32,
    pub y: f32,
    pub z: f32,
    #[cfg_attr(feature = "derive", rpcl2(rename("i")))]
    pub intensity: f32,
}

impl MyPointXYZI {
    pub fn new(x: f32, y: f32, z: f32, intensity: f32) -> Self {
        Self { x, y, z, intensity }
    }
}

// Manual implementation of PointConvertible without derive.
#[cfg(not(feature = "derive"))]
impl Fields<4> for MyPointXYZI {
    fn field_names_ordered() -> [&'static str; 4] {
        ["x", "y", "z", "i"] // Note the different field name for intensity.
    }
}

#[cfg(not(feature = "derive"))]
impl From<RPCL2Point<4>> for MyPointXYZI {
    fn from(point: RPCL2Point<4>) -> Self {
        Self::new(point[0].get(), point[1].get(), point[2].get(), point[3].get())
    }
}

#[cfg(not(feature = "derive"))]
impl From<MyPointXYZI> for RPCL2Point<4> {
    fn from(point: MyPointXYZI) -> Self {
        [point.x.into(), point.y.into(), point.z.into(), point.intensity.into()].into()
    }
}

#[cfg(not(feature = "derive"))]
impl PointConvertible<4> for MyPointXYZI {}

let first_p = MyPointXYZI::new(1.0, 2.0, 3.0, 0.5);
let cloud_points = vec![first_p, MyPointXYZI::new(4.0, 5.0, 6.0, 0.5)];
let msg_out = PointCloud2Msg::try_from_iter(cloud_points).unwrap();
let cloud_points_out: Vec<MyPointXYZI> = msg_out.try_into_iter().unwrap().collect();
assert_eq!(first_p, *cloud_points_out.first().unwrap());

An example without #[cfg_attr] looks like this:

#[derive(Clone, Debug, PartialEq, Copy, Default, PointConvertible, TypeLayout)]
pub struct MyPointXYZI {
    pub x: f32,
    pub y: f32,
    pub z: f32,
    #[rpcl2(rename("i"))]
    pub intensity: f32,
}

Modules§

  • Iterator implementations for PointCloud2Msg including a parallel iterator for rayon.
  • Predefined point types commonly used in ROS.
  • Commonly used types and traits for predefined and custom point conversions.
  • Types used to represent ROS messages and convert between different ROS crates.

Structs§

Enums§

Traits§

  • Matching field names from each data point. Always make sure to use the same order as in your conversion implementation to have a correct mapping.
  • This trait is used to convert a byte slice to a primitive type. All PointFieldMsg types are supported.
  • Getter trait for the datatype of a field value.
  • Trait to enable point conversions on the fly.