How I Developed the Scout Flight Controller, Part 4: Stabilizing Flight with PID Controllers
This article is part of a series documenting the development of a custom quadcopter flight controller from scratch. For a list of the other related articles, please refer to the introductory article found here.
In the previous chapters in this series, we explored the crucial steps of capturing telemetry data through the gyroscope and acquiring pilot input via the RC receiver. Through these processes, we discovered that a quadcopter flight controller determines the appropriate thrust to be applied to each of its four motors by comparing the actual rate of change of attitude (roll, pitch, and yaw) with the desired rate of change. In essence, the quadcopter adjusts the thrust delivered to each motor in order to execute the desired maneuvers as intended by the pilot.
Why do we need a PID controller?
Given the availability of both gyroscope telemetry (actual rate of change) and pilot input (desired rate of change), how does our quadcopter flight controller precisely calculate the required throttle input for each motor?
One could employ a simple algorithm to tackle this problem. For instance, if the actual roll angle measures +20°/second (indicating a right bank) while the desired angle is 0°/second (representing a stable state), one might apply a proportionally higher thrust to the right motors and a correspondingly lower thrust to the left motors, effectively correcting the roll. This approach might show promise, and it does possess elements reminiscent of the PID controllers commonly employed in modern quadcopter flight controllers. However, it falls short of sufficiency and often results in instability during flight due to the occurrence of overshoot errors.
What does a PID controller do?
A PID (Proportional, Integral, and Derivative) controller represents a simple mathematical system that dictates how a quadcopter attains the desired state specified by the pilot, considering the current actual state of the vehicle.
The diagram above depicts the PID system, with the blue line representing the goal or “setpoint.” The purple, green, and red lines illustrate distinct approaches through which a PID controller can operate to reach the desired goal. The user aims for the PID controller to approximate a goal value of 1.0, with the different lines representing various means of achieving this (such as by adjusting thrust levels).
In the case of the purple line example, the PID controller significantly overshoots the target. It applies excessive pressure, reaching the goal too quickly and overshooting it. Subsequently, it compensates by exerting less pressure, overshoots again, and repeats this process until it eventually achieves the desired goal.
Conversely, the red line example shows the PID controller undershooting the target. It applies insufficient pressure, causing a slow approach towards the goal. Although it avoids overshoot, this approach might prove too weak for a quadcopter flight controller.
The green line example demonstrates the most optimal behavior. The PID controller reaches the goal relatively swiftly, experiences a slight overshoot, but promptly corrects itself. Among the three examples, this approach strikes the best balance.
So, how exactly does a PID controller achieve this?
How does a PID controller work?
PID stands for Proportional, Integral and Derivative. These three “terms” collaborate to determine the throttle input for each motor, blending their outputs to produce an appropriate level of thrust at any given moment during flight.
The PID controller primarily focuses on the concept of error. Here, error denotes the discrepancy between the desired rate the pilot intends to achieve and the actual current rate along a specific axis.
- Proportional: The proportional component states that the thrust that should be applied should be proportional to the error. i.e. if the desired roll rate is +25°/second and our actual roll rate is -30°/second, greater thrust should be applied than in a scenario where our desired roll rate is +25°/second and our actual roll rate is 15°/second. This term asserts that the level of thrust we should apply should be proportional to the error we are encountering.
- Integral: The integral term bears resemblance to the proportional term, but it also takes cumulative error into account. Rather than solely considering a thrust correction proportionate to the error, the integral component continues to increase thrust (growing progressively) as long as the error persists. For example, if a +40°/second error remains unresolved (the pilot’s control input fails to rectify it), the integral component will continue escalating the recommended thrust with each passing second.
- Derivative: Unlike the proportional and integral components, the derivative component disregards the error itself, concentrating solely on the change in error. Put simply, it disregards whether the quadcopter is rolling to the left while the pilot desires a rightward roll; it simply aims to stabilize the situation. It is specifically engineered to counteract abrupt changes. In essence, it focuses on the change in error, rather than the error itself. Within the context of a quadcopter flight control system, it can be likened to a “fast twitch” mechanism that rapidly corrects momentary errors.
In a quadcopter flight control system, three PID controller systems operate in unison: one for each axis (roll, pitch, and yaw).
In addition to independently calculating the proportional, integral, and derivative terms, a unique gain value is applied to each term, allowing for the scaling up or down of the calculated value to a suitable level for the specific system utilizing the PID controller. These gains are typically denoted as kP, kI, and kD, representing the proportional, integral, and derivative gains, respectively.
The PID controllers function within a PID controller loop. Multiple times per second, the desired rates and the actual rates for each of the three axes (roll, pitch, and yaw) are fed into their corresponding PID controller systems. Each PID controller system calculates the proportional, integral, and derivative components, scales them using their respective gain values, combines them, and assigns the resulting thrust value to the applicable motors.
Integrating the PID Controller Output into the Flight Controller
As mentioned previously, the thrust levels for all four motors are determined by considering the outputs of all three PID controllers. Understanding the flight mechanics of a quadcopter outlined in the first chapter, we can assign each PID controller’s output to the throttle of the corresponding motor:
Snippet of code from the Scout Flight Controller:
# calculate throttle values
t1:float = adj_throttle + pid_pitch + pid_roll - pid_yaw
t2:float = adj_throttle + pid_pitch - pid_roll + pid_yaw
t3:float = adj_throttle - pid_pitch + pid_roll + pid_yaw
t4:float = adj_throttle - pid_pitch - pid_roll - pid_yaw
In the provided code snippet, the appropriate thrust levels (throttle input) for each of the four motors are calculated. t1
corresponds to the front left motor, t2
to the front right, t3
to the rear left, and t4
to the rear right. Each throttle value is calculated based on the pilot's desired throttle (adj_throttle), incorporating inputs from the three PID controllers and factoring them into the computation.
Given that t1
and t2
relate to the thrust of the front motors, while t3
and t4
pertain to the thrust of the rear motors, we need to inverse the application of the pitch PID value. Consequently, we add it to the front two motors and subtract it from the rear two motors. As explained in the first chapter of this quadcopter flight mechanics series, a quadcopter pitches forward by increasing the thrust of the rear motors while simultaneously reducing the thrust of the front motors. Hence, we reverse the operation by flipping the addition/subtraction operators. The same principle applies to the roll and yaw axes.
PID Controller Code
Below, you will find a straightforward implementation of a PID controller in Python. This mathematical system is utilized within the Scout Flight Controller to maintain stable flight.
Upon creating an instance of the PIDController
class, you can assign the desired values for the proportional gain, integral gain, and derivative gain. To ensure proper functioning of the integral and derivative terms (which rely on time-based calculations), you must provide the cycle_time_seconds
parameter. This value corresponds to the delay or interval between each PID loop iteration. In my case, the Scout Flight Controller operates at 250 Hz, resulting in a cycle time of 0.004 seconds (1 second / 250 = 0.004 seconds). Additionally, an optional parameter i_limit
exists to mitigate risks associated with unbounded integral term growth. As mentioned earlier, the integral term could inadvertently cause the motors to reach 100% thrust due to an uncorrected error (e.g., if the drone becomes entangled in a tree). Such a thrust level presents safety concerns such as the risk of injury or fires.
After instantiating the PIDController
class, you can utilize the calculate
function to obtain the recommended thrust value based on the error (the difference between the actual and goal values). This function calculates the precise thrust by factoring in the proportional, integral, and derivative terms.
Remember that the integral and derivative terms are time-dependent during flight, necessitating the use of the previous_error
and previous_i
variables mentioned earlier. It is vital to reset these values before each new flight. Resetting the PID state variables to 0 using the reset
function ensures that no carryover occurs from previous flights, facilitating a fresh start.
What’s next?
In this chapter, we learned about the role of a PID controller in determining the appropriate thrust levels for each of a quadcopter’s four motors. By leveraging the PID controller’s recommendations and adjusting the throttle inputs in a specific manner, we enable the PID controller to regulate the motor thrust and achieve stable, level flight.
In the next chapter, we will learn how we can set each motor’s input throttle to the recommended throttle by the PID controllers using pulse width modulation (PWM).