← MuJoCo Sandbox

Engineering journal · September 9, 2026 · MuJoCo 3.12 · Unitree Go2 model from MuJoCo Menagerie

A quadruped with leg-mounted floats walks into a basin, floats across and climbs out

An unarmed Unitree Go2 model, used as a stand-in, carries four float capsules on its thighs. It walks down a smooth ramp into a calm basin, reaches its front legs forward so the floats point ahead, floats across on two idealised thrusters and climbs out on the far bank. The gait is a scripted crawl written for this one course; nothing is learned.

The complete crossing, replayed from the saved simulation in three synchronised views: following, side and overview. Walking plays at 8×; from 2 s before floating until 6 s after the exit begins it slows to 2×, and the current speed is labelled on screen. It opens on a 2 s still and holds the last frame for 3 s. Panels show buoyancy, weight, foot contacts and thrust from the run.
10.8 s
longest continuous unsupported float, no ground contact
166.84 N
still-water buoyancy against 166.83 N weight; with buoyancy off it sinks 10.6 cm
4 / 4
feet on the far bank at the end, 0 N buoyancy, no MuJoCo warnings
446.0 s
simulated for the whole crossing, in 67.4 s of wall time
40 min 5 s
from new goal to a validated, recorded crossing
Where it stands: one complete crossing of one known course, validated and recorded on September 9, 2026. Simulated: rigid bodies and contact in MuJoCo, plus our own buoyancy and drag on each float and on an assumed sealed hull. Scripted: the crawl gait, the float posture and the thrust feedback, all written for this scene. Idealised: the two water thrusters, which are applied forces rather than simulated propellers. Not claimed: that a real Go2 is amphibious, that the float sizes match any product, or that one crossing shows robustness.

1 · The brief

September 9, 2026, 12:50 PDT · a new goal, from a static scene to a finished video

The request was for the smallest MuJoCo simulation that shows a legged robot walking into water and out again, and how leg-mounted float attachments work. It needed no radio links and no autonomy: just the route in and out, the attachments doing their job, and a video of the whole crossing.

Two pieces of steering from the user shaped the result. Rock-like obstacles were added to the banks and then removed at the user's request, so the scene would show what it is really about; the banks became smooth 15.2° ramps. The user also asked two direct questions. Was the robot using walking dynamics already programmed for it? And did the front legs reach forward so the floats pointed ahead with them? The second question led to a correction: the float posture was changed so that both front legs, and their floats, extend forward. Chapter 4 covers both answers.

Robot

  • Unitree Go2 physical model from MuJoCo Menagerie, used as a stand-in
  • Unarmed; vendor mass 15.206 kg
  • Four float capsules add 1.8 kg, for 17.006 kg in total

Course

  • Start at x = −2.55 m on a level bank, finish at x = 3.95 m
  • Two 1.4 m ramps at 15.2°, bed at −0.38 m
  • Waterline at −0.14 m: 0.24 m of water in the middle

Not in scope

  • Radio, perception or autonomy
  • Waves, CFD or a fluid mesh
  • A calibrated replica of any real hardware
The basin from above: two sand banks, ramps down to a blue basin, the robot with floats on the near bank
The static scene, rendered and inspected before anything moved: level banks, two ramps, a calm basin and the robot at its start. Orange posts stand at each end of the course.
Side view of the basin with the robot standing on the left bank
The side camera: the robot on the start bank, the finish bank across the basin.
View from the robot's body camera down the basin toward the far bank
The body-mounted front camera at the start. It renders images for the video, can dip below the waterline, and is not an input to the controller.

2 · Four floats on the thighs

September 9 · rigid attachments; the vendor model left as shipped

Each thigh carries one capsule float: radius 0.085 m, a 0.20 m straight section, 0.37 m overall, about 7.11 L. It is a rigid child body of the thigh, mounted 0.14 m outward and 0.07 m below the thigh joint, so it turns with the upper leg. There is no separate deployment joint: the floats point forward when the legs do. Each capsule has a mass of 0.45 kg and its own capsule inertia. The straps, brackets and thruster shells are drawn with zero mass; their mass is counted inside that allowance.

The Go2 model stays as Menagerie ships it: inertias, joint limits and meshes are untouched, and all 22 vendor files match a SHA-256 manifest. Our changes are made where the scene is assembled: the floats, a stiffer foot contact (chapter 6), and actuator force limits that enforce the motor torque caps. The floats are real collision shapes, not decoration.

Close-up of the Go2 model standing on sand with four dark teal float capsules strapped to its thighs
The attachments: one 0.37 m capsule per thigh, held by a bracket 0.14 m out from the leg.
PropertyValue
Vendor Go2 mass15.206408 kg
Floats4 × 0.45 kg = 1.8 kg
Total mass17.006408 kg, weight 166.83 N
Capsuleradius 0.085 m, straight section 0.20 m, overall 0.37 m, about 7.11 L
Mountrigid on the thigh, 0.14 m lateral, 0.07 m below the thigh origin
Torque capsabduction and thigh ±23.7 N·m, knee ±45.43 N·m, in both the control range and the actuator force limit
Joint limitsinherited from the Go2: abduction ±1.0472 rad, knee −2.7227 to −0.83776 rad
Integration2 ms implicitfast; control at 100 Hz; water forces every step (500 Hz)
A vendor model is not a vendor gait. Menagerie supplies the Go2's bodies, joints and actuators. It does not supply a walking controller, so everything that moves the robot here was written for this scene.
Design assumptions. Float mass and size are illustrative design choices, not a product specification. The sealed hull assumed in chapter 3 does not claim that a real Go2 is waterproof or amphibious.

3 · A water model from sampled volume

September 9 · buoyancy and drag on each float, rebuilt and applied every physics step

MuJoCo supplies rigid-body dynamics and contact, but no water. Its built-in fluid models act as a medium filling the whole scene, so we added localised forces instead. Each capsule is filled with sample points on a 25 mm grid, weighted to the capsule's exact volume. A sample counts as wet below the waterline at −0.14 m, with a 25 mm smooth transition, and only inside the basin. The wet fraction gives the buoyancy, F = ρ g Vsubmerged with fresh water at 1,000 kg/m³, applied at the centre of the submerged volume. A float riding lower than the others therefore also rolls or pitches the body. The central body is treated as a sealed hull of about 4.01 L, matching the base collision box.

Drag acts at the same point: linear plus quadratic, −f(7v + 12|v|v), and rotational, −f(0.20ω + 0.10|ω|ω), where f is the submerged fraction. The coefficients are chosen to dissipate energy; they are not tank measurements. The five contributions (four floats and the hull) and the thrusters are cleared and rebuilt at every 2 ms step and applied with mj_applyFT, so no stale force carries over from one step to the next.

The still-water check

Before any wading, an isolated test places the robot in its float posture over the middle of the basin and runs 7,000 steps of 2 ms. Then it runs again with buoyancy switched off.

166.84N
buoyancy at rest in still water
weight 166.83 N; no contact with the bottom
10.6cm
how far it sinks with buoyancy off
down onto the basin floor
170.99N
peak buoyancy during the full crossing
from the run report
0.0W
highest drag power over the crossing
drag only ever removed energy

Another test pins the model down. Fully submerged, the total buoyancy equals ρ g times the summed volume of the four capsules and the hull, and with the body moving the drag power is negative.

What is not modelled. There is no fluid mesh, CFD, waves, wakes, splashes, water ingress or deformable float. The model is a calm basin with buoyancy and drag applied at points.

4 · A scripted crawl and a float posture

September 9 · programmed control on a known course; nothing is learned

The walking is a slow crawl written for this scene, with one leg in the air at a time, in the order rear-left, front-left, rear-right, front-right. Before each step the body shifts its centre of mass over the three supporting feet (to within 4.5 cm, or for at most 2 s). The swing leg then moves 12 cm forward in 0.65 s with a 7 cm lift. The next step waits until that foot reports contact, after at least 0.2 s and at most 0.9 s. Inverse kinematics turns foot targets into joint targets. Each motor applies 120 N·m/rad stiffness and 4 N·m·s/rad damping plus gravity and contact-force feedforward, clipped to its torque cap. The body pitches to follow the ramp, and as the robot wades deeper, the feedforward subtracts the current buoyancy from the weight the feet must carry.

The course is known in advance. The controller reads exact ramp heights from the scene definition and walks a fixed lane. That is a scripted terrain fixture, not perception.

Whose gait is this? It isn't Unitree's and it isn't learned. Menagerie's Go2 comes without a controller, so the crawl, the float posture and the thrust feedback were all written here and tuned for this one course. That answers the user's question: the robot is not running walking dynamics programmed by its maker.

Front legs forward

When the body passes x = 0.10 m, in the deep middle of the basin, the controller stops stepping and moves the joints, at up to 0.8 rad/s, to the float posture. The front thighs go to −1.50 rad and the knees to −0.90 rad, so the front legs reach forward; the rear legs fold at 1.30 and −2.50 rad. The floats turn with the thighs. In this pose the front floats point about 99.7% forward and 7.1% downward relative to the body. A test checks the pose against the joint limits and checks the float direction. At x = 1.60 m the exit phase begins: the feet are placed back under the hips and the stepping order restarts.

The slowed section of the main film, all at 2×: from 2 s before the float posture until 6 s after the exit phase begins. Watch the buoyancy bar reach the weight, the feet lift off and the ground-contact flag clear. Rendered from the simulated trajectory.
PhaseStarts at (s, simulated)Body x (m)Switch
Approach2.00−2.57after a 2 s settle
Water entry75.79−1.60top of the entry ramp
Floating225.590.10deep middle of the basin
Exit238.221.60near the exit ramp
Complete443.013.96past the 3.95 m finish, nearly stopped

Phases switch on the measured body position along the known course. The thrusters (next chapter) are gated by measured float immersion instead.

5 · Idealised thrusters

September 9 · forces applied by feedback, with nothing simulated behind them

Floating needs propulsion, and the robot has no propellers. Two thrust points sit at the rear of the body, 0.21 m behind its centre and 0.10 m to each side. Each applies a force along the body's forward axis, capped at ±6 N. That is all they are: idealised applied forces. No propeller, paddling leg, wake or reaction flow is simulated.

A feedback loop sets them, with a forward-speed target of 0.17 m/s while floating and 0.055 m/s while wading, and a differential term that holds heading and lane. They act only inside the basin and only when the floats are wet: the force scales with mean float immersion and reaches full strength at 25%. On dry land no propulsion is applied at all. A test commands 6 N on both thrusters with the robot standing on the bank and checks that no force reaches the chassis.

Video frame: the robot floating with front legs forward, panels reading buoyancy 167 N, weight 167 N, no ground contact
A frame of the main film at 229.6 s simulated, 2× replay: buoyancy 167 N against 167 N weight, no ground contact, 0 of 4 feet touching, 5.0 N of thrust.
Gate actions on physical evidence. Thrust follows measured immersion rather than a phase label, so it cannot push a dry robot along the bank.

6 · What went wrong on the way

September 9 · four failures fixed during the build, and one change of scope

Several failed runs came before the crossing completed. Each fix changed physics settings or control. No test threshold was relaxed.

Soft feet sank. The vendor's foot contacts are soft, and under this robot the feet sank too far into the ground. The scene now sets foot contact to solref 0.01/1 and solimp 0.95/0.99/0.001 with no margin. The deepest ground penetration in the final crossing is 6.9 mm.
Two answers for the ramp height. The controller's ramp-height calculation disagreed with the ramp geometry, and the mismatch disrupted the climb out. A test now casts rays at 60 points along the course against the actual ramp meshes and requires the controller's height map to agree within 10−6 m.
Feet that walked on without the body. The foot targets ran open loop. When feet slipped, the script kept advancing, recording forward progress the body never made. The gait reference is now re-anchored to the measured body on every control update and is never more than 3.5 cm away from it. After swimming, the stepping order restarts with the feet placed under the hips.
A stall at the wet exit. After floating, the robot could not regain enough traction to climb out. Its weight had to pass from the floats back to the feet, and the feet were not reaching the bed firmly enough to take it. The fix asks for 0.34 m of leg clearance instead of the 0.30 m walking height until the body passes x = 2.4 m, so the legs reach down to the bed and take load, then returns to 0.30 m. In the final run the climb out, from 238.22 s to the finish at 443.01 s, is the longest phase of the crossing.
Rocks removed at the user's request. Rock-like obstacles were added to the banks, then removed so the scene would focus on the attachments and the change from walking to floating and back. The banks are smooth ramps.
These fixes fit this scene. The exit clearance and the restart sequence were tuned for this basin. They do not show that the controller can handle unfamiliar terrain or disturbances.

7 · Results

September 9, 13:30 PDT · one complete crossing, validated twice and recorded

10.8s
longest continuous unsupported float
11.01 s in total; no ground contact, buoyancy above 80% of weight
4/ 4
feet on the far bank at the end
buoyancy 0 N; finish line crossed at 443.0 s
6.9mm
deepest ground penetration
test limit 10 mm
16.9°
largest body tilt, about
zero MuJoCo warnings over the run
The climb out, from the start of the exit phase to the far bank. The first 3 s of the clip play at 2×, the rest at 8×; the speed and simulated time are labelled on screen. Rendered from the simulated trajectory.
Measured in the final crossingValue
Success: finished, floated over 2 s unsupported, 4 feet down, buoyancy under 1 N at the endyes
Simulated time, including a 3 s final settle446.0 s
Wall time for the run67.4 s
Longest / total unsupported float10.816 s / 11.010 s
Peak buoyancy (weight 166.83 N)170.99 N
Final buoyancy; feet touching0 N; 4 / 4
Finish line x = 3.95 m crossed at443.01 s
Final body x, after settling back about 4 cm3.92 m
Deepest ground penetration6.9 mm
Lowest body up-vector (largest tilt about 16.9°)0.957
Peak motor torque, fraction of cap100%
Float contact with terrainrear-left float, entry ramp
MuJoCo warnings0
Video frame at water entry: robot at the top of the ramp, all four feet down
Water entry at 75.8 s: four feet down, no buoyancy yet.
Video frame at the start of the exit phase: robot still floating near the exit ramp
Exit begins at 238.2 s: still afloat at 162 N buoyancy, no feet down yet.
Video frame at completion: robot standing on the far bank beside the finish posts
Complete at 443.3 s: on the far bank, four feet down, 0 N buoyancy.
Kept on record. Peak motor torque reached the caps, so the gait has no torque margin at its hardest moments. The rear-left float touched the entry ramp; the floats stay collision shapes and that contact is part of the run. After crossing the finish line the robot settled about 4 cm back, ending at x = 3.92 m, behind the 3.95 m line.

On Apple Silicon macOS, all 20 tests passed (16 from the earlier demos plus 4 new), the native viewer smoke test passed, and all 22 vendor file hashes matched. The final video is 1920 × 1080 at 25 fps, 1,712 frames and 68.48 s; its full stream decoded without errors, and the entry, floating, climb and final frames were checked by eye. This page plays a web re-encode of that video.

The goal took 40 min 5 s of elapsed time, from 12:50:04 to 13:30:09 PDT, including the user's steering, development, failed experiments, tests, rendering and communication. MuJoCo ran on the CPU, and nothing was trained.

8 · Next steps

What one scripted crossing on one known course does not yet show

  1. Many crossings, not oneVary the start position, ramp angle, water depth and float mass, and report success over every attempt, failures included.
  2. Rough banks againBring back the rocks set aside during the build, with the gait sensing the terrain instead of reading an exact height map.
  3. Simulated propulsionReplace the idealised thrusters with paddling legs or propellers whose forces come from the water model.
  4. Calibrated water and disturbancesFit the drag coefficients to tank measurements, and add waves or a current as a documented disturbance.
  5. A learned gait, measured against this oneLearn the crawl and the float transitions under disturbances, keeping this scripted controller as the yardstick on held-out seeds.

Float sizes, drag coefficients and the sealed hull are assumptions. This is a concept demonstration in simulation, not a calibrated replica of any hardware.