English | 日本語
箱庭プロジェクト (サイバーフィジカルシステムのシミュレーションのための,オープンソースランタイム環境)において作成された, 小型無人航空機(ドローン)プラントモデルのための,数学,物理,および動力学ライブラリです.
地上と機体座標系間の変換や,ロータの推力と重力からドローンの速度,加速度などが計算できます.
当初この箱庭 PX4 ドローンシミュレーションプロジェクト用に開発されましたが,その中から汎用的に利用できる部分を切り出し,さらに,インターフェイスをより使いやすくし,以下の参考文献との対応をとって公開することにしました. すべての関数は,以下の書籍および他の参考文献(末尾)の式の C++ 実装であり,ソースコードコメントに式番号が記載されています. 本のすべての式を実装しているわけではありませんが,箱庭プロジェクトのドローンはこのライブラリを使って実際に飛行しています.
なお,C 言語の I/F もあります.
このライブラリが,本を読んで実際にコードを書く方の参考になれば嬉しいです.
#include <iostream>
// include the library header
#include "drone_physics.hpp"
int main() {
// このライブラリの名前空間
using namespace hako::drone_physics;
// 機体座標系を Euler 角で指定
VectorType frame = {0, 0, M_PI/2};
VelocityType body_velocity = {100, 200, 300};
// 機体座標系から地上座標系への速度変換
VelocityType ground_velocity = ground_vector_from_body(body_velocity, frame);
// x,y,z 座標を取り出す
auto [u, v, w] = ground_velocity;
std::cout << "u = " << u << ", v = " << v << ", w = " << w << std::endl;
// output: u = 200, v = -100, w = 300
// 逆変換して戻す
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() {
// 機体座標系を Euler 角で指定, dp_ は dron_physics の接頭
dp_euler_t frame = {0, 0, M_PI/2};
dp_velocity_t body_velocity = {100, 200, 300};
// 機体座標系から地上座標系への速度変換
dp_velocity_t g = dp_ground_vector_from_body(&body_velocity, &frame);
// x,y,z 座標を取り出す
printf("x=%g, y=%g, z=%g\n", g.x, g.y, g.z);
// output: u = 200, v = -100, w = 300
// このように,初期化指定を使うこともできる
// 逆変換して戻す
dp_velocity_t b = dp_body_vector_from_ground(
&g, &(dp_euler_t){0, 0, M_PI/2}
);
printf("x=%g, y=%g, z=%g\n", b.x, b.y, b.z);
// output: x = 100, y = 200, z = 300, back again.
}
ソースコードをディレクトリごとコピーしてください.
CMkakeLists.txt
があるので,CMake でビルドしてください.
$ cmake .
$ make
C++ ライブラリが,libdrone_physics.a
として生成されます.
C言語ライブラリが,libdrone_physics_c.a
として生成されます.
テストプログラムが,utest
ctest
examples
cexamples
として生成されます.
あなたのプログラム中で,このライブラリを使うには,
- C++言語では,あなたのプログラムに,
drone_physics.hpp
をインクルードし,libdrone_physics.a
をリンクしてください. - C言語では,あなたのプログラムに,
drone_physics_c.h
をインクルードし,libdrone_physics_c.a
をリンクしてください.
examples.cpp
と utest.cpp
にC++言語での呼び出し例があります.
cexamples.c
と ctest.c
にC言語での呼び出し例があります.
VectorType
は,3次元のベクトル(座標値)です.機体座標系と地上座標系の両方で使用されます.以下のサブタイプがあります.
VelocityType
- 速度AccelerationType
- 加速度ForceType
- 力TorqueType
- トルク
AngularVelocityType
- 角速度AngularAccelerationType
- 角加速度
EulerType
は,3次元のオイラー角です.地上座標系から機体座標系への変換として使用されます.オイラー角はベクトルではありません.ベクトルのように加算・スカラー倍・行列演算することはできません.以下のサブタイプがあります.
EulerType
- オイラー角EulerRateType
- オイラー角の変化率EulerAccelerationType
- オイラー角の2次変化率
QuaternionType
は,4次元のベクトル
QuaternionType
- クォータニオンQuaternionVelocityType
- クォータニオンの変化率
関数は以下のカテゴリから構成され,書籍の式番号が記載されています(C++). 命名方針として、関数名の先頭は関数の出力を表し(get_ プレフィックスを省略)、 必要に応じて _from を使用して変換元を記述し、引数として入力を示します。 また、引数の型や数によって関数のオーバーロードを多用することがあります。 なお、内部に状態(static variable)を持つ関数はなく、副作用もありません。すべての引数は入力として扱い、変更されません。
関数 | 数式 | 意味 |
---|---|---|
ground_vector_from_body |
(1.71), (1.124) | 機体座標のベクトルを地上座標に変換 |
body_vector_from_ground |
(1.69), (1.124)の逆変換 | 地上座標のベクトルを機体座標に変換 |
euler_rate_from_body_angular_velocity |
(1.109) | 機体角速度をオイラー角の変化率に変換 |
body_angular_velocity_from_euler_rate |
(1.106) | オイラー角の変化率を機体各速度に変換 |
euler_from_quaternion |
(1.66) | クォータニオンからオイラー角に変換 |
quaternion_from_euler |
(1.74)-(1.77) | オイラー角からクォータニオンに変換 |
quaternion_velocity_from_angular_velocity |
(1.86)(1.87) | 角速度からクォータニオンの変化率に変換 |
関数 | 数式 | 意味 |
---|---|---|
acceleration_in_body_frame |
(1.136),(2.31) | 力による機体座標系での加速度計算 |
angular_acceleration_in_body_frame |
(1.137),(2.31) | 力による機体座標系での角加速度計算 |
acceleration_in_ground_frame |
(2.46), (2.47) | 力による地上座標系での加速度計算 |
euler_acceleration_in_ground_frame |
(1.109)の微分 | トルクによる地上座標系でのオイラー角2次変化率計算(not used) |
関数 | 数式 | 意味 |
---|---|---|
rotor_omega_acceleration |
(2.48) | ローター角速度の加速度計算(線形モデルと非線形モデル) |
rotor_thrust |
(2.50) | ローター推力計算(機体 |
rotor_anti_torque |
(2.56) | ローターの反トルク計算(機体 |
rotor_current |
ローターの電流計算 |
関数 | 数式 | 意味 |
---|---|---|
body_thrust |
(2.61) |
|
body_torque |
(2.60)-(2.62) |
|
C言語インターフェイスが,drone_physics_c.h
に用意されています.dp_
は drone_physics の接頭です.
地上座標系(Ground Frame)は,右手系で定義されており,
機体座標系の原点は機体の重心です.機体座標系は機体に固定されており,機体座標系は機体とともに移動します.
また,地上座標系の原点を機体座標系の原点に一致していると仮定します.これを特に "Vehicle Carried NED"
(機体に固定されたNED座標系)と呼びます.
すなわち,以下の式では,地上座標系の原点
これが成立するのは, 運動の方程式の中に速度と各速度,その時間微分が含まれていはいますが,位置についての変数が含まれていないからです. 言い換えると,並進加速度による慣性力を考えずに立式できます(位置は,最終的に地上座標系での速度を積分して求めます).
オイラー角は,地上座標系の座標軸を機体座標へと,ヨー角(
最も基本的なニュートンの運動方程式(並進)とオイラーの運動方程式(回転)は,地上座標系で以下のようになります
(添字
これがこのまま解ければよいのですが,
各文字は,以下の意味です.
-
$m$ - 機体の質量 -
$I$ - 機体の慣性行列 -
$v$ - 機体の速度$v=(u, v, w)^T$ -
$\omega$ - 機体の角速度$\omega = (p, q, r)^T$ -
$F$ - 機体に働く力のベクトル.重力($mg$ ),空気抵抗($-dv$ ),推力($T$ )からなる -
$\tau$ - 機体に働くトルクのベクトル.ローターの推力によるものと,反トルクの合計ベクトル
最初の式の第2項は,慣性力(コリオリの力)です.これは,機体座標系の角速度によって生じる力です. 遠心力は機体座標系を重心と一致させているため,生じません.
また,次の式の第2項は,慣性トルク(ジャイロ効果)です.これは,機体座標系の角速度によって生じるトルクです. これらを,ドローンの力学に適用すると,以下のようになります.
機体座標系の動力学方程式です.このライブラリでは基本的にこの方程式を後述の座標変換と合わせて使用することで,
速度と各速度からなる全状態変数
制御理論の式としては,状態変数のベクトルを
入力となる力
と書くことができます.残念ながら
プログラムでは,最終的に機体速度
なお、最後の部分については、Quaternionを使ったより安定的な手法があり、それについても後述します。
関数名は,acceleration_in_body_frame
.
関数名は,angular_acceleration_in_body_frame
.
ここで,
-
$m$ - 機体の質量($m>0$ ) -
$g$ - 重力加速度($g>0$ ) -
$(u, v, w)^T$ - 機体の速度(機体座標) -
$(p, q, r)^T$ - 機体の角速度(機体座標) -
$d$ - 並進に影響する空気抵抗($d>0$ ) -
$(\tau_\phi, \tau_\theta,\tau_\psi)^T$ - 機体に働くトルクのベクトル.ローターの推力の号力($\tau_\phi, \tau_\theta$ )と,反トルク($\tau_\psi$ ). -
$\phi, \theta, \psi$ は機体座標系のオイラー角.ロール($x$ -軸),ピッチ($y$ -軸),ヨー($z$ -軸)角. -
$I_{xx},I_{yy}, I_{zz}$ は機体座標系の慣性モーメント($x, y, z$ 軸は機体の主軸に沿っており,他の慣性モーメント$I_{xy}, I_{yz}, I_{zx}$ はゼロとする).
地上座標系では並進についてのみ以下のように計算できます. ただし,空気抵抗はすべての方向で同じとして扱っている. すべて地上座標系を使った回転はイナーシャの時間変化等のため複雑になるので,等ライブラリでは扱わない.
関数名は,acceleration_in_ground_frame
.
機体座標系と地上座標系の変換は以下のようになります.
機体座標系
この行列は,方向余弦行列(DCM: Direction Cosine Matrix)と呼ばれ,
3つの回転行列のこの順の積
関数名は,ground_vector_from_body
.逆変換は,body_vector_from_ground
.
新しい
さて、線形代数の一般論として、基底変換行列
と表される。旧座標
この2式から、座標についての変換式は次のようになる(最初の式の両辺に右から
右辺は変換後の「基底とその座標成分」の積になっていて、それは変換の前後で変化しない。
さて、これを今回の3つの回転行列すなわち、地上座標系から機体座標系への変換(
すなわち、
となって、これが機体座標系から地上座標系への変換行列(DCM)になる。 (線形代数のメモ終わり)
- Euler Angles and the Euler Rotation sequence(Christopher Lum), YouTube
- オイラー角とは?定義と性質、回転行列・角速度ベクトルとの関係(スカイ技術研究所ブログ)
- 飛行力学における機体座標系の定義(@mtk_birdman)
機体座標系の角速度
この行列は3つの角が
関数名は,euler_rate_from_body_angular_velocity
.逆変換は,body_angular_velocity_from_euler_rate
.
オイラー角による姿勢表現は、pitch 角( euler_rate_from_body_angular_velocity
内で、$\cos \theta$ によるゼロ割演算が発生し、オイラー角の変化率が求まりません。
ドローンのピッチ角が小さいような(旅客機のような)通常飛行では問題になりませんが、ドローンレースや戦闘機での「宙返り」のような過激な動きをする場合に不安定になります。 この問題を解決するために、姿勢表現としてオイラー角の代わりにクォータニオンが使われます。
ドローンの実装としては、姿勢を示すクォータニオンを内部で維持しながら常に更新し、必要に応じてオイラー角に変換して使うことになります。 ただし、クオータニオンが連続的に変化しても、オイラー角は不連続にジャンプすることが起こります(その場合でも姿勢としては正しいオイラー角が求まります)。
オイラー角(
関数名は、quaternion_from_euler
.
逆変換は以下のようになります(式 1.74-1.77). ただし、ジンバルロック問題対応については本書に記載がなく、@aa_debdeb(Atsushi Asakura)氏の記事を参考にしました。
std::atan2(y, x)
によって計算されます。
実計算においては、まず
関数名は、euler_from_quaternion
.
クォータニオンの時間微分は、角速度ベクトル
関数名は、quaternion_rate_from_body_angular_velocity
.
このライブラリには、2つのバージョンのローター力学があります。 両方のバージョンの関数名は同じですが,パラメータが異なります(C++ の関数オーバーロード)。
1つ1つのモーターの角速度を
時間領域の微分方程式は以下のようになります.
ここで,
-
$K_r$ - ローターのゲイン定数 -
$T_r$ - ローターの時定数 -
$d(t)$ - ローターのデューティー比 ($0.0 \le d(t) \le 1.0$ )
このモデルはこの後説明する非線形モデルの近似で、$K_r, T_r$ の2つのパラメータで表現されます.
関数名は,rotor_omega_acceleration
.
もう一つのモデルは、ローターの角速度
参照: https://www.docswell.com/s/Kouhei_Ito/KDVNVK-2022-06-15-193343#p2 eq (3)
ここで、
-
$J$ - プロペラの慣性モーメント。[$kg m^2$ ] -
$D$ - プロペラの減衰係数。[$N m s/rad$ ] -
$C_q$ - プロペラのトルク係数。[$N m^2/s^2$ ] -
$L$ - モーターのインダクタンス。[$H = Vs/A (Henry)$ ] -
$R$ - モーターの抵抗。[$\omega$ (Ohm) ] -
$K$ - ローターのトルク定数。[$N m/A$ ] -
$d(t)$ - モーターのデューティー比。($0.0 \le d(t) \le 1.0$ ) -
$V_{bat}$ - バッテリー電圧。[$V$ ] -
$e(t)$ - モーターに印加される電圧。$V_{bat}d(t)$ に等しい。[$V$ ] -
$i(t)$ - モーターの電流。[$A$ ] -
$\omega(t)$ - 得られるプロペラの角速度。[$rad/s$ ]
インダクタンス
さらに、電流
これを、
関数名は,rotor_omega_acceleration
の別バージョンです(関数オーバーロード).
また、流れる電流は、
で求まります。関数名は、rotor_current
.
1つのロータ推力
反トルク
ここで,
関数名は,rotor_thrust
と rotor_anti_torque
.
ここでは,状態変数,数式と関数の全体像を示します.機体の位置
私たちは,Hakoniwa を PX4 SITL シミュレータに接続し,ライブラリを以下の実験でテストしました. 全体アーキテクチャはこのようなものです.
実験のミッション:
- 離陸し,高度10mでホバリングする.
- 右へ10m移動して停止する.
全関数のユニットテストが, utest.cpp
にあります.読みにくいですが,使い方の参考にもどうぞ.C言語インターフェイスは ctest.c
にあります.
- すべての関数は,標準 C++17 で実装されています.
- std:: 名前空間以外の外部ライブラリは使用していません.
- すべて関数として実装され,クラスはありません.すなわち内部状態を持ちません.
難しい数学と力学について,非常に丁寧な解説書を書いていただいた,野波先生に感謝します.また、マルチコプタ制御の基礎について、こうへいさん(@Kohei_Ito)の解説を参考にしました。質問にも丁寧に答えていただきありがとうございました。
また,箱庭プロジェクトをリードし,実際に PX4, QGroundControl と接続し,長い時間を掛けて Unity でビジュアル表示しながら 仮想的にドローンの飛行テストを行ってくれた,箱庭ラボの森さん(@tmori)に感謝します!
特によく参照した文献は以下の通りです.コードの中にもリンクを埋め込んでいます.
- ドローン工学入門(野波健蔵博士)
- 小型無人航空機の厳密・簡易なモデリングとモデルベース制御(野波健蔵博士)
- Drone control Lecture(Seongheon Lee)
- 剛体の回転運動を支配するオイラーの運動方程式(スカイ技術研究所ブログ)
- オイラー角とは?定義と性質、回転行列・角速度ベクトルとの関係(スカイ技術研究所ブログ)
- 飛行力学における機体座標系の定義(@mtk_birdman)
- 「マルチコプタの運動と制御」基礎のきそ(伊藤恒平)
- Euler Angles and the Euler Rotation sequence(Christopher Lum), YouTube
- 回転行列、クォータニオン(四元数)、オイラー角の相互変換 by Atsushi Asakura (@aa_debdeb)
- Quaternion による3D回転変換 by @kenjihiranabe
- 線形代数の可視化 by @kenjihiranabe
多くの書籍やWebコンテンツで勉強し,触発されています.ありがとうございます!