- Published on
PX4 and Pixhawk Quadcopter Development Tutorial
- Authors
- Name
- Yinhuan Yuan
Table of Contents
- Introduction
- PX4 vs ArduPilot
- System Architecture
- Hardware Requirements
- Development Environment Setup
- Firmware Installation
- Initial Configuration
- Sensor Calibration
- Flight Modes
- Parameter Tuning
- SITL Simulation
- MAVLink and MAVROS
- Custom Development
- Debugging and Logs
- Advanced Features
Introduction
PX4 is a professional-grade open-source autopilot software designed for autonomous vehicles. When combined with Pixhawk hardware, it provides a robust platform for quadcopter development with emphasis on modularity, real-time performance, and safety. This tutorial covers everything from basic setup to advanced development techniques.
Key Features of PX4
- Real-time operating system (NuttX)
- Modular architecture
- Hardware abstraction layer
- Extensive simulation support
- Professional-grade safety features
- Active research community
PX4 vs ArduPilot
PX4 Advantages
- Modern Architecture: Cleaner, more modular codebase
- Better Simulation: Superior SITL and Gazebo integration
- Research-Friendly: Preferred in academic/research settings
- ROS Integration: Native MAVROS support
- Development Tools: Better toolchain and debugging
ArduPilot Advantages
- Feature-Rich: More vehicle types and features
- Mature Ecosystem: Larger user base
- Hardware Support: Wider hardware compatibility
- Documentation: More extensive wiki
Choose PX4 When:
- Developing custom controllers
- Integrating with ROS/ROS2
- Requiring professional certification
- Working on research projects
- Need advanced simulation
System Architecture
PX4 Software Stack
┌─────────────────────────────────────┐
│ Applications Layer │
│ (Missions, Safety, Commands) │
├─────────────────────────────────────┤
│ Flight Stack Layer │
│ (Controllers, Estimators, Mixers) │
├─────────────────────────────────────┤
│ Middleware Layer (uORB) │
│ (Inter-process Communication) │
├─────────────────────────────────────┤
│ Drivers Layer │
│ (Sensors, Actuators, Comms) │
├─────────────────────────────────────┤
│ Operating System (NuttX) │
└─────────────────────────────────────┘
Key Components
uORB (Micro Object Request Broker)
- Publish-subscribe messaging system
- Enables modular communication
- Real-time safe
- Type-safe message passing
Flight Tasks
- Modular flight mode implementation
- Easy to extend and customize
- Clean interfaces between modes
EKF2 (Extended Kalman Filter)
- State estimation
- Sensor fusion
- Handles GPS, IMU, barometer, magnetometer
Hardware Requirements
Supported Pixhawk Boards
Development Boards
- Pixhawk 4: Recommended for development
- Pixhawk 6C: Latest generation
- Pixhawk 6X: High-performance option
- Holybro Durandal: Robust alternative
Budget Options
- Pixhawk 2.4.8: Legacy but functional
- mRo Pixhawk: Compatible alternative
- CUAV V5+: Good price/performance
Quadcopter Components
Frame and Motors
Frame: 450-550mm for development
Motors: 920-1000KV brushless
ESCs: 30A with DShot support preferred
Props: 10-12 inch, balanced
Sensors
- GPS: M8N/M9N with compass
- Optical Flow: PX4Flow (optional)
- Range Finder: TFMini/Benewake (optional)
- Airspeed: Not needed for copters
Communication
- RC System: 8+ channel recommended
- Telemetry: SiK radio or WiFi
- Companion Link: USB or UART
Wiring Configuration
Standard Pixhawk 4 Connections:
┌─────────────────────────┐
│ Pixhawk 4 │
├─────────────────────────┤
│ POWER1: Power Module │
│ POWER2: Backup Power │
│ GPS1: GPS/Compass │
│ RC IN: Receiver │
│ MAIN 1-4: Motor ESCs │
│ TELEM1: Ground Station │
│ TELEM2: Companion │
│ USB: Development/Config │
└─────────────────────────┘
Development Environment Setup
Operating System Preparation
Ubuntu 20.04/22.04 (Recommended)
# Update system
sudo apt update && sudo apt upgrade -y
# Install dependencies
sudo apt install -y \
git \
python3-pip \
python3-setuptools \
python3-dev \
build-essential \
ccache
Windows (Using WSL2)
# Install WSL2
wsl --install
# Install Ubuntu 20.04
wsl --install -d Ubuntu-20.04
# Continue with Ubuntu instructions
macOS
# Install Homebrew
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
# Install tools
brew install git python3 ccache
PX4 Development Tools
Install PX4 Toolchain
# Clone PX4 Firmware
git clone https://github.com/PX4/PX4-Autopilot.git --recursive
cd PX4-Autopilot
# Run setup script
bash ./Tools/setup/ubuntu.sh
# Restart terminal or source profile
source ~/.bashrc
Install QGroundControl
# Download AppImage (Linux)
wget https://d176tv9ibo4jno.cloudfront.net/latest/QGroundControl.AppImage
chmod +x QGroundControl.AppImage
# Or install via snap
sudo snap install qgroundcontrol
Install Additional Tools
# MAVLink tools
pip3 install --user pymavlink mavproxy
# Analysis tools
pip3 install --user pyulog matplotlib
# ROS integration (optional)
sudo apt install ros-noetic-mavros ros-noetic-mavros-extras
Firmware Installation
Building PX4 Firmware
Standard Build
cd PX4-Autopilot
# Configure for Pixhawk 4
make px4_fmu-v5_default
# Other common targets:
# make px4_fmu-v3_default # Pixhawk 2.4.8
# make px4_fmu-v6c_default # Pixhawk 6C
# make holybro_durandal-v1_default
Custom Build Configuration
# Menu-based configuration
make px4_fmu-v5_default menuconfig
# Clean build
make clean
make px4_fmu-v5_default
Flashing Firmware
Using QGroundControl
- Connect Pixhawk via USB
- Open QGroundControl
- Go to Vehicle Setup → Firmware
- Select PX4 Flight Stack
- Choose version (stable/developer/custom)
- Follow prompts
Command Line Flashing
# Build and upload
make px4_fmu-v5_default upload
# If device not found
# Unplug and replug while command waits
DFU Mode Recovery
# For bricked boards
# 1. Unplug Pixhawk
# 2. Hold bootloader button
# 3. Plug in USB while holding button
# 4. Release after 3 seconds
# Flash via DFU
make px4_fmu-v5_default upload
Initial Configuration
QGroundControl Setup
Vehicle Configuration
1. Connect Pixhawk
2. Vehicle Setup → Summary
3. Select Airframe:
- Generic Quadcopter X
- Apply and Restart
Sensor Orientation
Vehicle Setup → Sensors → Set Orientations
- ROTATION_NONE for standard mounting
- Adjust if mounted differently
Essential Parameters
System Parameters
# Access via Parameters tab
SYS_AUTOSTART: 4001 (Generic Quadcopter)
SYS_AUTOCONFIG: 1 (Reset parameters)
CBRK_SUPPLY_CHK: 0 (Enable power checks)
Safety Parameters
COM_RC_LOSS_T: 0.5 (RC loss timeout)
COM_DL_LOSS_T: 10 (Datalink loss timeout)
NAV_RCL_ACT: 2 (Return on RC loss)
NAV_DLL_ACT: 0 (Hold on datalink loss)
Sensor Calibration
Magnetometer Calibration
Vehicle Setup → Sensors → Compass
1. Start calibration
2. Rotate vehicle in all orientations
3. Complete when progress reaches 100%
4. Check mag field strength (185-585 mGauss)
Accelerometer Calibration
Vehicle Setup → Sensors → Accelerometer
1. Place vehicle level
2. Start calibration
3. Follow orientation instructions:
- Level
- Nose down
- Nose up
- Left side
- Right side
- Upside down
Gyroscope Calibration
Vehicle Setup → Sensors → Gyroscope
1. Keep vehicle still
2. Start calibration
3. Wait for completion
4. Do not move during process
Level Horizon Calibration
Vehicle Setup → Sensors → Level Horizon
1. Place on perfectly level surface
2. Click calibrate
3. Verify artificial horizon matches
Radio Calibration
Vehicle Setup → Radio
1. Turn on transmitter
2. Start calibration
3. Move all sticks to extremes
4. Move all switches
5. Click Next/OK when complete
ESC Calibration
Vehicle Setup → Power
1. Remove props!
2. Set PWM_MIN and PWM_MAX
3. Perform ESC calibration sequence
4. Or use DShot (no calibration needed)
Flight Modes
PX4 Flight Mode Structure
Manual Modes
- Manual: Direct stick control (fixed-wing only)
- Stabilized: Self-leveling
- Acro: Rate control, no leveling
- Rattitude: Stabilized center, acro extremes
Assisted Modes
- Altitude: Altitude hold with position drift
- Position: Full position and altitude hold
- Hold: Stops and hovers at current position
Autonomous Modes
- Mission: Follows waypoints
- Return: Return to launch
- Land: Automated landing
- Follow Me: Follows ground station
- Orbit: Circles point of interest
Flight Mode Configuration
Vehicle Setup → Flight Modes
- Assign modes to RC channels
- Configure mode switch
- Set kill switch (recommended)
Recommended Setup
Channel 5 (3-position switch):
- Position 1: Stabilized
- Position 2: Position
- Position 3: Mission
Channel 6 (2-position switch):
- Kill switch / Emergency stop
Channel 7 (2-position switch):
- Return to Launch
Parameter Tuning
PID Tuning Overview
MC (Multicopter) Rate Controller
MC_ROLLRATE_P: 0.15 (default)
MC_ROLLRATE_I: 0.2
MC_ROLLRATE_D: 0.003
MC_PITCHRATE_P: 0.15
MC_PITCHRATE_I: 0.2
MC_PITCHRATE_D: 0.003
MC_YAWRATE_P: 0.2
MC_YAWRATE_I: 0.1
MC_YAWRATE_D: 0.0
Position Controller
MPC_XY_P: 0.95 (position P gain)
MPC_XY_VEL_P_ACC: 1.8 (velocity P gain)
MPC_Z_P: 1.0 (altitude P gain)
MPC_Z_VEL_P_ACC: 4.0 (vertical velocity P)
Tuning Process
1. Rate Controller Tuning
# Enable high-rate logging
param set SDLOG_PROFILE 1
# Fly in Acro mode
# Adjust P gains first
# Add D for damping
# Finally tune I for tracking
2. Attitude Controller Tuning
# Fly in Stabilized mode
# Adjust MC_ROLL_P and MC_PITCH_P
# Typical range: 6-10
3. Position Controller Tuning
# Fly in Position mode
# Tune MPC_XY_P for position holding
# Adjust MPC_XY_VEL_P_ACC for velocity response
Auto-tuning
# Enable autotune mode
param set MC_AT_EN 1
# Fly in autotune mode
# Perform requested maneuvers
# Land to save parameters
SITL Simulation
Gazebo Simulation Setup
Install Gazebo
# Install Gazebo 11
sudo apt install gazebo11 libgazebo11-dev
# Install PX4 SITL requirements
pip3 install --user jinja2 empy toml numpy packaging
Run SITL
cd PX4-Autopilot
# Basic quadcopter simulation
make px4_sitl gazebo
# With specific world
make px4_sitl gazebo_typhoon_h5
# Headless mode
HEADLESS=1 make px4_sitl gazebo
jMAVSim Simulation
# Lighter weight simulation
make px4_sitl jmavsim
# Console commands in SITL
commander takeoff
commander land
commander mode position
SITL with QGroundControl
# Terminal 1: Run SITL
make px4_sitl gazebo
# Terminal 2: QGroundControl
./QGroundControl.AppImage
# QGC auto-connects to SITL
Multi-Vehicle Simulation
# Launch multiple instances
Tools/gazebo_sitl_multiple_run.sh -n 3
# Connect to different instances
# MAV_SYS_ID must be unique
MAVLink and MAVROS
MAVLink Communication
Understanding MAVLink
- Binary protocol
- Message-based
- Platform independent
- Bandwidth efficient
Common MAVLink Messages
# Python example
from pymavlink import mavutil
# Create connection
master = mavutil.mavlink_connection('udp:127.0.0.1:14550')
# Wait for heartbeat
master.wait_heartbeat()
# Request data stream
master.mav.request_data_stream_send(
master.target_system,
master.target_component,
mavutil.mavlink.MAV_DATA_STREAM_ALL,
10, # Hz
1 # Start
)
# Read attitude
while True:
msg = master.recv_match(type='ATTITUDE', blocking=True)
print(f"Roll: {msg.roll}, Pitch: {msg.pitch}")
MAVROS Integration
Installation
# ROS Noetic
sudo apt install ros-noetic-mavros ros-noetic-mavros-extras
# Install geographiclib
wget https://raw.githubusercontent.com/mavlink/mavros/master/mavros/scripts/install_geographiclib_datasets.sh
sudo bash install_geographiclib_datasets.sh
Basic MAVROS Usage
# Launch MAVROS
roslaunch mavros px4.launch fcu_url:="udp://:14540@127.0.0.1:14557"
# Topics available
rostopic list
# Arm vehicle
rosservice call /mavros/cmd/arming "value: true"
# Set mode
rosservice call /mavros/set_mode "custom_mode: 'OFFBOARD'"
MAVROS Example Script
#!/usr/bin/env python
import rospy
from geometry_msgs.msg import PoseStamped
from mavros_msgs.msg import State
from mavros_msgs.srv import CommandBool, SetMode
class OffboardControl:
def __init__(self):
rospy.init_node('offboard_control')
# Publishers
self.local_pos_pub = rospy.Publisher(
'/mavros/setpoint_position/local',
PoseStamped,
queue_size=10
)
# Subscribers
self.state_sub = rospy.Subscriber(
'/mavros/state',
State,
self.state_callback
)
# Services
self.arming_client = rospy.ServiceProxy(
'/mavros/cmd/arming',
CommandBool
)
self.set_mode_client = rospy.ServiceProxy(
'/mavros/set_mode',
SetMode
)
self.current_state = State()
def state_callback(self, msg):
self.current_state = msg
def send_setpoint(self, x, y, z):
pose = PoseStamped()
pose.pose.position.x = x
pose.pose.position.y = y
pose.pose.position.z = z
self.local_pos_pub.publish(pose)
def set_mode(self, mode):
return self.set_mode_client(custom_mode=mode)
def arm(self):
return self.arming_client(value=True)
Custom Development
PX4 Module Development
Module Structure
// my_module.cpp
#include <px4_platform_common/module.h>
#include <px4_platform_common/module_params.h>
class MyModule : public ModuleBase<MyModule>, public ModuleParams
{
public:
MyModule();
~MyModule() = default;
static int task_spawn(int argc, char *argv[]);
static int custom_command(int argc, char *argv[]);
static int print_usage(const char *reason = nullptr);
void run() override;
private:
// Parameters
DEFINE_PARAMETERS(
(ParamFloat<px4::params::MY_PARAM>) _my_param
)
};
CMakeLists.txt
px4_add_module(
MODULE my_module
MAIN my_module
SRCS
my_module.cpp
DEPENDS
modules__uORB
)
Custom Flight Mode
Flight Task Implementation
// FlightTaskMyMode.hpp
#pragma once
#include "FlightTask.hpp"
class FlightTaskMyMode : public FlightTask
{
public:
FlightTaskMyMode() = default;
virtual ~FlightTaskMyMode() = default;
bool activate(const vehicle_local_position_s &last_setpoint) override;
bool update() override;
private:
void _updateSetpoints();
};
uORB Message Creation
Message Definition
# my_message.msg
uint64 timestamp
float32 my_value
float32[3] my_array
uint8 my_status
Using Custom Message
#include <uORB/topics/my_message.h>
// Advertise
orb_advert_t _my_msg_pub = orb_advertise(ORB_ID(my_message), nullptr);
// Publish
my_message_s msg{};
msg.timestamp = hrt_absolute_time();
msg.my_value = 42.0f;
orb_publish(ORB_ID(my_message), _my_msg_pub, &msg);
// Subscribe
int _my_msg_sub = orb_subscribe(ORB_ID(my_message));
Debugging and Logs
Console Access
MAVLink Shell
# Via QGroundControl
# Analyze → MAVLink Console
# Common commands
help
top
listener sensor_combined
param show MC_ROLLRATE_P
dmesg
NSH Console
# Direct serial connection
screen /dev/ttyACM0 57600
# Or via MAVLink
./Tools/mavlink_shell.py
Log Analysis
Download Logs
# Using QGroundControl
# Analyze → Log Download
# Or via script
python3 Tools/LogDownload.py --device /dev/ttyACM0
ULog Analysis
# Install tools
pip3 install pyulog
# Convert to CSV
ulog2csv log_file.ulg
# Info about log
ulog_info log_file.ulg
# Plot data
python3 Tools/ulog_analysis.py log_file.ulg
FlightPlot
- Java-based log analyzer
- Real-time plotting
- Multiple graph types
- Statistical analysis
Common Issues
GPS Problems
# Check GPS status
listener vehicle_gps_position
# Parameters to check
param show GPS_*
param show EKF2_GPS_*
IMU Issues
# Check sensor data
listener sensor_accel
listener sensor_gyro
# Calibration status
param show CAL_ACC*
param show CAL_GYRO*
Motor Problems
# Test motors
motor_test test -m 1 -p 50 # Test motor 1 at 50%
# Check mixer
mixer load /dev/pwm_output0 etc/mixers/quad_x.main.mix
Advanced Features
Precision Landing
IR-Lock Setup
# Hardware: IR-Lock sensor + beacon
# Parameters:
PLND_EST_EN: 1
PLND_MODE: 1 (IR-Lock)
PLND_OFFSET_X: 0.0
PLND_OFFSET_Y: 0.0
Computer Vision Landing
# Using MAVROS
import rospy
from mavros_msgs.msg import LandingTarget
# Publish landing target
pub = rospy.Publisher(
'/mavros/landing_target/raw',
LandingTarget,
queue_size=1
)
target = LandingTarget()
target.header.stamp = rospy.Time.now()
target.target_num = 0
target.angle_x = 0.1 # radians
target.angle_y = 0.1
target.distance = 5.0 # meters
pub.publish(target)
Obstacle Avoidance
Using Distance Sensors
# Parameters for rangefinder
SENS_EN_MB12XX: 1
SENS_MB12_0_ROT: 0 (forward)
MPC_COL_AVOID: 1
Computer Vision Avoidance
// Publish obstacle_distance message
#include <uORB/topics/obstacle_distance.h>
obstacle_distance_s obs{};
obs.timestamp = hrt_absolute_time();
obs.min_distance = 100; // cm
obs.max_distance = 2000; // cm
obs.distances[0] = 150; // Front distance
orb_publish(ORB_ID(obstacle_distance), _obs_pub, &obs);
Custom Mixer
Mixer File Format
# Custom X configuration
R: 4x
# Motor 1 (front right, CCW)
M: 1
S: 0 0 10000 10000 0 -10000 10000
S: 0 1 10000 10000 10000 0 10000
S: 0 2 10000 10000 0 0 10000
S: 0 3 0 20000 10000 0 10000
# Repeat for motors 2-4
Safety Features
Geofence
# Parameters
GF_ACTION: 2 (RTL on breach)
GF_ALTMODE: 0 (GPS altitude)
GF_COUNT: -1 (use uploaded fence)
GF_MAX_HOR_DIST: 1000 (meters)
GF_MAX_VER_DIST: 100 (meters)
Flight Termination
# Emergency motor stop
CBRK_FLIGHTTERM: 0 (enable)
FD_FAIL_P: 60 (max pitch)
FD_FAIL_R: 60 (max roll)
Best Practices
Development Workflow
Always simulate first
- Test in SITL before real hardware
- Use unit tests for modules
- Verify parameters in simulation
Version control
git checkout -b feature/my-feature git commit -m "descriptive message" git push origin feature/my-feature
Code style
# Format code make format # Check style make check_format
Safety Guidelines
Pre-flight checks
- Verify all calibrations
- Check battery voltage
- Confirm GPS lock
- Test failsafes
During development
- Remove props when testing
- Use kill switch
- Have safety pilot
- Test in open areas
Parameter management
# Save parameters param save # Export for backup param export params.txt # Reset to defaults param reset_all
Resources
Official Documentation
Community
Learning Resources
Tools and Software
Conclusion
PX4 with Pixhawk provides a professional-grade platform for quadcopter development, offering excellent simulation capabilities, clean architecture, and strong ROS integration. Success with PX4 comes from understanding its modular structure, leveraging its simulation tools, and following systematic development practices.
Key takeaways:
- Start with SITL simulation
- Master the parameter system
- Understand uORB messaging
- Use proper debugging tools
- Engage with the community
Whether you're building research platforms, commercial products, or hobby projects, PX4's flexibility and robustness make it an excellent choice for serious drone development.