robonix.service.pcld2lscan
service
v0.1.0
PointCloud2 to LaserScan conversion service.
service-pcld2lscan-rbnx
Atlas-managed sensor_msgs/PointCloud2 to sensor_msgs/LaserScan preprocessing
service. It lets mapping, localization, navigation, and safety consumers share
one canonical scan without making Mapping depend on Navigation.
Processing pipeline
lidar3d PointCloud2
-> optional motion deskew
-> latest-first queue and input-rate limit
-> one-pass TF + height/range + robot self-filter + angular projection
-> in-process isolated-return filter
-> LaserScan
The projection implementation deliberately fuses TF application, robot self-filtering, and projection into one C++ traversal. The upstream ROS node normally transforms/copies the complete cloud and then traverses it again. The service also defaults the input queue to one: old obstacle observations are dropped instead of being processed after newer clouds have arrived.
Example
- name: pcld2lscan
path: /home/jetson/cxk/service-pcld2lscan-rbnx
config:
scan_frame: base_link
scan_rate_hz: 6.0
queue_size: 1
max_scan_age_s: 0.5
transform_tolerance_s: 0.05
min_height_m: 0.05
max_height_m: 1.0
range_min_m: 0.03
range_max_m: 20.0
angle_increment_rad: 0.0087
# Circumscribed radius of Lite3's 0.305 x 0.185 m rectangular footprint.
self_filter_mode: circle
self_filter_footprint:
- [0.305, 0.185]
- [0.305, -0.185]
- [-0.305, -0.185]
- [-0.305, 0.185]
self_filter_margin_m: 0.0
speckle_filter_enabled: true
speckle_window_bins: 2
speckle_max_range_delta_m: 0.20
speckle_min_neighbors: 2
# Enable only if the cloud contains usable per-point timing and odom TF is
# available across the complete cloud interval.
deskewing: false
deskew_fixed_frame: odom
deskew_wait_for_transform_s: 0.2
scan_rate_hz is a maximum processing/output rate, not fabricated data rate.
Frames above this rate are discarded before the expensive TF and projection
work. max_scan_age_s: 0 disables the age guard; physical navigation should
set a finite limit. scan_time in the resulting message is derived from
scan_rate_hz.
Self-filter modes are:
none: preserve the legacy behavior.circle: compute the circumscribed radius fromself_filter_footprint, or use an explicitself_filter_radius_m, then add the configured margin.rectangle: remove returns inside the axis-aligned bounds ofself_filter_footprintplus the margin. Coordinates are evaluated inscan_frame, so this should normally be the robot base frame.
The speckle filter matches Navigation's range-neighborhood policy but runs in
the projector process, avoiding a second LaserScan serialization and Python
node. It removes a finite beam when fewer than speckle_min_neighbors nearby
angular bins have a range within speckle_max_range_delta_m.
The projector writes a throttled diagnostic line to
rbnx-build/data/pcld2lscan.log, including cloud age, processing time, point
counts, self-filtered returns, removed speckles, and dropped-frame counters.
Build and test
bash scripts/build.sh
python3 -m unittest discover -s tests -v