Skip to content

gsc_pgo: online and offline PGO #2587

Open
jeff-hykin wants to merge 25 commits into
mainfrom
jeff/feat/jnav_pgo
Open

gsc_pgo: online and offline PGO #2587
jeff-hykin wants to merge 25 commits into
mainfrom
jeff/feat/jnav_pgo

Conversation

@jeff-hykin

@jeff-hykin jeff-hykin commented Jun 24, 2026

Copy link
Copy Markdown
Member

gsc_pgo, Ports the PGO / loop-closure stack into the new jnav layout, plus a tf-tree feature for memory2 stores.

process_observable gains an optional on_drop callback fired once per
message dropped by the dispatcher's single-slot LATEST mailbox. The
Recorder uses it to count dropped frames per stream and log a throttled
warning, so a slow sink no longer loses data silently.
@greptile-apps

greptile-apps Bot commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR ports the PGO / loop-closure stack into the jnav layout and adds a tf-tree feature to memory2 stores. The core infrastructure changes (on_drop callback, async @pose_setter_for, DbTf) are clean and well-reasoned; the bulk of new code is the offline evaluation harness (eval.py, benchmark_table.py, and supporting utilities).

  • Infrastructure: process_observable gains an optional on_drop callback; Recorder now counts and logs dropped frames per stream. DbTf exposes tf-tree lookups over a recorded store with a double-checked lock and a safe-identifier guard for the SQLite table name.
  • Eval harness: LockstepReplay paces scan replays on corrected_odometry acks for machine-speed-independent benchmarking; benchmark_table.py orchestrates a resumable grid of (environment × PGO config) cells with fingerprint-based caching.
  • Issues found: benchmark_table.py's cell_is_fresh never actually compares against EVAL_VERSION (only checks it is non-None), breaking cache invalidation on version bumps; and LockstepReplay._replay does not reset _ack_event after a timeout, allowing a late ack for scan N to spuriously satisfy scan N+1's wait.

Confidence Score: 4/5

Safe to merge for production paths; the two defects are confined to the offline eval harness and do not affect live robot operation.

The memory2 and core changes are minimal and well-tested. The new eval harness carries two correctness issues: benchmark_table.py never enforces EVAL_VERSION on cached cells, so a version bump silently leaves stale results in place; and LockstepReplay can prematurely unblock a scan's ack wait when a late ack from a previously-timed-out scan arrives, breaking the one-ack-per-scan lockstep guarantee.

eval.py (LockstepReplay ack handling) and benchmark_table.py (cell_is_fresh version check) warrant a second look before the eval harness is used for authoritative benchmark results.

Important Files Changed

Filename Overview
dimos/core/module.py Adds optional on_drop callback to _make_async_dispatch and process_observable, firing once per message dropped by LATEST coalescing — a clean, additive change with no logic regressions.
dimos/memory2/module.py Makes @pose_setter_for require async def methods, converts _resolve_pose to async, adds per-stream drop counting via on_drop; docstring for _on_frame_dropped incorrectly states "power-of-ten" when the code uses modulo-1000.
dimos/memory2/db_tf.py New DbTf class providing tf-tree lookups over recorded tf/tf_static streams, with double-checked locking for lazy load and a safe-identifier guard for the SQL table name; addresses previously flagged SQL injection concern.
dimos/memory2/store/base.py Adds tf property to Store that lazily constructs a DbTf instance; import is deferred to break the circular dependency. Clean minimal change.
dimos/navigation/jnav/components/loop_closure/eval.py Main PGO evaluation harness; introduces LockstepReplay with an ack-based pacing mechanism that has a correctness flaw: a delayed ack from a timed-out scan can spuriously wake the next scan's ack wait, violating the one-ack-per-scan guarantee.
dimos/navigation/jnav/components/loop_closure/benchmark_table.py Resumable PGO benchmark runner; cell_is_fresh checks fingerprint.get("version") is not None instead of comparing against EVAL_VERSION, so bumping the version in eval.py never invalidates cached benchmark cells.
dimos/navigation/jnav/components/loop_closure/gsc_pgo/module.py Native C++ PGO module wrapping the GTSAM iSAM2 + PCL ICP binary; adds Landmark port and gravity-anchor config. Straightforward port with well-documented config fields.
dimos/hardware/sensors/lidar/fastlio2/recorder.py Converts _odom_pose and _lidar_pose from sync to async def to match the new @pose_setter_for requirement; functionally identical otherwise.
dimos/navigation/jnav/utils/trajectory_metrics.py New pure-math library for drift injection/correction, voxel agreement, ATE computation (Kabsch/Umeyama), and drift-delta lookups; no db dependencies, well-tested via internal assertions.
dimos/navigation/jnav/utils/recording_db.py Shared store cache for eval scripts; adds close_all() with atexit.register to release file handles, addressing the previously flagged resource-leak concern.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant BT as benchmark_table.py
    participant EP as eval.py
    participant LR as LockstepReplay
    participant PGO as PGO Module
    participant GC as GraphCapture

    BT->>EP: subprocess(eval.py, env×col)
    EP->>LR: start (load db, merge odom+lidar)
    EP->>PGO: start (native C++ iSAM2+ICP)
    EP->>GC: start (capture pose_graph)

    loop for each message (time-sorted)
        alt odom message
            LR->>PGO: odometry.publish (fire-and-forget)
        else lidar scan
            LR->>PGO: lidar.publish
            LR->>LR: wait for corrected_odometry ack (≤30s)
            PGO-->>LR: corrected_odometry (ack)
        end
        PGO->>GC: pose_graph / loop_closure_event
    end

    GC->>EP: write graph JSON (teardown)
    EP->>EP: score (tag agreement + voxel agreement)
    EP->>BT: summary.json (fingerprint + scores)
    BT->>BT: cell_is_fresh? (db size+mtime, version≠None ← bug)
Loading
%%{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"}}}%%
sequenceDiagram
    participant BT as benchmark_table.py
    participant EP as eval.py
    participant LR as LockstepReplay
    participant PGO as PGO Module
    participant GC as GraphCapture

    BT->>EP: subprocess(eval.py, env×col)
    EP->>LR: start (load db, merge odom+lidar)
    EP->>PGO: start (native C++ iSAM2+ICP)
    EP->>GC: start (capture pose_graph)

    loop for each message (time-sorted)
        alt odom message
            LR->>PGO: odometry.publish (fire-and-forget)
        else lidar scan
            LR->>PGO: lidar.publish
            LR->>LR: wait for corrected_odometry ack (≤30s)
            PGO-->>LR: corrected_odometry (ack)
        end
        PGO->>GC: pose_graph / loop_closure_event
    end

    GC->>EP: write graph JSON (teardown)
    EP->>EP: score (tag agreement + voxel agreement)
    EP->>BT: summary.json (fingerprint + scores)
    BT->>BT: cell_is_fresh? (db size+mtime, version≠None ← bug)
Loading

Reviews (7): Last reviewed commit: "Merge branch 'main' into jeff/feat/jnav_..." | Re-trigger Greptile

Comment thread dimos/memory2/db_tf.py
Comment thread dimos/memory2/db_tf.py Outdated
Comment thread dimos/navigation/jnav/utils/recording_db.py
Comment thread dimos/memory2/db_tf.py Outdated
Comment thread dimos/navigation/jnav/components/loop_closure/unrefined_pgo/module.py Outdated
@codecov

codecov Bot commented Jun 24, 2026

Copy link
Copy Markdown

❌ 3 Tests Failed:

Tests completed Failed Passed Skipped
1983 3 1980 159
View the top 3 failed test(s) by shortest run time
dimos.robot.test_all_blueprints::test_blueprint_is_valid[mid360-realsense-record-with-pcap]
Stack Traces | 0.006s run time
blueprint_name = 'mid360-realsense-record-with-pcap'

    @pytest.mark.parametrize("blueprint_name", UBUNTU_BLUEPRINTS)
    def test_blueprint_is_valid(blueprint_name: str) -> None:
        """Validate blueprints that should import on the ubuntu-latest runner."""
>       _check_blueprint(blueprint_name)

blueprint_name = 'mid360-realsense-record-with-pcap'

dimos/robot/test_all_blueprints.py:105: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
dimos/robot/test_all_blueprints.py:81: in _check_blueprint
    blueprint = get_blueprint_by_name(blueprint_name)
        blueprint_name = 'mid360-realsense-record-with-pcap'
        message    = '@pose_setter_for must decorate an `async def` method; PointlioPoseRecorder._odom_pose is not async'
dimos/robot/get_all_blueprints.py:47: in get_blueprint_by_name
    module = __import__(module_path, fromlist=[attr])
        attr       = 'mid360_realsense_record_with_pcap'
        module_path = 'dimos.robot.assembly.mid360_realsense_30'
        name       = 'mid360-realsense-record-with-pcap'
.../robot/assembly/mid360_realsense_30.py:55: in <module>
    from dimos.hardware.sensors.lidar.pointlio.pose_recorder import PointlioPoseRecorder
        In         = <class 'dimos.core.stream.In'>
        Mid360     = <class 'dimos.hardware.sensors.lidar.livox.module.Mid360'>
        PointLio   = <class 'dimos.hardware.sensors.lidar.pointlio.module.PointLio'>
        RealSenseCamera = <class 'dimos.hardware.sensors.camera.realsense.camera.RealSenseCamera'>
        __annotations__ = {}
        __builtins__ = <builtins>
        __cached__ = '.../assembly/__pycache__/mid360_realsense_30.cpython-312.pyc'
        __doc__    = 'The RealSense D435i + Mid-360 rig: static mount frames, recorder, record blueprints.\n\nA single physical sensor asse...-IMU extrinsic comes from the official Mid-360 config (extrinsic_T flipped gives\nthe IMU position in lidar coords).\n'
        __file__   = '/home/runner/work/dimos/dimos/.../robot/assembly/mid360_realsense_30.py'
        __loader__ = <_frozen_importlib_external.SourceFileLoader object at 0xff82462503b0>
        __name__   = 'dimos.robot.assembly.mid360_realsense_30'
        __package__ = 'dimos.robot.assembly'
        __spec__   = ModuleSpec(name='dimos.robot.assembly.mid360_realsense_30', loader=<_frozen_importlib_external.SourceFileLoader object at 0xff82462503b0>, origin='/home/runner/work/dimos/dimos/.../robot/assembly/mid360_realsense_30.py')
        annotations = _Feature((3, 7, 0, 'beta', 1), None, 16777216)
        autoconnect = <function autoconnect at 0xff8303047060>
        math       = <module 'math' (built-in)>
.../lidar/pointlio/pose_recorder.py:50: in <module>
    class PointlioPoseRecorder(Recorder):
        In         = <class 'dimos.core.stream.In'>
        Odometry   = <class 'dimos.msgs.nav_msgs.Odometry.Odometry'>
        OnExisting = <enum 'OnExisting'>
        PointCloud2 = <class 'dimos.msgs.sensor_msgs.PointCloud2.PointCloud2'>
        PointlioPoseRecorderConfig = <class 'dimos.hardware.sensors.lidar.pointlio.pose_recorder.PointlioPoseRecorderConfig'>
        Pose       = <class 'dimos.msgs.geometry_msgs.Pose.Pose'>
        Recorder   = <class 'dimos.memory2.module.Recorder'>
        RecorderConfig = <class 'dimos.memory2.module.RecorderConfig'>
        _POSE_MATCH_TOL = 0.1
        __builtins__ = <builtins>
        __cached__ = '.../pointlio/__pycache__/pose_recorder.cpython-312.pyc'
        __doc__    = 'Memory2 recorder base that anchors Point-LIO frames with the live odometry pose.\n\nSubclass with whatever companion ...s.hardware.sensors.lidar.pointlio.recorder`, the\nstandalone time-aligning recorder used by the pcap-replay tooling.\n'
        __file__   = '/home/runner/work/dimos/dimos/.../lidar/pointlio/pose_recorder.py'
        __loader__ = <_frozen_importlib_external.SourceFileLoader object at 0xff8246250620>
        __name__   = 'dimos.hardware.sensors.lidar.pointlio.pose_recorder'
        __package__ = 'dimos.hardware.sensors.lidar.pointlio'
        __spec__   = ModuleSpec(name='dimos.hardware.sensors.lidar.pointlio.pose_recorder', loader=<_frozen_importlib_external.SourceFileLo...ject at 0xff8246250620>, origin='/home/runner/work/dimos/dimos/.../lidar/pointlio/pose_recorder.py')
        annotations = _Feature((3, 7, 0, 'beta', 1), None, 16777216)
        pose_setter_for = <function pose_setter_for at 0xff824dc76020>
        time       = <module 'time' (built-in)>
.../lidar/pointlio/pose_recorder.py:59: in PointlioPoseRecorder
    @pose_setter_for("pointlio_odometry")
        __annotations__ = {'_last_odom_pose': 'Pose | None', '_last_odom_raw_ts': 'float', 'config': 'PointlioPoseRecorderConfig', 'pointlio_lidar': 'In[PointCloud2]', ...}
        __module__ = 'dimos.hardware.sensors.lidar.pointlio.pose_recorder'
        __qualname__ = 'PointlioPoseRecorder'
        _last_odom_pose = None
        _last_odom_raw_ts = 0.0
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

fn = <function PointlioPoseRecorder._odom_pose at 0xff8249034e00>

    def decorate(fn: Any) -> Any:
        if not inspect.iscoroutinefunction(fn):
>           raise TypeError(
                f"@pose_setter_for must decorate an `async def` method; "
                f"{getattr(fn, '__qualname__', fn)} is not async"
            )
E           TypeError: @pose_setter_for must decorate an `async def` method; PointlioPoseRecorder._odom_pose is not async

fn         = <function PointlioPoseRecorder._odom_pose at 0xff8249034e00>
stream_names = ('pointlio_odometry',)

dimos/memory2/module.py:287: TypeError
dimos.robot.test_all_blueprints::test_blueprint_is_valid[mid360-realsense-record]
Stack Traces | 0.008s run time
blueprint_name = 'mid360-realsense-record'

    @pytest.mark.parametrize("blueprint_name", UBUNTU_BLUEPRINTS)
    def test_blueprint_is_valid(blueprint_name: str) -> None:
        """Validate blueprints that should import on the ubuntu-latest runner."""
>       _check_blueprint(blueprint_name)

blueprint_name = 'mid360-realsense-record'

dimos/robot/test_all_blueprints.py:105: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
dimos/robot/test_all_blueprints.py:81: in _check_blueprint
    blueprint = get_blueprint_by_name(blueprint_name)
        blueprint_name = 'mid360-realsense-record'
        message    = '@pose_setter_for must decorate an `async def` method; PointlioPoseRecorder._odom_pose is not async'
dimos/robot/get_all_blueprints.py:47: in get_blueprint_by_name
    module = __import__(module_path, fromlist=[attr])
        attr       = 'mid360_realsense_record'
        module_path = 'dimos.robot.assembly.mid360_realsense_30'
        name       = 'mid360-realsense-record'
.../robot/assembly/mid360_realsense_30.py:55: in <module>
    from dimos.hardware.sensors.lidar.pointlio.pose_recorder import PointlioPoseRecorder
        In         = <class 'dimos.core.stream.In'>
        Mid360     = <class 'dimos.hardware.sensors.lidar.livox.module.Mid360'>
        PointLio   = <class 'dimos.hardware.sensors.lidar.pointlio.module.PointLio'>
        RealSenseCamera = <class 'dimos.hardware.sensors.camera.realsense.camera.RealSenseCamera'>
        __annotations__ = {}
        __builtins__ = <builtins>
        __cached__ = '.../assembly/__pycache__/mid360_realsense_30.cpython-312.pyc'
        __doc__    = 'The RealSense D435i + Mid-360 rig: static mount frames, recorder, record blueprints.\n\nA single physical sensor asse...-IMU extrinsic comes from the official Mid-360 config (extrinsic_T flipped gives\nthe IMU position in lidar coords).\n'
        __file__   = '/home/runner/work/dimos/dimos/.../robot/assembly/mid360_realsense_30.py'
        __loader__ = <_frozen_importlib_external.SourceFileLoader object at 0xff8246269940>
        __name__   = 'dimos.robot.assembly.mid360_realsense_30'
        __package__ = 'dimos.robot.assembly'
        __spec__   = ModuleSpec(name='dimos.robot.assembly.mid360_realsense_30', loader=<_frozen_importlib_external.SourceFileLoader object at 0xff8246269940>, origin='/home/runner/work/dimos/dimos/.../robot/assembly/mid360_realsense_30.py')
        annotations = _Feature((3, 7, 0, 'beta', 1), None, 16777216)
        autoconnect = <function autoconnect at 0xff8303047060>
        math       = <module 'math' (built-in)>
.../lidar/pointlio/pose_recorder.py:50: in <module>
    class PointlioPoseRecorder(Recorder):
        In         = <class 'dimos.core.stream.In'>
        Odometry   = <class 'dimos.msgs.nav_msgs.Odometry.Odometry'>
        OnExisting = <enum 'OnExisting'>
        PointCloud2 = <class 'dimos.msgs.sensor_msgs.PointCloud2.PointCloud2'>
        PointlioPoseRecorderConfig = <class 'dimos.hardware.sensors.lidar.pointlio.pose_recorder.PointlioPoseRecorderConfig'>
        Pose       = <class 'dimos.msgs.geometry_msgs.Pose.Pose'>
        Recorder   = <class 'dimos.memory2.module.Recorder'>
        RecorderConfig = <class 'dimos.memory2.module.RecorderConfig'>
        _POSE_MATCH_TOL = 0.1
        __builtins__ = <builtins>
        __cached__ = '.../pointlio/__pycache__/pose_recorder.cpython-312.pyc'
        __doc__    = 'Memory2 recorder base that anchors Point-LIO frames with the live odometry pose.\n\nSubclass with whatever companion ...s.hardware.sensors.lidar.pointlio.recorder`, the\nstandalone time-aligning recorder used by the pcap-replay tooling.\n'
        __file__   = '/home/runner/work/dimos/dimos/.../lidar/pointlio/pose_recorder.py'
        __loader__ = <_frozen_importlib_external.SourceFileLoader object at 0xff8246269b50>
        __name__   = 'dimos.hardware.sensors.lidar.pointlio.pose_recorder'
        __package__ = 'dimos.hardware.sensors.lidar.pointlio'
        __spec__   = ModuleSpec(name='dimos.hardware.sensors.lidar.pointlio.pose_recorder', loader=<_frozen_importlib_external.SourceFileLo...ject at 0xff8246269b50>, origin='/home/runner/work/dimos/dimos/.../lidar/pointlio/pose_recorder.py')
        annotations = _Feature((3, 7, 0, 'beta', 1), None, 16777216)
        pose_setter_for = <function pose_setter_for at 0xff824dc76020>
        time       = <module 'time' (built-in)>
.../lidar/pointlio/pose_recorder.py:59: in PointlioPoseRecorder
    @pose_setter_for("pointlio_odometry")
        __annotations__ = {'_last_odom_pose': 'Pose | None', '_last_odom_raw_ts': 'float', 'config': 'PointlioPoseRecorderConfig', 'pointlio_lidar': 'In[PointCloud2]', ...}
        __module__ = 'dimos.hardware.sensors.lidar.pointlio.pose_recorder'
        __qualname__ = 'PointlioPoseRecorder'
        _last_odom_pose = None
        _last_odom_raw_ts = 0.0
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

fn = <function PointlioPoseRecorder._odom_pose at 0xff8246364900>

    def decorate(fn: Any) -> Any:
        if not inspect.iscoroutinefunction(fn):
>           raise TypeError(
                f"@pose_setter_for must decorate an `async def` method; "
                f"{getattr(fn, '__qualname__', fn)} is not async"
            )
E           TypeError: @pose_setter_for must decorate an `async def` method; PointlioPoseRecorder._odom_pose is not async

fn         = <function PointlioPoseRecorder._odom_pose at 0xff8246364900>
stream_names = ('pointlio_odometry',)

dimos/memory2/module.py:287: TypeError
dimos.robot.test_all_blueprints::test_blueprint_is_valid[unitree-go2-mid360-record]
Stack Traces | 0.018s run time
blueprint_name = 'unitree-go2-mid360-record'

    @pytest.mark.parametrize("blueprint_name", UBUNTU_BLUEPRINTS)
    def test_blueprint_is_valid(blueprint_name: str) -> None:
        """Validate blueprints that should import on the ubuntu-latest runner."""
>       _check_blueprint(blueprint_name)

blueprint_name = 'unitree-go2-mid360-record'

dimos/robot/test_all_blueprints.py:105: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
dimos/robot/test_all_blueprints.py:81: in _check_blueprint
    blueprint = get_blueprint_by_name(blueprint_name)
        blueprint_name = 'unitree-go2-mid360-record'
        message    = '@pose_setter_for must decorate an `async def` method; PointlioPoseRecorder._odom_pose is not async'
dimos/robot/get_all_blueprints.py:47: in get_blueprint_by_name
    module = __import__(module_path, fromlist=[attr])
        attr       = 'unitree_go2_mid360_record'
        module_path = 'dimos.robot.unitree.go2.blueprints.basic.unitree_go2_mid360_record'
        name       = 'unitree-go2-mid360-record'
.../blueprints/basic/unitree_go2_mid360_record.py:43: in <module>
    from dimos.robot.unitree.go2.go2_mid360_recorder import Go2Mid360Recorder
        GO2Connection = <class 'dimos.robot.unitree.go2.connection.GO2Connection'>
        Mid360     = <class 'dimos.hardware.sensors.lidar.livox.module.Mid360'>
        Mid360PcapRecorder = <class 'dimos.hardware.sensors.lidar.virtual_mid360.recorder.Mid360PcapRecorder'>
        ModuleCoordinator = <class 'dimos.core.coordination.module_coordinator.ModuleCoordinator'>
        MovementManager = <class 'dimos.navigation.movement_manager.movement_manager.MovementManager'>
        Path       = <class 'pathlib.Path'>
        PointLio   = <class 'dimos.hardware.sensors.lidar.pointlio.module.PointLio'>
        __builtins__ = <builtins>
        __cached__ = '.../basic/__pycache__/unitree_go2_mid360_record.cpython-312.pyc'
        __doc__    = 'Drive-and-record blueprint for the Go2 + Mid-360 rig.\n\nPygame WASD teleop drives the dog while Point-LIO odom+lidar...TLIO_LIDAR_IP=192.168.1.171\n    uv run python .../blueprints/basic/unitree_go2_mid360_record.py\n'
        __file__   = '/home/runner/work/dimos/dimos/.../blueprints/basic/unitree_go2_mid360_record.py'
        __loader__ = <_frozen_importlib_external.SourceFileLoader object at 0xff8244d12ab0>
        __name__   = 'dimos.robot.unitree.go2.blueprints.basic.unitree_go2_mid360_record'
        __package__ = 'dimos.robot.unitree.go2.blueprints.basic'
        __spec__   = ModuleSpec(name='dimos.robot.unitree.go2.blueprints.basic.unitree_go2_mid360_record', loader=<_frozen_importlib_extern...4d12ab0>, origin='/home/runner/work/dimos/dimos/.../blueprints/basic/unitree_go2_mid360_record.py')
        autoconnect = <function autoconnect at 0xff8303047060>
        datetime   = <class 'datetime.datetime'>
        global_config = GlobalConfig(robot_ip=None, robot_ips=None, unitree_aes_128_key=None, xarm7_ip=None, xarm6_ip=None, can_port=None, dev...e, obstacle_avoidance=True, detection_model='moondream', listen_host='127.0.0.1', dimsim_scene='apt', dimsim_port=8090)
        os         = <module 'os' (frozen)>
.../unitree/go2/go2_mid360_recorder.py:27: in <module>
    from dimos.hardware.sensors.lidar.pointlio.pose_recorder import PointlioPoseRecorder
        In         = <class 'dimos.core.stream.In'>
        __builtins__ = <builtins>
        __cached__ = '.../go2/__pycache__/go2_mid360_recorder.cpython-312.pyc'
        __doc__    = 'Records the Go2 + Mid-360 rig into a memory2 SQLite db.\n\nCaptures Point-LIO odom + lidar (trajectory baked into ``p...rint to\ncapture it. Companion streams are recorded as-is and anchored via the static mount\nframes published on tf.\n'
        __file__   = '/home/runner/work/dimos/dimos/.../unitree/go2/go2_mid360_recorder.py'
        __loader__ = <_frozen_importlib_external.SourceFileLoader object at 0xff8244d14830>
        __name__   = 'dimos.robot.unitree.go2.go2_mid360_recorder'
        __package__ = 'dimos.robot.unitree.go2'
        __spec__   = ModuleSpec(name='dimos.robot.unitree.go2.go2_mid360_recorder', loader=<_frozen_importlib_external.SourceFileLoader object at 0xff8244d14830>, origin='/home/runner/work/dimos/dimos/.../unitree/go2/go2_mid360_recorder.py')
        annotations = _Feature((3, 7, 0, 'beta', 1), None, 16777216)
.../lidar/pointlio/pose_recorder.py:50: in <module>
    class PointlioPoseRecorder(Recorder):
        In         = <class 'dimos.core.stream.In'>
        Odometry   = <class 'dimos.msgs.nav_msgs.Odometry.Odometry'>
        OnExisting = <enum 'OnExisting'>
        PointCloud2 = <class 'dimos.msgs.sensor_msgs.PointCloud2.PointCloud2'>
        PointlioPoseRecorderConfig = <class 'dimos.hardware.sensors.lidar.pointlio.pose_recorder.PointlioPoseRecorderConfig'>
        Pose       = <class 'dimos.msgs.geometry_msgs.Pose.Pose'>
        Recorder   = <class 'dimos.memory2.module.Recorder'>
        RecorderConfig = <class 'dimos.memory2.module.RecorderConfig'>
        _POSE_MATCH_TOL = 0.1
        __builtins__ = <builtins>
        __cached__ = '.../pointlio/__pycache__/pose_recorder.cpython-312.pyc'
        __doc__    = 'Memory2 recorder base that anchors Point-LIO frames with the live odometry pose.\n\nSubclass with whatever companion ...s.hardware.sensors.lidar.pointlio.recorder`, the\nstandalone time-aligning recorder used by the pcap-replay tooling.\n'
        __file__   = '/home/runner/work/dimos/dimos/.../lidar/pointlio/pose_recorder.py'
        __loader__ = <_frozen_importlib_external.SourceFileLoader object at 0xff8244d15c10>
        __name__   = 'dimos.hardware.sensors.lidar.pointlio.pose_recorder'
        __package__ = 'dimos.hardware.sensors.lidar.pointlio'
        __spec__   = ModuleSpec(name='dimos.hardware.sensors.lidar.pointlio.pose_recorder', loader=<_frozen_importlib_external.SourceFileLo...ject at 0xff8244d15c10>, origin='/home/runner/work/dimos/dimos/.../lidar/pointlio/pose_recorder.py')
        annotations = _Feature((3, 7, 0, 'beta', 1), None, 16777216)
        pose_setter_for = <function pose_setter_for at 0xff824dc76020>
        time       = <module 'time' (built-in)>
.../lidar/pointlio/pose_recorder.py:59: in PointlioPoseRecorder
    @pose_setter_for("pointlio_odometry")
        __annotations__ = {'_last_odom_pose': 'Pose | None', '_last_odom_raw_ts': 'float', 'config': 'PointlioPoseRecorderConfig', 'pointlio_lidar': 'In[PointCloud2]', ...}
        __module__ = 'dimos.hardware.sensors.lidar.pointlio.pose_recorder'
        __qualname__ = 'PointlioPoseRecorder'
        _last_odom_pose = None
        _last_odom_raw_ts = 0.0
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

fn = <function PointlioPoseRecorder._odom_pose at 0xff8244bd84a0>

    def decorate(fn: Any) -> Any:
        if not inspect.iscoroutinefunction(fn):
>           raise TypeError(
                f"@pose_setter_for must decorate an `async def` method; "
                f"{getattr(fn, '__qualname__', fn)} is not async"
            )
E           TypeError: @pose_setter_for must decorate an `async def` method; PointlioPoseRecorder._odom_pose is not async

fn         = <function PointlioPoseRecorder._odom_pose at 0xff8244bd84a0>
stream_names = ('pointlio_odometry',)

dimos/memory2/module.py:287: TypeError

To view more test analytics, go to the Test Analytics Dashboard
📋 Got 3 mins? Take this short survey to help us improve Test Analytics.

@jeff-hykin jeff-hykin changed the title jnav: port PGO/loop-closure + tf-tree for memory2 stores gsc_pgo: online and offline PGO Jun 24, 2026
@jeff-hykin jeff-hykin enabled auto-merge (squash) June 24, 2026 08:35
@github-actions github-actions Bot added the ready-to-merge Required CI checks have passed on this PR label Jun 24, 2026
@github-actions github-actions Bot added ready-to-merge Required CI checks have passed on this PR and removed ready-to-merge Required CI checks have passed on this PR labels Jun 24, 2026
@TomCC7 TomCC7 disabled auto-merge June 26, 2026 03:27
@github-actions github-actions Bot removed the ready-to-merge Required CI checks have passed on this PR label Jun 26, 2026
TomCC7
TomCC7 previously approved these changes Jun 26, 2026

@TomCC7 TomCC7 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

test

@TomCC7 TomCC7 dismissed their stale review June 26, 2026 03:28

test

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants