Skip to content

fix(sim): report gripper readback in the command coordinate - #3697

Merged
mustafab0 merged 1 commit into
mainfrom
mustafa/fix/sim-gripper-readback
Aug 26, 2026
Merged

fix(sim): report gripper readback in the command coordinate#3697
mustafab0 merged 1 commit into
mainfrom
mustafa/fix/sim-gripper-readback

Conversation

@mustafab0

@mustafab0 mustafab0 commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

The finding

The two sides of the SHM gripper block disagreed on which end of the joint
range means closed:

So the readback ran opposite to the coordinate commands are given in. Measured
on data/xarm7 (mj_step to steady state):

ctrl settled qpos jaw separation
0 0.003 0.141 m (open)
255 0.848 0.054 m (closed)

Solution

The sim was handing back MuJoCo's raw joint angle, which counts up as the jaws shut. Commands count down. Same physical state, opposite numbers.

The fix flips the readback so it lands on the command's scale. Now you send 0.0 and read 0.0.

The sim published the raw MJCF joint value as gripper state while the
command path inverts joint->ctrl, so a closed gripper read back as the
open end of its range (normalized 1.00 under PR #3381's convention).
Reflect the readback about the published joint range so a read equals
the command that produced it.
@codecov

codecov Bot commented Aug 26, 2026

Copy link
Copy Markdown

❌ 1 Tests Failed:

Tests completed Failed Passed Skipped
4853 1 4852 74
View the full list of 1 ❄️ flaky test(s)
dimos.e2e_tests.test_dimsim_spatial_memory::test_go_to_the_bed

Flake rate in main: 15.43% (Passed 137 times, Failed 25 times)

Stack Traces | 317s run time
lcm_spy = <dimos.e2e_tests.lcm_spy.LcmSpy object at 0x77cd3596ec90>
start_blueprint = <function start_blueprint.<locals>.set_name_and_start at 0x77cd357ff380>
human_input = <function human_input.<locals>.send_human_input at 0x77cd357fe7a0>
dim_sim = <dimos.e2e_tests.dim_sim_client.DimSimClient object at 0x77ce46785e80>
explore_house = <function explore_house.<locals>.explore at 0x77cd357ac220>

    @pytest.mark.self_hosted_large
    def test_go_to_the_bed(lcm_spy, start_blueprint, human_input, dim_sim, explore_house) -> None:
        start_blueprint(
            "run",
            "unitree-go2-agentic",
            simulator="dimsim",
        )
        lcm_spy.save_topic(".../McpClient/on_system_modules/res")
        lcm_spy.wait_for_saved_topic(".../McpClient/on_system_modules/res", timeout=1200.0)
    
        explore_house()
    
        human_input("go to the bed")
    
>       lcm_spy.wait_until_odom_position(-3.567, -1.332, threshold=2, timeout=180)

dim_sim    = <dimos.e2e_tests.dim_sim_client.DimSimClient object at 0x77ce46785e80>
explore_house = <function explore_house.<locals>.explore at 0x77cd357ac220>
human_input = <function human_input.<locals>.send_human_input at 0x77cd357fe7a0>
lcm_spy    = <dimos.e2e_tests.lcm_spy.LcmSpy object at 0x77cd3596ec90>
start_blueprint = <function start_blueprint.<locals>.set_name_and_start at 0x77cd357ff380>

dimos/e2e_tests/test_dimsim_spatial_memory.py:32: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
dimos/e2e_tests/lcm_spy.py:167: in wait_until_odom_position
    self.wait_for_message_result(
        predicate  = <function LcmSpy.wait_until_odom_position.<locals>.predicate at 0x77cd357ffa60>
        self       = <dimos.e2e_tests.lcm_spy.LcmSpy object at 0x77cd3596ec90>
        threshold  = 2
        timeout    = 180
        x          = -3.567
        y          = -1.332
dimos/e2e_tests/lcm_spy.py:153: in wait_for_message_result
    wait_until(
        event      = <threading.Event at 0x77cd36fb3e90: unset>
        fail_message = 'Failed to get to position x=-3.567, y=-1.332'
        listener   = <function LcmSpy.wait_for_message_result.<locals>.listener at 0x77cd357ac5e0>
        predicate  = <function LcmSpy.wait_until_odom_position.<locals>.predicate at 0x77cd357ffa60>
        self       = <dimos.e2e_tests.lcm_spy.LcmSpy object at 0x77cd3596ec90>
        timeout    = 180
        topic      = '/odom#geometry_msgs.PoseStamped'
        type       = <class 'dimos.msgs.geometry_msgs.PoseStamped.PoseStamped'>
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

predicate = <bound method Event.is_set of <threading.Event at 0x77cd36fb3e90: unset>>

    def wait_until(
        predicate: Callable[[], bool],
        *,
        timeout: float,
        interval: float = 0.1,
        message: str | None = None,
    ) -> None:
        """Poll ``predicate`` until it returns truthy or ``timeout`` elapses."""
        deadline = time.monotonic() + timeout
        while time.monotonic() < deadline:
            if predicate():
                return
            time.sleep(interval)
>       raise TimeoutError(message or f"Timed out after {timeout}s waiting for condition")
E       TimeoutError: Failed to get to position x=-3.567, y=-1.332

deadline   = 4202146.279438983
interval   = 0.1
message    = 'Failed to get to position x=-3.567, y=-1.332'
predicate  = <bound method Event.is_set of <threading.Event at 0x77cd36fb3e90: unset>>
timeout    = 180

.../utils/testing/waiting.py:35: TimeoutError

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

@greptile-apps

greptile-apps Bot commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This change aligns simulated gripper feedback with the coordinate system used by gripper commands. Raw MuJoCo joint position is reflected across the declared gripper range, so closed and open feedback retain their intended meanings for command consumers. The endpoint, midpoint, and normalized closed-state cases are covered in the updated adapter tests.

T-Rex validation blocked

Focused shared-memory round-trip and adapter-test execution could not complete because the repository .venv Python interpreter symlink points to a removed interpreter, and the fallback environment lacks required runtime and test packages. The blocked items are the Python interpreter tool and the required Python packages.

Confidence Score: 5/5

No product defect was established in the changed gripper coordinate conversion; the implementation matches the simulator's inverse command mapping.

There are no final actionable findings. Code inspection shows the reader reflection maps the raw simulated joint value back to the command coordinate while preserving the existing invalid-range fallback.

Files Needing Attention: No files require author changes. Runtime confirmation remains unavailable until the local Python interpreter and required packages are restored.

T-Rex T-Rex Logs

What T-Rex did

  • A pre-capture plan was established to demonstrate the legacy raw-qpos mismatch at the two endpoints and the midpoint.
  • An end-to-end attempt was made for the legacy baseline, shared-memory gripper round-trip, and focused MuJoCo adapter tests, but progress stopped because the repository virtual environment interpreter target was missing and required runtime dependencies were unavailable.
  • Command records and blockers were captured and saved as artifacts to support inspection of the environment issues.

View all artifacts

T-Rex Ran code and verified through T-Rex

Reviews (1): Last reviewed commit: "fix(sim): report gripper readback in the..." | Re-trigger Greptile

@github-actions github-actions Bot added the ready-to-merge Required CI checks have passed on this PR label Aug 26, 2026
@mustafab0
mustafab0 added this pull request to the merge queue Aug 26, 2026
Merged via the queue into main with commit 57ec50b Aug 26, 2026
30 of 31 checks passed
@mustafab0
mustafab0 deleted the mustafa/fix/sim-gripper-readback branch August 26, 2026 18:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready-to-merge Required CI checks have passed on this PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants