デジタル制御するためのソフトウェアアーキテクチャ

# これはなに？
初めてデジタル制御を作ってなにか動かしてみたいけど
右も左も分からないという方に対して、
デジタル制御におけるソフトウェアアーキテクチャの全体像を示したメモです。嘘です。怪文書でした。

自分なりに嘘を書いてないかチェックしたつもりですが、
私は基本的に自分の考えをオープンにして、
周りからボコボコに指摘されて自分の知識をUpdate（矯正？）するスタイルなので
大変お手数ですが間違い等ありましたらご指摘お願いします。

# 動くやつないの？
ちなみにここで言ってることの一部をSimulinkモデルにしたものを
Githubにアップロードしましたので合わせてご確認ください。
https://github.com/mathworks/adc-synchronized-with-pwm

# ここでいうデジタル制御って？
マイコンを使ってモータやインバータ、DCDC電源回路のような制御対象（Plant）に対して
PWMを使ったパルスを入力し
制御対象となる電流や電圧等を所望の値に制御したいようなシチュエーションを想定しています。

シーケンシャルに状態を遷移するような制御ではなく、
いわゆるフィードバック制御を対象とします。
この場ではデジタル制御がアナログ制御に対してどのようなメリットがあるか、
デジタル制御で補償器を構築するには？というテーマについては取り扱いません。
気が向いたら書こうかなと思いますが、このようなテーマはすでに先人がドキュメントをWebにおいてくれていたりしますので
そちらを見れば十分ではないかなと思います。

# この記事を書いたきっかけ
知り合いとの集まりで「そもそも周期的な演算がどのように行われるか全体像がわからない」という方もいらっしゃったので
ちょっと自分の理解が正しいかも確認するためにメモ的に書いてみたいなと思いました。

**（追記）**
割とこの記事の内容はこの本に大体書いてましたので
こちらを確認されるほうが良い気がします！
**【オーム社：ACドライブシステムのセンサレスベクトル制御　電気学会・センサレスベクトル制御の整理に関する調査委員会】**

# デジタル制御のソフトウェアアーキテクチャ

## アナログによるフィードバック制御構成との違い
アナログのフィードバック回路では、オペアンプやレギュレーター、コンデンサや抵抗等の素子を組み合わせて
アクティブアナログフィルターを形成し、それをフィードバック制御用の補償器として利用します。
図１に示すような構成、すなわちハードウエアのアーキテクチャになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/331637/81310038-7d4c-7592-d063-de56e3f7fe8f.png)


図１：アナログ回路で補償器を構成した際のフィードバック例

一方でデジタルでフィードバックを構成しようとすると図２のようなアーキテクチャになります。
図1との違いはアナログからデジタルに変換する作業が必要であることと、
補償器をアナログではなくいわゆるデジタルフィルターとして構築することにあります。

ここら辺は何かしらのプログラム言語でソフトウェアを構築する必要がありそうです。ではこの部分どのようにな構成が必要でしょうか？
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/331637/a966fd2d-8553-688a-2ede-d5785f51c0e1.png)


図２：マイコンでデジタル補償器を構成した際のハードウエアアーキテクチャ

# デジタル制御に必要なペリフェラル

フィードバック制御を行うソフトウェアには
プログラムで記述された命令や値を保持するレジスタ、メモリや、逐次的に演算してくれるCPU以外に
下記の構成が必要です

・タイマー:周期実行のタイミング定義
・AD変換(ADC):アナログ値を読み取りデジタル値に変換する
・PWMモジュール:タイマーが生成するキャリアをベースとしたパルスを生成

私が初学者の頃こいつらはCPUが持っている機能なのかなと思ってましたが、実はCPUとは独立した別のハードウェアで動作が行われています。
この様なCPUを支援するために搭載されてる補助ハードウェアをペリフェラルと呼びます。
(図２のオレンジ色の四角がペリフェラル）

マイコンの製品ごとにコア（CPU）が同じでもペリフェラルの数や種類、性能が違うと型番も違います。最初CPUめちゃ遅いのになんで値段高いと思ったのですが、
PWMが生成するパルス幅の出力分解能が数百ピコ秒という性能をもっていたりします。

マイコンはCPUやメモリに加え、ペリフェラルを一つのパッケージにまとめてるので、
人間の目には単一モジュールに見えますが実は複数モジュールの集合体であるシステムなんです。

で、本題のソフトウェアアーキテクチャは、ペリフェラルを操作するタイミングを記述したり、
どのペリフェラルに情報を受け渡すかということが表現されている必要がありそうです。

その流れを理解するためにも各ペリフェラルについてもう少し詳しくみてみましょう。

## クロック
クロックはマイコンが持っているハードウェアの同期を取るために用いる周期的なパルス信号です。

CPUやペリフェラルにこのクロックを割り当てますが、
機種によってはペリフェラルとCPUに割り当てるクロックは同一ではなく分周によって周期を落として割り当てることもあります。

分周を使うシーンはまぁありますが、
基本的にはCPU,ADC,PWMモジュール,タイマーは同一クロックが分配されるのが一般的かと思います。

## タイマとタイマ割り込み

タイマはクロックに合わせてひたすらカウントアップをしています。1,2,3,4,・・・と数を数えてるんです。
前項で説明した通り、設定変更してなければクロック周期に合わせてカウントアップしていきます。

これはクロック周期時間を単位時間として時間経過を数えてるとも解釈できます。
だからこれはタイマと呼ばれます。

アナログ回路においてタイマーは三角波、およびのこぎり波発生回路に相当します。
アナログ回路は物理的な電圧で波形を作りますが、デジタルではこれに相当する波形を値をカウントアップすることで作ります。

タイマにはカウントアップの上限値を設定することができます。
この上限値に達するとタイマはそのことを通知する処理を行なってカウンターを0に戻します。
もしくは上限に達した次のスタッフからはカウントダウンを行い、カウンターが0になったら再びカウントアップをするようにすることもできます。(タイマモジュールによってはできないものもある)

またタイマーは「タイマ割り込み」と呼ばれる機能を持っています。
これはタイマーが上限値に達したタイミングで特定の関数をCallする機能です。
Callする関数には後ほど説明するAD変換を実行します。
これによってCPUに一定の時間間隔で処理（ひとまずはAD変換）を行ってもらうことができます。


## タイマを利用したPWM生成

アナログ回路はのこぎり波や三角形を基準電圧といった物理的な電圧値を比較することでパルス波形を再生します。
一方デジタルでは離散的な値の比較を元にPWM出力をhighにするかlowにするか決めています。
こののこぎり波、三角波、すなわちキャリアに相当する信号を作るのがタイマです。

PWMモジュールはタイマーカウント比較レジスタを持っています。
そして特定のタイマカウント値を監視してタイマー比較レジスタの値とクロックごとに比較してます。
大小関係が反転するとPWM出力ピンの出力を反転させることでパルス波形を生成します。

マイコンには複数のPWMモジュールやタイマーモジュールを持つものがあります。
メーカーの違いによって設定を行う方法は変わりますが、必ずどのPWMモジュールがどのタイマを参照するのか設定する必要があります。

その辺の設定は対応するレジスタのビットを変更するか算出先のタイマーモジュールを選択できたりします。
ちょっと脱線しますが、多くのマイコンはPWMに限らずペリフェラルの設定について
レジスタを直接触らなくても良いように設定用の関数を作ってくれたり、
(マイコンのメーカで資料を取得できるがだいたい1000ページを超える膨大な資料になっている)
GUIで指定すれば設定用のコードは自動生成してくれることもあります。

例えばST MicroのCubeIDEとかです。

https://www.st.com/ja/development-tools/stm32cubeide.html

## AD変換
AD変換はアナログには対応する部分がないデジタル制御特有の要素です。
計測対象となる電圧や電流をCPUが扱える離散データに変換します。

ここで周期的な演算によるデジタル制御ループ構築に大切なポイントは、
"AD変換を実施するタイミングをPWMモジュールが算出しているタイマーと同期させる"ことです。

デジタル制御ではタイマ割り込みを使って周期毎にPWMを更新すると言いましたが、
当然PWM幅を決定するための材料となるフィードバック信号がPWMと同期したタイミングで取らないと、
データの周期性がずれていってしまいます。
そうすると、その周期性のズレががむだ時間が増加して制御不安定となることがあります。
この辺は後ほど具体的に説明します。

基本的にはタイマ割り込みで呼ばれる関数が一番最初に行うのは
AD変換を開始し、現在のフィードバック信号を読み取ることです。

PWMが基準としているタイマの割り込みをトリガーとしてAD変換を行うことで
常にPWM周期と同期したタイミングでAD変換を行うことができます。

## 補償器演算

アナログ回路ではオペアンプやTL431のようなシャントレギュレータに抵抗やコンデンサを使って
周波数特性をもたせることで、任意の周波数フィルターを構成します。

これを制御補償器として利用することが一般的だと思いますが、
デジタル制御では、デジタルフィルターによって補償器を構成します。
デジタルフィルターではアナログで構築するような連続における伝達関数に対して細かいところでは誤差があるものの、その誤差を正しく理解して設計すればほとんど同じ効果を得ることができます。

デジタルフィルターに対しての細かい話は私はこちらのサイトにかなりお世話になりました。
http://www.ic.is.tohoku.ac.jp/~swk/lecture/yaruodsp/main.html

理論はいいから手っ取り早く連続伝達関数をデジタルフィルターに変換したいという方は
こちらをお試しください。あっというまに変換します。
https://jp.mathworks.com/help/control/ref/c2d.html

補償器演算部分は基本的にAD変換が終了したタイミングで実行されなければなりません。
AD変換が更新される前に補償器演算をしてしまったら、古いAD変換結果を使って演算するので
同じ演算をもう一回してしまうことになります。
(補償器に積分項があるため、必ずしも出力は同じ結果になりませんが）
なので、毎回新たに取得したAD変換値が使われるように”AD変換終了割り込み”を使って呼び出します。

そうすることで、確実にAD変換が実行されたあとに補償器演算が実行できます。

## 補償器演算結果をもとにPWMモジュールのタイマーカウント比較レジスタを更新

最後に補償器の演算結果をPWMモジュールのタイマカウント比較レジスタの値を更新します。
これによってPWMのDuty幅を増やしたり減らしたりすることができます。

マイコンの仕様にもよりますがPWMのDuty幅が更新されるのは必ずしもレジスタが更新された瞬間ではありません。
PWMモジュールの設定でタイマ割り込みタイミングでレジスタの値を実波形に反映させるなんて設定もできます。
図2のタイマからPWMモジュールに伸びるオレンジの矢印は、レジスタの更新タイミングを通知する割り込みをイメージして記入しました。


## タイムチャートによる動作呼び出し順番の確認
今まで書くペリフェラルごとに細かい説明をしてきましたが、最後に全体の流れをまとめてみましょう。

図3に一般的な制御プログラム処理のタイムチャートを示します。
これをソフトウェアアーキテクチャというとちょっとカッコつけすぎかもしれませんが、
ペリフェラルに対応付けるような形でタスクを分割し、
そのタスクを関数単位で取りまとめ、最後は割り込み等によって呼び出し順番をコントロールするところは
小さいながらもソフトウェアの構成、「ソフトウェアアーキテクチャ」と言えるかなと思います。

![SWTiming.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/331637/2c7dd425-6ebe-e8f1-0fde-7e555f29d545.jpeg)
図3　ソフトウェア実行タイムチャート


# ソフトウェアアーキテクチャ設計で気をつけること

今回はパワエレ系の電力変換回路を例にしてますが、
懸念事項は対象となるアプリケーションによって大きく変わると思います。
アプリケーションAでは気にしなくても良い些細なことも、アプリケーションBではすごく重要、なんでそれを無視して設計してるの？　みたいなこともあったり、
設計において大事にしたいこと、
　性能はもちろんですが、保守性、デバッグ容易性、設計変更容易性といった商品のライフサイクルを含めて考えると観点が変わってくるので
**必ずしもどのようなケースにおいても正しい意見ではない**ことを言及しておきます。

ただ、逆に言えばここらへんでエンジニアの皆さんのこだわりが出るところなので
いろんな考え方が出てくる面白いところだと思います。
**間違いだけでなく「僕はこんなふうに考えてるよー」などコメントございましたら
気軽に書いてくださると著者はめちゃくちゃ喜びます。**


## むだ時間遅れによる制御設計への影響
制御工学の世界ではなにか入力に対してその入力が反映されるまでの遅延時間を”むだ時間”といいます。
概念的な話で言えば海外からの生中継をする際の音声遅延が”むだ時間”です。
日本のスタジオでMCが話した音声が海外のキャスターの耳に届くまでに遅延があるせいで、しばらく会話のキャッチボールが止まるときがありますよね？

あれが”むだ時間”です。

むだ時間がなにが困るかというと位相が回ってしまうということに尽きると思います。
むだ時間はゲインに対しては一切変化を及ぼさないけれど、位相だけが回ってしまう特性をもつものです。
このページを少し下にめくるとボード線図が乗ってます。
https://jp.mathworks.com/help/control/ug/specifying-time-delays.html;jsessionid=7f659f0c046f31474e2581d7784f#d123e10402

制御系は位相が180°回ってしまう周波数帯域においてゲインが残っていると、制御系が発振してしまいますので位相が回る要素はなるべくなくすか、その影響を少なくしたいですよね。

デジタル制御は（アナログ制御でも結局そうなのですが）
AD変換でデータを取得してからPWMに反映するまでTswだけ時間がかかります。
これはデータを取得してからそれに対して応答するまでに「Tswだけむだ時間が発生している」ということになります。

タイマ割り込みでADタイミングを指定したり、
PWM更新タイミングもタイマ値基準で行ったりする理由はこの取得とPWMへの反映のタイミングを
タイマを基準に明確に決めておきたいというのが理由です。
もしデジタルフィルタ演算直後に反映してしまったら、CPUの負荷状況に応じてむだ時間が変動することになりますので、
「CPUの演算不可に応じて制御安定性についての性能が変動する可能性がある」システムになってしまいます。

なので、AD変換とPWM更新タイミングは明示的に指定している方が好ましいと思います。

〜追記〜
ただ、補償器演算直後にPWM更新反映というのもメリットはあります。
むだ時間というのは　”取得から反映までの時間”となりますので、演算が終わったら即時反映するほうが
むだ時間を少なくすることができます。

その点、Twitterでもてぃお様からコメント、RRいにょ様から論文の情報をいただきました。
この論文の方法でむだ時間による位相遅れの影響が軽減されるので通常より比例ゲインを4倍大きくしても、
安定性を確保できるようになったそうです。

これによりおそらくDCDCなら電圧立ち上がり時間の短縮、位相余裕改善によりオーバシュート量の軽減が期待できますし、
DCACなら高調波の抑制に少なからず寄与すると思います。
また、これは条件によるので一概には言えないと思いますが外乱応答性も改善される可能性があります。
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">今はPDF等が手に入らないようですが，この論文ではFPGAを使い，三角波キャリアの山・谷でサンプリングした電圧・電流からPWMの変調波を計算する電流制御の計算を数百ns以内に完了し，次の山・谷を待たずにすぐに出力するという方法を実験していました。<a href="https://t.co/ACwtW97c7g">https://t.co/ACwtW97c7g</a></p>&mdash; RR-いにょ (@RR_Inyo) <a href="https://twitter.com/RR_Inyo/status/1433589407420796929?ref_src=twsrc%5Etfw">September 3, 2021</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>


## 補償器の演算時間　（ちょっと修正）

図３で示されているデジタルフィルタはタイマが生成するキャリアがカウントアップして、
カウントダウンされてさいごに0になるまでに演算が終わっています。

では、もしこれが次のタイマ割り込みが発生するまでに
デジタルフィルタ演算タスクが終了しなかったらどうなるでしょう？

これはデジタルフィルタ演算タスク直後に実行される演算結果を比較レジスタにPushという作業がされないので
なんと1Tsw周期分過去のPWM演算値がもう一回出力されてしまいます。

そして演算が終了してやっと次のタイミングでPWMを更新したとすると
2Tsw周期分過去のAD変換タスクの結果から演算された値が使われたことになるので
**むだ時間が2倍増えてしまいます**

むだ時間が及ぼす影響は前節で述べたとおり、演算負荷に依存して制御性能が悪化してしまいます。
そのため、デジタルフィルタ演算がちゃんと制御周期内に終了するようにしないといけません。

そうするためにはもちろん使用するマイコンのCPUのクロック周波数が早いほうが有利ですが、
スイッチング周波数を早く設定しすぎるとTswが短くなるので
いくら早いクロックで動くマイコンを使っても演算が追いつかなくなります。
なので、スイッチング周波数は使用するマイコンの性能も考慮して決定する必要があります。

こういうのは、デジタル演算に必要なステップ数の感覚がない初学者が
あまり考えずに演算時間ギリギリの周波数で動かしてしまい、
制御特性を見ると実は2周期に1回でしか値が反映されてなかったということがあるかもしれません。
殆どの方は「いやそうはならんやろ流石に」と思うかもしれませんが、
恥ずかしながら私は過去にやってしまいました。

### 〜追記〜実際に演算時間でむだ時間遅れの影響がでることはあるの？

こちらTwitterでコメントいただいた方がいらっしゃったので追記します。
上記のむだ時間2倍というのは理論上起こり得ると思いますが実際のところは、
タスクの優先順位を管理することで回避できると思います。

たとえば、カスケード制御と呼ばれるような制御方式
https://www.rkcinst.co.jp/technical_commentary/14294/
では2つの制御系をつなげて構成することがありますが、
（必ずしもそうする必要はないのですが）
外側のループと内側のループでは演算の周期が異なることがあります。
たとえば内側は0.1ms周期、外側は10ms周期とか。（値適当です）。

このとき、1つのCPUに2つの周期を実行させると遅い外側の周期を実行している最中に、
早い内側のタスク割り込みが発生することがあります。

こういうときは遅い外側のタスクのプライオリティを上げておけば一旦スタックにおいておき（PUSHしておき）、
早いほうの処理をしたのちスタックを取り出す（POPする）ことで中断したところから処理を再開することができます。

こういう設計をしておけば仮に他のタスクが割り込まれる状況であっても
周期内に演算を終了させるようにすることができます。
これがしっかり出来ていればあまり演算時間でむだ時間が問題になることはないと思われます。
ですから、必ずしもPWMへの反映をキャリアカウント０のタイミングに明示的に決めなくとも、
制御性能に関わらないように設計することは可能だと思います。

ただ・・・これは個人的な好みになってしまいますが、
私は即時反映は慎重派です。
なぜなら即時反映してしまうと
**製品のありとあらゆる条件下においても、設計通りのタイミング、順番で演算が繰り返されるか？**
と言われると証明が難しくなるかなと感じています。

また、なかなかそういうケースはないと思いますが、即時反映をした際に
スイッチング周波数が変わってしまうケースもあります。
例えば超極端な例だと１周期前の演算結果として90%の太いパルスを出力している最中に、今の演算が終了して10%という非常に短い値がPWMに反映されると、演算された瞬間にPWMがLowになり、その時のPWMパルス幅は演算時間で成り行きできまってしまいます。
ここではPWMの周期も一瞬短くなりますので、1パルスだけ周波数が上がってしまうということがあると思います。

この挙動がどんなリスクを持つかはやはりアプリごとに違うので
議論が必要だと思いますが、個人的にはEMI試験あたりが気になります。
規格試験はPathしてるのに、とある条件では周辺機器が誤動作するといった面倒くさいやつが発生するかもしれないなと思います。
（実際に発生するかはわかりません。あくまで想像です。）

こういうケースは考えれば考えるほど無限に出てきますし、それに全て完璧に対策するのは現実的ではありません。
高品質な製品を追求するなら、こういう外的要因によって変動してしまう可能性を、あえてむだ時間が伸びることを許容して回避するというのも一つの考え方ではないかと思います。
ほんと、それがいいかどうかは常に考えないといけないとこですが。
（そういうのが面倒でもあり、エンジニアリングの面白いとこでもあるかなと思います。）


# こぼれ話的な
## Simulinkで作成した補償器をマイコンに書き込み
手前味噌で恐縮ですがSimulinkという製品を使うと、デジタルフィルタのアルゴリズムをグラフィカルに
モデルを使って記述することができます。
そして、そのモデルを等価な動作をすC言語に変換して出力することができます。

モデルという抽象的な表現をすることで、C言語の言語制約にとらわれずに
補償器アルゴリズム設計という本質的な作業に集中することができるのがいいところだと思います。

でも・・・C言語に変換するためのツール、EmbeddedCoderはおもに企業向けの製品でして
めんたま飛び出るくらい高いんです。。。

そこで、そのような状況を脱獄する方法として
いつもお世話になっているモータ制御マンさんがこちらでお話してるのでぜひご覧ください。

**[MATLAB Homeでのモータ制御MBD開発に向けた"脱獄指南書"（モータ制御マン）]**
https://www.youtube.com/watch?v=ok0MKih-7KI&t=3050s


## CV系のプログラムとの共通点
実はCV(Computer Vision)系のリアルタイムで動画を処理するようなプログラムも同じような感じなんです。
画像はfpsで定められている周期で取り込まれますので、その周期に合わせて処理をして次の画像が取り込まれるまでに
何かしら処理を施して反映。。。という流れはまさにデジタル制御におけるソフトウェアアーキテクチャと同じです。

参考までに過去の駄文を貼り付けますね。
ここではリアルタイムに人間の目や口をトラッキングするサンプルコードを紹介してます。
3.1のところでタイマ割り込みを使って周期的に処理をするって書いてあります。

https://qiita.com/griffin921/items/48d14854dd409e06b2d8


# まとめ
えーとオチは特に無いです！
駄文にお付き合いいただきありがとうございます。

# Special Thanks!!
もてぃお様、RR-いにょ様,Twitterでのコメントありがとうございます！
超うれしいです！