A 2-axis camera gimbal stabilizer with auto-framing and IMU-based stabilization, running entirely on Raspberry Pi.
- IMU-Based Stabilization: Uses MPU6050/MPU9250 gyroscope and accelerometer to compensate for camera shake
- Auto-Framing: Detects people and automatically keeps them centered with proper composition
- Real-Time Subject Tracking: Face and body detection with smooth servo following
- Photo Capture: Capture perfectly framed photos with single keypress
- 2-Axis Control: Pitch and yaw servo control via PCA9685 PWM driver
- Web Control Interface: Control from any device on your network with live video stream
- Gamepad Support: Use Xbox/PlayStation controllers for smooth manual control
- All-in-One Raspberry Pi: No Arduino required - everything runs on the Pi
| Component | Specification | Purpose |
|---|---|---|
| Raspberry Pi | 3B+ or 4 (4 recommended) | Main controller |
| Camera | Pi Camera Module V2 or USB webcam | Video capture |
| Servo Driver | PCA9685 16-channel PWM (I2C) | Servo control |
| Servos | 2x MG996R or DS3218 (high torque) | Gimbal movement |
| IMU | MPU6050 or MPU9250 (I2C) | Motion sensing |
| Power Supply | 5V 4A (separate from Pi) | Servo power |
| Gimbal Frame | 3D printed or purchased | Camera mount |
| Gamepad (optional) | Xbox, PlayStation, or USB | Manual control |
PCA9685 (Servo Driver):
VCC -> Pi 3.3V or 5V
GND -> Pi GND
SDA -> Pi GPIO 2 (Pin 3)
SCL -> Pi GPIO 3 (Pin 5)
V+ -> External 5V 4A power supply
GND -> External PSU GND (common with Pi)
Servo Pitch -> Channel 0
Servo Yaw -> Channel 1
MPU6050/MPU9250 (IMU):
VCC -> Pi 3.3V
GND -> Pi GND
SDA -> Pi GPIO 2 (Pin 3) [shared with PCA9685]
SCL -> Pi GPIO 3 (Pin 5) [shared with PCA9685]
Pi Camera:
Connect to CSI port on Pi
OR USB Camera:
Connect to USB port
Gamepad (optional):
Connect via USB or Bluetooth
sudo raspi-config
# Navigate to: Interface Options -> I2C -> Enable
sudo rebootVerify I2C is working:
sudo apt-get install i2c-tools
sudo i2cdetect -y 1You should see devices at 0x40 (PCA9685) and 0x68 (MPU6050).
# Update system
sudo apt-get update
sudo apt-get upgrade -y
# Install required packages
sudo apt-get install -y python3-pip python3-opencv libcamera-dev
# Clone repository
git clone https://github.com/Si6gma/GyroGimbal.git
cd GyroGimbal
# Install Python packages
pip3 install -r requirements.txtEdit config.py to match your setup:
# If using USB camera instead of Pi Camera
CAMERA_INDEX = 1
# Adjust servo limits if needed
PITCH_MIN = 0
PITCH_MAX = 180
# Fine-tune stabilization
STABILIZATION_GAIN = 0.7 # Increase if footage is still shakyControl the gimbal from any device on your network:
cd src
sudo python3 web_server.pyThen open a browser on any device and go to:
http://[raspberry-pi-ip]:5000
Find your Pi's IP:
hostname -I- Live Video Stream: MJPEG feed from the camera
- Virtual Joystick: Click/touch and drag to pan/tilt manually
- Mode Toggles: Turn stabilization and tracking on/off
- Photo Capture: Click to save photos
- Center Button: Return to center position
- Mobile Friendly: Works on phones and tablets
Use an Xbox, PlayStation, or USB gamepad for smooth manual control:
cd src
sudo python3 gamepad_controller.py| Control | Action |
|---|---|
| Left Stick | Pan/Tilt gimbal |
| Right Stick | Fine adjustment |
| A / Cross | Capture photo |
| B / Circle | Center gimbal |
| X / Square | Toggle stabilization |
| Y / Triangle | Toggle tracking |
| LB / L1 | Decrease speed |
| RB / R1 | Increase speed |
| Start | Exit |
The analog sticks provide much smoother control than keyboard or web joystick.
Run the gimbal without any control interface:
cd src
sudo python3 gimbal_controller.pyControls:
| Key | Action |
|---|---|
c |
Capture photo |
s |
Toggle stabilization |
t |
Toggle tracking |
q |
Quit |
Photos are saved to ./photos/ with timestamp:
photos/
capture_20240217_143022.jpg
web_capture_20240217_143156.jpg
gamepad_capture_20240217_143220.jpg
- IMU Sampling: MPU6050 samples gyroscope and accelerometer at 100Hz
- Sensor Fusion: Complementary filter combines gyro (fast response) and accel (drift-free)
- Motion Compensation: Detected rotation is inverted and applied to servos
- Smoothing: Low-pass filter and jerk limiting prevent servo jitter
- Detection: OpenCV Haar cascades detect faces/bodies
- Tracking: Kalman filter predicts subject movement
- Composition: Calculates optimal gimbal angles using rule of thirds
- Smoothing: Exponential moving average prevents hunting
┌─────────────┐ WebSocket/HTTP ┌─────────────┐
│ Browser │ <--------------------> │ Flask API │
│ (Phone/PC) │ MJPEG Stream │ (Pi) │
└─────────────┘ └──────┬──────┘
│
┌─────────────┬─────────────┼─────────────┐
│ │ │ │
┌────▼────┐ ┌────▼────┐ ┌────▼────┐ ┌────▼────┐
│ Camera │ │ IMU │ │ PCA9685 │ │ GPIO │
│ Module │ │MPU6050 │ │(Servos) │ │ (LEDs) │
└─────────┘ └─────────┘ └─────────┘ └─────────┘
Gamepad Input → Pygame Events → Position Update → PCA9685 → Servos
↑ ↓
└──────────── Visual Feedback ← Camera ←─────────────┘
GyroGimbal/
├── src/
│ ├── gimbal_controller.py # Main standalone application
│ ├── web_server.py # Flask web interface
│ ├── gamepad_controller.py # Gamepad control
│ ├── servo_driver.py # PCA9685 servo control
│ ├── imu_sensor.py # MPU6050 interface
│ ├── stabilizer.py # Sensor fusion & PID
│ └── auto_framing.py # Subject detection & framing
├── config.py # Hardware configuration
├── requirements.txt # Python dependencies
└── README.md
# config.py
STABILIZATION_GAIN = 0.4 # Reduce compensation
STABILIZATION_SMOOTHING = 0.5 # More smoothing# config.py
TRACKING_SMOOTHING = 0.3 # Faster response- Check power supply - servos need stable 5V
- Increase smoothing values
- Check I2C cable length (keep under 30cm)
- Add capacitors near servo power input
# Check I2C is enabled
sudo raspi-config
# Check wiring - common ground between Pi and servos
# Verify device addresses
sudo i2cdetect -y 1# For Pi Camera
sudo raspi-config # Enable camera interface
# For USB camera
check CAMERA_INDEX in config.py (try 0, 1, 2...)- Check external power supply is connected
- Verify PCA9685 V+ is powered (not just VCC)
- Check servo channel numbers in config
- Test with
servo_driver.pydirectly
# Check Flask is listening on all interfaces
# In web_server.py, app.run() should have host='0.0.0.0'
# Check firewall
sudo ufw allow 5000
# Check Pi's IP
hostname -I# List connected controllers
python3 -c "import pygame; pygame.init(); print([pygame.joystick.Joystick(i).get_name() for i in range(pygame.joystick.get_count())])"
# For Bluetooth controllers, pair first
bluetoothctl- Calibrate IMU on startup (keep still for 2 seconds)
- Adjust STABILIZATION_GAIN
- Check IMU is firmly mounted to camera
- Reduce mechanical backlash in gimbal
MIT License - See LICENSE file
Built for smooth footage and perfect composition.