13回目(2015年度秋) 応用プログラミング演習(制御工学)(1)
制御シミュレーションの数値解法
これから学ぶであろう,制御工学について情報処理演習の観点から扱ってみたいと思います.
制御工学で学ぶのは,線形系であるため,基本は解析解が求められる状態を扱います.紙と
鉛筆で解けるので,例えば,オペアンプ等の電気回路を組めば制御系を構成することが可能
なのです.
しかしながら,昨今の制御系はディジタルディバイスを利用するため,離散量を基にした制御を
考えておく必要があります.
制御理論のごく基礎(知っていて損はありません)
設定したとおりに対象を制御する方法の基本は,フィードバック制御と呼ばれます.
動作原理はいたってシンプルで,制御対象の状態をセンサなどで観測し,設定した値と比較して
ズレ(偏差)があれば修正を行います.
以下に原理図を示します.
上記の図の記号は,
となっており,目標値と計測値を比較し,その差(偏差)を解消するように制御を行うのが基本の
機能となっています.制御対象の特性に合わせて制御器を工夫することにより制御特性が改善
されます.
モータ回転数の制御
制御対象として一般的に用いられるモータを考えましょう.
モータの応答はかけた電圧に対して一次遅れと呼ばれる応答を示すことが知られています.
また,その知見を基に数式を当てはめ,制御モデルとしています.
ちょうど以下の応答がその例であり,
これを数式で示すと,
y(t) = K (1 - exp(-t / T))
となるものであり,Kはゲイン係数(定数),Tは時定数と呼ばれるもので,1次遅れの基本的な
特性を示しています.時定数Tは応答の遅れ具合と考えればよいのです.
ある値に向かってはじめは急に応答し,徐々に穏やかに回転が上昇します.身近なものでは,
掃除機のモータ音などを思い出してみれば分かりやすいかと思います.
P制御コントローラ
制御器の感度を調整することで,応答の様子を変化させることが可能です.いちばん基本的な
ものを比例(Propotional)制御と呼び,偏差e(入力-応答)に比例ゲインKpをかけて操作量MVを
生成します.これは,
MV = Kp e = Kp (y(t) -SV)
で示されます.Kpを調整すれば,偏差に対して敏感に修正する特性にすることも可能となります.
先の表記に則り,検出器が応答y(t)をそのまま示すとすれば,
MV = Kp e = Kp (PV -SV)
と示すことができます.
ディジタル制御
制御理論を学ぶときにはアナログ量を基にした解析を行いますが,現在はマイコンを主体とした
制御装置を利用することが大半ですので,実際にはディジタル制御を行います.
ディジタル制御を行うため,周期的な演算を繰り返します.制御周期Δtを決め,その間に,計測,
演算,操作量MVの出力を周期的に繰り返して実行します.(正確には,最後に記すような処理を
行います.)
従って,Δtは短い時間とはいえ,間隔をおいた断続的な制御を行っているのです.
アナログ量では時間の関数であり,PV(t), MV(t),
e(t)と示すことができましたが,ディジタルの
場合,Δtごとにn周期目と考えることができるので,PVn,MVn,enのように考えます.
1周期前でしたらPVn-1,MVn-1,en-1となるわけです.
位置型ディジタル制御アルゴリズム
位置型というキーワードは考えません.さて,アナログ量で扱う場合での時刻tにおける偏差と
操作量MV(t)はそれぞれ,
e(t) = SV(t) - PV(t), MV(t) = Kp e(t) =
Kp (SV(t) - PV(t))
となりますが,ディジタル制御では,現在の周期nで考えますので,
en = SVn - PVn, MVn = Kp en + MV0 = Kp
(SVn - PVn) + MV0
と考えればよいのです.制御を開始した時の初期値MV0を加える必要があります.
速度型ディジタル制御アルゴリズム
同様に詳細は省略しますが,計算量を少なくする制御アルゴリズムに速度型制御アルゴリズム
と呼ばれるものがあり,以下の式で操作量を示すことが可能です.
MVn = MVn-1 + ΔMVn --- (a)
この式の意味は,前回の操作量MVn-1に今回の変化分ΔMVnだけを加える,という意味です.
P制御においては,偏差の変化量と考えることができるので,
ΔMVn = Kp (en - en-1) --- (b)
と計算することができます.
※ P制御では位置型でも速度型でも計算量に大きな違いはありませんが,他の制御方式で,
積分が入ったり,フィルタが組み合わされるような場合に効果があることもあります.
1次遅れモデルの離散表現
アナログ値では1次遅れ系においてTは時定数で,目標値と操作量は以下の関係にあります.
(d/dt)PV + (1/T) PV = MV
周期Δtごとに制御するため,n周期後の応答は以下の式で示されます.
PVn = (T / (T + Δt)) PVn-1 + (Δt / (T
+ Δt)) MVn --- (c)
なお,P制御をかけているので,今与えるべき操作量は,
MVn = Kp en = Kp (PVn-1 - SVn) --- (d)
位置型,速度型におけるMVnの与え方
位置型であれば,式(d)を式(c)に代入すれば,現在の制御周期における応答が計算できます.
速度型に関しては,前回の制御周期における偏差との差を計算する式(b)を計算し,前回の制御
周期における操作量に加える式(a)を行った上で式(c)に代入すれば同様に計算できます.
速度型の方がひと手間多いように見えますが,前回の制御周期との差だけで計算できるので,
初期状態からのデータの蓄積に依存しないで済み,その結果,制御法を途中で切り替えた時に
急に値が変化することなくスムースに移行できるなどのメリットがあります.
あるいは離散化の際には必ず誤差が発生しますが,誤差の蓄積を抑えられる可能性もある点で
利用されています.
シミュレーションプログラム作成にあたって
上記のように速度型制御アルゴリズムを扱っているため,以下のような変数のみを用意すれば
制御できます.
を用意すればよいのです.nやn-1と言っても全周期分用意する必要はなく,現制御周期と前制御
周期に関するデータを一時的に記録しておけばよく,fprintfを用いてファイルに書き出してしまえば
良いのです.
もちろん配列に記録してからファイルに書き出してもかまいません.
以上が,制御に関するシミュレーションについての説明です.かなり簡素化していますが,プログラム
もやってみるとそれほど大変ではないことが分かると思います.
(参考) 関数ポインタ
変数を関数で渡す方法は既に学んでいると思いますが,実は関数もポインタを利用してやりとりをする
ことが可能なのです.
以下に例を示したいと思います.
double fnc(double x){ return x; } main(){ double x; double (*pfnc)(double); pfnc=fnc; x=10.0; *pfnc(x); } |
通常の関数を宣言します. メイン関数内で関数ポインタを宣言します. 先の関数のアドレスを関数ポインタに送ります. 関数ポインタに引数を渡し,先の関数を実行します. |
または,
double fnc(double x){ return x; } main(){ double x; double (*pf)(double); *pf=fnc; x=10.0; } |
通常の関数を宣言します. メイン関数内で関数のポインタを宣言します. 先の関数を関数のポインタの中身に送ります. |
のようにしてもやりとりができます.
関数をわざわざポインタにする理由は何かというと,
既に作っておいた関数を利用する際,ポインタで受け渡しをすることで,例えば関数名が合わなくても
その制約をなくして利用できるので再利用性が向上します.また,関数をモジュール(ユニット)のような
ものとして利用できるので関数内のアルゴリズムの構造を何度も形を変えて利用できるという点があり
ます.何かの数値を算出するための関数ではなく,計算するための仕組みとしての関数にでき,汎用性,
再利用性が格段に上がります.
授業の課題であれば,プログラムが小規模な上に,あまり再利用する機会がないのでメリットがつかみ
にくいと思いますが,自作のプログラムを作る際は大変便利ですので参考にしてみて下さい.
関数ポインタ(補足)
関数ポインタを関数に呼び込む方法が分かりにくいと思いますので,追加説明します.以下の例を見て
下さい.
fnc_1の引数として関数ポインタを用いています.メイン関数内では関数fnc_A,
fnc_Bをこのfnc_1に渡して
います.
fnc_1の引数(*fnc)(double)は関数ポインタの中身を表しているので,
(*fnc) = fnc_Aとして関数ごと引き渡すことができると考えて下さい.
#include <stdio.h> double fnc_1(double (*fnc)(double), double
a){ double fnc_A(double a){ double fnc_B(double a){ main(){ printf("fnc_A=%f\n", fnc_1(fnc_A,
h)); |
(参考): 時間ごとの制御処理(タイマ割り込み)
如何でしょうか?今回作成した制御プログラムはシミュレーションのためのものであり,データを
ファイルに出力したり,画面に表示するだけかと思うかも知れませんが,センサからのテータを
取得し,モータに命令を送るための信号生成とそれを受ける回路を用意すれば,今回の授業で
扱ったアルゴリズムを使うことで実際の制御を行うことができるのです.
プログラムにすると以下のような流れとなります.
int main(){ タイマ初期化関数 タイマ(カウンタ)スタート関数 while(1){ if(変数==○){ 割り込み用処理 カウンタ変数=0; } 通常の処理 } } int タイマ割り込み本体関数(){ タイマ停止 割り込み処理 カウンタリセット タイマスタート } |
main関数 タイマ初期化 タイマ(カウンタ)スタート ループ 割り込み発生(カウンタ変数を満たす) 割り込み用処理の実行 カウンタ変数のリセット 割り込み以外の通常処理 タイマ割り込み本体関数 タイマ停止 割り込み処理 カウンタリセット タイマスタート |
これは割り込みという制御で,特に機械類の制御の場合,短い時間の周期(1〜50msec)位
の制御周期を保ちながら動作する必要があるのです.
これまでのプログラムのように,一つの処理が終わったら次の処理,但し,それぞれ完了する
時間は分からない,という状態では制御ができないので,タイマークロックを基準にし,時間が
来たら必ず動作させるような処理を行います.
これを図で示すと以下のようになります.
課題1
比例制御について以下のファイルをダウンロードし,シミュレーションを行いましょう.どちらを用いても
構いません.
速度型アルゴリズム p_p_cl.c
位置型アルゴリズム p_v_cl.c
時定数T=2.0秒,シミュレーション時間SimTは5〜10.0秒にし,目標値はSV=5000[rpm],Kpを自由に
調整して良いと思われる応答になるよう調整し.グラフに出力しましょう.
いくつか比較できればなおよいので挑戦してみて下さい.
ソースファイルは完全ではありません.このファイルは開発途中の様相となっているため,余計な
機能があったり,書きかけの記述があったり,コメントアウトがあったりするので,適宜整えて必要な
ソースを作成して下さい.画面に数値を出力したり,ファイルに数値が書き出せるよう,変更して下さい.
なお,エクセルに読み込ませるには,以下のような形式で記述するとよいので参考にして下さい.
エクセルに読み込めるファイルの形式の代表(これらをまとめてCSVファイルともいう)
ファイル出力例 | ||
CSV(カンマ区切り)ファイル | TSV(タブ区切り)ファイル | SSV(スペース区切り)ファイル |
1.00000, 2.00000, 3.00000 | 1.00000 2.00000 3.00000 | 1.00000 2.00000 3.00000 |
プログラムの表記例 | ||
printf("%lf,%lf,%lf\n", a, b, c); | printf("%lf\t%lf\t%lf\t\n", a, b, c); | printf("%lf %lf %lf\n", a, b, c); |
提出するもの: 自ら書き換えたソースファイル,Excelなどで作成したグラフの入ったファイル
課題2
PID制御について以下のファイルをダウンロードし,シミュレーションを行いましょう.どちらを用いても
構いません.
速度型アルゴリズム pid_p_cl.c
位置型アルゴリズム pid_v_cl.c
時定数T=2.0秒,シミュレーション時間SimTは5〜10.0秒にし,Kpを自由に調整して良いと思われる
応答になるよう調整し.グラフに出力しましょう.
ソースファイルは完全ではありません.画面に数値を出力したり,ファイルに数値が書き出せるよう,
変更して下さい.
提出するもの: 自ら書き換えたソースファイル,Excelなどで作成したグラフの入ったファイル
課題3 予習課題
上記のファイルはコマンドライン上でグラフ概形を知るための苦肉の策であるため,本当に概形で
しかないので,グラフィックを利用することが有効です.以下のページを読んでおき,もし可能で
あれば,本日の課題を改変して,グラフィック表示できるようにしてみてください.
参考ページ windows グラフィック 入門
さらに,もし可能であれば,引数に関数ポインタを使ってみてください.
関数ポインタについて