# AlohaMini2 browser model

This is the AlohaMini2 with AM-ARM200 follower arms, not ALOHA 2, AlohaMini1, or AlohaMini2 Pro.

Sources cloned for this implementation:

* https://github.com/liyiteng/AlohaMini — exact model revision in model.json. Source: AlohaMini2/urdf/alohamini2/urdf/alohamini2.urdf.
* https://github.com/liyiteng/lerobot_alohamini at 964f5342bd7de39f86ba1c79016ebd15b89eca32 — src/lerobot/robots/alohamini/{model_specs.py,alohamini.py,alohamini_client.py,lift_axis.py}.
* V2 assembly guide photos inspected: top-camera-mount-installed, chest-camera-installed, follower-arms-mounted, arm-camera-cables-routed.

Changes from upstream: mesh references made relative, CAD visual meshes decimated for browser speed, MJCF generated with preserved joint origins/axes and source inertias. Dummy zero/nonpositive inertias receive a 0.001 kg / 0.001 kg m² placeholder; MuJoCo balances invalid inertia triangles. The original URDF is retained in upstream.urdf. The modified URDF keeps the original numerical joint limits; its mesh files are in meshes/. Full-fidelity upstream mesh geometry remains in the public source repository.

The base is kinematic with three planar coordinates. Wheels are fixed visual geometry. This models commanded planar motion, not wheel contact, traction, acceleration, backlash or a real base controller's response. Lift travel follows the URDF's -0.3 to +0.3 meters, displayed as 0–600 mm. It is not a calibrated physical homing offset. The real V2 lift conversion is 131 mm/revolution; the driver applies direction sign -1 and has its own homing and descent-floor logic. Those motor/homing dynamics are not simulated.

## Cameras

All five mount transforms come directly from the V2 URDF; xyz below is meters in the named parent. The source CAD frame is +X left, -Y forward, +Z up. RPY is radians, URDF fixed-axis XYZ.

| Camera | Parent | xyz | rpy |
|---|---|---|---|
| Chest | vertical_link | -0.00022576, -0.06901636, 0.44798544 | 1.57079633, 1.57079633, 0 |
| Top forward | base_link | 0.00030929, -0.05490845, 1.10145848 | -3.14159265, 0.78538568, 1.57079633 |
| Top backward | base_link | 0.00034085, 0.16827, 1.0972 | -3.1416, -0.63429, 1.5708 |
| Left wrist | left_Fixed_Jaw | 0.0018122, -0.07041, -0.0052089 | -0.42318, 0, 0 |
| Right wrist | right_Fixed_Jaw | 0.0018122, -0.07041, -0.0052089 | -0.42318, 0, 0 |

The chest camera moves with the lift. Both top cameras are fixed to the base/tower; the front camera points forward/down and the back camera backward/down. The wrist cameras move with the fixed jaws and look toward the working area. Camera model +Z is treated as optical forward based on the CAD orientation and assembly images; this is not a published calibrated optical frame. A 65° vertical field of view, no distortion, and zero lens-center offset are explicit preview estimates. These previews show the simulated scene, not live video. Use measured intrinsics/extrinsics for perception work.

## Drive parity

Use the real driver's V2 values: wheel radius 0.063 m, base radius 0.195 m. The BOM's nominal wheel diameter is 127 mm; using 0.063 rather than 0.0635 preserves software parity. Wheel ordering is left/back/right, servo IDs 8/9/10. Effective row angles are 150°, -90°, 30°.

For requested forward velocity f, left velocity l, and yaw rate w in rad/s:

    wheel_rad_s[i] = (-cos(angle[i])*f - sin(angle[i])*l + .195*w) / .063
    raw[i] = round_even(wheel_rad_s[i] * 4096 / (2*pi))

Before rounding, scale all wheels proportionally if any exceeds 3000. The simulation inverts the quantized commands and integrates the resulting twist with an exact constant-twist SE(2) update. The real driver's external theta argument is degrees/second; our core stores radians/second. Positive yaw is left/CCW. Default requested caps are application choices: 0.25 m/s translation, 0.65 rad/s turn, 0.08 m/s lift, with the driver's saturation still applied. These are not certified hardware maximums.

## IK and actuator tracking

Official @mujoco/mujoco 3.13.0 WASM supplies kinematics, Jacobians, geometry distances and articulated dynamics. A C++ WASM port of the G1 application's Mink 1.3.0 task math uses upstream DAQP 0.9.1 to solve both arms together over the full model tangent space. It preserves Mink's body-frame SE(3) logarithm, Jlog correction, position/orientation costs 12/2, global LM damping .1, posture cost .03, regularization 1e-4, configuration-limit gain .95, non-arm freezing and ±1.5 rad/s operating caps. The Aloha URDF's zero velocity fields are placeholders; 1.5 rad/s is an application cap, not a manufacturer specification.

The joint reference uses the G1 application's uniform speed scaling, inward nextafter joint bounds and tick-local velocity from the guarded displacement. Position actuators receive q_input = q_reference + (kv/kp)*dq_reference, using the actual model gains. Here kp=80, kv=8, with bounded input and ±12 N m simulated force. Holds and failed solves clear feedforward. These are simulation tuning choices, not measured AM-ARM200 actuator parameters. Gravity compensation is enabled.

Collision constraints cover opposite arms, arms versus tower/chest, and arms versus the floor. They use Mink's gain .85, 8 mm minimum distance and 50 mm detection range, including the pinned implementation's `/dt` collision bound in the displacement QP. Arm segments are approximated by capsules, the tower by a capsule and chest by a box; their dimensions are explicit in prepare-aloha.py. These proxies also participate in MuJoCo contacts. They are not measured collision meshes and do not cover every part of the robot or environmental obstacle. Matching the solver does not establish physical collision safety.

Grip target offset (0,0,0.085) in each Fixed_Jaw frame is a visualization/control estimate, not a calibrated tool center. AM-ARM200 has one gripper DOF, so the three pinch choices change closing range; they do not create the G1 hand's multi-finger grasps or regulate contact force. Full/fine/half ranges use 0.65/0.35/0.5 radians of simulated jaw closure.

## Controls

* Left stick: forward/back, strafe left/right. Right stick: CCW/CW turn and lift up/down.
* Main trigger: close that gripper. Stick click: cycle pinch range.
* Hold Y/B: clutch that arm. Both edges reanchor to the current robot pose.
* Hold X/A: 0.1× motion for that hand and its associated stick controls.
* Left Menu: latched emergency stop; reset cannot clear it. Release stop through the explicit screen button; arms remain paused.
* Right Menu: arm pause/resume; reanchor to current robot hands and controller coordinates. Base/lift remain independently available while arms are paused.
* Two complete X/Y/A/B chords, with release between: reset the robot and leave arms paused. No extra hold step. Right Menu resumes.
* Enabled arms follow poses independently of button/stick report timing; no grip or trigger needs to be held. A pose older than 300 ms holds only the affected arm and zeros its drive inputs. Reacquisition discards that hand's old controller reference.
* Button/stick reports older than 500 ms zero base/lift input, without stopping arm tracking. Digital buttons and pinch state stay latched until a real change or disconnect. Menu and mode edges come from reports, never from timeout-generated releases. A new connection's first button state is a baseline (stop is immediately actionable).
* Per-hand connection epochs prevent pose jumps across fast reconnects. Page blur/hidden state pauses arms and clears keyboard motion.

No real robot transport is implemented or invoked. All robot commands end at the local simulation.

## Validation

test-robot.mjs runs the actual MuJoCo and C++ WASM modules in Node: drive round trips and saturation, exact circular motion, all camera frames, lift-parent behavior, bimanual convergence, unreachable target/joint/speed limits, stop/reset, stale data, clutch and 0.1× mapping. validation.json stores one synthetic timing/convergence run, not browser frame timing, controller latency, or hardware accuracy. test-mink-parity.mjs compares native task math, DAQP outputs and guarded actuator commands with pinned Python G1/Aloha fixtures. test-mink-integration.mjs independently assembles constraints using MuJoCo WASM and replays the Aloha command trajectory. Detailed provenance and measurements are in tests/mink-reference.

The existing Bluetooth decoder and connection-lifecycle tests also run. Browser visual QA and physical controller/robot tests were not performed in this implementation pass.

test-arm-input.mjs adds independent notification streams through the actual browser application's decoder/snapshot and MuJoCo engine. It tests continuous enabled arm movement with no button heartbeat, absent initial button packets, long-held clutch/slow/pinch, menu edges, independent pose loss/recovery, base/lift timeouts, stop and reconnects. This deterministic test reproduced the old unintended arm dead-man before the fix.

drive-fixtures.json in the repository root contains 100 fixtures evaluated directly from the pinned upstream Python driver's conversion functions. JavaScript wheel commands match all 100 exactly, including proportionally saturated combinations. The IK suite also checks orientation convergence.
