← back

Computer science bachelor thesis · Goethe University Frankfurt · 2023

A bicycle that balances itself.

Original title: Entwurf einer Steuerung für ein selbstbalancierendes Fahrrad

I wrote the control software for an existing full-size test bicycle. A BeagleBone Black ran the Linux application, while one of its real-time coprocessors measured the rear wheel. In the final documented test, the bicycle balanced after release for more than a minute while an operator ran nearby and used a remote to trim its direction.

University · Goethe University FrankfurtThesis date · 31.08.2023Documented test · 24.08.2023
An operator walking beside the test bicycle on a paved course, with the support wheels raised.

Why this problem

The earlier work behind this platform compared two ways to counter a fall: add a reaction wheel, or steer the front wheel into the lean. This bicycle used the second approach. A motor above the head tube turned the handlebar, so the balance controller had to work through the same steering geometry used to change direction.

Other students had built and modified the mechanical and electrical platform before my thesis. My part was the software and control work described here: interfaces for the sensors and motor electronics, concurrent control loops, data logging, calibration, controller tuning, and the integrated test campaign.

One Linux board, one real-time coprocessor, two motors.

A BeagleBone Black sits at the center. A BNO055 orientation sensor reports lean over I²C, and an MD49 reads the steering motor encoder over UART. The remote receiver supplies speed and steering requests through an analog conversion circuit. PWM and GPIO outputs drive the BTS7960 steering electronics and the rear hub motor controller. One Programmable Real-Time Unit (PRU) counts the rear motor's Hall-sensor transitions independently of Linux scheduling.

Block diagram of the bicycle hardware: a BeagleBone Black connects to the BNO055, steering encoder electronics, remote input circuit, steering motor driver, and rear hub motor Hall sensor.
Hardware overview from the thesis. Arrows show data flow. The German labels translate as lean sensor, remote control, steering motor, and rear hub motor.

Four Linux threads and one PRU program.

On the Linux side, the application is written in C and splits into a main thread plus three workers: one reads lean and remote input, one controls steering position, and one controls rear wheel speed. Each worker writes a timestamped CSV. Those logs made controller behavior visible after a run, although the archived thesis material does not include the application source code.

The PRU runs a small assembly program that samples a Hall-sensor input and shares its counter with the Linux process through memory. This isolates wheel measurement from variable userspace timing.

The C side is split into small, single-purpose modules:

  • autobike
  • bno055
  • md49
  • bts7960
  • io
  • fernbedienung
  • pid
Flowchart of the application: a main thread starts three Linux workers for lean input, steering, and rear-wheel speed, while a PRU program measures Hall-sensor transitions.
Software overview. Each dashed row is a pthread on Linux; the bottom band is the PRU program. Top row: start → init sensors and PRU → spawn threads → wait for time-out or stop → shut down. Each worker repeats its own read, compute, actuate, and log cycle.

The actual idea

Two cascaded balance loops, plus wheel-speed control.

The outer lean loop maps the BNO055 angle to a target steering angle with a PD controller. The inner steering loop tracks that target with a PID controller and encoder feedback. Separately, the rear-wheel loop combines a calibrated feed-forward command with integral correction. Their measured rates in the final test were about 128 Hz, 184 Hz, and 6.6 Hz respectively.

Feedback diagram: tilt angle from the BNO sensor feeds into a controller that produces a steering-angle setpoint.
Tilt loop. Measured lean angle becomes the steering-angle setpoint for the inner loop.
Steering-angle control loop with proportional, integral, and filtered-derivative branches driving a PWM to the BTS7960 and EMG49 steering motor, with encoder feedback from the MD49.
Steering loop. PID control with derivative averaging; feedback comes from the MD49 encoder.
Rear-wheel speed loop: a feed-forward term (v·1820 + 23923) plus an integral term produces PWM for the hub motor controller, with PRU-measured actual speed fed back.
Rear-wheel loop. Feed-forward for the linear range plus an integral term; PRU timing closes the loop.

The steering motor drives the handlebar through a belt.

The EMG49 sits above the head tube and turns the handlebar through a belt drive. Its encoder feeds the steering loop through the MD49, while a BTS7960 supplies the motor current. Two adjustable support wheels limit a fall during tests. They are raised for a balancing run and lowered for setup work.

Front-on view of the bicycle with the fork pointed straight ahead, showing the steering motor mounted above the head tube and outrigger training wheels extended to both sides.
Steering at zero, with the front wheel aligned to the frame.
The same bicycle with the front wheel turned to the right by the steering motor.
Right steering deflection under motor control.

Characterise the plant before you tune the controller.

Before a PID controller can do useful work, you have to know how the motors actually respond. I measured the rear motor's PWM-to-speed response and the steering encoder's counts-per-degree relationship before tuning the loops.

Scatter plot of PWM duty cycle against measured rear-wheel speed, showing a roughly linear region before saturation.
Rear hub motor calibration. The thesis used a linear fit over the selected operating range and notes saturation near the top of the PWM range.
Linear fit mapping raw MD49 encoder counts to steering angle in degrees.
Steering calibration. The fitted relationship converts MD49 encoder counts to degrees.

Bench tests first, then the complete bicycle.

I tested the steering and rear-wheel loops separately before tuning balance on the complete bicycle. The archived CSV files and thesis plots cover tests from 17 to 24 August 2023. They show why a result needed a run identifier and conditions, not just a claim that the controller worked.

Early steering-motor test plot with noisy tracking around a step setpoint.
17 Aug: first steering-motor step tests. Plenty of ringing.
Early hub-motor plot showing commanded versus actual speed with a visible offset and slow settling.
17 Aug: an early rear-wheel test with visible speed error.
Hub-motor plot from the next day showing tight tracking of the commanded speed.
18 Aug: rear-wheel tracking after the calibration work.
Lean-angle plot from the 24 August integrated test, including the straight section and remotely commanded turns.
24 Aug, run 02-00-24: lean angle during the integrated test. The bicycle was released around second 8; turns begin around second 39.

The result

What the final test established.

On 24 August 2023, the operator checked the bicycle, brought it to a 3.1 m/s target, and released it around second 8. It travelled mostly straight until about second 39, then completed a tight remotely commanded turn and several wider turns. Logs continue to roughly second 89. The thesis reports that the support wheels did not touch and concludes that the 60-second goal was met.

Under the hood

Tech stack

  • C application on Debian Linux running on a BeagleBone Black
  • PRU assembly (TI Code Generation Tools) for Hall-sensor sampling
  • BNO055 over I²C for orientation; MD49 over UART for the steering encoder
  • BTS7960 driver taking PWM + GPIO to run the EMG49 steering motor
  • 2.4 GHz receiver handling rider inputs; Makefile build; per-thread CSV logging

Design decisions

  • Active steering for balance instead of adding a reaction wheel
  • Measure rear-wheel Hall transitions on the PRU, outside the Linux scheduler
  • Use a PD lean controller, PID steering controller, and calibrated wheel-speed controller
  • Write timestamped data from each worker so tuning decisions can be checked after a run

What I would do differently

  • Make the controller depend on speed; the fixed PD gains became unstable at 5 m/s
  • Add a real-time watchdog for blocked sensor or motor communication
  • Replace relative steering zero with a reliable absolute reference
  • Test tire pressure and load as controlled variables instead of setup details
← back

The hardware platform came from earlier student work in the same lab. My thesis covers the software interfaces, controller design, calibration, and tests summarized on this page.