差分とフィードバック制御
情報制御基礎の授業を通して、入力値を制御するため、コンピュータを使う場合の数値処理の基礎的な話として、信号の平滑化を説明してきたので、最後に差分について説明をする。また、実際には、入力値を制御に利用する一般的な構成のフィードバック制御について説明する。
変化の検出
例えば、以下のような若干のノイズが混ざった入力信号が与えられたとする。この波形で「大きな山が何ヶ所ありますか?」と聞かれたら、いくつと答えるべきであろうか?山の判断方法は色々あるが、4カ所という答えは、1つの見方であろう。では、この4カ所という判断はどうすればいいだろうか?
こういった山の数を数えるのであれば、一定値より高いか低いか…という判断方法もあるだろう。この絵であれば、15ステップ目、32ステップ目付近は、100を越えていることで、2つの山と判断できるだろう。
こういった予め決めておいた値より「上か?/下か?」で判断するときの基準値は、しきい値(閾値)と呼ぶ。
しかし、この閾値では、40ステップ目から50ステップ目も100を越えており、以下のようなプログラムを書いたら、40ステップ目~50ステップ目すべてをカウントしてしまう。
#define THRESHOLD 100 int x[ 100 ] = { // 波形のデータが入っているとする。 } ; int count = 0 ; for( int i = 0 ; i < 100 ; i++ ) { if ( x[i] >= THRESHOLD ) count++ ; }
また、65ステップ目の小さな山も1個とカウントしてしまう。
この問題を避けるために、閾値を130にすると、今度は最初の2つの山をカウントできない。どうすれば、山の数をうまくカウントできるのだろうか?
差分を求める
前述のような問題で山の数を数える方法を考えていたが、数学で山を見つける時には、何をするだろうか?
数学なら、山や谷の頂点を求めるのならば、微分して変化量が0となる場所を求めることで、極大値・極小値を求めるだろう。そこで、山を見つけるために入力値の変化量を求めてみよう。
表計算ソフトで差分を計算するのであれば、セルに図のような式を入力すればいいであろう。このようなデータ点で前の値との差を差分と呼ぶ。数学であれば、微分に相当する。
このグラフを見ると、波形が大きく増加する部分で、差分が大きな正の値となる。さらに波形が大きく減少する部分で差分が負の大きな値となる。特にこのデータの場合、山と判断したい部分は差分が20以上の値の部分と定義することも考えられる。
#define TH_DIFF 20 int x[ 100 ] = { // 波形のデータが入っているとする。 } ; int count = 0 ; for( int i = 0 ; i < 100 ; i++ ) { if ( x[i] - x[i-1] >= TH_DIFF && x[i+1] - x[i] <= -TH_DIFF ) count++ ; }
しかし、このプログラムでは、山の数をうまくカウントしてくれない。うまく、山の数を数えるためには、差分の値を山と判断するための閾値(この場合は20)を調整することになるだろう。
制御工学の概要
以下に、制御工学ではどのようなことを行うのか、概要を述べる。
ここで紹介する制御理論は、古典制御理論と呼ばれる。
制御工学では、入力値と、何らかの処理を施し出力値
が得られるシステムで、どのように制御するかを考える。
例えば、電気ポットの温度制御をする場合、設定温度の値を入力値とし、何らかの処理を行い、出力となるヒーターの電流を制御し、最終的には温度
が測定される。ヒーターは、設定温度
と温度計の値
の差
に応じて電流量を変化させる。このように一般的な制御では、最終的な温度が入力に戻っている。このように目標値に近づけるために、目標値との差に応じて制御することをフィードバック制御という。
制御の仕方には様々な方法があるが、 がとある時間で0からYに変化した場合を考える。入力と出力で制御された波形の例を示す。
この波形では、黒のように入力値が変化した場合、それに追いつこうと出力が変化する。(1)理想的には、速やかに追いつく赤のように変化したい。しかし、(2)慎重に制御をする人なら、変化への制動が大きい過制動(青点線)となり、目標値に追いつくまでに時間がかかる。(3)一方、すこしでもずれたら直そうとする人なら、時間的には速い反応ができるかもしれないが、目標値を追い越したり、増えすぎ分を減らしすぎたりして脈動する過制御(赤点線)となるかもしれない。
PID制御
目標値、出力
、ずれ(偏差)
、制御量
とした時、基本的なフィードバック制御として偏差の使い方によってP動作,I動作,D動作がある。参考 Wikipedia PID制御
比例制御(P制御)
偏差に比例した制御を行う方式(を比例ゲインと呼ぶ)
積分制御(I制御)
偏差のある状態が長い時間続く場合、入力値の変化を大きくすることで目標値に近づけるための制御。(は積分ゲイン)
微分制御(D制御)
急激な出力値の変化が起こった場合、その変化の大きさに応じて妨げようとする制御。(は微分ゲイン)
PID制御
上記のI制御やD制御だけでは、安定させることが難しいので、これらを組み合わせたPID制御を行う。
この中で、の値は、制御が最も安定するように調整を行うものであり、数値シミュレーションや、ステップ応答を与えた時の時間的変化を測定して調整を行う。
UML課題
期末試験も近いので、今後の日程の確認。
7/12 UMLレポート課題作成, 7/19 オブジェクト指向のソフトウェア工学, 7/26 課題作成
オブジェクト指向プログラミングの第3回レポート課題は、以下の通り。
特別研究や関連の内容でUMLを記述
専攻科の自分自身の特別研究での自身のプログラムをUMLで表現せよ。もしプログラム作成でない場合は、特別研究で行なっている実験方法とそのデータを UML で表現せよ。
- UMLとして表現する対象の説明として、レポートとして表現する部分の特別研究の内容を説明
- 扱うデータ構造などについて、UMLの構造図のうちの1つを選んでその構造図を示せ。またその構造図で表現した理由がわかるような説明をすること(is-a,has-aなど)
- その処理などについて、UMLの振る舞い図のうちの1つを選んで、その振る舞いを図で説明せよ。同じくその図について説明せよ
リストへの追加処理
前期期末試験までの授業予定( 7/10 リスト追加+課題, 7/17 stackとque+課題 7/24 リストで集合計算 )
最初のリスト生成の説明では、補助関数 cons を用いて、直接リストを生成していた。
しかし、実際にはデータを入力しながらの処理となるであろう。
最も単純なリスト挿入
struct List* top = NULL ; int main() { int x ; while( scanf( "%d" , &x ) == 1 ) { top = cons( x , top ) ; } print( top ) ; // 前回示したリスト全要素表示 return 0 ; }
ここで示したコードは、新しい要素を先頭に挿入していく処理となる。このため、作られたリストは、与えられた要素順とは逆順となる。この方法は、リストを管理するポインタが1つで分かりやすい。
要素を末尾に追加
前に示した方法は、逆順になるので、与えられた要素が末尾に追加する方法を示す。
struct List* top = NULL ; struct List** tail = &top ; int main() { int x ; while( scanf( "%d" , &x ) == 1 ) { *tail = cons( x , NULL ) ; tail = &((*tail)->next) ; } print( top ) ; // 前回示したリスト全要素表示 return 0 ; }
この方法は、次回にデータを追加する場所(末尾だからNULLが入っている)を覚える方式である。ただし、リストへのポインタのポインタを使う方法なので、少しプログラムがわかりづらいかもしれない。
理解の確認のために、末尾のポインタを動かす部分の式を、型で解説すると以下のようになる。
途中でデータ挿入・データ削除
リスト構造の特徴は、途中にデータを入れたり、途中のデータを抜くのが簡単にできる所。そのプログラムは以下のようになるだろう。
void insert( struct List*p , int data ) { // あえて、補助関数consを使わずに書いてみる struct List* n ; n = (struct List*)malloc( sizeof( struct List ) ) ; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~(A) if ( n != NULL ) { n->data = data ; ~~~~(B) n->next = p->next ; ~~~~~~~(C) p->next = n ; } // consを使って書けば、簡単 // p->next = cons( data , p->next ) ; }
void remove_after( struct List* p ) { struct List* del = p->next ; p->next = del->next ; free( del ) ; }
理解度確認
上記プログラムinsert() の中の、下線部(A),(B),(C)の型は何か答えよ。
レポート課題
以下に示すようなデータを扱うリスト構造を作り、そのリストを扱うプログラムを作成せよ。
( 出席番号 % 3 ) + 1 の番号の課題に取り組むこと。
- 緯度(latitude)経度(longitude)とその場所の都市名(city)
- 名前(name)と誕生日(month,day)(1つの変数に2月7日を0207のように保存するのは禁止)
- 複素数(re,im)
このようなプログラムを作るのであれば、以下の例を参考に。
struct NameAgeList { char name[ 20 ] ; // 名前 int age ; // 年齢 struct NameAgeList* next ; // 次のデータへのポインタ } ; struct NameAgeList* na_cons( char* nm, int ag, struct NameAgeList*p ) { struct NameAgeList* ans ; ans = (struct NameAgeList*)malloc( sizeof( struct NameAgeList ) ) ; if ( ans != NULL ) { strcpy( ans->name , nm ) ; ans->age = ag ; ans->next = p ; } return ans ; }