Point-LIO: mirror FAST-LIO cleanups (no-YAML config, memory2 Recorder, frame scheme)#2559
Conversation
…lio_native flake/cmake consuming dimos-module-fastlio2 pointlio branch + Estimator/parameters sources)
…y path) The fast-lio input was pinned to file:///Users/jeffhykin/... which only exists on the Mac. Repoint to the dimensionalOS/dimos-module-fastlio2 pointlio branch on github so the flake builds on Linux. Same locked rev.
Rename the mirrored fastlio_blueprints.py to pointlio_blueprints.py and wire it to PointLio (was incorrectly using FastLio2). Adds mid360-pointlio and mid360-pointlio-voxels to the blueprint registry.
…race-fix flake relock Adds the offline replay/inspection tooling for the pointlio_native module: pcap_replay.hpp (streams a Livox pcap into the SDK callbacks), deterministic_clock + dual-thread replay options, the ruwik2_pt3 replay harness, the pcap_to_db tool (append pointlio_odometry into an existing memory db at ~30Hz, streaming), validated Point-LIO mid360.yaml, and the live smoke-test helper. flake.lock relocks onto the fastlio2 pointlio rev carrying the mtx_buffer race fix; main.cpp comment corrected to attribute the divergence fix to that lock (not the publish-rate gating).
Switches the flake's fast-lio input from path:/home/dimos/repos/dimos-module-fastlio2 to github:dimensionalOS/dimos-module-fastlio2/pointlio so the module builds on any machine without a local fastlio2 clone. Relocked onto rev 7e5d88f (the mtx_buffer race fix); verified pointlio_native builds from the github source.
…hful config OUTPUT-model replay of the Jun-7 hand-shake bag diverged to ~25km (vs hku-mars master's bounded ~5m). Two issues, both fixed here (paired with the dimos-module-fastlio2 pointlio-branch curvature/deque fixes): - main.cpp: heap-use-after-free thread race in bag-frame replay. The feeder thread drives run_main_iter via step() after each feed, but the main thread also drove run_main_iter, so both raced on the shared PCL measurement cloud (ASan: free in sync_packages, read-after-free in ImuProcess::Process). Force serial_replay in bag-frame mode so only the feeder thread touches the EKF. - CMakeLists.txt + flake.nix: the iVox map backend needs glog; add the find_package/link and the nix buildInput. Drop ikd_Tree.cpp from sources (iVox replaces ikd-Tree). - config/mid360.yaml (+ cpp/config duplicate): set satu_acc 5.5->3.0 (master value — accel >=3g saturated, residual zeroed, keeps velocity bounded) and add ivox_grid_resolution=2.0 / ivox_nearby_type=6 (NEARBY6) matching the master config that produced the 5.028m baseline. - replay_ruwik2_pt3.py: make MAX_WALL_SEC env-overridable. After the fix the OUTPUT model stays bounded (peak |pos| 3.898m) on the full bag; ASan reports zero memory errors.
…onsumes the iVox fix The iVox-port + divergence fixes live in dimos-module-fastlio2 @ pointlio commit 02d5066, which is committed locally but NOT pushed (per the task's no-push constraint). The flake's fast-lio input was pointing at github:dimensionalOS/dimos-module-fastlio2/pointlio, whose lock pinned the pre-fix rev 7e5d88f. Since CMakeLists.txt already drops ikd_Tree.cpp (iVox replaces ikd-Tree), building against that stale github source fails with KD_TREE undefined-reference linker errors. Repoint fast-lio at path:/home/dimos/repos/dimos-module-fastlio2 and bump the lock so `nix build .#pointlio_native` consumes the local 02d5066 source. Verified: build exits 0, no KD_TREE errors, 661176-byte binary. NOTE: this bakes a machine-specific absolute path into the lock. Once Repo A's pointlio branch is pushed, switch this input back to github:dimensionalOS/dimos-module-fastlio2/pointlio and bump the lock to 02d5066 for portability.
Now that dimos-module-fastlio2 pointlio (02d5066, the iVox port + divergence fix) is on origin, switch the fast-lio flake input from the local path: pin back to github:dimensionalOS/dimos-module-fastlio2/pointlio and bump the lock to rev 02d5066. This makes jeff/feat/pointlio_native self-contained and portable (no machine-specific path in the lock). Verified: nix build .#pointlio_native exits 0, no KD_TREE errors.
… add --force Extends pcap_to_db.py to populate both pointlio_odometry and pointlio_lidar streams (start-aligned onto the db's earliest ts) so pointlio can be compared against fastlio in one recording. --force overwrites existing pointlio streams. Untracks the machine-specific fastlio_test/setup_network symlinks (enhance overlay) and drops the empty tools/__init__.py.
…o tools - Consolidate config/ to one default.yaml (the tuned Mid-360/iVox config); remove the unused upstream sensor presets (avia, horizon, marsim, ouster64, velodyne, mid360). Module now defaults to default.yaml. - Remove tools/replay_ruwik2_pt3.py (pcap_to_db.py covers offline replay) and tools/demo_live_test.py.
…egistry - --db is now optional: with no existing db, build one from scratch (defaults to <pcap>.db next to the pcap); with an existing db, append + time-align as before. - Rename the internal recorder Rec/RecConfig -> _Rec/_RecConfig so the blueprint-registry generator skips it (a tool helper isn't a public module); fixes test_all_blueprints_is_current. - Docstring: document from-scratch generation using the ruwik2_part3 LFS sample.
… rm cpp/README + config copies; concise comments
- config: consolidate to a single config/default.yaml; drop the ROS-only
lid_topic/imu_topic + odom frame-id/publish/pcd_save keys (parsed-but-unused
by the native binary), and note which params are Mid-360-specific.
- delete cpp/config/{mid360.json,mid360.yaml} (duplicate) and cpp/README.md.
- tighten verbose comments in main.cpp, pcap_replay.hpp, timing.hpp
(comments only; binary rebuilds clean).
… db name in docstring
120s clip (elapsed 55-175s) of the ruwik velocity-spike recording — the data
that diverges through FAST-LIO at the 0.5m pre-KF voxel and is bounded under
Point-LIO. Use via get_data('ruwik2_part3'); pcap_to_db --pcap <it> builds the
db from scratch.
….0 clock bug - module.py docstring: Point-LIO (not FAST-LIO2) + correct import path. - pcap_to_db.py: use 'is not None' (not 'or') for the ts fallback so a real sensor ts of 0.0 isn't replaced by wall time (which would misclassify the stream clock in _resolve_offset).
… NIC Reads a Livox Mid-360 pcap into RAM, rewrites packet timestamps to current-time, and replays point/IMU/status onto a virtual network interface at a configurable rate + delay. Synthesizes the Livox SDK2 control protocol (discovery + GetInternalInfo/FwType ACKs, CRC16/CRC32) so an unmodified consumer (pointlio) handshakes with it as a real sensor. Builds via nix (rustPlatform.buildRustPackage, cargoLock from Cargo.lock).
…bal_map - Cloud now published in the sensor frame (mid360_link): use fastlio2 get_body_cloud() (undistorted scan, no world registration) instead of inverse-transforming the world cloud. No transform in publish_lidar. - Split frames: frame_id (mid360_link) on both the cloud + odometry headers; odom_parent_frame_id (odom) -> odom_frame_id (base_link) for the TF publish. - Remove global_map / voxel_map.hpp entirely (deleted file, config, blueprint + pcap_to_db references). - Bump fast-lio pin to fcbd1c2 (adds get_body_cloud).
get_data() at module level triggered a git-LFS download during blueprint validation (test_blueprint_is_valid), which CI blocks via git-lfs-guard — failing the whole test matrix. Default pcap to empty; resolve the capture path at run time instead.
Port the minimal pcap-replay subsystem from jeff/feat/go2_record into the clean branch so FAST-LIO can run offline from a Mid-360 pcap, matching the Point-LIO pcap_to_db workflow: - cpp: pcap_replay.hpp + timing.hpp (header-only), main.cpp refactored so the main loop runs from either the live SDK or a pcap feeder thread, with an optional deterministic sensor-clock mode. Keeps the clean branch's velocity-cap (guarded set_max_velocity_norm_ms) and flake (velocity-cap fast-lio); does not pull go2_record's tcpdump record path. - module.py: replay_pcap / replay_skip_until_ns / first_packet_marker / deterministic_clock config fields; skip network validation in replay mode. - tools/pcap_to_db.py: replay a pcap through FastLio2 (real-time, non- deterministic) and append fastlio_odometry + fastlio_lidar into an existing memory2 db, time-aligned onto its clock. --force overwrites.
…FastLio2Recorder - livox/pcap_recorder.py: standalone tcpdump pcap capture (LivoxPcapRecorder), decoupled from FAST-LIO. The lidar/SLAM module no longer owns packet capture. - fastlio2/recorder.py: FastLio2Recorder records fastlio_odometry + fastlio_lidar and rewrites ONLY those streams' timestamps onto the db clock (promoted from pcap_to_db's inline recorder; fixes the ts==0.0 falsy-fallback bug). - pcap_to_db.py now imports FastLio2Recorder instead of an inline copy.
FastLio2 no longer produces a global voxel map — odometry + registered lidar only. Removed global_map Out, map config, and the mapping.GlobalPointcloud spec. Updated all consumers (fastlio_blueprints, alfred_nav, g1_onboard, g1_nav_onboard, mobile, pcap_to_db) to drop map args + the global_map remap. Nav blueprints lose their fastlio map; full nav wants a separate mapper wired in (follow-up).
…-only)" This reverts commit 0260030.
# Conflicts: # dimos/hardware/sensors/lidar/fastlio2/tools/pcap_to_db.py # dimos/memory2/module.py
…tive # Conflicts: # dimos/hardware/sensors/lidar/pointlio/module.py
Convert the memory2 Recorder from thread/disposable rx subscriptions to manual async callbacks via process_observable, and let pose_setter_for methods be async (awaited in _resolve_pose). Update the fastlio and go2 recorders accordingly.
Raise TypeError at decoration time if a non-async function is decorated, and always await the setter in _resolve_pose.
Switch recorders to async: the base Recorder now uses process_observable async callbacks and pose_setter_for requires async setters. Converted the pointlio recorder's pose setters to async to match.
❌ 1 Tests Failed:
View the full list of 1 ❄️ flaky test(s)
To view more test analytics, go to the Test Analytics Dashboard |
Greptile SummaryThis PR mirrors the FAST-LIO cleanups from #2498 onto Point-LIO, keeping the two native LiDAR modules consistent in their configuration pattern, recorder structure, and TF frame scheme.
Confidence Score: 5/5Safe to merge — changes are mechanical cleanups mirroring an already-landed FAST-LIO refactor with no novel logic. The diff removes the YAML-loading path and replaces it with typed Pydantic fields whose CLI serialisation (including list[float] comma-joining for gravity/extrinsic vectors) is handled by the existing NativeModuleConfig.to_cli_args. The Recorder migration is structurally identical to the FAST-LIO change already in main, and the memory2/module.py async promotion is guarded by a runtime iscoroutinefunction check. The macOS unicast fix is a symmetrical change across Rust and C++ headers. No new algorithmic logic is introduced. No files require special attention. The two pre-existing style observations in module.py and main.cpp (extrinsic vector validation and parse_doubles error surfacing) were noted in the prior review and remain unchanged. Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
subgraph Hardware["Hardware / Replay"]
MID360["Mid-360 LiDAR\n(real or VirtualMid360 PCAP)"]
end
subgraph PointLIO["PointLio NativeModule (C++ subprocess)"]
SDK["Livox SDK2 callbacks"]
ALGO["Point-LIO IESKF estimator"]
CLI["CLI args from PointLioConfig\n(no YAML)"]
CLI --> ALGO
SDK --> ALGO
ALGO --> |"odometry (nav_msgs.Odometry)"|OUT_ODOM["Out: odometry"]
ALGO --> |"lidar (sensor_msgs.PointCloud2)\nstamped with sensor_frame_id"|OUT_LIDAR["Out: lidar"]
ALGO --> |"TF: frame_id → child_frame_id"|TF["TF bus"]
end
subgraph Recorder["PointlioRecorder (memory2 Recorder)"]
IN_ODOM["In: pointlio_odometry\n(remapped ← odometry)"]
IN_LIDAR["In: pointlio_lidar\n(remapped ← lidar)"]
POSE_SETTER["@pose_setter_for async\n_odom_pose → caches _last_odom_pose\n_lidar_pose → returns cached pose"]
DB["SQLite db\npointlio_odometry / pointlio_lidar tables\n(with pose anchor per row)"]
IN_ODOM --> POSE_SETTER
IN_LIDAR --> POSE_SETTER
POSE_SETTER --> DB
end
MID360 -->|"UDP point/IMU\n(multicast Linux / unicast macOS)"| SDK
OUT_ODOM -->|"autoconnect"| IN_ODOM
OUT_LIDAR -->|"autoconnect"| IN_LIDAR
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
subgraph Hardware["Hardware / Replay"]
MID360["Mid-360 LiDAR\n(real or VirtualMid360 PCAP)"]
end
subgraph PointLIO["PointLio NativeModule (C++ subprocess)"]
SDK["Livox SDK2 callbacks"]
ALGO["Point-LIO IESKF estimator"]
CLI["CLI args from PointLioConfig\n(no YAML)"]
CLI --> ALGO
SDK --> ALGO
ALGO --> |"odometry (nav_msgs.Odometry)"|OUT_ODOM["Out: odometry"]
ALGO --> |"lidar (sensor_msgs.PointCloud2)\nstamped with sensor_frame_id"|OUT_LIDAR["Out: lidar"]
ALGO --> |"TF: frame_id → child_frame_id"|TF["TF bus"]
end
subgraph Recorder["PointlioRecorder (memory2 Recorder)"]
IN_ODOM["In: pointlio_odometry\n(remapped ← odometry)"]
IN_LIDAR["In: pointlio_lidar\n(remapped ← lidar)"]
POSE_SETTER["@pose_setter_for async\n_odom_pose → caches _last_odom_pose\n_lidar_pose → returns cached pose"]
DB["SQLite db\npointlio_odometry / pointlio_lidar tables\n(with pose anchor per row)"]
IN_ODOM --> POSE_SETTER
IN_LIDAR --> POSE_SETTER
POSE_SETTER --> DB
end
MID360 -->|"UDP point/IMU\n(multicast Linux / unicast macOS)"| SDK
OUT_ODOM -->|"autoconnect"| IN_ODOM
OUT_LIDAR -->|"autoconnect"| IN_LIDAR
Reviews (5): Last reviewed commit: "-" | Re-trigger Greptile |
Mirrors onto the Point-LIO native module the same cleanups already landed for FAST-LIO in #2498, so the two modules stay consistent.