Some simple packages for common pointcloud needs
Components are also available as standalone executables of the same name.
hector_pointcloud_processing:
| Component | Class | Description |
|---|---|---|
pointcloud_accumulator |
hector_pointcloud_processing::PointcloudAccumulatorNode |
Voxel-filtered accumulation of multiple clouds into one frame |
pointcloud_decimator |
hector_pointcloud_processing::PointcloudDecimator |
Reduces point count and republishes via point_cloud_transport |
distance_adaptive_pointcloud_decimator |
hector_pointcloud_processing::DistanceAdaptivePointcloudDecimator |
Random decimation with a keep probability varying by distance |
voxel_filter |
hector_pointcloud_processing::VoxelFilter |
Uniform voxel grid downsampling |
distance_adaptive_voxel_filter |
hector_pointcloud_processing::DistanceAdaptiveVoxelFilter |
Voxel grid downsampling with voxel size growing by distance |
pointcloud_relay |
hector_pointcloud_processing::PointcloudRelay |
Point cloud relay, republishes via point_cloud_transport |
hector_pointcloud_io:
| Component | Class | Description |
|---|---|---|
pointcloud_saver |
hector_pointcloud_io::PointcloudSaver |
Saves a cloud from a topic to file on service call |
load_pointcloud |
— | Publishes a cloud loaded from a file |
save_pointcloud |
— | Saves the first cloud received on a topic to file |
Contains nodes for accumulation and decimating of point clouds.
All pointcloud inputs and outputs default to SensorDataQoS (best effort, keep last 5). The one
exception is the accumulator's accumulated_pointcloud, which stays reliable and transient local so
late joiners still receive the accumulated map.
History, depth and reliability — plus durability on the accumulator — can be overridden per topic
without code changes via the standard qos_overrides parameters, either at launch or through a
params file:
ros2 run hector_pointcloud_processing pointcloud_decimator --ros-args \
-p qos_overrides./pointcloud_decimated.publisher.reliability:=reliable \
-p qos_overrides./pointcloud.subscription.depth:=10The parameter name follows qos_overrides.<resolved topic>.<publisher|subscription>.<policy>. Nodes
that only subscribe once their output is subscribed to declare the subscription overrides when that
subscription is first created; overrides supplied at startup are applied at that point.
Voxel-filtered pointcloud accumulator. You can choose if it should store a running average, the highest z or the point closest to the center for each voxel.
Example:
ros2 launch hector_pointcloud_accumulator pointcloud_accumulator.launch.yaml resolution:=0.025 frame:=map publish_rate:=0.2 aggregation_mode:=average topics:=["pointcloud1","pointcloud2"] output_topic:=accumulated_pointcloud| Topic | Type | Description |
|---|---|---|
pointcloud |
sensor_msgs/msg/PointCloud2 |
Default input topic for pointclouds if parameter topics is not used. |
| Topic | Type | Description |
|---|---|---|
accumulated_pointcloud |
sensor_msgs/msg/PointCloud2 |
Output topic for accumulated pointcloud |
| Service | Type | Description |
|---|---|---|
enable_pointcloud |
std_srvs/srv/SetBool |
Turn pointcloud processing on or off |
reset_pointcloud |
std_srvs/srv/Trigger |
Clear all accumulated data |
| Parameter | Type | Default | Description |
|---|---|---|---|
resolution |
double |
0.025 |
Resolution of the voxel filter |
frame |
string |
"map" |
The frame into which all pointclouds are accumulated |
publish_rate |
double |
0.2 |
Frequency at which the node publishes on /cloud_out |
aggregation_mode |
string |
"average" |
Controls how points within the same voxel are integrated. Allowed values are: "average", "highest_z", "closest_to_center" |
topics |
string_array |
["pointcloud"] |
The topics names of all topics that should be accumulated together |
output_topic |
string |
"accumulated_pointcloud" |
The topic name of the accumulated pointcloud |
This node reduces the point count of a pointcloud and publishes them in a compressed format via point_cloud_transport.
The desired ammount of points can be specified as either a fraction of the original point count or as a total number of points.
There are currently two methods to decimate a pointcloud:
random picks points randomly from the pointcloud.
This method should prevent noticeable patterns in the pointcloud, but will likely not match the desired point count/fraction precisely.
count picks points equally from the whole range of the pointcloud. This method is the more performant and will match the desired point count/fraction.
| Topic | Type | Description |
|---|---|---|
pointcloud |
sensor_msgs/msg/PointCloud2 |
Input topic for pointclouds |
| Topic | Type | Description |
|---|---|---|
pointcloud_decimated |
point_cloud_transport |
Output topic for decimated pointclouds |
None
None
| Parameter | Type | Default | Description |
|---|---|---|---|
elimination_method |
std::string |
"count" |
How the pointcloud is decimated (random/count) |
elimination_quantifier |
std::string |
"fraction" |
How the amount of points to be kept is quantified (fraction/count) |
point_fraction |
double |
0.1 |
Fraction of points to keep (used if elimination_quantifier = "fraction") |
point_count |
int |
1000 |
Number of points to keep (used if elimination_quantifier = "count") |
Like pointcloud_decimator's random method, but the keep probability varies with each point's distance from the origin, so near points can be kept densely while distant points are thinned.
The schedule is given as two equal-length lists: distances (ascending control-point ranges, m) and percentages (keep fraction in [0, 1]), paired by index.
Only distances has to be ascending; percentages may rise as well as fall, so the schedule can also be inverted to keep distant points with a higher probability than close ones.
For a point at range r the keep probability is the linear interpolation between the surrounding control points; outside the first/last distance it is clamped to the first/last percentage (no extrapolation).
Each point is then kept by an independent random draw, so the output size matches the requested fractions only in expectation.
Kept points are copied verbatim, so all fields are preserved.
With target_frame set, the distance is evaluated in that frame (via tf), e.g. the robot body frame when the cloud is published in a sensor or odom frame. The published points stay in the input frame; only the probability lookup changes.
ros2 launch hector_pointcloud_processing distance_adaptive_pointcloud_decimator.launch.yaml distances:="[2.0, 10.0, 30.0]" percentages:="[1.0, 0.5, 0.1]"| Topic | Type | Description |
|---|---|---|
pointcloud |
sensor_msgs/msg/PointCloud2 |
Input topic for pointclouds |
| Topic | Type | Description |
|---|---|---|
pointcloud_decimated |
point_cloud_transport |
Output topic for decimated pointclouds |
| Parameter | Type | Default | Description |
|---|---|---|---|
distances |
std::vector<double> |
[2.0, 10.0, 30.0] |
Ascending control-point ranges (m); paired by index with percentages |
percentages |
std::vector<double> |
[1.0, 0.5, 0.1] |
Keep fraction in [0, 1] per range; paired by index with distances |
target_frame |
std::string |
"" |
Frame the point position is expressed in for the distance; empty uses raw xyz |
tf_prefix |
std::string |
"" |
Prefix prepended to the frame id before publishing |
All parameters are read-only and are set at startup. Invalid values, including two lists of different length, make the node fail to start.
Executables to load and publish a pointcloud from a file, and to save a pointcloud from a given topic to a file.
Includes the service server node pointcloud_saver that can save pointclouds from a topic on service call.
Supported formats: ifs, pcd, ply, vtk
Continuously publishes the selected pointcloud on the selected topic.
Example:
ros2 run hector_pointcloud_io load_pointcloud /pointcloud_topic path_to_file.pcdStores the first pointcloud received on the selected topic.
Example:
ros2 run hector_pointcloud_io save_pointcloud /pointcloud_topic path_to_file.ply| Topic | Type | Description |
|---|---|---|
<topic> |
sensor_msgs/msg/PointCloud2 |
The topic from where pointclouds are saved |
| Service | Type | Description |
|---|---|---|
~/save_pointcloud |
std_srvs/srv/Trigger |
Saves the first pointcloud received on <topic> within <timeout_ms> milliseconds after the service call |
| Parameter | Type | Description |
|---|---|---|
topic |
string |
Topic to listen for pointclouds on |
ouput_folder |
string |
Folder to save pointclouds to |
output_format |
string |
Output format of pointclouds. Supported: pcd, ifs, ply, vtk |
output_filename_prefix |
string |
Prefix for output filenames. Name will be <prefix>.<timestamp>.<output_format> |
timeout_ms |
int |
Timeout for waiting for pointclouds in ms. |
Example:
ros2 run hector_pointcloud_io pointcloud_saver --ros-args -p topic:=/pointcloud_topic -p output_folder:=/path/to/save/folder -p output_format:=vtk -p output_filename_prefix:=my-pointcloud-prefixAll these parameters can be reconfigured.
When the service is called using the std_srvs::srv::Trigger, it will wait for a message on the given topic and save it to the given folder with the filename <prefix>.<timestamp>.<output_format>.
Contains the voxel_filter and distance_adaptive_voxel_filter nodes.
Uniform voxel grid filter. A fixed-size voxel grid is overlaid on the cloud and the first input point falling into each voxel is kept and copied verbatim, so all fields are preserved. Points farther than max_distance from the cloud origin (the sensor/robot) are dropped first. The output is published in a compressed format via point_cloud_transport.
ros2 launch hector_pointcloud_processing voxel_filter.launch.yaml voxel_size:=0.1 max_distance:=30.0| Topic | Type | Description |
|---|---|---|
pointcloud |
sensor_msgs/msg/PointCloud2 |
Input topic for pointclouds |
| Topic | Type | Description |
|---|---|---|
pointcloud_filtered |
point_cloud_transport |
Output topic for filtered pointclouds |
| Parameter | Type | Default | Description |
|---|---|---|---|
voxel_size |
double |
0.1 |
Edge length (m) of each voxel |
max_distance |
double |
30.0 |
Points farther than this (m) from the cloud origin are dropped |
tf_prefix |
std::string |
"" |
Prefix prepended to the frame id before publishing |
keep_fields |
std::vector<std::string> |
[] |
Whitelist of fields to copy to the output; empty keeps all fields |
Like voxel_filter, but the voxel size grows with distance so points near the robot are filtered finely and distant points coarsely. The schedule is given as distance bands via two equal-length lists: band_distances (ascending upper bounds, m) and band_voxel_sizes (edge length, m), paired by index. A point at range r uses the voxel size of the first band whose distance is >= r; points farther than the last band's distance are dropped. As above, the first point per voxel is kept verbatim.
With target_frame set, the distance and binning are evaluated in that frame (via tf), e.g. the robot body frame when the cloud is published in a sensor or odom frame. The published points stay in the input frame; only the bin assignment changes.
ros2 launch hector_pointcloud_processing distance_adaptive_voxel_filter.launch.yaml band_distances:="[5.0, 15.0, 30.0]" band_voxel_sizes:="[0.05, 0.15, 0.4]"| Topic | Type | Description |
|---|---|---|
pointcloud |
sensor_msgs/msg/PointCloud2 |
Input topic for pointclouds |
| Topic | Type | Description |
|---|---|---|
pointcloud_filtered |
point_cloud_transport |
Output topic for filtered pointclouds |
| Parameter | Type | Default | Description |
|---|---|---|---|
band_distances |
std::vector<double> |
[5.0, 15.0, 30.0] |
Ascending upper distance bounds (m); paired by index with sizes |
band_voxel_sizes |
std::vector<double> |
[0.05, 0.15, 0.4] |
Voxel edge length (m) per band; paired by index with distances |
target_frame |
std::string |
"" |
Frame the point position is expressed in for binning; empty uses raw xyz |
tf_prefix |
std::string |
"" |
Prefix prepended to the frame id before publishing |
keep_fields |
std::vector<std::string> |
[] |
Whitelist of fields to copy to the output; empty keeps all fields |
Relays a cloud from input to output, republishing it through point_cloud_transport. The subscription takes a ConstSharedPtr, so in a component container with intra-process comms enabled the cloud is handed over without a copy. With tf_prefix empty the same shared message is forwarded untouched; setting tf_prefix prepends a tf prefix (prefix/frame) to the header frame id, which requires an owned copy to modify. The subscriber is only created while the output has subscribers.
ros2 launch hector_pointcloud_processing pointcloud_relay.launch.yaml tf_prefix:=robot1| Topic | Type | Description |
|---|---|---|
pointcloud |
sensor_msgs/msg/PointCloud2 |
Input topic for pointclouds |
| Topic | Type | Description |
|---|---|---|
pointcloud_relayed |
point_cloud_transport |
Output topic for relayed pointclouds |
| Parameter | Type | Default | Description |
|---|---|---|---|
tf_prefix |
std::string |
"" |
Prefix prepended to the header frame id before publishing; empty relays it unchanged |