Skip to content

Flying a quadcopter in Simulink with cascaded PID

Modelling a 6-DoF quadcopter from Newton–Euler dynamics, and how four setpoints end up controlling six degrees of freedom.

Rayhan M. Project: May 2025 Written 4 min read Source on GitHub

QuadX is a quadcopter modelled from first principles in MATLAB/Simulink and stabilised with cascaded PID control. It was a five-person project for EE291 System Dynamics and Control at Maynooth. I worked on the mathematical modelling, the PID design and tuning, and the Simulink implementation, and acted as the team’s main contact with our facilitator.

It’s simulation only: no physical drone was built. The model, parameters and reports are on GitHub.

3D flight simulation

The vehicle

ParameterValue
Mass1.5 kg
Arm length0.25 m
InertiaIx = Iy = 0.02, Iz = 0.04 kg·m²
Thrust per rotor0–15 N, saturated

Ix equals Iy because the cross frame is symmetric. Iz is double because all four rotor masses sit at full arm length from the yaw axis. Hover needs m·g = 14.72 N in total, or 3.68 N per rotor: about a quarter of each rotor’s ceiling, which leaves room for attitude corrections.

Dynamics

Translation and rotation are derived separately from free-body diagrams and built as separate Simulink subsystems, one per axis, each integrating acceleration to velocity to position.

  • Translation is Newton’s second law in the inertial frame. Total thrust acts along the body z-axis. Tilting the body projects part of that thrust sideways, which is the only way a quadcopter moves horizontally. Gravity is the only other force.
  • Rotation is Euler’s equations in the body frame. Roll comes from a thrust difference between the left and right rotors, pitch from front and rear, and yaw from the reaction drag between the clockwise and counter-clockwise pairs.

Translational free-body diagram

Four setpoints, six degrees of freedom

The controller only ever receives four setpoints: x, y, z and yaw. Roll and pitch are never commanded directly. That works because the controller has two nested loops.

The outer loop handles position. It turns horizontal position error into a desired acceleration. Since horizontal acceleration comes from tilting thrust, the required tilt follows from dividing by g and taking the arcsine. Those angles become roll and pitch setpoints.

The inner loop handles attitude. It tracks the roll, pitch and yaw setpoints by commanding a torque about each body axis.

Outer position loop

A mixing matrix converts the four controller outputs (total thrust plus three torques) into individual rotor thrusts:

[ 1   1   1   1 ]   thrust
[ 1  -1  -1   1 ]   roll torque
[ 1  -1   1  -1 ]   pitch torque
[ 1   1  -1  -1 ]   yaw torque

Each rotor’s output is then clamped to 0–15 N, so the simulation respects actuator limits. That matters, because saturation is exactly where cascaded PID tends to misbehave under large setpoint steps.

Tuning

The full dynamics are nonlinear and strongly coupled, so classical PID design has nothing to work with directly. We linearised the model about hover with Simulink’s linear analysis tools, which gave transfer functions for step response and root locus work. Simulation then refined the gains.

LoopPID
Roll, pitch, yaw1.180.60.3
Position x1.40.42.2
Position y1.20.51.4
Altitude z2.50.60.85

The pattern in the gains is the interesting part. On x and y, derivative gain is larger than proportional gain. Horizontal corrections have to travel through the attitude loop before they move the vehicle, which adds phase lag, and the D term damps it. Altitude is the opposite: z acts directly on total thrust with no inner loop in between, so proportional gain does most of the work.

Validating bottom-up

We tested each subsystem on its own before connecting anything: motor mixing, motor model, translational dynamics, rotational dynamics. When the closed loop later misbehaved, we already knew the plant was right, so the fault had to be in the controller. That saved a lot of guessing.

The final tests were a hover hold and an approach to x = 0, y = 0, z = 5 m with zero yaw.

Trajectory results

What I’d change

  • Yaw shares roll and pitch’s gains despite having double the inertia, so it has roughly half the effective authority. It works, but it was inherited rather than tuned.
  • The controller reads perfect state straight from the simulation. A real vehicle would need a state estimator, such as a Kalman filter fusing IMU data, and we never built one.
  • No drag, wind or motor lag. Any of them would degrade the tuned gains.
  • No benchmark. LQR is the natural comparison for a coupled multi-input system like this. Without it, there’s no evidence PID was the best choice rather than the familiar one.

The parameters and gains are in model/init_params.m if you want to run it yourself.

  • #control-systems
  • #matlab
  • #simulink
  • #robotics