Testudog

3 min read

Testudog is the quadruped we’re building at Robotics @ Maryland for the IEEE Legged Robotics Challenge. It has twelve CubeMars AK60-6 motors, a Unitree L1 LiDAR mounted under the front of the body, and a Jetson running the show. I’m the team’s software officer, and my part is everything between the CAD model and a walking policy: a simulation I can trust, and a reinforcement learning setup that teaches the robot to walk in it.

Testudog standing on the lab floor, with its red top cover, white body and silver legs
Testudog in the lab.

A simulation worth trusting

A policy exploits every way the simulator differs from the real robot, so most of the early work was making the simulator honest.

The robot is designed in Onshape and exported to MJCFMuJoCo's XML format for describing a robot: its bodies, joints, masses, collision shapes and motors., then cleaned up by scripts with no hand edits. The most important fix is the joint zero: the exporter treats whatever pose the CAD was left in as zero, so the script reads the live mate values and rotates every joint back to legs straight down, hips vertical. The CAD also gets mass wrong (the battery is a 4.54 kg block of solid aluminum, the converters and bearings weigh nothing) so another script corrects each part.

A render of Testudog's MuJoCo model standing on a checkered floor
The MuJoCo model, generated from the Onshape CAD.

For training, the detailed meshes are swapped for simple shapes. MuJoCo collides meshes by their convex hull, so thin plates and bolts become blobs that snag on the floor. Primitives give clean contacts and keep thousands of parallel robots fast.

The motors get the same treatment: the driver’s PD law, its torque, current and 24 V limits, and a DC motor with back-EMF and inductance are all modeled, so the simulated motor can’t do anything the real one can’t.

Learning to walk

The task is velocity tracking: a joystick asks for a forward speed, sideways speed and turn rate, and the policy has to produce it. I adapted mjlab’s Unitree Go2 setup and train with PPOProximal Policy Optimization, a standard reinforcement learning algorithm for training control policies in simulation.. The policy reads 56 numbers (joints, body motion, the command and a gait clock) and outputs 12 joint targets, 50 times a second.

I later ported the task to Isaac Lab 3.0 with the same inputs, outputs, rewards and motor model, and checked the two against each other: standing still, the base settles at 0.278 m in MuJoCo and 0.281 m in Isaac. On one RTX 5080, 4,096 robots train a full run in about two hours.

Fixing the gait

The first policies that walked were shufflers. Plotting every foot’s path showed a foot took about 45 ms to clear 2 cm, and roughly 30% of its forward travel happened below that.

So I changed what a good step looks like: lift the foot to full height early in the swing, carry it, then set it down, with touchdown placed by Raibert’s heuristic.M. H. Raibert, Legged Robots That Balance, MIT Press, 1986. Place each foot relative to where the body is going, not where it is. A penalty on horizontal foot speed near the ground discourages dragging. Timing mattered: with the penalty on from the start, the robot never learned to walk and just squatted and tapped a foot, so it now ramps in once walking emerges.

The released policy tracks commanded velocity to within 0.076 m/s with no falls in evaluation.

Built for the real robot

The real robot sends motor commands at 200 Hz while the policy runs at 50 Hz, so training interpolates each output into four motor commands and adds 0 to 5 ms of random latency. The policy also has to work from estimated state, not simulator truth, so the plan is to train with realistic estimator errors and validate against the team’s real estimator.

The rest of the robot

Walking is only one piece of Testudog. Here are some of the other interesting parts of the robot.

Testudog on a workbench, its body open and wired up, with motors labeled by CAN ID
On the bench at ICRA 2026 in Vienna.
  • State estimation: an EKFExtended Kalman filter: fuses noisy sensors with a motion model to estimate things like body velocity. fusing five IMUs (one per foot, one on the body) with the joint encoders, plus a fallback that uses only the LiDAR’s IMU.
  • LiDAR SLAM with RTAB-Map, optionally fused with an OAK RGB-D camera.
  • Motor feedback through shared memory straight off the CAN bus, skipping ROS.
  • System identification that fits MuJoCo’s leg parameters to recordings from the real leg.
  • Fall recovery: a get-up policy trained in the same simulation.
  • Deployment: a ROS node on the Jetson that runs the walking policy on the robot.
A colorful 3D point cloud of a room, captured by LiDAR
A point cloud from the robot's Unitree L1 LiDAR.

What’s next

Walking on hardware: measure the real latency and sensor noise, feed them back into training, and close the sim-to-real gap. After that, give the policy a LiDAR height map and get Testudog off flat ground.

Testudog in simulation, standing among plywood walls and obstacles
Testudog in a simulated obstacle course. The goal is to walk through it.

Notes

  1. M. H. Raibert, Legged Robots That Balance, MIT Press, 1986. Place each foot relative to where the body is going, not where it is. ↩