Kevin Corvisier
← Hexapod overview

Hexapod, part 4 of 4

Learning to walk

The scripted gait from part 2 assumes six working legs. Here the robot learns to walk by trial and error in simulation, first finding a tripod gait on its own, then learning to keep going when a leg gives out.

The setup

Training uses PPO in NVIDIA Isaac Lab, with 4,096 copies of the robot simulated in parallel on one GPU. I started from Isaac Lab's velocity-tracking task, the same one used to train ANYmal, Go1 and Spot, and rescaled it. Those robots walk at about 1 m/s, while this one walks at 0.1-0.2 m/s, so the velocity targets, noise, pushes and reset speeds are all about five times smaller.

TaskFollow a random velocity command: forward −0.1 to 0.2 m/s, sideways ±0.1 m/s, turning ±1 rad/s
ObservationsBody velocity, gravity direction, the command, the 18 joint positions and velocities, the previous action
Actions18 joint position offsets from the standing pose
PolicyMultilayer perceptron, 256-128-128, ELU; trained with rsl-rl
Servo modelXL430 torque-speed curve: 1.4 N·m stall torque, 6 rad/s no-load speed
Randomised per robotFoot friction (rubber, 0.9–1.2), body friction (PETG, 0.3–0.5), added mass, centre of mass ±1 cm, random pushes

A bug that broke every foot

The robot comes from the same URDF description as the ROS packages. A script turns it into a training model, with servo limits, a fixed head, and collision shapes only on the feet, legs and body. One problem hid in that import step, and it would have been easy to mistake for a reward problem.

Isaac Sim's URDF importer nests each link inside its parent, but Isaac Lab's contact sensors expect every body to sit directly under the robot. With the nested layout, only the body reported contacts. Every reward term that depends on foot contact (air time, slip, support) was silently computing zero. I wrote a second script that flattens the bodies into a single USD file, and added sanity checks that run before any training: does the robot stand, and is the load balanced? With 3.3 kg on six feet, each foot should carry about 5.4 N.

Shaping a gait

I deliberately didn't impose a gait clock. A fixed tripod rhythm stops making sense the moment a leg is lost, and the damage study below would be biased if the policy were built around one. So the gait had to come out of the rewards. It took several runs:

RunChangeResulting gaitFalls, 1 leg lost
v1First setupSkating and shuffling85%
v2Rubber feet, self-collision, slip penaltyWalks on two diagonal feet19%
v3Penalty for fewer than 3 feet down; air time capped at 0.35 sLimping tripod62%
v4–v5Foot clearance penaltyFailed to learn–
v6v3 + left-right symmetryClean symmetric tripod78%
v7–v9Rewards for standing on six feet when idleAction noise grew without bound–

The diagonal gait of v2 came from the reward Isaac Lab's template uses for quadrupeds: "time in the air". Combined with penalties on slip and power for every foot on the ground, it made balancing on two feet, with quick taps of the others, the best deal. A penalty for having fewer than three feet loaded fixed that. Insects keep three feet down at walking speed for the same stability reason.

Gait diagram of run v2: long stance bars on R3 and L1 only, the other legs tapping briefly
Run v2: R3 and L1 carry the robot while the other four legs only tap.
Gait diagram of run v6: two groups of three legs alternating regularly
Run v6: R1, R3 and L2 alternate with R2, L3 and L1, the same tripod as the scripted gait.

Two failures were as instructive as the successes:

Symmetry

The robot is left-right symmetric, but nothing forces a policy to be. PPO here trains on a mirrored copy of every sample: legs 1↔6, 2↔5 and 3↔4 are swapped and the sign of the coxa angles is flipped. I checked the mirroring in simulation to 0.01 mm. Symmetry was the change that turned the limping tripod of v3 into the clean gait of v6.

Losing a leg

Policy A (run v6) walks well but falls 78% of the time when one leg stops working. Policy B is trained for that case:

Both policies were evaluated on 22 conditions: intact, each of the 6 single legs, and each of the 15 pairs. Each condition ran 50 episodes at 0.15 m/s.

A · standardB · damage-aware
Falls, one leg lost78%0.3%
Speed with one leg lost, vs intactusually falls102%
Falls, two legs lost (never seen in training)87%72%
Energy, intact71 J/m97 J/m
Policy A, front-left leg disabled: it falls.
Policy B, same leg disabled: it keeps walking.

The hardest test is losing a leg mid-walk. Policy A falls immediately; policy B continues and covers 1.19 m in the next 8 seconds. Robustness has a cost, though: B uses about 35% more energy per metre when intact. And it doesn't generalise to two lost legs, which it never saw in training.

What's next

Takeaways

Most of the work in RL locomotion is in the details. The robot model, the contact setup and the reward terms decide whether the policy learns a real gait or finds a loophole, and a silent sensor bug can look exactly like a reward problem. Gait diagrams and systematic evaluation over many episodes say far more than a single good-looking video.