Skip to content
Voltomni
← Cart-pole balancing kit

Build guide: Cart-pole balancing kit

A stepper motor pulls a cart along a linear rail with a toothed belt; a free pole hangs from the cart. The firmware swings the pole up and balances it upright by moving the cart. Build time: 4–5 hours. Level: intermediate.

1. What's in the box

PartQtyUsed for
Arduino Nano (CH340)1Controller
NEMA 17 stepper motor, 40 mm1Drives the cart
TMC2209 stepper driver1Drives the motor quietly and smoothly
AS5600 magnetic encoder with magnet1Measures the pole angle
MGN12 linear rail, 400 mm, with MGN12H carriage1Guides the cart
2020 aluminium extrusion, cut: 400 mm beam and two 295 mm legs3Frame
Corner brackets4Join the legs to the beam
M5 T-nuts20Frame screws (14 used, 6 spare)
GT2 belt, 1 m, and two 20-tooth GT2 pulleys1 setDrive
608 ball bearings4Pole pivot (2) and idler (2)
Limit switches2Homing and end-of-travel safety
12 V 5 A power supply1Power
M3 screw and nut kit1Assembly
Printed parts: cart, pole, magnet cap, motor mount, idler mount, two feet, two belt clamps, idler sleeve10Structure
Wiring harness, power lead with DC jack, encoder and switch cables1 setWiring
M5 × 10 screws (14), M5 × 40 idler bolt and nut, M8 × 50 pivot bolt with washers and nuts, zip ties1 setFrame, idler, pivot

You also need: a computer, a USB Mini-B cable, the Arduino IDE (free), 2.5 mm and 3 mm hex keys, a small screwdriver, and two small G-clamps (or two wood screws) to fix the feet to a table. Python 3 with numpy is optional (for designing your own controller).

2. Frame

  1. Legs into the feet. Slide a T-nut into one slot near the bottom of each leg. Push each leg into a foot's socket and fix it with an M5 × 10 screw through the side of the socket into the T-nut.
  2. Beam onto the legs. Lay the beam across the tops of the legs, centred, with the legs 240 mm apart (centre to centre). Fix each leg with two corner brackets, one on each side of the leg, each with two M5 × 10 screws into T-nuts (slide the T-nuts into the beam's bottom slot and the legs' side slots first).
  3. Rail onto the beam. Slide eight M3 nuts into the beam's top slot. Lay the rail on top, centred and flush with both ends, and fix it with M3 × 10 screws through every second rail hole. Leave the carriage on the rail: if it slides off, its balls fall out.
  4. Fix the rig to the table with the two holes in each foot (or G-clamps over the feet). The cart pushes hard while it balances; an unfixed rig walks.

3. Drive

  1. Motor mount. Screw the motor to the underside of the motor mount's plate, shaft up, with four M3 × 8 screws. Slide two T-nuts into the beam's belt-side slot at the left end and fix the mount's flange with two M5 × 10 screws, with the mount's end web against the end of the beam.
  2. Idler mount. Press the two 608 bearings into the idler mount's boss from above, one on top of the other, until the top one is flush with the plate. Push the printed sleeve into the bearings. Fix the mount at the right end of the beam like the motor mount, but leave its screws loose.
  3. Pulleys. Put one pulley on the motor shaft, teeth up, with its grub screw on the flat of the shaft, about 1 mm above the plate. Put the M5 × 40 bolt up through the idler bearings from below, add a washer and the second pulley, teeth up, and the nut. Tighten the nut so the bolt, sleeve and pulley turn together with the bearings' inner rings, then tighten the pulley's grub screws onto the bolt.
  4. Cart. Fix the cart to the carriage with four M3 × 8 screws (the belt wall faces the motor side of the rail). Put four M3 nuts into the traps inside the belt wall.
  5. Belt. Clamp one end of the belt to the left end of the cart's wall with a belt clamp (teeth into the clamp's grooves, two M3 × 12 screws). Run the belt round the motor pulley, back along the far side, round the idler pulley and back to the cart. Pull it snug and clamp the second end. Then slide the idler mount outwards to tension the belt (it should twang, not flap) and tighten its screws.
  6. Check: with the motor unplugged, the cart slides the whole length of the rail smoothly, and both pulleys run level with the belt.

4. Pole and encoder

  1. Bearings. Press one 608 bearing into each end of the cart's bearing housing until it stops against the lip in the middle.
  2. Pivot bolt. From the encoder-bracket side, pass the M8 bolt through the bracket opening and both bearings. On the far side, add a washer and one nut and tighten until the bolt turns freely with no play.
  3. Magnet. Press the magnet cap over the bolt head and glue the round magnet into its pocket, centred, with a drop of superglue.
  4. Encoder. Screw the AS5600 board to the bracket, chip facing the magnet, with M3 screws and nuts. Centre the chip on the bolt. Gap between chip and magnet: 1–3 mm. Wire the board's DIR pin to GND (the harness does this).
  5. Pole. Slide the pole's hub onto the bolt after the nut so it hangs straight down, tighten the pinch screw and lock it with the second nut.
  6. Check: the pole swings freely for 20+ seconds after a push.

5. Limit switches and wiring

Push a limit switch into the pocket on top of each end mount's tower, lever on top and pointing towards the cart, and hold it with a zip tie through the tunnel under it. Push the cart against each tower by hand: the switch must click just before the cart touches the tower.

The harness is pre-wired. Connect it as follows (Nano pin → device):

NanoConnects to
D2TMC2209 STEP
D3TMC2209 DIR
D4TMC2209 EN
D5Left (motor end) switch, other side to GND
D6Right (idler end) switch, other side to GND
5V, GNDTMC2209 VIO and GND; AS5600 VCC and GND
A4AS5600 SDA
A5AS5600 SCL
  • Switches use their common (C) and normally-open (NO) terminals.
  • TMC2209 MS1 and MS2 → GND (1/8 microstepping). VM → 12 V, GND → supply GND (the 100 µF capacitor across VM/GND is on the harness: never run the driver without it).
  • The Nano is powered from USB while you program it; the motor needs the 12 V supply.
  • Never unplug the motor while the 12 V supply is on: it can destroy the driver.

6. Firmware

  1. Install the Arduino IDE. Download the firmware (cartpole.ino, linked at the top of this guide) into a folder named cartpole and open it.
  2. Tools → Board: Arduino Nano; Processor: ATmega328P (Old Bootloader) for most CH340 boards.
  3. Upload. Open the Serial Monitor at 115200 baud, line ending Newline.

7. First run

  1. Plug in the 12 V supply. Type p and press Enter, then press each switch by hand: the printout shows L1 or R1 while it's pressed. If a switch shows 1 when it isn't pressed, it's wired to NC instead of NO.
  2. Type h. The cart drives slowly to the motor end until the switch clicks, then parks in the middle of the rail. If it drives towards the idler end instead, swap the two wires of one motor coil (with the power off).
  3. Let the pole hang still and type s. The cart makes a small jog (this finds the encoder direction), waits, then swings the pole up and balances it, usually within 3–6 seconds.
  4. Type x to stop at any time. The firmware also brakes and stops by itself if the cart goes more than 150 mm from the centre or touches a switch.

If it doesn't balance, see Troubleshooting. Most rigs balance with the default gains.

8. Logging and tuning

  • l streams ms,x,phi,dx,dphi,u,mode at 100 Hz (copy from the Serial Monitor into a CSV).
  • p prints the current gains, the cart position, the pole angle and the switches.
  • g k1 k2 k3 k4 sets the balance (LQR) gains; e ke kc kd sm sets the swing-up gains.

Design your own controller. cartpole.py holds the model of this rig. Weigh your pole and put its mass in Rig, measure the friction from a free swing (time how long the swing takes to halve, then fit b), and run:

python3 cartpole.py

It prints LQR gains for your rig as a g … line to paste into the console, and simulates a swing-up and balance (it saves a plot if matplotlib is installed).

9. Experiments

  1. System identification: fit the pole's natural frequency and damping from a free swing.
  2. Pole placement vs LQR: design both from the same model and compare how far the cart moves when you tap the pole.
  3. Swing-up: change ke and sm and see how fast and how violently it swings up within the rail.
  4. Reinforcement learning: cartpole.py's simulate is a ready environment. Train a linear policy in simulation (random search over the four gains is enough), load it with g, and compare it with LQR on the real rig.
  5. Sim-to-real: compare logged data with the simulation from the same starting point.

10. Troubleshooting

SymptomFix
"Left switch not found"Check the switch is on D5 and NO; check the motor turns the right way (step 7.2)
"Encoder didn't see the pole move"Magnet off-centre or gap over 3 mm; check SDA/SCL and 5 V
Motor buzzes but doesn't turnSwap one coil pair; check the 12 V supply is on
Cart stutters or skips at speedBelt too tight or too loose; check the pulley grub screws
Swings up but overshoots and fallsReduce ke (try e 0.15 80 2 6)
Balances but the cart drifts to the limitMake k1 and k3 more negative (stronger centring)
Jitters while balancingIncrease the encoder gap slightly, or reduce k4
The rig walks across the tableFix the feet to the table

11. Safety

The cart moves fast and stops hard. Keep fingers, hair and cables clear of the rail while it runs, and stop with x before touching it. Use only the supplied 12 V supply.


© 2026 Voltomni. The firmware and Python model are free to use, change and share for learning and teaching. Questions: [email protected].