English | 日本語
This is a math, physics, and dynamics library for the drone plant model in the Hakoniwa project(Open Source Runtime Environment for Simulating Cyber-Physical Systems).
You can transform vectors between the ground and the body frame coordinate system, and also calculate the drone's speed, acceleration from the rotors' thrust and gravity.
It is first designed for Hakoniwa px4sim project, but I found it was more general, so made it a separate library with more user-friendly interface, and also added the reference to the equations in the book below.
Most of the functions are implemented based on the equations in the following book:
All the functions are implemented in C++, with the equation numbers in the book as comments in the sourde code. The coverage is not enough but the drone in the project started flying with this library, although not all the equations in the book are not implemented yet.
I hope this can be a reference implmentation for the basic drone dynamics.
#include <iostream>
// include the library header
#include "drone_physics.hpp"
int main() {
// the namespace of the library
using namespace hako::drone_physics;
// create a body frame from Euler angles.
VectorType frame = {0, 0, M_PI/2};
VelocityType body_velocity = {100, 200, 300};
// Convert the body velocity to the ground frame.
VelocityType ground_velocity = ground_vector_from_body(body_velocity, frame);
// get the x,y,z components of the velocity.
auto [u, v, w] = ground_velocity;
std::cout << "u = " << u << ", v = " << v << ", w = " << w << std::endl;
// output: u = 200, v = -100, w = 300
// reverse the conversion to the body frame.
VelocityType body_velocity2 = body_vector_from_ground(
{u, v, w},
{0, 0, M_PI/2}
);
auto [u2, v2, w2] = body_velocity2;
std::cout << "u2 = " << u2 << ", v2 = " << v2 << ", w2 = " << w2 << std::endl;
// output: u2 = 100, v2 = 200, w2 = 300, back again.
}
#include <stdio.h>
// include the library header
#include "drone_physics_c.h"
int main() {
// create a body frame from Euler angles. dp_ is the prefix for this lib.
dp_euler_t frame = {0, 0, M_PI/2};
dp_velocity_t body_velocity = {100, 200, 300};
// Convert the body velocity to the ground frame.
dp_velocity_t g = dp_ground_vector_from_body(&body_velocity, &frame);
// get the x,y,z components of the velocity.
printf("x=%g, y=%g, z=%g\n", g.x, g.y, g.z);
// output: x = 200, y = -100, z = 300
// you can also use explicit initialization.
// reverse the conversion to the body frame.
dp_velocity_t b = dp_body_vector_from_ground(
&g, &(dp_euler_t){0, 0, M_PI/2}
);
// get the x,y,z components of the velocity.
printf("x=%g, y=%g, z=%g\n", b.x, b.y, b.z);
// output: x = 100, y = 200, z = 300, back again.
}
Copy this whole directory to your project.
There is CMkakeLists.txt
, use CMake to build.
$ cmake .
$ make
- The C++ library is built as
libdrone_physics.a
. - The C library is built as
libdrone_physics_c.a
. - Test programs
utest
ctest
examples
cexamples
are also built as unit tests and examples.
I your programs,
- In C++, include
drone_physics.hpp
into your C++ code and link withlibdrone_physics.a
. - In C, include
drone_physics_c.h
into your C code and link withlibdrone_physics_c.a
.
See examples.cpp
, utest.cpp
for more examples in C++,
and cexamples.c
, ctest.c
for more examples in C.
VectorType
is a 3-dimensional vector, used in both the ground frame and the body frame. The following subtypes are available.
VelocityType
- VelocityAccelerationType
- AccelerationForceType
- ForceTorqueType
- Torque
AngularVelocityType
- Angular velocityAngularAccelerationType
- Angular acceleration
EulerType
is a 3-dimensional vector, used in transformation between the ground frame and the body frame. The following subtypes are available.
Note that the euleer angles are not vectors, and cannot be added, scaled, or multiplied by matrices. The following subtypes are available.
EulerType
- Euler anglesEulerRateType
- Change rate of the Euler anglesEulerAccelerationType
- Acceleration of the Euler angles(2nd order differential)
QuaternionType
is a 4-dimensional vector
QuaternionType
- QuaternionQuaternionVelocityType
- Time derivative of the quaternion
Functions(C++) are implemented in the following categories, with the referece to the book. As our naming policy, the function names represent the output of the function(omitting get_ prefix) and describe the source using _from if needed, and the input as the arguments, (maybe) overly using function overloading by argument types. None of the functions have states(static variable) nor side effects. All the arguments are passed by value(or const reference) and won't be changed in the function.
Function | equation | note |
---|---|---|
ground_vector_from_body |
(1.71), (1.124) | Body velocity to ground velocity |
body_vector_from_ground |
(1.69), inverse of (1.124) | Ground velocity to body velocity |
euler_rate_from_body_angular_velocity |
(1.109) | Body angular velocity to euler rate |
body_angular_velocity_from_euler_rate |
(1.106) | Euler rate to body angular velocity |
euler_from_quaternion |
(1.66) | Quaternion to Euler angles |
quaternion_from_euler |
(1.74)-(1.77) | Euler angles to Quaternion |
quaternion_velocity_from_angular_velocity |
(1.86)(1.87) | Angular velocity to Quaternion velocity |
Function | equations in the book | note |
---|---|---|
acceleration_in_body_frame |
(1.136),(2.31) | Acceleration in body frame by force |
angular_acceleration_in_body_frame |
(1.137),(2.31) | Angular acceleration in body frame by force |
acceleration_in_ground_frame |
(2.46), (2.47) | Acceleration in ground frame by torque |
euler_acceleration_in_ground_frame |
differential of (1.109) | Euler acceleration by torque(not used) |
Function | equations in the book | note |
---|---|---|
rotor_omega_acceleration |
(2.48) | Rotor angular rate acceleration from dury rate (linear and non-linear versions) |
rotor_thrust |
(2.50) | Rotor thrust from rotor angular rate( |
rotor_anti_torque |
(2.56) | Rotor anti-torque from rotor thrust( |
rotor_current |
Rotor current |
Function | equations in the book | note |
---|---|---|
body_thrust |
(2.61) | Sum of the |
body_torque |
(2.60)-(2.62) | Sum of the torques from the |
There are C language interfaces for all the functions above, with the prefix dp_
for "drone physics".
The ground frame coordinate system fixed to the ground is defined by right hand rule,
in which
The origin of the body frame is the center of gravity of the drone. The body frame is attached to the drone, and the body frame moves with the drone. And the origin of the ground frame is assumed to be the same as the origin of the body frame. This ground frame is paticularly called the "Vehicle Carried NED".
In the equations below, we use the ground frame(Vehicle Carried NED) and the body frame in calculating the drone dynamics.
This is reasonable because the equations do not contain the position variables, and the position is obtained in the last step by integrating the velocity in the ground frame.
The rotation order from the ground frame to the body frame
is yaw :
Note
The most basic Newtonian(translation) and Euler(rotational) equations of motion
in the ground frame are as follows,
where the subscript
The basic dynamics equations in the body frame are as follows eq.(2.31).
where;
-
$m$ - mass of the drone -
$I$ - inertia matrix of the drone -
$v$ - linear velocity of the drone$v=(u, v, w)$ -
$\omega$ - angular rate of the drone$\omega = (p, q, r)$ -
$F$ - force vector including gravity($mg$ ), drag($-dv)$ , and thrust$(T)$ -
$\tau$ - torque vector from the rotors
In the first equation, the second term is the inertial force(Coriolis force), wihch is the force due to the angular velocity of the body frame. The centrifugal force does not occur because the origin of the body frame is at the center of gravity.
In the second equation, the second term is the inertial torque(gyro effect), which is the torque due to the angular velocity of the body frame.
The body frame dynamics above are expanded based on the body angles
Here is the body frame dynamics equations used in this library.
All the state variables
Letting
The time-derivative of
Unfortunately the function
In the program, finally the body velocity
And the body angular velocity
The last part above can alternatively be implemented by quaternion, which is more stable than the euler angles. We will describe the quaternion-based implementation in the later section.
The function name: acceleration_in_body_frame
.
The function name: angular_acceleration_in_body_frame
.
where;
-
$m$ - mass of the drone($m>0$ ). -
$g$ - acceleration due to gravity($g>0$ ). -
$(u, v, w)^T$ - velocity of the drone in the body frame. -
$(p, q, r)^T$ - angular velocity of the drone in the body frame. -
$d$ - air friction coefficient "drag", affecting the velocity terms(d>0). -
$(\phi, \theta, \psi)^T$ - roll($x$ -axis), pitch($y$ -axis) and yaw($z$ -axis) euler angles. -
$I_{xx},I_{yy}, I_{zz}$ are inertia moments around the body-frame axes$x, y, z$ respectively. ($x, y, z$ ) should be aligned with the drone's principal axes, from the gravity-center(other slant inertia$I_{xy}, I_{yz}, I_{zx}$ are assumed to be zero).
The ground frame dynamics of the translational motion is as follows. Note that the fraction of the air is also assumed to be the same in the three direction. The rotational motion in the ground frame are not calculated in this library because time-varying inertia is too complex(to me) in the ground frame.
The function name is acceleration_in_ground_frame
.
The transformations from/to the body frame and the ground frame are as follows.
The dynamics above are calculated using the transformations between the ground frame and the body frame.
The body velocity
This matrix is called the direction cosine matrix(DCM) and is the product of three rotation matrices in the order
DCM is always orthogonal and has a '1' as its eigenvalue. The direction of the eigenvector corresponding to it is the direction of the rotation, which is the quaternion's imaginary part. This makes sense that the DCM is a rotation matrix around the eigenvector with the eigenvalue 1(length preserving).
The function name is ground_vector_from_body
,
and the inverse transformation is body_vector_from_ground
.
The rotation matrix is a basis transformation matrix, which transforms the basis vectors of the original space to the basis vectors of the new space.
Focusing on the new
Now, as a general theory of linear algebra, using the basis transformation matrix
The old coordinates
From these two equations, the transformation equation for the coordinates is as follows (multiplying both sides of the first equation by
The right side is the product of the transformed "basis" and its "coordinate components", and the product doesn't change before and after the transformation.
Now, apply this to the three rotation matrices in this case, i.e., the transformation from the ground frame coordinate system to the body frame coordinate system, rotated (
That is,
This becomes the transformation matrix (DCM) from the body frame coordinate system to the ground frame coordinate system. (End of linear algebra memo)
- Euler Angles and the Euler Rotation sequence(Christopher Lum), YouTube
- Flight Dynamics in Body Axes Coordinate System(Japanese) by @mtk_birdman
- Euler's Angles(Japanese) by Sky Engineering Laboratory Inc
- 3D rotation in Quaterinon by @kenjihiranabe
The body angular rate
Note that this matrix is close to
The function name is euler_rate_from_body_angular_velocity
,
and the inverse transformation is body_angular_velocity_from_euler_rate
.
The attitude representation using Euler angles has a problem called "gimbal lock"
when the pitch angle is 90 degrees (the nose is vertical), and there are two Euler angle representations to represent the same attitude.
For example, euler_rate_from_body_angular_velocity
, a zero division occurs due to
This problem does not occur in normal flight with a small pitch angle like a passenger plane, but it becomes unstable when performing extreme movements such as drone racing or "aerobatics" in fighter jets. To solve this problem, quaternions are used as the attitude representation instead of Euler angles.
In the implementation of the drone, the quaternion representing the attitude should be maintained internally while always updating it, and converting it to Euler angles as needed. But even if the attitude quaternion changes continuously, the Euler angles could jump discontinuously(the attitude is still correct in that case).
Euler angles
The function name is quaternion_from_euler
.
The inverse conversion, quaternion to Euler angles, is as follows(eq. 1.74-1.77). But the gimbal lock problem is not described in the book, and I referred to the article by @aa_debdeb(Atsushi Asakura).
std::atan2(y, x)
.
In the implementation, first
The function name is euler_from_quaternion
.
The time derivative of the quaternion is obtained from the angular velocity
The function name is quaternion_velocity_from_angular_velocity
.
There are two versions of the rotor dynamics in this library. Note that the function name is the same for both versions, but the parameters are different (using C++ funtion overlading).
Each rotor can be modeled as a first-order lag system, in which the rotor angular rate
is
and the time domain differential equation is as follows.
where;
-
$K_r$ - rotor gain constant. -
$T_r$ - rotor time constant. -
$d(t)$ - duty rate of the rotor. ($0.0 \le d(t) \le 1.0$ )
This model is an approximation of the non-linear model described later, with two parameters rotor_omega_acceleration
.
Another model is that the rotor angular rate
See https://www.docswell.com/s/Kouhei_Ito/KDVNVK-2022-06-15-193343#p2 eq (3)
where;
-
$J$ - The inertia of the propeller. [$kg m^2$ ] -
$D$ - The damping coefficient of the propeller. [$N m s/rad$ ] -
$C_q$ - The torque coefficient of the propeller. [$N m^2/s^2$ ] -
$L$ - The inductance of the motor. [$H = Vs/A$ (Henry)] -
$R$ - The resistance of the motor. [$\Omega$ (Ohm)] -
$K$ - The torque constant of the rotor. [$N m/A$ ] -
$d(t)$ - The duty rate of the motor. ($0.0 \le d(t) \le 1.0$ ) -
$V_{bat}$ - The battery voltage. [$V$ ] -
$e(t)$ - The voltage applied to the motor, equals to$V_{bat}d(t)$ . [$V$ ] -
$i(t)$ - The current of the motor. [$A$ ] -
$\omega(t)$ - The angular velocity of the propeller. [$rad/s$ ]
Neglecting the inductance
Then the differential equation for
Soving this differential equation for
Let
The function name is (another version of)rotor_omega_acceleration
(overloaded function name).
And the current
The function name is rotor_current
.
Note that when
The thrust
The anti-torque
where
The function name is rotor_thrust
and rotor_anti_torque
.
The body location
We connected Hakoniwa to PX4 SITL simulator and tested the library with the following experiments. The architecture of the simulation is described here.
Mission:
- Lift off and hover at height 10m.
- Move to the right 10m.
utest.cpp
has unit tests for all the functions. It is not easy to read, but you can use it as a reference.
ctest.c
has C interface tests.
- All the functions are implemented in standard C++17.
- No external libraries are used other than ones in the std:: namespace.
- Implemented as functions, not classes. Meaning stateless.
I thank Dr. Nonami for writing the detailed description of the math around the drone development. And Kohei Ito(@Kouhei_Ito) for the detailed explanation of the drone dynamics in his blog and also kind replies to my questions. And also I thank (@tmori)for connecting Hakoniwa to PX4, QGroundControl, and Unity, and spending a long time testing the drone flight virtually, and also for leading this whole Hakoniwa project.
A lot of good references are available on the web. I list some of them here.
- "Introduction to Drone Engineering" by Dr. Kenzo Nonami(Japanese)
- Model Based Control of UAV by Nonami(Japanese)
- Drone control Lecture by Seongheon Lee
- Euler's Equations of Motion(Japanese) by Sky Engineering Laboratory Inc
- Euler's Angles(Japanese) by Sky Engineering Laboratory Inc
- Flight Dynamics in Body Axes Coordinate System(Japanese) by @mtk_birdman
- Basics of Multi-Copter(Japanese) by @Kouhei_Ito
- Euler Angles and the Euler Rotation sequence(Christopher Lum), YouTube
- 3D rotation in Quaterinon by @kenjihiranabe
- The art of Linear Algebra by @kenjihiranabe
We have learned a lot from them and also the links are embedded in the source code comments. Thank you all !!