practicableRobotics · 2023 – 2025 · Prototype v1

Meerkat

A dog-sized, modular ground robot designed as the base unit for a neighbourhood service: one fleet doing small domestic jobs like snow clearing across many homes.

Design, build & software: Leo La Plante Status: working prototype, not deployed
Meerkat prototype with its sand-coloured body shell on the workbench
Drive4WD skid-steer
Motors2 × 24 V DC gearmotor
Wheels2.50-4 pneumatic
LinksBluetooth LE + cellular

Concept

One small robot, many jobs

Instead of one large machine per house, Meerkat units share a common software stack and can work together. One unit handles a snow-shovel job; three or four clear a sidewalk; a larger group takes on a driveway. A remote operator watches the fleet and steps in only for unusual conditions.

Sized to be welcome

Large machines worry people on residential streets. The target size is roughly that of a dog, so units look approachable while working near homes and sidewalks.

Operated as a fleet

Customers define job boundaries in an app. Units move by remote control first, then console control, then GPS guidance with basic vision for traffic safety stops.

Self-supporting

A base station, the Burrow, holds the units and charges them from mains or solar power. A disabled unit can be recovered by other members of the fleet.

System architecture

Modelled first, then built

The system was defined in SysML using Gaphor before any parts were cut. The model breaks the domain into the robot and its four subsystems (drivetrain, chassis, electrical, command module), the Mission Control centre, the Burrow base station, and a deployment and maintenance unit.

SysML block definition diagram of the practicableRobotics domain
SysML block definition diagram of the domain, drawn in Gaphor.

Design decisions

What was weighed, and what was chosen

DecisionOptions investigatedOutcome
Running gearTracked versus wheeledWheeled four-wheel drive with skid steering, using pneumatic 2.50-4 tires rated 220 lb each at 30 psi.
Drivetrain layoutMotor placement, gearing, and how each motor reaches the wheelsTwo centrally mounted gearmotors, one per side. Each drives both wheels on its side through roller chain and sprockets, with spring-loaded chain tensioners.
Motor sizingMotor power against payload and working loads24 V DC gearmotors with 200 rpm output, sized against the expected payload.
BatteryLead-acid, LFP and other lithium-ion chemistries, compared on usable capacity, cycle life, weight and safetyLithium-ion pack built from 18650 cells, with work on cell balancing and charge circuits.
Motor controlCommercial controllers against custom driver circuitsCustom MOSFET driver circuits on the prototype. In hindsight, complete modules would have been faster.
CommunicationsLocal and wide-area linksBluetooth LE between robot and an onboard phone, which provides cellular wide-area connectivity to Mission Control. Both were designed and tested.
VideoImage capture and streaming approachesBenchmarked to find the most bandwidth-efficient approach for remote monitoring over cellular.
ChargingMains, solar and EV charging standards for the BurrowInvestigated using the J1772 EV charging handshake and rectifier designs to charge from standard equipment.

Build record

From CAD to workbench

Parts were modelled and assembled in FreeCAD, then fabricated and assembled in a home workshop: steel frame and plates, chain drive, battery mounting, and a formed body shell.

Software

First integrated release

Version 0.1 (December 2023) connected all three tiers end to end.

  • Robot firmware (Arduino C++): reads drive commands from the Bluetooth module, controls direction and PWM speed on each side, and reports battery voltage every 60 seconds.
  • Command module (Kotlin, Android): scans for and connects to the robot over Bluetooth LE, handles runtime permissions, and opens the drive controls.
  • Mission Control (Python, Django Channels): a WebSocket server that relays control messages and status between the operator and robots in a group.
// pRCommandModule v0_1 · MainActivity.kt
val gattCallback = object : BluetoothGattCallback() {
    @SuppressLint("MissingPermission")
    override fun onConnectionStateChange(
        gatt: BluetoothGatt, status: Int, newState: Int
    ) {
        val deviceAddress = gatt.device.address

        if (status == BluetoothGatt.GATT_SUCCESS) {
            if (newState == BluetoothProfile.STATE_CONNECTED) {
                Handler(Looper.getMainLooper()).post {
                    connectedGatt = gatt
                    intent = Intent(this@MainActivity,
                        ControlActivity::class.java)
                    intent.putExtra(BluetoothDevice.EXTRA_DEVICE,
                        gatt.device)
                    startActivity(intent)
                    connectedGatt.discoverServices()
                }
            } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
                gatt.close()
            }
        } else {
            Log.w("BluetoothGattCallback",
                "Error $status encountered for $deviceAddress! Disconnecting...")
            gatt.close()
        }
    }
}
Bluetooth LE connection handling in the command module app (Kotlin): on a successful connection it hands the device to the drive-control screen and starts service discovery; on an error it closes the link cleanly.

Results and lessons

What worked, and what I'd change

Mechanical design

The machine design worked very well. The chain drive, motor sizing and frame performed as intended on the built prototype.

Electronics

Next time I would buy complete, proven modules instead of designing custom circuits. That would free time for integration and testing.

Software

I would use AI-assisted development much more heavily to increase the pace of software work across the three tiers.