Udacity Flying Car Nanodegree - Term 1 - Project 3 - 3D Quadrotor Controller
In this project, we implement and tune a cascade PID controller in a simulator for quadcopter. The controller design uses feed-forward strategy as explained in this paper, Feed-Forward Parameter Identification for Precise Periodic Quadrocopter Motions, by Angela P. Schoellig. The following diagram could be found on that paper describing the cascaded control loops of the trajectory-following controller:
The simulator is realistic in the physics that it models. So we need to put some constraints on the outputs of different part of the controller otherwise things can go wrong when some of those limits are not implemented correctly. Instructions on how to use the simulator can be found in How to use simulator. There are in total five scenarios that cover all the aspects of the controller.
- /config/QuadControlParams.txt: This file contains all control gains and other desired tuning parameters.
- /src/QuadControl.cpp: This file contains all of the code for the controller.
For Linux, the recommended IDE is QtCreator.
- Download and install QtCreator.
- Open the
.pro
file from the<simulator>/project
directory. - Compile and run the project (using the tab
Build
select theqmake
option. You should see a single quadcopter, falling down.
NOTE: You may need to install the GLUT libs using sudo apt-get install freeglut3-dev
For doing manual builds using cmake
, we have provided a CMakeLists.txt
file with the necessary configuration.
NOTE: This has only been tested on Ubuntu 16.04, however, these instructions should work for most linux versions. Also note that these instructions assume knowledge of cmake
and the required cmake
dependencies are installed.
- Create a new directory for the build files:
cd FCND-Controls-CPP
mkdir build
- Navigate to the build directory and run
cmake
and then compile and build the code:
cd build
cmake ..
make
- You should now be able to run the simulator with
./CPPSim
and you should see a single quadcopter, falling down.
If the mass doesn't match the actual mass of the quad, it'll fall down. So we tune the Mass parameter in /config/QuadControlParams.txt to make the vehicle more or less stay in the same spot.
The standard output when the scenario 1 passes:
PASS: ABS(Quad.PosFollowErr) was less than 0.500000 for at least 0.800000 seconds
To accomplish this we implement:
- GenerateMotorCommands method - It implement these equations:
F_0
to F_3
are the individual motor's thrust, tau(x,y,z)
are the moments along xyz axes.
F_t
is the total thrust, kappa
is the drag/thrust ratio and l
is the drone arm length over square root of 2.
(The calculation implementation for the motor commands is in /src/QuadControl::GenerateMotorCommands method from line 56 to 90)
NOTE: We are using NED coordinates ie, the z
axis is inverted. This means the
yaw is defined positive when drones yaw clockwise looking from above.
- BodyRateControl method - A P controller that controls body rate ie roll rate, pitch rate and yaw rate.
At this point, the kpPQR
parameter has to be tuned to stop the drone from flipping.
(The body rate control is implemented in /src/QuadControl::BodyRateControl method from line 92 to 125)
- RollPitchControl method - A P controller for the roll, pitch and yaw.
This controller receives the commanded accelerations in x and y directions. And to achieve these accelerations
we need to apply a P controller to the elements R13
and R23
of the rotation matrix from world frame to body frame.
(The roll pitch control is implemented in /src/QuadControl::::RollPitchControl method from line 128 to 165)
But to output roll and pitch rates we another equation to apply:
NOTE: It is important to note that the thrust received is positive but while working with NED coordinates
we need it to be inverted and converted to acceleration before applying the equations.
After the implementation is done, start tuning kpBank
and kpPQR
.
The standard output when the scenario 2 passes:
PASS: ABS(Quad.Roll) was less than 0.025000 for at least 0.750000 seconds
PASS: ABS(Quad.Omega.X) was less than 2.500000 for at least 0.750000 seconds
There are three methods to implement here:
- AltitudeControl: This is a PD controller to control the acceleration in z direction and outputs the thrust needed to control the altitude.
Here, c is the magnitude of acceleration produced by the motors thrust. We already included the direction of c
while subtracting the matrix.
(The altitude control is implemented in /src/QuadControl::AltitudeControl method from line 167 to 208)
- LateralPositionControl Here we use a cascaded proportional controller: an inner one for velocity,
with gain Kv and an outer one with gain Kp to control accelerations in
x
andy
direction.
(The lateral position control is implemented in /src/QuadControl::LateralPositionControl method from line 211 to 254)
- YawControl: This is a simple P controller which outputs the yaw rate (in body frame). We wrap the yaw error in range
[-pi, pi]
.
When looking at a controller linearized for small motions, r is approximately equal to the yaw rate. If we used a linearized controller for roll and pitch, the equations would also be much simpler, but we would have much larger control errors, so we need to take into account the attitude in those cases. For yaw, with this type of control, we can get away with keeping the linearized assumption on the controller, which is why we pass yaw rate back directly without taking into account the attitude.
(The yaw control is implemented in /src/QuadControl::YawControl method from line 257 to 292)
We start tuning for altitude controller using kpPosZ
and kpVelZ
and then move to tuning
the lateral controller by using kpPosXY
, kpVelXY
. In last we tune yaw controller with kpYaw
. Here is a video of the scenario when it passes:
The standard output when the scenario 3 passes:
PASS: ABS(Quad1.Pos.X) was less than 0.100000 for at least 1.250000 seconds
PASS: ABS(Quad2.Pos.X) was less than 0.100000 for at least 1.250000 seconds
PASS: ABS(Quad2.Yaw) was less than 0.100000 for at least 1.000000 seconds
By now each controller is implemented and tuned. Ok, now we need to add an integral part to the altitude controller to move it from PD to PID controller.
Now we need to tune the integral control, and other control parameters until all the quads successfully move properly.
The standard output when the scenario 4 passes::
PASS: ABS(Quad1.PosFollowErr) was less than 0.100000 for at least 1.500000 seconds
PASS: ABS(Quad2.PosFollowErr) was less than 0.100000 for at least 1.500000 seconds
PASS: ABS(Quad3.PosFollowErr) was less than 0.100000 for at least 1.500000 seconds
Now that we have all the working parts of a controller, we will put it all together and test it's performance once again on a trajectory.
The drone needs to follow a 8 shaped trajectory. In order to pass this we need to put a constraint on bx
and
by
so that the drone does not try to tilt more than a limit. For example, it could tilt 90 degrees and try to
produce infinite thrust to hover.
The standard output when the scenario 5 passes::
PASS: ABS(Quad2.PosFollowErr) was less than 0.250000 for at least 3.000000 seconds