3軸リアクションホイール倒立振子を作るアドカレの12日目です。
1. はじめに:AIの指令を物理的な力へ
前回(Day 11)では、Pythonで学習したAIモデルをSTM32マイコンに実装しました。AIが出力するのは、例えば「0.5 Nmのトルクを出せ」という抽象的な指令値です。
しかし、実際に駆動する3相ブラシレスモータ(PMSM)は、DCモータのように端子電圧を変えるだけではスムーズに回転しません。ロータの角度に合わせて、3つの相(U, V, W)に適切な位相差を持った正弦波電流を流し続ける必要があります。
この複雑な交流制御を、数学的な座標変換によって簡素化し、あたかもDCモータであるかのように 「トルク電流 ($I_q$)」 と 「磁束電流 ($I_d$)」 の2軸で制御可能にする技術。それが ベクトル制御 (FOC: Field Oriented Control) です。
本日は、FOCの理論的背景と、STM32G4におけるCORDICを活用した実装戦略について解説します。
2. なぜFOCが必要なのか?(DCモータとの比較)
まず、制御対象であるPMSM(永久磁石同期電動機)の特性を、DCモータと比較して整理します。
DCモータ(ブラシ付き)の制御モデル
DCモータは、固定子(ステータ)が磁石、回転子(ロータ)がコイルで構成され、ブラシと整流子によって機械的に電流の向きが切り替わります。
この構造により、トルク $T$ は電機子電流 $I_a$ に比例するという単純な関係が成り立ちます。
$$T = K_t \cdot I_a$$
したがって、電流 $I_a$ をPID制御するだけで、リニアなトルク制御が可能です。

3相PMSMの制御モデル
一方、PMSMはロータが磁石、ステータがコイルです。ロータの回転に伴い、各コイルから見た「トルク発生に寄与する磁束の向き」が刻一刻と変化します。
最大トルクを得るには、常にロータの磁石に対して90度の位相を持つ電流ベクトルを作る必要がありますが、これを静止した3つのコイル(U, V, W)への交流電流として直接制御するのは困難です。

FOCのアプローチ
FOCは、「回転しているロータの上にまたがった視点(回転座標系)」 で電流を観測することで、この問題を解決します。
ロータと一緒に回転する座標系から見れば、ステータの回転磁界は静止して見えます。つまり、3相の交流電流が、一定値の直流(DC)電流として扱える ようになります。
これにより、PMSMを擬似的にDCモータと等価なモデルに変換し、「磁束成分 ($I_d$)」 と 「トルク成分 ($I_q$)」 に分離して制御することが可能になります。
3. 制御ブロック図:システム全体像
STM32G4内部で、PWM周期(20kHz = 50µs)ごとに実行されるFOCアルゴリズムの全体像は以下の通りです。
処理フロー:
- 計測 (Sensing): 2相の電流 $I_u, I_v$ (キルヒホッフの法則より $I_w$ は計算可能)と、ロータ電気角 $\theta_e$ を取得します。
- 順変換 (Forward Transform): 3相交流電流を、回転座標系の直流電流 $I_d, I_q$ に変換します。
- PI制御 (Feedback): $I_d$ を0(または弱め界磁用目標値)、$I_q$ をAIからのトルク指令値に追従させるよう、電圧指令値 $V_d, V_q$ を計算します。
- 逆変換 (Inverse Transform): 電圧指令値を3相のPWMデューティ比に変換し、インバータへ出力します。
4. 座標変換の数学:交流を直流にするプロセス
FOCの中核となる2段階の座標変換について解説します。
以下の図は、3つの異なる座標系がどのように重なり合っているかを可視化したものです。
-
3相固定座標系 ($U, V, W$):
- 物理的なコイルの配置です。赤・緑・青の点線が互いに120度の角度をなしています。ここに交流電流が流れています。
-
2相固定座標系 ($\alpha, \beta$):
- 黒い矢印で示された直交座標です。$\alpha$軸は$U$軸と重なっています。3相のベクトルを合成し、この静止した平面に投影するのが 「Clarke変換」 です。
-
2相回転座標系 ($d, q$):
- 紫とオレンジの点線で示された軸です。この座標系自体が、ロータ(磁石)と同じ速度でグルグル回転しています。
- 静止座標 ($\alpha, \beta$) から見た電流ベクトル $\vec{I}$(太い黒矢印)を、この回転する $d, q$ 軸に投影し直すのが 「Park変換」 です。
4.1 Clarke変換:3次元の交流を2次元の交流へ
物理的に120度の位相差で配置されたU, V, W軸の電流を、ステータ(固定子)に固定された直交座標系 $\alpha-\beta$ 軸へ投影します。
本来、変数は3つ ($I_u, I_v, I_w$) ありますが、キルヒホッフの法則により「流入する電流と流出する電流の総和は0」であるため、以下の拘束条件が成り立ちます。
$$I_u + I_v + I_w = 0 \quad \Rightarrow \quad I_w = -(I_u + I_v)$$
これを利用して、$I_w$ を計測・計算することなく、残りの2相だけで情報を復元できます。
1. $\alpha$軸成分(U軸と一致)
単純にU相の電流がそのまま $\alpha$ 成分となります。
$$I_\alpha = I_u$$
2. $\beta$軸成分(U軸から90度)
幾何学的な射影と、上記の $I_w$ の消去を行うことで、以下のシンプルな式に変形されます。
$$
\begin{aligned}
I_\beta &= \frac{1}{\sqrt{3}} (I_v - I_w)\
&= \frac{1}{\sqrt{3}} { I_v - (-(I_u + I_v)) } \
&= \frac{1}{\sqrt{3}} (I_u + 2I_v)
\end{aligned}
$$
この変換により、3本の矢印が2本の矢印($\alpha, \beta$)に整理されました。ただし、この段階ではまだモータは回転しているため、電流 $I_\alpha, I_\beta$ は依然として交流(正弦波)のまま です。
4.2 Park変換:回転行列による「視点の移動」
Clarke変換で得られた $\alpha-\beta$ 座標系は、まだステータ(固定子)に張り付いています。そのため、ロータが回転すると、電流ベクトル $\vec{I}$ は $\alpha-\beta$ 平面上をグルグルと回り続け、観測値は依然として交流(正弦波)のままです。
ここで、線形代数の 「回転行列 (Rotation Matrix)」 を使用して、座標系自体をロータの電気角 $\theta$ だけ回転させます。
1. 数学的定義:回転行列
静止座標ベクトル $\mathbf{I}_ {\alpha\beta}$ を、角度$\theta$だけ回転させて回転座標ベクトル $\mathbf{I}_{dq}$ に変換する操作は、以下の行列演算で定義されます。
\mathbf{I}_{dq} = \mathbf{R}(\theta) \cdot \mathbf{I}_{\alpha\beta}
これを成分で書き下すと、2次元の回転行列が現れます。
\begin{bmatrix} I_d \\ I_q \end{bmatrix} =
\underbrace{
\begin{bmatrix}
\cos\theta & \sin\theta \\
-\sin\theta & \cos\theta
\end{bmatrix}
}_{\text{回転行列 } \mathbf{R}(\theta)}
\begin{bmatrix} I_\alpha \\ I_\beta \end{bmatrix}
この行列演算を展開すると、よく見るPark変換の式になります。
$$
I_d = I_\alpha \cos\theta + I_\beta \sin\theta
$$
$$
I_q = -I_\alpha \sin\theta + I_\beta \cos\theta
$$
2. 【証明】なぜ交流が直流になるのか?
「座標を回すと値が一定になる」という感覚を、数式で証明してみましょう。
モータが角速度 $\omega$ で一定回転しているとき、$\alpha-\beta$ 軸上の電流は振幅 $I$ の正弦波(交流)として観測されます。
$$
\begin{cases}
I_\alpha = I \cos(\omega t) \
I_\beta = I \sin(\omega t)
\end{cases}
$$
また、このときのロータ角度 $\theta$ も時間とともに増加するため、$\theta = \omega t$ となります。
これらをPark変換の式($I_d$ の行)に代入してみます。
$$
\begin{aligned}
I_d &= (I \cos \omega t) \cos \omega t + (I \sin \omega t) \sin \omega t \
&= I (\cos^2 \omega t + \sin^2 \omega t)
\end{aligned}
$$
ここで、三角関数の基本公式 $\cos^2 x + \sin^2 x = 1$ を適用すると……
$$
\mathbf{I_d = I \cdot 1 = I \quad (\text{定数})}
$$
時間変数 $t$ が完全に消え去り、定数(DC値) になりました!
同様に $I_q$ についても計算すると(電流位相が適切であれば)一定値になります。
3. 物理的意味
この変換を経ることで、それぞれの電流成分は明確な物理的役割を持つようになります。
-
$I_d$ (Direct axis / 磁束軸):
永久磁石のN極(磁束)と同じ向きの成分です。ここに電流を流しても、磁石を径方向に引っ張る(または反発する)力しか発生せず、回転トルクには寄与しません。通常はエネルギー効率を最大化するため $I_d = 0$ になるよう制御します。 -
$I_q$ (Quadrature axis / トルク軸):
磁石と直交する方向(90度)の成分です。ここに流れる電流は全て回転力(トルク)に変換されます。つまり、$I_q$ こそがDCモータにおける電機子電流 $I_a$ に相当します。
この変換により、AIは複雑な交流波形を意識することなく、単に $I_q$ を指令するだけで所望のトルクを得られるようになります。
5. 実装戦略:STM32G4の「CORDIC」活用
理論上は単純な行列演算ですが、Park変換には三角関数 ($\sin, \cos$) の計算が含まれます。これを標準的なソフトウェア(math.h)で実装し、3軸分かつ高頻度(20kHz)で実行すると、CPUリソースの大半を消費してしまいます。
そこで、STM32G4シリーズに搭載されている演算アクセラレータ CORDIC (Coordinate Rotation Digital Computer) を活用します。
CORDICとは
CORDICは、三角関数演算や座標回転をハードウェアで行う専用回路です。CPUは計算を行わず、レジスタに入力値を書き込むだけで、数サイクル後に計算結果を取得できます。
コード実装イメージ:
// CPU負荷をかけずにPark変換を実行する例
// 1. CORDICの設定 (Vector Rotation Mode)
CORDIC->CSR = (CORDIC_CSR_FUNC_1 | CORDIC_CSR_PRECISION_4CYCLES);
// 2. データの入力 (32bitパック形式で書き込み)
// 下位16bit: Ialpha, 上位16bit: Ibeta (固定小数点)
CORDIC->WDATA = (q31_Ibeta & 0xFFFF0000) | (q31_Ialpha >> 16);
// 3. 角度の入力
CORDIC->WDATA = q31_theta;
// ...(この間に数サイクル、CPUは別の処理を実行可能)...
// 4. 結果の読み出し
int32_t result_d = (int16_t)(CORDIC->RDATA); // Id
int32_t result_q = (int16_t)(CORDIC->RDATA >> 16); // Iq
このオフロード機能により、170MHzのCPUリソースをFOCの計算から解放し、AIの推論処理やセンサフュージョンといった上位タスクに割り当てることが可能となります。
6. SVPWM:電圧利用率の最大化
PI制御器によって算出された電圧指令値 ($V_d, V_q$) は、逆Park変換によって静止座標系の ($V_\alpha, V_\beta$) に戻されます。
これをインバータへのPWM信号に変換する際、単純な正弦波PWM (SPWM) ではなく 空間ベクトル変調 (SVPWM: Space Vector PWM) を採用します。
6.1 そもそも「空間ベクトル」とは?
「空間」や「ベクトル」という言葉が出てくると難しく感じますが、考え方はシンプルです。
-
従来のPWM (SPWM):
- U相、V相、W相を 「3つの独立した交流電源」 として扱います。「U相は今 0.5V、V相は -0.2V...」と個別に指示を出します。
-
空間ベクトル (SVPWM):
- 3相をまとめて 「1つの大きなベクトル(合力)」 として扱います。
インバータの「8つの基本状態」
3相インバータにはスイッチが3ペアあり、それぞれの出力は High(1) か Low(0) のどちらかです。
組み合わせは $2^3 = 8$ 通りしかありません。
- 有効ベクトル (6通り): (1,0,0), (1,1,0) など。これらを平面上にプロットすると、原点を中心とした 正六角形 が描かれます。これが「空間」の正体です。
- ゼロベクトル (2通り): (0,0,0) と (1,1,1)。これはモータをショートさせる状態で、出力電圧はゼロ(原点)です。
ベクトルで制御する
ベクトル制御で作りたいのは、この六角形の中をグルグルと滑らかに回転する 「電圧ベクトル」 です。
しかし、インバータは上記の8つの状態(六角形の頂点と中心)しか出せません。
そこで、「隣り合う2つの頂点ベクトルと、ゼロベクトルを高速で切り替える」 ことで、平均的に狙った位置のベクトルを作り出します。これが SVPWM(空間ベクトル変調)の基本原理です。
この「六角形をフルに使い切る」制御を行うことで、各相をバラバラに制御するよりも無駄がなくなり、結果として出力電圧の最大値を引き上げることができるのです。
グラフの解説:なぜ波形を歪ませるのか?
上図は、通常の正弦波PWM(上段)と、SVPWM(下段)のデューティ比の比較です。
-
上段 (SPWM):
- 純粋な正弦波です。振幅が電源電圧($\pm 1.0$)に達すると、それ以上電圧を上げることはできず、クリップしてしまいます。
-
下段 (SVPWM):
- 「M字型(鞍型)」 に歪んだ波形になっています。
- 注目すべきは 灰色の点線 (Offset) です。これが意図的に注入された 「3次高調波成分」 です。
- ある相の電圧が高くなりすぎて天井(+1.0)にぶつかりそうになったとき、このオフセット成分が全相を一律に下側に引きずり下ろします。逆に底(-1.0)にぶつかりそうなときは、全体を持ち上げます。
魔法の正体:線間電圧
「波形を歪ませて大丈夫なのか?」と思われるかもしれませんが、全く問題ありません。モータが回転するために必要なのは、端子単独の電位(相電圧)ではなく、端子間の電位差である 線間電圧 ($V_{uv}, V_{vw}, V_{wu}$) だからです。
$$V_{uv} = V_u - V_v$$
ここで、全相に同じオフセット電圧 $V_{offset}$ を足してみます。
$$
\begin{aligned}
V_{uv}' &= (V_u + V_{offset}) - (V_v + V_{offset}) \
&= V_u - V_v + (V_{offset} - V_{offset}) \
&= V_{uv}
\end{aligned}
$$
数式の通り、オフセット成分は引き算によって完全に消滅します。
つまり、「モータから見れば綺麗な正弦波のまま、インバータ視点では電圧の天井と底を有効活用できる」 のがSVPWMの正体です。
導入のメリット:15.5%のパワーアップ
この手法の最大の利点は、電圧利用率の向上です。
-
SPWMの限界:
- 電源電圧を $V_{dc}$ とすると、出力できる正弦波の最大振幅は $V_{dc} / 2$ です。
-
SVPWMの限界:
- 正六角形の空間ベクトル空間を使い切ることで、最大振幅は $V_{dc} / \sqrt{3}$ になります。
この比率は $2 / \sqrt{3} \approx 1.1547$ となり、SVPWMを使うだけで 約15.5% も高い電圧(=高いトルクと回転数) を出力可能になります。ハードウェアを変えずにソフトウェアだけで性能を底上げできるため、FOCでは標準的に採用されます。
鞍型波形(サドル波形)による電圧ブースト
SVPWMを適用した各相の電圧指令波形は、純粋な正弦波ではなく、上図のようなM字型の「鞍型波形」になります。これは意図的に3次高調波を注入した結果です。
3相モータの線間電圧(例: U-V間)は、各相電圧の差分 ($V_u - V_v$) で決まります。全相に共通して加算された高調波成分(コモンモード電圧)は、差分を取る際に打ち消し合い、モータには綺麗な正弦波電圧が印加されます。
実装アルゴリズム (Min-Max法)
「空間ベクトル」という名前はいかついですが、実装上は「最大値と最小値の中間をゼロに合わせる」という単純な計算(Min-Max法)で実現できます。これは数学的に厳密なSVPWMと等価です。
// 1. 3相電圧指令の最大値・最小値を探索
float V_max = fmaxf(fmaxf(Va, Vb), Vc);
float V_min = fminf(fminf(Va, Vb), Vc);
// 2. 波形を上下枠の中心に寄せるためのオフセットを計算
float V_common = -0.5f * (V_max + V_min);
// 3. オフセットを注入してPWMデューティを決定
// これにより自動的に「鞍型波形」が生成される
TIM1->CCR1 = (uint32_t)((Va + V_common) * PWM_PERIOD_COUNTS);
TIM1->CCR2 = (uint32_t)((Vb + V_common) * PWM_PERIOD_COUNTS);
TIM1->CCR3 = (uint32_t)((Vc + V_common) * PWM_PERIOD_COUNTS);
7. まとめ
本日は、3相ACモータを自在に制御するための理論的枠組みであるFOCについて解説しました。
- 理論の核心: Clarke/Park変換を用いることで、交流モータを数学的にDCモータと等価なモデルに変換し、トルク成分 ($I_q$) を直接制御可能にしました。
- ハードウェア実装: STM32G4の CORDIC を活用することで、高負荷な三角関数演算をハードウェア化し、AIとの共存を実現しました。
- 出力段の最適化: SVPWM 技術により、限られた電源電圧から最大限のパワーを引き出す構成としました。
これで、「AIによる判断」から「モータの駆動」までが理論的に繋がりました。
次回(Day 13)からは、制御のもう一つの要である**「入力(センシング)」**の理論編に入ります。ジャイロセンサと加速度センサの値を融合し、高精度に傾きを推定する Madgwickフィルタ のアルゴリズムに迫ります。

