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.
try_from_vec
requiresderive
feature (enabled by default)try_into_vec
requiresderive
feature (enabled by 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.
try_into_par_iter
requiresrayon
featuretry_from_par_iter
requiresrayon
+derive
feature
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 thePointConvertible
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§
- Dimensions of the point cloud as width and height.
- Creating a
CloudDimensions
type with the builder pattern to avoid invalid states when using 1-row point clouds. - The intermediate point cloud type for ROS integrations.
- Creating a
PointCloud2Msg
with the builder pattern to avoid invalid states. - Single data representation for a point.
- Byte buffer alias for endian-aware point data reading and writing.
- Internal point representation. It is used to store the point data entries.
Enums§
- Density flag for the message. Writing sparse point clouds is not supported.
- Endianess encoding hint for the message.
- Datatypes from the
PointFieldMsg
. - All errors that can occur while converting to or from the message type.
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.
- PointConvertible
derive
Trait to enable point conversions on the fly.