Configure a Custom PX4 Airframe in 20 Minutes

Add a custom airframe to PX4 Autopilot v1.16 from scratch — set up mixer, parameters, and QGroundControl without touching existing configs.

Problem: PX4 Doesn't Know Your Vehicle

You built a drone or fixed-wing that doesn't match any stock PX4 airframe. QGroundControl shows nothing useful in the airframe list, and generic presets give you wrong mixer outputs or broken parameter defaults.

You'll learn:

  • Where PX4 v1.16 stores airframe definitions and why the structure changed
  • How to add your own airframe file without editing upstream configs
  • How to load and verify it in QGroundControl

Time: 20 min | Level: Beginner


Why This Happens

PX4 uses a per-airframe shell script to set startup parameters and assign mixer outputs. If your vehicle isn't in the default library, the flight controller either falls back to a generic profile (wrong motor order, bad PIDs) or refuses to arm entirely.

In v1.16, airframe files moved from ROMFS/px4fmu_common/init.d/ into a CMake-managed structure under src/lib/airframes/. You can't just drop a file in — you need to register it properly.

Common symptoms:

  • QGC airframe page shows "Generic" with no matching preset
  • Motors spin in wrong order during pre-arm checks
  • Default PID gains cause immediate oscillation on first flight

QGroundControl airframe selection screen showing no matching airframe No matching entry — this is the starting point


Solution

Step 1: Clone PX4 and Check Your Version

git clone https://github.com/PX4/PX4-Autopilot.git --branch v1.16.0 --depth 1
cd PX4-Autopilot
git submodule update --init --recursive

Confirm the airframe directory exists where v1.16 expects it:

ls src/lib/airframes/

Expected: You should see numbered folders like 4001_quad_x, 2100_standard_plane, etc.

If it fails:

  • Directory missing: You may be on an older branch — run git checkout v1.16.0 and re-run submodule init

Step 2: Pick a Base Airframe Number

PX4 groups airframes by number range. Pick one that matches your vehicle type, then choose an unused number in that range:

RangeType
1xxxPlane
2xxxVTOL
4xxxMultirotor
6xxxGround rover

For a custom quadcopter, use something in 4900–4999 (reserved for custom/community frames):

# Check nothing is using your chosen number
grep -r "4901" src/lib/airframes/

Expected: No output means the number is free.


Step 3: Create Your Airframe File

Create a new directory and shell script for your airframe:

mkdir -p src/lib/airframes/4901_my_quad_x
touch src/lib/airframes/4901_my_quad_x/4901_my_quad_x.px4board

Now write the airframe init script:

cat > src/lib/airframes/4901_my_quad_x/init.d << 'EOF'
#!/bin/sh
# Custom quad-X airframe
# Airframe ID: 4901

. ${R}etc/init.d/rc.mc_defaults  # Load multirotor defaults

# --- Vehicle geometry ---
param set-default CA_ROTOR_COUNT 4
param set-default CA_ROTOR0_PX  0.15   # Front-right X offset (meters)
param set-default CA_ROTOR0_PY -0.15
param set-default CA_ROTOR1_PX -0.15   # Rear-left
param set-default CA_ROTOR1_PY  0.15
param set-default CA_ROTOR2_PX  0.15   # Front-left
param set-default CA_ROTOR2_PY  0.15
param set-default CA_ROTOR3_PX -0.15   # Rear-right
param set-default CA_ROTOR3_PY -0.15

# --- Spin directions (1=CW, -1=CCW) ---
param set-default CA_ROTOR0_KM -0.05
param set-default CA_ROTOR1_KM -0.05
param set-default CA_ROTOR2_KM  0.05
param set-default CA_ROTOR3_KM  0.05

# --- Tune these after maiden flight ---
param set-default MC_ROLLRATE_P  0.15
param set-default MC_PITCHRATE_P 0.15
param set-default MC_YAWRATE_P   0.20
EOF

Why rc.mc_defaults: It loads safe baseline values for all multirotor parameters so you only override what's different on your frame.


Step 4: Register the Airframe in CMake

Open the airframe list file:

nano src/lib/airframes/CMakeLists.txt

Find the section for multirotors (search for 4xxx) and add your entry:

# Custom airframes
px4_add_airframe(
    AIRFRAME 4901
    NAME "My Custom Quad X"
    TYPE multirotor
    INIT_FILE 4901_my_quad_x/init.d
)

Save and close.

CMakeLists.txt open in Terminal with new entry added Your new entry goes in the multirotors block — keep the formatting consistent


Step 5: Build and Flash

# Build for Pixhawk 6x (adjust board for your hardware)
make px4_fmu-v6x_default

# Flash via USB
make px4_fmu-v6x_default upload

Expected: Build completes with no errors. Flash tool reports "Board is already in bootloader" or "Programming" then success.

If it fails:

  • CMake configure error: Check indentation in CMakeLists.txt — CMake is whitespace-sensitive in function calls
  • **init.d not found:** Confirm the file path in INIT_FILE` exactly matches your directory structure
  • USB not detected: Install udev rules — Tools/setup/ubuntu.sh handles this on Linux

Verification

Power up the flight controller and connect QGroundControl. Navigate to Vehicle Setup → Airframe.

# On the flight controller's MAVLink shell, confirm the airframe loaded:
param show SYS_AUTOSTART

You should see: SYS_AUTOSTART = 4901

If QGC still shows "Generic," click Apply and Restart after selecting your new airframe from the list — it won't take effect until reboot.

QGroundControl showing custom airframe selected Your airframe name appears and SYS_AUTOSTART matches your ID


What You Learned

  • PX4 v1.16 requires CMake registration — dropping a file alone won't work
  • The 4900–4999 number range is safe for custom frames without colliding with upstream
  • rc.mc_defaults saves you from setting 50+ parameters manually

Limitation: This approach requires rebuilding firmware. If you need airframe switching without reflashing, look into PX4's parameter-only configuration via QGC's advanced parameter editor instead — though you lose the clean per-airframe defaults.

When NOT to use this: If an existing airframe is 90% right, override only the differing parameters via QGC rather than creating a new airframe. Save custom airframes for significantly different geometries or motor counts.


Tested on PX4 Autopilot v1.16.0, Pixhawk 6X, QGroundControl v4.3, Ubuntu 24.04