Skip to content

A tilt-controlled racing game for the ESP32, featuring stable motion controls via a complementary filter.

License

Notifications You must be signed in to change notification settings

taherfattahi/esp32-gyro-racer

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 
 
 
 
 

Repository files navigation

GyroRacer ESP32

A tilt-controlled racing game for the ESP32, featuring stable motion controls via a complementary filter.

Gameplay Video

Dino Game Screenshot

Hardware Requirements

  • ESP32 WROOM Development Board
  • MPU6050 Gyroscope/Accelerometer Module
  • 0.96-inch SSD1306 OLED Display (I2C)
  • Breadboard
  • Jumper Wires

Wiring Diagram

The components are connected using the I2C communication protocol.

ESP32 Pin MPU6050 Pin OLED Display Pin
3.3V VCC VCC
GND GND GND
GPIO 22 SCL SCL
GPIO 21 SDA SDA
    +-----------------+      +-----------------+      +-----------------+
    |   ESP32 WROOM   |      |     MPU6050     |      |  OLED Display   |
    |                 |      |                 |      |                 |
    |      3.3V ------|----->| VCC             |----->| VCC             |
    |      GND  ------|----->| GND             |----->| GND             |
    |      GPIO 22 ----|----->| SCL             |----->| SCL             |
    |      GPIO 21 ----|----->| SDA             |----->| SDA             |
    +-----------------+      +-----------------+      +-----------------+

Installation

  1. Install Libraries: Open the Arduino IDE Library Manager (Sketch -> Include Library -> Manage Libraries...) and install the following:
    • Adafruit GFX Library
    • Adafruit SSD1306
    • Adafruit MPU6050
    • Adafruit Unified Sensor
  2. Connect Hardware: Wire your components as shown in the diagram above.
  3. Upload Code: Open the main esp32-gyro-racer.ino file in the Arduino IDE, select your ESP32 board and COM port, and click Upload.

Mathematical Concepts:

1. The Problem: Sensor Imperfections

An ideal MPU6050 would be perfectly silent when still. In reality, it has two major flaws:

  • Bias: When stationary, the gyroscope doesn't output a perfect zero. It has a small, non-zero offset called bias.
  • Drift: If we continuously add up the gyroscope's readings to calculate the angle, this tiny bias error accumulates, causing the calculated angle to "drift" away from the true angle over time.

2. Solution Step 1: Calibration

To combat bias, we perform a calibration routine at the start. The logic is simple: if we measure the sensor's error while it's still, we can subtract that error from all future readings.

The mathematical process is to calculate the average error over a set of samples ($N$):

$$ Offset_{axis} = \frac{1}{N} \sum_{i=1}^{N} Reading_i $$

In the code, we take $N=1000$ samples when the game starts. This gyroXoffset is then subtracted from every new gyroscope reading to get a corrected rate of rotation.

3. Solution Step 2: The Complementary Filter

Calibration alone doesn't stop all drift. To get a truly stable angle, we fuse data from two different sensors: the gyroscope and the accelerometer.

  • Gyroscope: Excellent for measuring fast, short-term rotation but drifts over the long term.
  • Accelerometer: Can determine the angle relative to gravity, making it stable over the long term, but it's very "noisy" and unreliable during quick movements.

A Complementary Filter combines the best of both worlds.

a. Getting the Accelerometer Angle

We can calculate the board's tilt angle using basic trigonometry. The accelerometer measures the force of gravity across its X, Y, and Z axes. For X-axis tilt (pitch), we can use the Y and Z components. Using the atan2(y, z) function, which is more robust than atan(y/z), we get the angle in radians, which we convert to degrees:

Equation

Where $A_y$ and $A_z$ are the accelerometer readings on their respective axes.

b. Fusing the Data

The complementary filter is a simple weighted average that combines the integrated gyroscope angle with the accelerometer angle. The formula in our code is:

$$ Angle_{fused} = \alpha \cdot (Angle_{fused, prev} + Rate_{gyro} \cdot \Delta t) + (1-\alpha) \cdot Angle_{accel} $$

Let's break this down:

  • $Angle_{fused, prev} + Rate_{gyro} \cdot \Delta t$: This is the new angle predicted by the gyroscope. It takes the previous angle and adds the change measured by the gyro over the small time interval $\Delta t$.
  • $Angle_{accel}$: This is the stable angle calculated from the accelerometer.
  • $\alpha$ and $(1-\alpha)$: These are the weighting factors. In our code, $\alpha = 0.98$. This means we trust the responsive gyroscope for 98% of our final angle calculation, but we constantly "nudge" it with 2% of the stable accelerometer data. This nudge is enough to completely eliminate the long-term drift.

4. Final Touch: Control Mapping

Once we have a stable fusedAngleX, we map it to the screen coordinates. The map() function performs a linear transformation:

playerX = map(fusedAngleX, -30, 30, SCREEN_WIDTH - playerWidth, 0);

This maps an input angle from -30° to +30° to a pixel coordinate on the screen. The output range is inverted (SCREEN_WIDTH to 0) to create the inverted control scheme, providing the final layer of polish to the player experience.

Contributing

Contributions are welcome. If you find a bug or have a feature request, please open an issue or submit a pull request.

About

A tilt-controlled racing game for the ESP32, featuring stable motion controls via a complementary filter.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages