Skip to content

Add robot profile: unitree/g1/unitree.g1.mujoco-drake-push.v1 - #98

Open
hossein6191 wants to merge 2 commits into
fabricfoundation:unitree-g1-tier-1from
hossein6191:unitree-g1-tier-1-push
Open

Add robot profile: unitree/g1/unitree.g1.mujoco-drake-push.v1#98
hossein6191 wants to merge 2 commits into
fabricfoundation:unitree-g1-tier-1from
hossein6191:unitree-g1-tier-1-push

Conversation

@hossein6191

Copy link
Copy Markdown

Tier 1 · Simulator Skill Execution · Unitree G1

A payer names where an object is and where it should end up. The robot turns
to face it, plans a collision-free path over it with a constrained IK solver
(Drake), and pushes it to the commanded destination in MuJoCo.

Scope: simulator-only submission. No physical robot was used.

Not a replayed trajectory

Both ends of the motion are parameters of the paid action. The turn angle
comes from the object's observed bearing, and every waypoint is solved at run
time against its measured pose — the trajectory does not exist until the
request arrives. Stage transitions fire on sensed conditions; timers are
failure guards only.

Payment safety

Case Result Settles
Unpaid request PAYMENT_REQUIRED no — robot does not move
Params edited after signing PARAMS_HASH_MISMATCH no
Expired envelope ACTION_EXPIRED no
Target outside workspace PARAMS_OUT_OF_RANGE no
Deliberate failure skill ACTION_FAILED no
Simulator crash ACTION_FAILED no
Replayed idempotency key IDEMPOTENCY_REPLAY no — not re-executed
Valid paid action success yes

Sim-to-Sim

The same policy object drives MuJoCo (menagerie MJCF, position servos) and
Drake (official Unitree URDF, PD actuators). 4 of 4 sampled target pairs
agree, object finishing within 26–47 mm across engines.

Task performance

7 of 8 sampled targets delivered. The eighth is outside the reachable set
and is refused in 0.15 s with an explicit reason, before any motion.

Evidence

  • registry/.../docs/validation-report.md — every number, plus measured limitations
  • registry/.../docs/evidence/ — screen recording + correlated log, same actionId
  • pytest bridge/unitree/g1/sim_bridge/tests — 42 tests, no simulator needed

Known limitations

Stated up front: the pelvis is welded in both engines (the G1 has no balance
controller and the IK planner plans against a welded pelvis); hand/table
collisions are filtered in Drake (its convex hulls are much fatter than
MuJoCo's collision primitives); and the task is a push rather than a grasp,
because this hand's fingers are fixed 57 mm apart and cannot hold the object.

Paid manipulation in simulation for the Unitree G1. A payer names where an
object is and where it should end up; the robot turns to face it, plans a
collision-free path over it with a constrained IK solver, and pushes it to
the commanded destination.

Not a replayed trajectory: both ends of the motion are parameters of the
paid action, the turn angle comes from the puck's observed bearing, and every
waypoint is solved at run time against its measured pose. Stage transitions
fire on sensed conditions; timers are failure guards only.

Payment gate:
  - unpaid request returns PAYMENT_REQUIRED and does not actuate the robot
  - params are hashed by the payer and re-checked here, so parameters altered
    in flight are refused before the simulator sees them
  - expired envelopes and envelopes for another robot are refused
  - a repeated idempotency key returns the first outcome without re-executing
  - failure, timeout and simulator crash all return settle=false

Sim-to-Sim: the same policy object drives MuJoCo (menagerie MJCF, position
servos) and Drake (official Unitree URDF, PD actuators). Both agree on 4 of 4
sampled target pairs, with the puck finishing within 26-47mm across engines.

Task performance: 7 of 8 sampled targets delivered. The eighth is outside the
reachable set and is refused in 0.15s with an explicit reason.

Evidence, measured limitations and the reproduction commands are in
registry/.../docs/validation-report.md and docs/README.md.
@hossein6191
hossein6191 requested a review from a team August 15, 2026 05:14
This bridge was written against a flat action envelope that nothing in this
repository publishes. Reading tunnel/internal/handlers/handlers.go, `POST
/action` sits behind the x402 middleware and the handler publishes

    {payload, transaction_details, timestamp}

to robot/tunnel/action -- the client's body wrapped verbatim, with the
resolved x402 payload and requirements beside it. Two defects followed, and
neither could be caught by tests that spoke the same invented dialect as the
bridge:

  * Only the flat envelope parsed, so every message the real tunnel sends
    would have been refused as malformed. An integration that passes its own
    tests and works with nothing.

  * A transaction hash was required before the robot would act. x402 verifies,
    runs the resource handler, then settles -- skipping settlement when the
    handler fails (x402/server.go calls the cancellation path "after a
    successful Verify but before/instead of Settle when the resource handler
    errors"). No hash exists when the robot is asked to move. Demanding one
    rejected every real message, and inverted the exact lifecycle that gives
    no-settle-on-failure its meaning.

Both shapes now go through one parser. On arrival the bridge requires a
verified payment plus an authorizationRef -- a digest of the x402
authorisation the tunnel verified -- rather than a settlement reference that
cannot exist yet.

Also closes a hole the wrapper opens: the tunnel forwards the request body
verbatim, so a caller can put `payment: {verified: true}` in it. Verification
is now read only from transaction_details. Measured, a body claiming a
verified payment of 999999 with a forged hash is refused with
PAYMENT_REQUIRED.

Verified against the tunnel itself, not only against its source.
tunnel/cmd/tunnelprobe drives the real handlers.PostAction through the real
zenoh.Session publisher, with the two context values the x402 gin middleware
sets on a verified payment. With this bridge subscribed:

  default -> act_probe_697261368000  SUCCESS, settle=true
  -unpaid -> act_probe_731465445000  PAYMENT_REQUIRED, settle=false

The bytes that crossed the wire are committed as
evidence/tunnel-wire-capture.json.

  48 tests (6 new, built from the Go source and the x402 v2 types)
  13 payment gate cases, 4 of them through the real wrapper

Known limitations now also records what remains unexercised: no payment was
actually settled, and the tunnel's WebSocket link to the Fabric proxy was not
used. Neither can be closed from this repository alone.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@hossein6191

Copy link
Copy Markdown
Author

Pushed a follow-up commit (0cfc670) that fixes a real integration defect
found after the original submission.

This bridge was written against a flat action envelope that nothing in this
repository publishes.
Read against tunnel/internal/handlers/handlers.go,
POST /action sits behind the x402 middleware and the handler publishes

{payload, transaction_details, timestamp}

to robot/tunnel/action — the client's body wrapped verbatim, with the
resolved x402 payload beside it. Two defects followed, and neither could have
been caught by tests that spoke the same invented dialect as the bridge:

  1. Only the flat envelope parsed, so every message the real tunnel sends
    would have been refused as malformed.
  2. A transaction hash was required before the robot would act. x402
    verifies, runs the resource handler, then settles — skipping settlement when
    the handler fails. No hash exists when the robot is asked to move. Demanding
    one rejected every real message, and inverted the exact lifecycle that gives
    no-settle-on-failure its meaning.

Both shapes now go through one parser. On arrival the bridge requires a
verified payment plus an authorizationRef — a digest of the x402
authorisation the tunnel verified — rather than a settlement reference that
cannot exist yet.

This also closes a hole the wrapper opens: the tunnel forwards the request body
verbatim, so a caller reaching the topic can put
payment: {verified: true, txHash: ...} inside it. Verification is now read
only from transaction_details. Measured — a body claiming a verified payment
of 999999 with a forged hash is refused with PAYMENT_REQUIRED.

Verified against the tunnel itself, not only its source.
tunnel/cmd/tunnelprobe drives the real handlers.PostAction through the real
zenoh.Session publisher, with the two context values the x402 gin middleware
sets on a verified payment. With this bridge subscribed:

probe bridge verdict
default (payment verified) act_probe_697261368000 SUCCESS, settle=true
-unpaid act_probe_731465445000 PAYMENT_REQUIRED, settle=false

The bytes that crossed the wire are committed as
docs/evidence/tunnel-wire-capture.json. Two things in them are why the fix
was needed: the action fields sit wrapped under payload, and there is no
tx_hash anywhere in the message.

Tests are now 48 (from 42); the payment gate table in the validation report is
now 13 cases (from 9), four of them through the real wrapper. Known limitations records what remains unexercised: no payment was actually settled,
and the tunnel's WebSocket link to the Fabric proxy was not used — neither can
be closed from this repository alone.

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