Skip to content

Go2 Precision based controller#2274

Draft
mustafab0 wants to merge 49 commits into
mainfrom
mustafa/task/go2-controller-blueprints
Draft

Go2 Precision based controller#2274
mustafab0 wants to merge 49 commits into
mainfrom
mustafa/task/go2-controller-blueprints

Conversation

@mustafab0
Copy link
Copy Markdown
Contributor

@mustafab0 mustafab0 commented May 28, 2026

Problem

Hand-tuning the Go2 path follower for precision nav was guesswork: missing a measured plant.

Issue: #


Solution

Three blueprints, run in order:
unitree-go2-characterization (fits FOPDT plant),
unitree-go2-benchmark (operating-point map), unitree-go2-precision-nav (click-to-goal with live e_max). Full operator guide in dimos/utils/benchmarking/reports/tuning_README.md.


Breaking Changes

None


How to Test (Needs Go2 robot)

Reviewers do NOT need to re-run characterization/benchmark on hardware. Use the bundled pre-recorded artifacts.

  1. Pull the artifact tarballs from LFS
    git lfs pull --include="data/.lfs/characterization.tar.gz,data/.lfs/benchmark.tar.gz"
  2. Extract them
    tar -xzf data/.lfs/characterization.tar.gz -C data/
    tar -xzf data/.lfs/benchmark.tar.gz -C data/
  3. Run: `dimos run unitree-go2-precision-nav
  4. In rerun window: click a goal point on the costmap.
  5. Verify robot follows the planned path. Press keys 0–9 in the pygame window to live-tune corridor half-width (0.0–0.9 m); the precision follower re-solves and swaps the per-waypoint cap atomically.

Want to regenerate the artifacts yourself? See tuning_README.md for the full Step 1 / Step 2 operator flow.

@codecov
Copy link
Copy Markdown

codecov Bot commented May 28, 2026

❌ 3 Tests Failed:

Tests completed Failed Passed Skipped
1982 3 1979 27
View the top 3 failed test(s) by shortest run time
dimos.robot.test_all_blueprints::test_blueprint_is_valid[coordinator-sim-fopdt]
Stack Traces | 0.222s run time
file_path = PosixPath('.../dimos/data/.lfs/unitree_g1_local_planner_precomputed_paths.tar.gz')
repo_root = PosixPath('.../work/dimos/dimos')

    def _lfs_pull(file_path: Path, repo_root: Path) -> None:
        try:
            relative_path = file_path.relative_to(repo_root)
    
            env = os.environ.copy()
            env["GIT_LFS_FORCE_PROGRESS"] = "1"
    
>           subprocess.run(
                ["git", "lfs", "pull", "--include", str(relative_path)],
                cwd=repo_root,
                check=True,
                env=env,
            )

env        = {'ACCEPT_EULA': 'Y', 'ACTIONS_ID_TOKEN_REQUEST_TOKEN': 'eyJhbGciOiJSUzI1NiIsImtpZCI6IjM4ODI2YjE3LTZhMzAtNWY5Yi1iMTY5LT...b53?api-version=2.0', 'ACTIONS_ORCHESTRATION_ID': '6833a07d-0f24-4ee1-9da8-d357087b0c03.tests._3_11_ubuntu_false', ...}
file_path  = PosixPath('.../dimos/data/.lfs/unitree_g1_local_planner_precomputed_paths.tar.gz')
relative_path = PosixPath('data/.lfs/unitree_g1_local_planner_precomputed_paths.tar.gz')
repo_root  = PosixPath('.../work/dimos/dimos')

dimos/utils/data.py:201: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

input = None, capture_output = False, timeout = None, check = True
popenargs = (['git', 'lfs', 'pull', '--include', 'data/.lfs/unitree_g1_local_planner_precomputed_paths.tar.gz'],)
kwargs = {'cwd': PosixPath('.../work/dimos/dimos'), 'env': {'ACCEPT_EULA': 'Y', 'ACTIONS_ID_TOKEN_REQUEST_TOKEN': 'eyJ...53?api-version=2.0', 'ACTIONS_ORCHESTRATION_ID': '6833a07d-0f24-4ee1-9da8-d357087b0c03.tests._3_11_ubuntu_false', ...}}
process = <Popen: returncode: 1 args: ['git', 'lfs', 'pull', '--include', 'data/.lfs/u...>
stdout = None, stderr = None, retcode = 1

    def run(*popenargs,
            input=None, capture_output=False, timeout=None, check=False, **kwargs):
        """Run command with arguments and return a CompletedProcess instance.
    
        The returned instance will have attributes args, returncode, stdout and
        stderr. By default, stdout and stderr are not captured, and those attributes
        will be None. Pass stdout=PIPE and/or stderr=PIPE in order to capture them,
        or pass capture_output=True to capture both.
    
        If check is True and the exit code was non-zero, it raises a
        CalledProcessError. The CalledProcessError object will have the return code
        in the returncode attribute, and output & stderr attributes if those streams
        were captured.
    
        If timeout is given, and the process takes too long, a TimeoutExpired
        exception will be raised.
    
        There is an optional argument "input", allowing you to
        pass bytes or a string to the subprocess's stdin.  If you use this argument
        you may not also use the Popen constructor's "stdin" argument, as
        it will be used internally.
    
        By default, all communication is in bytes, and therefore any "input" should
        be bytes, and the stdout and stderr will be bytes. If in text mode, any
        "input" should be a string, and stdout and stderr will be strings decoded
        according to locale encoding, or by "encoding" if set. Text mode is
        triggered by setting any of text, encoding, errors or universal_newlines.
    
        The other arguments are the same as for the Popen constructor.
        """
        if input is not None:
            if kwargs.get('stdin') is not None:
                raise ValueError('stdin and input arguments may not both be used.')
            kwargs['stdin'] = PIPE
    
        if capture_output:
            if kwargs.get('stdout') is not None or kwargs.get('stderr') is not None:
                raise ValueError('stdout and stderr arguments may not be used '
                                 'with capture_output.')
            kwargs['stdout'] = PIPE
            kwargs['stderr'] = PIPE
    
        with Popen(*popenargs, **kwargs) as process:
            try:
                stdout, stderr = process.communicate(input, timeout=timeout)
            except TimeoutExpired as exc:
                process.kill()
                if _mswindows:
                    # Windows accumulates the output in a single blocking
                    # read() call run on child threads, with the timeout
                    # being done in a join() on those threads.  communicate()
                    # _after_ kill() is required to collect that and add it
                    # to the exception.
                    exc.stdout, exc.stderr = process.communicate()
                else:
                    # POSIX _communicate already populated the output so
                    # far into the TimeoutExpired exception.
                    process.wait()
                raise
            except:  # Including KeyboardInterrupt, communicate handled that.
                process.kill()
                # We don't call process.wait() as .__exit__ does that for us.
                raise
            retcode = process.poll()
            if check and retcode:
>               raise CalledProcessError(retcode, process.args,
                                         output=stdout, stderr=stderr)
E               subprocess.CalledProcessError: Command '['git', 'lfs', 'pull', '--include', 'data/.lfs/unitree_g1_local_planner_precomputed_paths.tar.gz']' returned non-zero exit status 1.

capture_output = False
check      = True
input      = None
kwargs     = {'cwd': PosixPath('.../work/dimos/dimos'), 'env': {'ACCEPT_EULA': 'Y', 'ACTIONS_ID_TOKEN_REQUEST_TOKEN': 'eyJ...53?api-version=2.0', 'ACTIONS_ORCHESTRATION_ID': '6833a07d-0f24-4ee1-9da8-d357087b0c03.tests._3_11_ubuntu_false', ...}}
popenargs  = (['git', 'lfs', 'pull', '--include', 'data/.lfs/unitree_g1_local_planner_precomputed_paths.tar.gz'],)
process    = <Popen: returncode: 1 args: ['git', 'lfs', 'pull', '--include', 'data/.lfs/u...>
retcode    = 1
stderr     = None
stdout     = None
timeout    = None

.../usr/lib/python3.12/subprocess.py:571: CalledProcessError

During handling of the above exception, another exception occurred:

blueprint_name = 'coordinator-sim-fopdt'

    @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 = 'coordinator-sim-fopdt'

dimos/robot/test_all_blueprints.py:99: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
dimos/robot/test_all_blueprints.py:75: in _check_blueprint
    blueprint = get_blueprint_by_name(blueprint_name)
        blueprint_name = 'coordinator-sim-fopdt'
        message    = "Failed to pull LFS file .../dimos/data/.lfs/unitree_g1_local_planner_precomputed_paths.tar.gz: Co... 'pull', '--include', 'data/.lfs/unitree_g1_local_planner_precomputed_paths.tar.gz']' returned non-zero exit status 1."
dimos/robot/get_all_blueprints.py:47: in get_blueprint_by_name
    module = __import__(module_path, fromlist=[attr])
        attr       = 'coordinator_sim_fopdt'
        module_path = 'dimos.control.blueprints.mobile'
        name       = 'coordinator-sim-fopdt'
.../control/blueprints/mobile.py:196: in <module>
    "paths_dir": str(G1_LOCAL_PLANNER_PRECOMPUTED_PATHS),
        ControlCoordinator = <class 'dimos.control.coordinator.ControlCoordinator'>
        FastLio2   = <class 'dimos.hardware.sensors.lidar.fastlio2.module.FastLio2'>
        FopdtPlantConnection = <class 'dimos.robot.sim.fopdt_plant_connection.FopdtPlantConnection'>
        G1_LOCAL_PLANNER_PRECOMPUTED_PATHS = <[RuntimeError("Failed to pull LFS file .../dimos/data/.lfs/unitree_g1_local_planner_precomputed_p...nner_precomputed_paths.tar.gz']' returned non-zero exit status 1.") raised in repr()] LfsPath object at 0x7f62fb4d2d50>
        HardwareComponent = <class 'dimos.control.components.HardwareComponent'>
        HardwareType = <enum 'HardwareType'>
        JointState = <class 'dimos.msgs.sensor_msgs.JointState.JointState'>
        KeyboardTeleop = <class 'dimos.robot.unitree.keyboard_teleop.KeyboardTeleop'>
        LCMTransport = <class 'dimos.core.transport.LCMTransport'>
        MovementManager = <class 'dimos.navigation.movement_manager.movement_manager.MovementManager'>
        Pose       = <class 'dimos.msgs.geometry_msgs.Pose.Pose'>
        PoseStamped = <class 'dimos.msgs.geometry_msgs.PoseStamped.PoseStamped'>
        Quaternion = <class 'dimos.msgs.geometry_msgs.Quaternion.Quaternion'>
        RerunBridgeModule = <class 'dimos.visualization.rerun.bridge.RerunBridgeModule'>
        RerunWebSocketServer = <class 'dimos.visualization.rerun.websocket_server.RerunWebSocketServer'>
        TaskConfig = <class 'dimos.control.coordinator.TaskConfig'>
        Twist      = <class 'dimos.msgs.geometry_msgs.Twist.Twist'>
        Vector3    = <class 'dimos.msgs.geometry_msgs.Vector3.Vector3'>
        __builtins__ = <builtins>
        __cached__ = '.../work/dimos/dimos/dimos/control/blueprints/__pycache__/mobile.cpython-312.pyc'
        __doc__    = 'Mobile manipulation coordinator blueprints.\n\nUsage:\n    dimos run coordinator-mock-twist-base                # Moc...drive)\n    dimos run coordinator-sim-fopdt                      # FOPDT sim plant on /go2/cmd_vel|odom (Go2-shaped)\n'
        __file__   = '.../work/dimos/dimos/.../control/blueprints/mobile.py'
        __loader__ = <_frozen_importlib_external.SourceFileLoader object at 0x7f62fad10200>
        __name__   = 'dimos.control.blueprints.mobile'
        __package__ = 'dimos.control.blueprints'
        __spec__   = ModuleSpec(name='dimos.control.blueprints.mobile', loader=<_frozen_importlib_external.SourceFileLoader object at 0x7f62fad10200>, origin='.../work/dimos/dimos/.../control/blueprints/mobile.py')
        _base_joints = ['base/vx', 'base/vy', 'base/wz']
        _catalog_xarm7 = <function xarm7 at 0x7f62fad59440>
        _flowbase_mid360_mount = Pose(position=Vector([        0.2        -0.2         0.1]), orientation=Quaternion(0.000000, 0.000000, 0.000000, 1.000000))
        _flowbase_twist_base = <function _flowbase_twist_base at 0x7f62fa53bd80>
        _mock_twist_base = <function _mock_twist_base at 0x7f62fa5c3ba0>
        annotations = _Feature((3, 7, 0, 'beta', 1), None, 16777216)
        autoconnect = <function autoconnect at 0x7f6410906fc0>
        coordinator_flowbase = Blueprint(blueprints=(BlueprintAtom(kwargs={'hardware': [HardwareComponent(hardware_id='base', hardware_type=<Hardware...lobal_config_overrides=mappingproxy({}), remapping_map=mappingproxy({}), requirement_checks=(), configurator_checks=())
        coordinator_flowbase_keyboard_teleop = Blueprint(blueprints=(BlueprintAtom(kwargs={'hardware': [HardwareComponent(hardware_id='base', hardware_type=<Hardware...lobal_config_overrides=mappingproxy({}), remapping_map=mappingproxy({}), requirement_checks=(), configurator_checks=())
        coordinator_mock_twist_base = Blueprint(blueprints=(BlueprintAtom(kwargs={'hardware': [HardwareComponent(hardware_id='base', hardware_type=<Hardware...lobal_config_overrides=mappingproxy({}), remapping_map=mappingproxy({}), requirement_checks=(), configurator_checks=())
        create_nav_stack = <function create_nav_stack at 0x7f62fa539080>
        make_twist_base_joints = <function make_twist_base_joints at 0x7f64133e58a0>
        nav_stack_rerun_config = <function nav_stack_rerun_config at 0x7f62fa538540>
        os         = <module 'os' (frozen)>
dimos/utils/data.py:361: in __str__
    return str(self._ensure_downloaded())
        self       = <[RuntimeError("Failed to pull LFS file .../dimos/data/.lfs/unitree_g1_local_planner_precomputed_p...nner_precomputed_paths.tar.gz']' returned non-zero exit status 1.") raised in repr()] LfsPath object at 0x7f62fb4d2d50>
dimos/utils/data.py:339: in _ensure_downloaded
    cache = get_data(filename)
        cache      = None
        filename   = 'unitree_g1_local_planner_precomputed_paths'
        self       = <[RuntimeError("Failed to pull LFS file .../dimos/data/.lfs/unitree_g1_local_planner_precomputed_p...nner_precomputed_paths.tar.gz']' returned non-zero exit status 1.") raised in repr()] LfsPath object at 0x7f62fb4d2d50>
dimos/utils/data.py:296: in get_data
    archive_path = _decompress_archive(_pull_lfs_archive(archive_name))
        archive_name = 'unitree_g1_local_planner_precomputed_paths'
        data_dir   = PosixPath('.../work/dimos/dimos/data')
        file_path  = PosixPath('.../work/dimos/dimos/data/unitree_g1_local_planner_precomputed_paths')
        name       = 'unitree_g1_local_planner_precomputed_paths'
        nested_path = None
        path_parts = ('unitree_g1_local_planner_precomputed_paths',)
dimos/utils/data.py:240: in _pull_lfs_archive
    _lfs_pull(file_path, repo_root)
        file_path  = PosixPath('.../dimos/data/.lfs/unitree_g1_local_planner_precomputed_paths.tar.gz')
        filename   = 'unitree_g1_local_planner_precomputed_paths'
        repo_root  = PosixPath('.../work/dimos/dimos')
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

file_path = PosixPath('.../dimos/data/.lfs/unitree_g1_local_planner_precomputed_paths.tar.gz')
repo_root = PosixPath('.../work/dimos/dimos')

    def _lfs_pull(file_path: Path, repo_root: Path) -> None:
        try:
            relative_path = file_path.relative_to(repo_root)
    
            env = os.environ.copy()
            env["GIT_LFS_FORCE_PROGRESS"] = "1"
    
            subprocess.run(
                ["git", "lfs", "pull", "--include", str(relative_path)],
                cwd=repo_root,
                check=True,
                env=env,
            )
        except subprocess.CalledProcessError as e:
>           raise RuntimeError(f"Failed to pull LFS file {file_path}: {e}")
E           RuntimeError: Failed to pull LFS file .../dimos/data/.lfs/unitree_g1_local_planner_precomputed_paths.tar.gz: Command '['git', 'lfs', 'pull', '--include', 'data/.lfs/unitree_g1_local_planner_precomputed_paths.tar.gz']' returned non-zero exit status 1.

env        = {'ACCEPT_EULA': 'Y', 'ACTIONS_ID_TOKEN_REQUEST_TOKEN': 'eyJhbGciOiJSUzI1NiIsImtpZCI6IjM4ODI2YjE3LTZhMzAtNWY5Yi1iMTY5LT...b53?api-version=2.0', 'ACTIONS_ORCHESTRATION_ID': '6833a07d-0f24-4ee1-9da8-d357087b0c03.tests._3_11_ubuntu_false', ...}
file_path  = PosixPath('.../dimos/data/.lfs/unitree_g1_local_planner_precomputed_paths.tar.gz')
relative_path = PosixPath('data/.lfs/unitree_g1_local_planner_precomputed_paths.tar.gz')
repo_root  = PosixPath('.../work/dimos/dimos')

dimos/utils/data.py:208: RuntimeError
dimos.project.test_no_sections::test_no_section_markers
Stack Traces | 0.732s run time
def test_no_section_markers():
        """
        Fail if any file contains section-style comment markers.
    
        If a file is too complicated to be understood without sections, then the
        sections should be files. We don't need "subfiles".
        """
        violations = find_section_markers()
        if violations:
            report_lines = [
                f"Found {len(violations)} section marker(s). "
                "If a file is too complicated to be understood without sections, "
                'then the sections should be files. We don\'t need "subfiles".',
                "",
            ]
            for path, lineno, text in violations:
                report_lines.append(f"  {path}:{lineno}: {text.strip()}")
>           raise AssertionError("\n".join(report_lines))
E           AssertionError: Found 53 section marker(s). If a file is too complicated to be understood without sections, then the sections should be files. We don't need "subfiles".
E           
E             .../utils/benchmarking/scoring.py:82: # ---------------------------------------------------------------------------
E             .../utils/benchmarking/scoring.py:84: # ---------------------------------------------------------------------------
E             .../utils/benchmarking/scoring.py:129: # ---------------------------------------------------------------------------
E             .../utils/benchmarking/scoring.py:131: # ---------------------------------------------------------------------------
E             .../utils/benchmarking/scoring.py:199: # ---------------------------------------------------------------------------
E             .../utils/benchmarking/scoring.py:201: # ---------------------------------------------------------------------------
E             .../utils/benchmarking/test_tuning.py:50: # --- DERIVE ---------------------------------------------------------------
E             .../utils/benchmarking/test_tuning.py:137: # --- artifact round-trip --------------------------------------------------
E             .../utils/benchmarking/test_tuning.py:160: # --- tolerance -> max-safe-speed inversion --------------------------------
E             .../utils/benchmarking/paths.py:78: # ---------------------------------------------------------------------------
E             .../utils/benchmarking/paths.py:80: # ---------------------------------------------------------------------------
E             .../utils/benchmarking/paths.py:190: # ---------------------------------------------------------------------------
E             .../utils/benchmarking/paths.py:192: # ---------------------------------------------------------------------------
E             .../utils/benchmarking/paths.py:284: # ---------------------------------------------------------------------------
E             .../utils/benchmarking/paths.py:286: # ---------------------------------------------------------------------------
E             .../utils/benchmarking/tuning.py:60: # --- DERIVE tunable constants (documented; single source of truth) -------
E             .../utils/benchmarking/tuning.py:93: # --- Artifact schema -----------------------------------------------------
E             .../utils/benchmarking/tuning.py:208: # --- methodology v2: floor/ceiling envelope + per-amplitude tables -------
E             .../utils/benchmarking/tuning.py:289: # --- serialization ---
E             .../utils/benchmarking/tuning.py:373: # --- helpers -------------------------------------------------------------
E             .../utils/benchmarking/tuning.py:538: # --- DERIVE: pure model -> config ---------------------------------------
E             .../utils/benchmarking/tuning.py:727: # --- tolerance -> max-safe-speed inversion (pure) ------------------------
E             .../utils/benchmarking/plant.py:176: # --- Vendored fitted FOPDT plant for the Go2 base ------------------------
E             .../utils/benchmarking/plant.py:203: # --- Per-robot profile (single source of truth for robot specifics) -----
E             .../utils/benchmarking/benchmark.py:361: # --- benchmark ----------------------------------------------------------
E             .../utils/benchmarking/benchmark.py:728: # === ARTIFACTS FIRST (before any teardown that might segfault) ===
E             .../utils/benchmarking/benchmark.py:775: # === BEST-EFFORT TEARDOWN (artifacts already on disk) ===
E             .../utils/characterization/trajectories.py:75: # ---------------------------------------------------------------------------
E             .../utils/characterization/trajectories.py:77: # ---------------------------------------------------------------------------
E             .../utils/characterization/trajectories.py:290: # ---------------------------------------------------------------------------
E             .../utils/characterization/trajectories.py:292: # ---------------------------------------------------------------------------
E             .../utils/characterization/trajectories.py:329: # ---------------------------------------------------------------------------
E             .../utils/characterization/trajectories.py:331: # ---------------------------------------------------------------------------
E             .../utils/characterization/trajectories.py:357: # ---------------------------------------------------------------------------
E             .../characterization/modeling/fopdt.py:245: # --- Two-stage path: detect L from the data, then fit (K, tau) ---
E             .../control/tasks/velocity_profiler.py:79: # ------------------------------------------------------------------
E             .../control/tasks/velocity_profiler.py:81: # ------------------------------------------------------------------
E             .../tasks/precision_path_follower_task/precision_path_follower_task.py:91: # ------------------------------------------------------------------
E             .../tasks/precision_path_follower_task/precision_path_follower_task.py:93: # ------------------------------------------------------------------
E             .../tasks/precision_path_follower_task/precision_path_follower_task.py:121: # ------------------------------------------------------------------
E             .../tasks/precision_path_follower_task/precision_path_follower_task.py:123: # ------------------------------------------------------------------
E             .../tasks/precision_path_follower_task/reference_governor.py:54: # ---------------------------------------------------------------------------
E             .../tasks/precision_path_follower_task/reference_governor.py:56: # ---------------------------------------------------------------------------
E             .../tasks/precision_path_follower_task/reference_governor.py:75: # ---------------------------------------------------------------------------
E             .../tasks/precision_path_follower_task/reference_governor.py:77: # ---------------------------------------------------------------------------
E             .../tasks/precision_path_follower_task/reference_governor.py:178: # ---------------------------------------------------------------------------
E             .../tasks/precision_path_follower_task/reference_governor.py:180: # ---------------------------------------------------------------------------
E             .../tasks/path_follower_task/path_follower_task.py:155: # ------------------------------------------------------------------
E             .../tasks/path_follower_task/path_follower_task.py:157: # ------------------------------------------------------------------
E             .../tasks/path_follower_task/path_follower_task.py:249: # ------------------------------------------------------------------
E             .../tasks/path_follower_task/path_follower_task.py:251: # ------------------------------------------------------------------
E             .../tasks/path_follower_task/path_follower_task.py:325: # ------------------------------------------------------------------
E             .../tasks/path_follower_task/path_follower_task.py:327: # ------------------------------------------------------------------

lineno     = 327
path       = '.../tasks/path_follower_task/path_follower_task.py'
report_lines = ['Found 53 section marker(s). If a file is too complicated to be understood without sections, then the sections should...utils/benchmarking/scoring.py:131: # ---------------------------------------------------------------------------', ...]
text       = '    # ------------------------------------------------------------------'
violations = [('.../utils/benchmarking/scoring.py', 82, '# ----------------------------------------------------------------------...s/benchmarking/scoring.py', 201, '# ---------------------------------------------------------------------------'), ...]

dimos/project/test_no_sections.py:145: AssertionError
dimos.robot.test_all_blueprints_generation::test_all_blueprints_is_current
Stack Traces | 3.81s run time
def test_all_blueprints_is_current() -> None:
        root = DIMOS_PROJECT_ROOT / "dimos"
        all_blueprints, all_modules = _scan_for_blueprints(root)
    
        common = set(all_blueprints.keys()) & set(all_modules.keys())
        assert not common, (
            f"Names must be unique across blueprints and modules, "
            f"but these appear in both: {sorted(common)}"
        )
    
        generated_content = _generate_all_blueprints_content(all_blueprints, all_modules)
    
        file_path = root / "robot" / "all_blueprints.py"
    
        if "CI" in os.environ:
            if not file_path.exists():
                pytest.fail(f"all_blueprints.py does not exist at {file_path}")
    
            current_content = file_path.read_text()
            if current_content != generated_content:
                diff = difflib.unified_diff(
                    current_content.splitlines(keepends=True),
                    generated_content.splitlines(keepends=True),
                    fromfile="all_blueprints.py (current)",
                    tofile="all_blueprints.py (generated)",
                )
                diff_str = "".join(diff)
>               pytest.fail(
                    f"all_blueprints.py is out of date. Run "
                    f"`pytest dimos/robot/test_all_blueprints_generation.py` locally to update.\n\n"
                    f"Diff:\n{diff_str}"
                )
E               Failed: all_blueprints.py is out of date. Run `pytest dimos/robot/test_all_blueprints_generation.py` locally to update.
E               
E               Diff:
E               --- all_blueprints.py (current)
E               +++ all_blueprints.py (generated)
E               @@ -99,21 +99,11 @@
E                    "unitree-go2-agentic-huggingface": "dimos.robot.unitree.go2.blueprints.agentic.unitree_go2_agentic_huggingface:unitree_go2_agentic_huggingface",
E                    "unitree-go2-agentic-ollama": "dimos.robot.unitree.go2.blueprints.agentic.unitree_go2_agentic_ollama:unitree_go2_agentic_ollama",
E                    "unitree-go2-basic": "dimos.robot.unitree.go2.blueprints.basic.unitree_go2_basic:unitree_go2_basic",
E               -    "unitree-go2-benchmark": "dimos.robot.unitree.go2.blueprints.basic.unitree_go2_benchmark:unitree_go2_benchmark",
E               -    "unitree-go2-benchmark-rage": "dimos.robot.unitree.go2.blueprints.basic.unitree_go2_benchmark:unitree_go2_benchmark_rage",
E               -    "unitree-go2-benchmark-rg": "dimos.robot.unitree.go2.blueprints.basic.unitree_go2_benchmark_rg:unitree_go2_benchmark_rg",
E               -    "unitree-go2-benchmark-rg-rage": "dimos.robot.unitree.go2.blueprints.basic.unitree_go2_benchmark_rg:unitree_go2_benchmark_rg_rage",
E               -    "unitree-go2-characterization": "dimos.robot.unitree.go2.blueprints.basic.unitree_go2_characterization:unitree_go2_characterization",
E               -    "unitree-go2-characterization-rage": "dimos.robot.unitree.go2.blueprints.basic.unitree_go2_characterization:unitree_go2_characterization_rage",
E               -    "unitree-go2-coordinator": "dimos.robot.unitree.go2.blueprints.basic.unitree_go2_coordinator:unitree_go2_coordinator",
E               -    "unitree-go2-coordinator-rage": "dimos.robot.unitree.go2.blueprints.basic.unitree_go2_coordinator:unitree_go2_coordinator_rage",
E                    "unitree-go2-detection": "dimos.robot.unitree.go2.blueprints.smart.unitree_go2_detection:unitree_go2_detection",
E                    "unitree-go2-fleet": "dimos.robot.unitree.go2.blueprints.basic.unitree_go2_fleet:unitree_go2_fleet",
E                    "unitree-go2-keyboard-teleop": "dimos.robot.unitree.go2.blueprints.basic.unitree_go2_keyboard_teleop:unitree_go2_keyboard_teleop",
E                    "unitree-go2-markers": "dimos.robot.unitree.go2.blueprints.smart.unitree_go2:unitree_go2_markers",
E                    "unitree-go2-memory": "dimos.robot.unitree.go2.blueprints.smart.unitree_go2:unitree_go2_memory",
E               -    "unitree-go2-precision-nav": "dimos.robot.unitree.go2.blueprints.basic.unitree_go2_precision_nav:unitree_go2_precision_nav",
E               -    "unitree-go2-precision-nav-rage": "dimos.robot.unitree.go2.blueprints.basic.unitree_go2_precision_nav:unitree_go2_precision_nav_rage",
E                    "unitree-go2-relocalization": "dimos.robot.unitree.go2.blueprints.smart.unitree_go2:unitree_go2_relocalization",
E                    "unitree-go2-ros": "dimos.robot.unitree.go2.blueprints.smart.unitree_go2_ros:unitree_go2_ros",
E                    "unitree-go2-security": "dimos.robot.unitree.go2.blueprints.agentic.unitree_go2_security:unitree_go2_security",

all_blueprints = {'alfred-nav': 'dimos.robot.diy.alfred.blueprints.alfred_nav:alfred_nav', 'coordinator-basic': 'dimos.control.blueprin...sian_ik_mock', 'coordinator-cartesian-ik-piper': 'dimos.control.blueprints.teleop:coordinator_cartesian_ik_piper', ...}
all_modules = {'alfred-high-level': 'dimos.robot.diy.alfred.effector_high_level.AlfredHighLevel', 'arm-teleop-module': 'dimos.teleop..._navigation.BBoxNavigationModule', 'b1-connection-module': 'dimos.robot.unitree.b1.connection.B1ConnectionModule', ...}
common     = set()
current_content = '# Copyright 2025-2026 Dimensional Inc.\n#\n# Licensed under the Apache License, Version 2.0 (the "License");\n# you m...ebsocket_vis_module.WebsocketVisModule",\n    "zed-camera": "dimos.hardware.sensors.camera.zed.camera.ZEDCamera",\n}\n'
diff       = <generator object unified_diff at 0x7f10e8723c60>
diff_str   = '--- all_blueprints.py (current)\n+++ all_blueprints.py (generated)\n@@ -99,21 +99,11 @@\n     "unitree-go2-agentic-hu...     "unitree-go2-security": "dimos.robot.unitree.go2.blueprints.agentic.unitree_go2_security:unitree_go2_security",\n'
file_path  = PosixPath('.../dimos/robot/all_blueprints.py')
generated_content = '# Copyright 2025-2026 Dimensional Inc.\n#\n# Licensed under the Apache License, Version 2.0 (the "License");\n# you m...ebsocket_vis_module.WebsocketVisModule",\n    "zed-camera": "dimos.hardware.sensors.camera.zed.camera.ZEDCamera",\n}\n'
root       = PosixPath('.../dimos/dimos/dimos')

dimos/robot/test_all_blueprints_generation.py:66: Failed

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

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.

1 participant