Michael McNally
Unmanned Aerial Vehicles (UAVs or drones) are an increasingly popular technology with a wide range of uses. While they were originally developed for military applications, drones are are gaining traction in industries including agriculture, search and rescue, environmental monitoring & mapping, and healthcare¹. The global drone market was worth $22.4 billion in 2022 and is expected to grow at a Compound Annual Growth Rate of around 25% through 2031². Given that they are highly maneuverable, relatively energy efficient³, and do not require a human operator to be on-board, the appeal of drones is obvious. Even more exciting is the possibility for drones to operate autonomously which will greatly increase the breadth of tasks they can be used for, will easily scale, and can even involve swarms of drones working together without any human intervention.
The Crazyflie is a small (27 gram) open-source drone made by Bitcraze. It is powered by a 250mAh battery, is easy to assemble, and its parts can be easily swapped out for new ones in the event that they sustain any damage. The Crazyflie has 2 on-board microcontrollers - an STM32F405 MCU for its main operations, and an nRF51822 MCU for radio and power management. It is able to communicate with a PC via the Crazyradio PA USB attachment⁴ and can be controlled via a smartphone app, a video game controller, or with Python code via the CFLib library⁵.
Bitcraze offers several expansion decks which can improve the functionality of the Crazyflie. The Flow Deck allows the Crazyflie to understand where it is and in what direction it is moving. It includes a VL53L1X ToF sensor which measures distance to the ground, and a PMW3901 optical flow sensor which measures movement⁶. We will use the Flow Deck throughout the Crazyflie projects. The Multi-Ranger Deck allows the Crazyflie to detect objects around it in the front/back/left/right/up direction⁷. This makes it a useful tool for things like Localization and Mapping and we will use the Multi-Ranger Deck for our Particle Filter and SLAM projects. The AI Deck is comprised of a GAP8 IoT application processor and a ULP camera. This allows it to stream images and to run fully autonomous navigation algorithms on-board⁸. We will use the AI Deck for Object Following and for Autonomous Navigation.
We can fully specify the state of the Crazyflie as $\bar{x} = [x,y,z,\phi,\theta,\psi, \dot{x}, \dot{y}, \dot{z},p,q,r]^T$ where $(x,y,z)$ is its position in space, $(\phi, \theta, \psi)$ is its orientation (roll, pitch, and yaw respectively), $(\dot{x}, \dot{y}, \dot{z})$ is its velocity, and $(p,q,r)$ is its angular velocity. There are 12 different options for Euler Angles so we need to specify the convention - we choose to use the Space 1-2-3 convention. That is in order to specify the orientation of the Crazyflie relative to the world frame, we first rotate by $\phi$ about the x-axis, then rotate by $\theta$ about the y-axis, and finally rotate by $\psi$ about the z-axis.
In addition to the 12 states of the Crazyflie, we can specify 4 control inputs $\bar{u} = [F_{tot}, M1, M2, M3]$ where $F_{tot}$ is the total thrust of the 4 propellers, $M1$ is the moment about the x-axis, $M2$ is the moment about the y-axis, and $M3$ is the moment about the z-axis. We can only directly control the angular velocity of each rotor so we need to compute the thrust generated by each rotor as $F_i = k_f \omega_i^2$ where $k_f$ is the thrust coefficient and $\omega_i$ is the angular velocity of rotor $i$. We also need to compute the aerodynamic moment for each rotor $M_i = k_m \omega_i^2$ where $k_m$ is the drag coefficient and $\omega_i$ is again the angular velocity of rotor $i$.
As a quick note, the Crazyflie actually doesn't allow for specifying angular velocities themselves, but rather a percentage of the maximum possible angular velocity. The input is a 16-bit integer where 0 corresponds to 0% of the maximum angular velocity, and $2^{16} - 1$ corresponds to 100% of the maximum angular velocity. We can then compute the coefficient of thrust $k_f$ by running the propellers at various percentages of max angular velocity, recording the measured thrust, and running a linear regression. That is, given a dataset $\{(x_i, y_i\}$ where $x_i$ is the percentage of max angular velocity for sample $i$ and $y_i$ is the measured thrust for sample $i$, we seek to compute $a$ such that $y = xa$. This is an over-determined system of equations, so it is unlikely that there is an exact solution. Instead our aim is to minimize the squared error by computing $min_a ||y - xa||^2$. This can easily be done with the scipy.optimize.lsq.linear()
**function, which gives $k_f = 1.822 \times 10^{-6}$. The graph below shows that this fits the data quite well (and our value is very close to the $k_f = 1.938952 \times 10^{-6}$ computed by [9])
Per [10], for the Crazyflie $\frac{k_f}{k_m} \approx 40.7333$ so we can also compute $k_m = 4.473 \times 10^{-8}$.
The dynamics of the Crazyflie are of the form $\dot{\bar{x}} = f(\bar{x}, \bar{u}) = [\dot{x}, \dot{y}, \dot{z}, \dot{\phi}, \dot{\theta}, \dot{\psi}, \ddot{x}, \ddot{y}, \ddot{z}, \dot{p}, \dot{q}, \dot{r}]^T$. We can define the position vector as $\bar{r} = [x, y, z]^T$, so we have the velocity vector $\dot{\bar{{r}}} = [\dot{x}, \dot{y}, \dot{z}]^T$ and the acceleration vector $\ddot{\bar{{r}}} = [\ddot{x}, \ddot{y}, \ddot{z}]^T$. Using Newton's Second Law, we have $\ddot{\bar{{r}}} = [0,0,-g]^T + \bar{R}[0,0,\frac{F{tot}}{m}]^T$ where $\bar{R}$ is a rotation matrix mapping the body frame of the Crazyflie to the world frame using the Space 1-2-3 convention. We can obtain $\bar{R}$ by multiplying the elementary rotation matrices $R_{z,\psi}R_{y,\theta}R_{x,\phi}$. Note that since we are using the space 1-2-3 convention, we need to pre-multiply in order to get the desired transformation. Then we have $\bar{R}$:
$$ \begin{bmatrix} \cos(\theta)\cos(\psi) && \sin(\phi)\sin(\theta)\cos(\psi) - \sin(\psi)\cos(\phi) && \cos(\phi)\sin(\theta)\cos(\psi) + \sin(\psi)\sin(\phi)\\ \cos(\theta)\sin(\psi) && \sin(\phi)\sin(\theta)\sin(\psi) + \cos(\psi)\cos(\phi) && \cos(\phi)\sin(\theta)\sin(\psi) - \cos(\theta)\sin(\phi)\\ -\sin(\theta) && \sin(\phi)\cos(\theta) && \cos(\phi)\cos(\theta) \end{bmatrix} $$