Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
142 commits
Select commit Hold shift + click to select a range
a7a9be9
pgo: publish pose graph (nodes + odom/loop edges) over LCM + top-down…
jeff-hykin May 12, 2026
ba0e17e
pgo: lift pgo_graph_{nodes,edges} via agentic_debug visual override
jeff-hykin May 12, 2026
1801759
pgo: drop second rerun pane; revert to single 3D view
jeff-hykin May 12, 2026
82d7799
pgo: publish per-keyframe delta on every loop-closure event + slow test
jeff-hykin May 12, 2026
3c90a7f
pgo: scan-context-based loop-closure search
jeff-hykin May 12, 2026
3e33003
pgo: synthetic drift test — proves Scan Context catches loops positio…
jeff-hykin May 13, 2026
5f64131
pgo tests: replace _log() print wrappers with the dimos logger
jeff-hykin May 14, 2026
6880ab6
pgo: expand short variable names introduced on this branch
jeff-hykin May 14, 2026
c7fd631
pgo: KITTI-360 benchmark scaffolding (loader + groundtruth + runner)
jeff-hykin May 15, 2026
e45b228
merge main into jeff/feat/better_pgo
jeff-hykin May 15, 2026
1cf0045
make model_path optional
jeff-hykin May 15, 2026
c9a426a
add flowbase robot
jeff-hykin May 15, 2026
272833c
add alfred
jeff-hykin May 15, 2026
0863ad1
add blueprint
jeff-hykin May 15, 2026
9020415
naming
jeff-hykin May 15, 2026
6ea0251
name fix
jeff-hykin May 15, 2026
8e9c445
mypy
jeff-hykin May 15, 2026
277cd29
mypy
jeff-hykin May 15, 2026
f6010b0
-
jeff-hykin May 15, 2026
4f43322
-
jeff-hykin May 15, 2026
c0f6bb9
fixup vis_throttle
jeff-hykin May 16, 2026
45f57a5
-
jeff-hykin May 16, 2026
d1d484f
Merge branch 'jeff/feat/better_pgo' of github.com:dimensionalOS/dimos…
jeff-hykin May 16, 2026
726808e
add recording topics
jeff-hykin May 16, 2026
84bfe56
-
jeff-hykin May 16, 2026
36a7454
-
jeff-hykin May 16, 2026
fe3c43d
Merge branch 'jeff/clean/nav2' into jeff/clean/nav3
jeff-hykin May 16, 2026
8fe1f54
-
jeff-hykin May 16, 2026
d780dac
cleanup
jeff-hykin May 16, 2026
559a0c1
Merge branch 'jeff/clean/nav2' into jeff/clean/nav3
jeff-hykin May 16, 2026
4480966
Merge branch 'main' into jeff/feat/better_pgo
jeff-hykin May 16, 2026
5b828e2
fix: mypy errors in pgo benchmark and demo viz
jeff-hykin May 16, 2026
59b751c
fix: drop dead positive-yaw wrap in yaw_from_shift
jeff-hykin May 16, 2026
4329aac
fix: drop empty pgo benchmark __init__.py
jeff-hykin May 16, 2026
2d77b76
Merge branch 'main' into jeff/feat/flowbase
jeff-hykin May 16, 2026
ac17686
-
jeff-hykin May 16, 2026
1d76b35
Merge branch 'jeff/feat/flowbase' of github.com:dimensionalOS/dimos i…
jeff-hykin May 16, 2026
98a79a5
-
jeff-hykin May 16, 2026
b0fb1e1
Merge branch 'jeff/feat/flowbase' into jeff/clean/nav3
jeff-hykin May 16, 2026
a88f712
Merge branch 'main' into jeff/feat/better_pgo
jeff-hykin May 16, 2026
67f2345
transform frames fix
jeff-hykin May 16, 2026
3eba77b
add missing encoding
jeff-hykin May 16, 2026
b28cd00
important PGO optimizations
jeff-hykin May 16, 2026
a0fafbb
fix: annotate pgo smoke_test lcm callback to satisfy mypy
jeff-hykin May 16, 2026
4479d8d
fix bug in cmu stack
jeff-hykin May 16, 2026
4c412e7
transform frame fixups
jeff-hykin May 16, 2026
0ffa39a
add additional check
jeff-hykin May 16, 2026
4a27dd8
Merge branch 'jeff/feat/better_pgo' of github.com:dimensionalOS/dimos…
jeff-hykin May 16, 2026
6901d71
fix: ignore sklearn import for mypy (no stubs available)
jeff-hykin May 16, 2026
037f0c7
-
jeff-hykin May 16, 2026
ca977fa
-
jeff-hykin May 16, 2026
1c34ab9
nits + greptile fixes on PGO benchmark + reverse-loop test
jeff-hykin May 16, 2026
80adfb6
Merge branch 'main' into jeff/feat/better_pgo
jeff-hykin May 16, 2026
4a0de4d
nits: rename msg/msg_cls/msg_type → message/message_class/message_type
jeff-hykin May 16, 2026
6de903a
nits: lift scipy import to module top in run_kitti360_benchmark
jeff-hykin May 16, 2026
fbd00dc
nits: C++ rename mod→native_module, cp→cloud_with_pose, fix tresh typo
jeff-hykin May 16, 2026
e9f8356
generic-ize PGO output topic names
jeff-hykin May 16, 2026
5cc294b
-
jeff-hykin May 16, 2026
5fe55c8
WIP: move KITTI-360 benchmark out of pgo, refactor into Modules + Blu…
jeff-hykin May 16, 2026
44f41f9
fix: annotate runner.get_results local to satisfy mypy
jeff-hykin May 16, 2026
3015232
flatten pgo/benchmark/ — drop the dir, files live at pgo/
jeff-hykin May 16, 2026
c1308e2
fix: regenerate all_blueprints.py for new pose_graph_kitti360 modules
jeff-hykin May 16, 2026
ff2ccb1
rename
jeff-hykin May 16, 2026
8afcf97
nits: expand fid/src/dst/tp/fp/fn/p/r in loop_groundtruth.py
jeff-hykin May 16, 2026
91cbdca
benchmark_kitti_smoke_test: rewrite on top of Modules + Blueprint
jeff-hykin May 16, 2026
b07f9ac
test_pgo_synthetic_drift: rewrite via Modules + Blueprint
jeff-hykin May 16, 2026
7f12682
cleaning
jeff-hykin May 16, 2026
73669bc
benchmark_place_recognition: humanise top-of-file docstring
jeff-hykin May 16, 2026
999a961
-
jeff-hykin May 16, 2026
d1d3356
fix: simulation arg (#2103)
paul-nechifor May 16, 2026
1b045e9
fix: ignore sklearn import for mypy (no stubs available)
jeff-hykin May 16, 2026
caaad9e
fix: drop ASCII section markers from test_pgo_synthetic_drift
jeff-hykin May 16, 2026
5bcf724
test_pgo_loop_closure: rewrite via Modules + Blueprint
jeff-hykin May 16, 2026
410b7c9
fix: drop ASCII section markers from test_pgo_loop_closure
jeff-hykin May 16, 2026
2bdb6e1
test_pgo_rosbag + rosbag_fixtures: drop hardcoded LCM topics, use Mod…
jeff-hykin May 16, 2026
0a9b92d
remove empty configs + inline _message_to_dict + pydantic Field
jeff-hykin May 16, 2026
101fa7b
Merge origin/jeff/feat/better_pgo (PGO improvements + KITTI360 benchm…
jeff-hykin May 16, 2026
3ce2811
-
jeff-hykin May 16, 2026
2ae7f18
move HACK comment from PGO.start() to _STARTUP_SETTLE_SEC
jeff-hykin May 16, 2026
0227c1a
Merge remote-tracking branch 'origin/main' into jeff/clean/nav3
jeff-hykin May 16, 2026
17a0d67
Merge branch 'jeff/clean/nav0' of github.com:dimensionalOS/dimos into…
jeff-hykin May 16, 2026
f4512c2
-
jeff-hykin May 16, 2026
74e2c43
fix: cover playback-vs-PGO-startup race + section markers + regen all…
jeff-hykin May 16, 2026
936c33d
fix: drop duplicate `bool debug` in pgo/main.cpp + section markers in…
jeff-hykin May 16, 2026
f4a8e2a
fix: stop the host-side RPC client even when proxy.stop() raises
jeff-hykin May 16, 2026
bcc422d
test: mark PGO synthetic_drift + loop_closure as self_hosted
jeff-hykin May 16, 2026
3eb80b8
test: add skipif_no_nix and apply it to PGO C++-binary tests
jeff-hykin May 16, 2026
ed64f25
fix: query-level loop scoring + surface playback errors
jeff-hykin May 16, 2026
158f3eb
fix: RosbagScanOdomPlaybackModule surfaces playback errors
jeff-hykin May 16, 2026
6dccb2d
fix: SyntheticDriftPlaybackModule surfaces playback errors
jeff-hykin May 16, 2026
c01756d
fix: query-level FP in scoring (TP + FP now dimensionally consistent)
jeff-hykin May 16, 2026
8d3d768
Update dimos/navigation/nav_stack/main.py
jeff-hykin May 16, 2026
064af1e
fix: move _maybe_build to NativeModule.build() — fixes PGO startup race
jeff-hykin May 16, 2026
abc78e4
fix: address greptile review on nav_stack/main.py max_hz block
jeff-hykin May 16, 2026
6e236e7
feat: subprocess-ready handshake — native modules signal when they're…
jeff-hykin May 16, 2026
ae6b59a
feat: ready handshake for all C++ native modules
jeff-hykin May 16, 2026
e376930
Merge remote-tracking branch 'origin/main' into jeff/feat/better_pgo
jeff-hykin May 16, 2026
eff5254
test: rename benchmark_kitti_smoke_test.py -> demo_benchmark_kitti_sm…
jeff-hykin May 16, 2026
9e42096
fix: regenerate all_blueprints.py for TopicCounterModule
jeff-hykin May 16, 2026
9af58da
revert
jeff-hykin May 16, 2026
c7dc341
simplify
jeff-hykin May 16, 2026
d45adb2
-
jeff-hykin May 16, 2026
5d95b34
nav: add LoopClosure spec and use it in run_benchmark
jeff-hykin May 16, 2026
026adba
docs
jeff-hykin May 16, 2026
beba4dd
-
jeff-hykin May 16, 2026
e0fecd7
pgo: ready handshake — block start() until C++ binary is subscribed
jeff-hykin May 17, 2026
9128a70
Merge branch 'jeff/clean/nav0' into jeff/clean/nav3
jeff-hykin May 17, 2026
be7412b
Revert "pgo: ready handshake — block start() until C++ binary is subs…
jeff-hykin May 17, 2026
90e062a
make test reliable
jeff-hykin May 17, 2026
5e1e225
fixup start
jeff-hykin May 17, 2026
1584fe6
-
jeff-hykin May 17, 2026
a7af8ac
remove flakey test
jeff-hykin May 17, 2026
5697835
fixup on macos
jeff-hykin May 17, 2026
547205b
fixup LoopClosure spec
jeff-hykin May 17, 2026
ab133a0
-
jeff-hykin May 17, 2026
df16d46
fix timeout
jeff-hykin May 17, 2026
c138f66
Merge remote-tracking branch 'origin/main' into jeff/feat/better_pgo
jeff-hykin May 17, 2026
b9df4fa
Merge remote-tracking branch 'origin' into jeff/clean/nav3
jeff-hykin May 17, 2026
29dfafe
-
jeff-hykin May 17, 2026
cf6772e
Merge remote-tracking branch 'origin/main' into HEAD
jeff-hykin May 17, 2026
5769d48
Merge remote-tracking branch 'origin/main' into HEAD
jeff-hykin May 17, 2026
124c4e3
build native modules in the build step
jeff-hykin May 17, 2026
c152325
feat(msgs): GraphNodes3D Kaitai Struct schema
jeff-hykin May 17, 2026
04d73a3
feat(msgs): expose LineSegments3D public attrs + per-segment timestamps
jeff-hykin May 17, 2026
3564a77
feat(msgs): C++ LineSegments3D typed publisher header
jeff-hykin May 17, 2026
75b4e8d
feat(msgs): Graph3D pose-graph message type
jeff-hykin May 17, 2026
9ead549
refactor(far_planner): switch graph_nodes+graph_edges to graph: Out[G…
jeff-hykin May 17, 2026
6185f62
refactor(pgo): pose_graph_nodes+edges → pose_graph: Out[Graph3D]; dro…
jeff-hykin May 17, 2026
f97e1bd
chore(pgo): rename demo_benchmark_kitti_smoke → benchmark_kitti360_smoke
jeff-hykin May 17, 2026
be9f2ca
remove .ksy
jeff-hykin May 17, 2026
edb0584
test(pgo): restore filename test_pgo_loop_closure.py
jeff-hykin May 17, 2026
7311034
Merge remote-tracking branch 'origin/main' into jeff/feat/better_pgo
jeff-hykin May 17, 2026
bf3c570
Merge branch 'jeff/feat/better_pgo' of github.com:dimensionalOS/dimos…
jeff-hykin May 17, 2026
1c59f23
chore(blueprints): regenerate all_blueprints.py for smoke-test rename
jeff-hykin May 17, 2026
6c87dcc
misc
jeff-hykin May 17, 2026
e4dd71a
feat(msgs): GraphDelta3D + wire PGO's loop_closure_event to it
jeff-hykin May 17, 2026
446fa16
clean comments
jeff-hykin May 17, 2026
4c6fd58
Merge branch 'jeff/feat/better_pgo' into jeff/clean/nav3
jeff-hykin May 17, 2026
444b0ad
Merge branch 'jeff/feat/better_pgo' into jeff/clean/nav3
jeff-hykin May 17, 2026
5a0def2
fix nits: drop self-evident comments
jeff-hykin May 17, 2026
29564c6
fix(rerun): auto-dispatch via to_rerun_multi when message exposes it
jeff-hykin May 17, 2026
e0002e4
clean
jeff-hykin May 17, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ jobs:
run: ./bin/run-doc-codeblocks --ci --no-cache

tests:
timeout-minutes: 20
timeout-minutes: 25
strategy:
matrix:
pyver: ['3.10', '3.11', '3.12', '3.13', '3.14']
Expand Down
7 changes: 7 additions & 0 deletions dimos/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import hashlib
import os
import platform
import shutil
import tempfile
import threading

Expand Down Expand Up @@ -74,6 +75,10 @@ def _has_ros() -> bool:
return False


def _has_nix() -> bool:
return shutil.which("nix") is not None


def _is_macos() -> bool:
return platform.system() == "Darwin"

Expand All @@ -90,6 +95,7 @@ def pytest_configure(config):
config.addinivalue_line("markers", "skipif_no_openai: skip when OPENAI_API_KEY is not set")
config.addinivalue_line("markers", "skipif_no_alibaba: skip when ALIBABA_API_KEY is not set")
config.addinivalue_line("markers", "skipif_no_ros: skip when ROS dependencies are not present")
config.addinivalue_line("markers", "skipif_no_nix: skip when the `nix` binary is not on PATH")
config.addinivalue_line("markers", "skipif_macos_bug: skip known-buggy tests on macOS")
config.addinivalue_line("markers", "skipif_macos: skip tests not intended to run on macOS")

Expand Down Expand Up @@ -122,6 +128,7 @@ def pytest_collection_modifyitems(config, items):
"skipif_no_openai": (not os.getenv("OPENAI_API_KEY"), "OPENAI_API_KEY not set"),
"skipif_no_alibaba": (not os.getenv("ALIBABA_API_KEY"), "ALIBABA_API_KEY not set"),
"skipif_no_ros": (not _has_ros(), "ROS dependencies are not present"),
"skipif_no_nix": (not _has_nix(), "nix binary is not on PATH"),
"skipif_macos_bug": (_is_macos(), "Some tests are buggy on Mac OS"),
"skipif_macos": (_is_macos(), "Not intended to run on macOS"),
}
Expand Down
14 changes: 12 additions & 2 deletions dimos/core/module.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,12 +123,17 @@ class ModuleBase(Configurable, CompositeResource):
_main_gen: AsyncGenerator[None, None] | None = None
_tools: dict[str, Any]
_tools_lock: threading.Lock
# Set by ModuleCoordinator after every module's start() has returned.
# Producers that publish into shared streams should await this before
# emitting their first message — see ModuleBase.wait_for_system_ready.
_system_ready: threading.Event

def __init__(self, config_args: dict[str, Any]) -> None:
super().__init__(**config_args)
self._module_closed_lock = threading.Lock()
self._tools = {}
self._tools_lock = threading.Lock()
self._system_ready = threading.Event()
self._loop, self._loop_thread = get_loop()
try:
self.rpc = self.config.rpc_transport( # type: ignore[call-arg]
Expand Down Expand Up @@ -232,6 +237,7 @@ def __getstate__(self): # type: ignore[no-untyped-def]
state.pop("_main_gen", None)
state.pop("_tools", None)
state.pop("_tools_lock", None)
state.pop("_system_ready", None)
return state

def __setstate__(self, state) -> None: # type: ignore[no-untyped-def]
Expand All @@ -246,12 +252,16 @@ def __setstate__(self, state) -> None: # type: ignore[no-untyped-def]
self._main_gen = None
self._tools = {}
self._tools_lock = threading.Lock()
self._system_ready = threading.Event()

_tf_lock: threading.Lock = threading.Lock()

@property
def tf(self): # type: ignore[no-untyped-def]
if self._tf is None:
# self._tf = self.config.tf_transport()
self._tf = LCMTF()
with self._tf_lock:
if self._tf is None:
self._tf = LCMTF()
return self._tf
Comment on lines +257 to 265
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 _tf_lock is a class-level attribute shared by every ModuleBase instance

Because _tf_lock is declared directly in the class body, all instances share the exact same threading.Lock object. When dozens of modules initialize concurrently at startup, they all serialize on this single lock even though their _tf fields are entirely independent. Moving initialization to __init__ (e.g., self._tf_lock = threading.Lock()) gives each instance its own lock and eliminates the cross-instance contention.


@tf.setter
Expand Down
49 changes: 47 additions & 2 deletions dimos/core/native_module.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,11 @@ def _set_process_to_die_when_parent_dies() -> None:

logger = setup_logger()

# Native binaries emit this marker on stderr after their LCM subscribes are
# live. NativeModule.start() blocks until it sees it, ensuring upstream
# publishers don't race ahead and drop messages.
_READY_MARKER = "[DIMOS_NATIVE_READY]"


class LogFormat(enum.Enum):
TEXT = "text"
Expand All @@ -100,6 +105,11 @@ class NativeModuleConfig(ModuleConfig):
shutdown_timeout: float = DEFAULT_THREAD_JOIN_TIMEOUT
log_format: LogFormat = LogFormat.TEXT
auto_build: bool = False
# How long start() blocks waiting for the subprocess to emit
# "[DIMOS_NATIVE_READY]" on stderr. Default 0 = no wait (legacy binaries
# that don't emit the marker). Subclasses whose binaries emit the marker
# should bump this to ~10s. See NativeModule.start.
ready_timeout_sec: float = 0.0

# New version of Native Modules read json configs from stdin
# Enable this to read from stdin instead of cli args
Expand Down Expand Up @@ -163,6 +173,7 @@ class NativeModule(Module):
_watchdog: threading.Thread | None = None
_stopping: bool = False
_stop_lock: threading.Lock
_ready_event: threading.Event

@functools.cached_property
def _module_label(self) -> str:
Expand All @@ -172,13 +183,19 @@ def _module_label(self) -> str:
def __init__(self, **kwargs: Any) -> None:
super().__init__(**kwargs)
self._stop_lock = threading.Lock()
self._ready_event = threading.Event()

if self.config.cwd is not None and not Path(self.config.cwd).is_absolute():
base_dir = Path(inspect.getfile(type(self))).resolve().parent
self.config.cwd = str(base_dir / self.config.cwd)
if not Path(self.config.executable).is_absolute() and self.config.cwd is not None:
self.config.executable = str(Path(self.config.cwd) / self.config.executable)

@rpc
def build(self) -> None:
super().build()
self._maybe_build()

@rpc
def start(self) -> None:
super().start()
Expand All @@ -190,8 +207,6 @@ def start(self) -> None:
)
return

self._maybe_build()

topics = self._collect_topics()

cmd = [self.config.executable]
Expand Down Expand Up @@ -234,16 +249,43 @@ def start(self) -> None:
pid=self._process.pid,
)

self._ready_event.clear()
watchdog = threading.Thread(
target=self._watch_process,
daemon=True,
name=f"native-watchdog-{self._module_label}",
)
# Capture proc before launching the watchdog — if the subprocess dies,
# the watchdog calls stop() which nulls out self._process and this loop
# would crash otherwise.
proc = self._process
with self._stop_lock:
self._stopping = False
self._watchdog = watchdog
watchdog.start()

# Block until the subprocess emits [DIMOS_NATIVE_READY] on stderr, or
# the timeout fires. The watchdog's stderr reader sets _ready_event
# when it sees the marker. If the subprocess exits before that, drop
# out and let the watchdog handle the cleanup — start() always
# returns; failures surface later via the watchdog's stop() path.
if self.config.ready_timeout_sec > 0:
deadline = time.monotonic() + self.config.ready_timeout_sec
while not self._ready_event.is_set():
if proc.poll() is not None:
break
remaining = deadline - time.monotonic()
if remaining <= 0:
logger.warning(
"Native process did not emit [DIMOS_NATIVE_READY] within "
f"{self.config.ready_timeout_sec}s — proceeding anyway. "
"Upstream publishers may race the subprocess's LCM subscribes.",
module=self._module_label,
pid=proc.pid,
)
break
self._ready_event.wait(timeout=min(remaining, 0.5))

@rpc
def stop(self) -> None:
# Capture refs under lock, but signal/wait/join outside it to avoid
Expand Down Expand Up @@ -346,6 +388,9 @@ def _read_log_stream(
line = raw.decode("utf-8", errors="replace").rstrip()
if not line:
continue
if _READY_MARKER in line:
self._ready_event.set()
continue
if self.config.log_format == LogFormat.JSON:
try:
data = json.loads(line)
Expand Down
2 changes: 1 addition & 1 deletion dimos/core/test_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ def test_classmethods() -> None:
# Check that we have the expected RPC methods
assert "navigate_to" in class_rpcs, "navigate_to should be in rpcs"
assert "start" in class_rpcs, "start should be in rpcs"
assert len(class_rpcs) == 7
assert len(class_rpcs) == 8

# Check that the values are callable
assert callable(class_rpcs["navigate_to"]), "navigate_to should be callable"
Expand Down
5 changes: 5 additions & 0 deletions dimos/hardware/sensors/lidar/fastlio2/cpp/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -510,6 +510,11 @@ int main(int argc, char** argv) {

if (debug) printf("[fastlio2] SDK started, waiting for device...\n");

// NativeModule.start() in Python reads stderr for this marker and only
// returns once it sees it.
fprintf(stderr, "[DIMOS_NATIVE_READY]\n");
fflush(stderr);

// Main loop
auto frame_interval = std::chrono::microseconds(
static_cast<int64_t>(1e6 / g_frequency));
Expand Down
34 changes: 25 additions & 9 deletions dimos/hardware/sensors/lidar/fastlio2/module.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@
from dimos.msgs.geometry_msgs.Vector3 import Vector3
from dimos.msgs.nav_msgs.Odometry import Odometry
from dimos.msgs.sensor_msgs.PointCloud2 import PointCloud2
from dimos.navigation.nav_stack.frames import FRAME_BODY, FRAME_ODOM
from dimos.spec import mapping, perception
from dimos.utils.generic import get_local_ips
from dimos.utils.logging_config import setup_logger
Expand All @@ -74,6 +73,8 @@ class FastLio2Config(NativeModuleConfig):
cwd: str | None = "cpp"
executable: str = "result/bin/fastlio2_native"
build_command: str | None = "nix build .#fastlio2_native"
# The fastlio2 binary emits [DIMOS_NATIVE_READY] after Livox SDK is up.
ready_timeout_sec: float = 15.0
# Livox SDK hardware config
host_ip: str = "192.168.1.5"
lidar_ip: str = "192.168.1.155"
Expand All @@ -83,11 +84,9 @@ class FastLio2Config(NativeModuleConfig):
# Converted to init_pose CLI arg [x, y, z, qx, qy, qz, qw] in model_post_init.
mount: Pose = Pose()

# Frame IDs for output messages. "odom" reflects that FastLio2 provides
# locally-smooth, continuous odometry (no loop-closure jumps). PGO
# publishes the map→odom correction via TF.
frame_id: str = FRAME_ODOM
child_frame_id: str = FRAME_BODY
frame_id: str = "start_point"
child_frame_id: str = "current_point"
sensor_frame: str = "mid360_link"

# FAST-LIO internal processing rates
msr_freq: float = 50.0
Expand Down Expand Up @@ -169,10 +168,11 @@ def start(self) -> None:
)

def _on_odom_for_tf(self, msg: Odometry) -> None:
ts = msg.ts or time.time()
self.tf.publish(
Transform(
frame_id=FRAME_ODOM,
child_frame_id=FRAME_BODY,
frame_id=self.config.frame_id,
child_frame_id=self.config.child_frame_id,
translation=Vector3(
msg.pose.position.x,
msg.pose.position.y,
Expand All @@ -184,7 +184,23 @@ def _on_odom_for_tf(self, msg: Odometry) -> None:
msg.pose.orientation.z,
msg.pose.orientation.w,
),
ts=msg.ts or time.time(),
ts=ts,
)
)
# Static sensor mount
mount = self.config.mount
self.tf.publish(
Transform(
frame_id=self.config.child_frame_id,
child_frame_id=self.config.sensor_frame,
translation=Vector3(mount.x, mount.y, mount.z),
rotation=Quaternion(
mount.orientation.x,
mount.orientation.y,
mount.orientation.z,
mount.orientation.w,
),
ts=ts,
)
)

Expand Down
5 changes: 5 additions & 0 deletions dimos/hardware/sensors/lidar/livox/cpp/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,11 @@ int main(int argc, char** argv) {

printf("[mid360] SDK started, waiting for device...\n");

// NativeModule.start() in Python reads stderr for this marker and only
// returns once it sees it.
fprintf(stderr, "[DIMOS_NATIVE_READY]\n");
fflush(stderr);

// Main loop: periodically emit accumulated point clouds
auto frame_interval = std::chrono::microseconds(
static_cast<int64_t>(1e6 / g_frequency));
Expand Down
2 changes: 2 additions & 0 deletions dimos/hardware/sensors/lidar/livox/module.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ class Mid360Config(NativeModuleConfig):
cwd: str | None = "cpp"
executable: str = "result/bin/mid360_native"
build_command: str | None = "nix build .#mid360_native"
# The mid360 binary emits [DIMOS_NATIVE_READY] after Livox SDK is up.
ready_timeout_sec: float = 15.0
host_ip: str = "192.168.1.5"
lidar_ip: str = "192.168.1.155"
frequency: float = 10.0
Expand Down
Loading
Loading