GitHub
Andino (official image)
Photo: Ekumen — the real robot, from the official Andino repository (docs/real_robot.png)
#001

Andino

Ground — wheeled · Differential rover
Differential-drive ground robot
FailHarness checks: 2 clean · 1 warn · 0 err · 29 not checkedLatest recorded run: drive 0.347% · turn 9.104° vs ground truth

Evidence: the recorded runs and their sources ↓

An open-source ROS 2 robot from Ekumen, built to teach real robotics on a budget: two driven wheels and a caster, a spinning lidar on top, a Raspberry Pi for a brain. It is the first robot in the Robodex that RobotForge did not design — its spec here was read out of the official project's own files.

Spec

Battery3x 18650 Li-ion cells, ~11.1 V nominal (official BOM holder); capacity varies by cell — the spec uses 2600 mAh as a labelled estimate
Chassis0.25 m long x 0.10 m wide base deck (official URDF base.yaml); overall width 0.153 m across the wheels, wheel track 0.128 m
ComputerRaspberry Pi 4 + Arduino Nano (official BOM)
Drivetraindifferential — 2 driven wheels + ball caster
Ground clearance~0.03 m — estimate; the URDF base plane rides at wheel radius (0.0331 m)
Height~0.18 m — estimate derived from the URDF deck stack
Mass3.364 kg — the sum of the official URDF link masses (several are round values upstream)
Motors2x DG01D-E gearmotors with encoders, 48:1 gearbox, rated 3-9 V (SparkFun listing linked from the official BOM), driven via an L298N
SensorsRPLIDAR A1M8 2D lidar (top), Raspberry Pi Camera V2 (front), BNO055 IMU, wheel encoders
Wheels66.2 mm diameter x 25 mm — official wheel.yaml radius 0.0331 m
Source: Ekumen-OS/andino (official project repository) · linkLicense: BSD-3-ClauseSpec file: examples/andino.jsonLicense is per-package LICENSE files in the repo (each reads BSD 3-Clause, (c) 2023 Ekumen Inc.); the repo root carries no single LICENSE file. Spec values were read from andino_description/config/andino/*.yaml and the andino_hardware BOM.

GateA pass/fail test in the Gazebo simulator: the twin drives 1 m, turns 90° and stops, and the result is measured against where it really went. runs

Measured against simulator ground truth at the locked thresholds: pass ≤ 5% / 5°, fail over 10% / 10°. The gate tests the spec's simulated twin, not a physical robot.

RunDrive vs truthTurn vs truthOdometry distanceOdometry heading
Run 11.497%3.425°0.003%8.285°
Run 20.347%9.104°0.003%11.624°
  • Run 1: Gate run 1 on scythe, 2026-10-26, build/gate-andino-1/gate-report.json — run verdict MARGINAL (motion vs ground truth PASS; odometry heading MARGINAL)
  • Run 2: Gate run 2 on scythe, 2026-10-26, build/gate-andino-2/gate-report.json — run verdict FAIL (turn vs ground truth MARGINAL at 9.104 deg; odometry heading FAIL at 11.624 deg)
Two recorded runs on scythe (2026-10-26), no PASS — recorded as measured; the locked thresholds were not touched. The drive is excellent in both runs (1.497% / 0.347% against ground truth, odometry distance error 0.003%), but the pivot turn is the failure mode: the robot is heavy (3.364 kg — the sum of the official URDF link masses) on two passive caster balls, so in a turn it physically under-rotates while its wheel odometry (perfect joint integration) over-reads heading by 8.285-11.624 degrees. The gate's own suspect line names wheel_separation / caster scrub. Diagnosis recorded; the verdict was not massaged.

HarnessThe robot’s wiring. The harness check adds up current on each power rail and flags wires that are too thin or rails that are overloaded. checks

Electrical checks over this robot's documented wiring. Not a simulation; the headline is counts, never a blanket pass.

Harness checks: 2 clean · 1 warn · 0 err · 29 not checked

2 checked clean · 1 with warnings · 0 with errors · 2 partial · 29 not checked · missing data: cable.gauge_awg ×12, connector at both ends (connector_from / connector_to) ×12, current_a for nano ×7

Check-by-check counts and missing data
CheckCheckedPartialNot checked
voltage drop006
ampacity006
logic levels303
rail budget022
connector match0012

Findings (1):

  • [WARN] controller -> nano: logic levels 3.3 V and 5 V do not agree and no level shifter is recorded on the link — a 3.3 V input is not 5 V tolerant; add a shifter or record one

Missing data — the datasheets still to hunt. A check that could not run says what it needs:

Missing inputBlocksNeeded by
cable.gauge_awg12battery -> driver, battery -> regulator, regulator -> controller, controller -> nano …
connector at both ends (connector_from / connector_to)12battery -> driver, battery -> regulator, regulator -> controller, controller -> nano …
current_a for nano7battery -> regulator, regulator -> controller, controller -> nano, controller rail
cable.length_mm6battery -> driver, battery -> regulator, regulator -> controller, controller -> nano …
current_a for motors5battery -> driver, driver -> motors, driver rail
connection voltage (volts)2driver -> motors, controller -> lidar
current_a for controller1regulator rail
current_a for driver1battery rail
current_a for regulator1battery rail
logic_v for camera1controller -> camera
logic_v for imu1controller -> imu
logic_v for motors1motors -> nano
rated output current for battery1battery rail
rated output current for controller1controller rail

Report: catalog/harness-reports/andino.json · checked 2026-10-27 · reproduce with robotforge harness examples/andino.json

Take it apart

Every spec becomes a kit of simple, 3D-printable parts (robotforge cad). This viewer lays the kit out the way the kit's own assembly file does, straight from the spec's numbers — flat views, or in 3D with the toggle. Click any part.

Loading examples/andino.json…

Parts kit

The take-apart kit robotforge cad generates from this robot's spec: every part the generator defines, what it does, and the parameters worth tweaking with the values this robot's kit ships with. Each parameter carries the generator's own source label — from spec numbers come out of the spec, derived numbers are computed from them, and assumption numbers are typical values the spec does not carry.

Chassis plate ×1

parts/chassis_plate.scad

The deck everything else bolts to.

250 x 100 mm footprint, 4 mm thick

ParameterValueWhere the number comes from
chassis_length_mm250 mmfrom spec length_m
chassis_width_mm100 mmfrom spec width_m
plate_thickness_mm4 mmassumption — not in the spec
corner_radius_mm6 mmassumption — not in the spec
mount_hole_diameter_mm3.4 mmassumption — M3 bolt clearance
mount_hole_inset_mm12 mmassumption — not in the spec

Print note: Print flat as modeled. Suggestion: 3-4 walls, ~25% infill.

Wheel ×2

parts/wheel.scad

Rolls on the ground; one per wheel position on the robot.

66.2 mm diameter x 25 mm wide, 6 mm shaft hole

ParameterValueWhere the number comes from
wheel_diameter_mm66.2 mmfrom spec wheel.diameter_m
wheel_width_mm25 mmfrom spec wheel.width_m
shaft_diameter_mm6 mmassumption — typical DC gearmotor shaft; measure yours
hub_diameter_mm26.5 mmderived — 0.4 x wheel diameter, for looks/strength
lightening_hole_diameter_mm7.9 mmderived — decorative; delete the loop below for a solid wheel

Print note: Print flat as modeled. Suggestion: print one first and check the shaft fit before printing the rest.

Motor mount ×2

parts/motor_mount.scad

Side plate that holds one drive motor so its shaft lines up with a wheel's axle.

41 mm wide plate, shaft hole at axle height 33.1 mm

ParameterValueWhere the number comes from
shaft_center_height_mm33.1 mmfrom spec wheel radius (diameter_m / 2) — the axle height above ground
mount_width_mm41 mmderived — wheel width + 16 mm of plate around the motor
mount_height_mm44.1 mmderived — reaches from the ground past the axle to the chassis plate
mount_thickness_mm4 mmassumption — not in the spec
shaft_hole_diameter_mm6.4 mmderived — shaft diameter + 0.4 mm slip fit
flange_depth_mm16 mmassumption — how far the bolting flange reaches under the chassis
flange_hole_diameter_mm3.4 mmassumption — M3 bolt clearance

Print note: Suggestion: 40% infill — this part takes the motor's twist. The motor itself is not a printed part.

Caster ×2

parts/caster.scad

Ball caster that keeps a two-wheel robot level (one front, one rear). Placeholder for a real ball-caster unit.

ball diameter 30 mm (= ground clearance, so the ball tops out at the deck)

ParameterValueWhere the number comes from
caster_ball_diameter_mm30 mmfrom spec ground_clearance_m — ball top meets the deck underside
caster_stem_height_mm10 mmderived — reaches into/past the deck plate for bolting
caster_stem_diameter_mm15 mmderived — half the ball diameter

Print note: Suggestion: expect a loose fit — sand or scale 1-2% for a snug cup. A bought ball caster rolls far better.

Lidar mount ×1

parts/lidar_mount.scad

Post that raises the 2D lidar to the top of the robot so its scan plane clears the chassis.

post 170 mm tall, top disc 70 mm diameter

ParameterValueWhere the number comes from
post_height_mm170 mmderived — spec height minus deck and disc thicknesses
post_diameter_mm12 mmassumption — not in the spec
lidar_disc_diameter_mm70 mmassumption — typical 2D lidar puck; measure yours
disc_thickness_mm3 mmassumption — not in the spec

Print note: Print upright as modeled. Suggestion: ~25% infill; the lidar itself is not a printed part.

Camera bracket ×1

parts/camera_bracket.scad

Bracket that holds a camera (rgb_camera) at the front of the robot.

44 x 34 mm plate, 12 mm lens hole

ParameterValueWhere the number comes from
bracket_width_mm44 mmassumption — small-camera class; not in the spec
bracket_height_mm34 mmassumption — not in the spec
bracket_thickness_mm3 mmassumption — not in the spec
lens_hole_diameter_mm12 mmassumption — measure your camera
foot_depth_mm20 mmassumption — how far the bolting foot reaches

Print note: Print flat as modeled. Suggestion: the lens hole is a visual guide — measure your camera before drilling.

Reproduce it

Run these from a clone of the repository.

robotforge validate examples/andino.json
robotforge urdf examples/andino.json --out build/andino.urdf
robotforge cad examples/andino.json
robotforge run-gate examples/andino.json
robotforge harness examples/andino.json