飛行機のプロペラのプログラムだが、よりリアルな動きを追求するため、改造を施したよ。
どんな改造を施したのですか?
プロペラが止まるときに、いきなり止まるのではなく、徐々に回転が遅くなってから止まるようにしたのだ。
リアルですね~
どんな改造を施したのか?
これまで見てきたプログラムでは、「エンジンでプロペラが高速回転」の状態で「Bボタン」を押すと、いきなりプロペラが止まってしまうようになっていた。
これを、徐々に回転が遅くなるように改造したのですね?
うむ。その通り。これを実現するため、新たに「徐々に減速」という状態を追加したのだ。
状態 | プロペラの動き | Aボタン | Bボタン | プロペラが減速完了 |
停止 | 止まっている。 | セルモーターの状態になる。 | 停止の状態のまま | (減速しない) |
セルモーター | ゆっくり回る。 | エンジンの状態になる。 | 停止の状態になる | (減速しない) |
エンジン | 速く回る。 | エンジンの状態のまま | 徐々に減速の状態になる | (減速しない) |
徐々に減速 | 徐々に遅くなる。 | 徐々に減速のまま | 徐々に減速のまま | 停止の状態になる。 |
前に出てきた表ですね。一番下に「徐々に減速」が追加されていますね。
そうなのだ。そして、この「徐々に減速」では、他の状態と異なり、プロペラの回転速度が徐々にゆっくりに変化していくのが特徴なのである。
「エンジン」の状態で「Bボタン」を押すと、この「徐々に減速」の状態になるのですね?
修正前のプログラムでは、「停止」の状態に移行していたが、今回は「徐々に減速」の状態に移行するようにしたのだよ。
「プロペラが減速完了」という列が増えていますね。これはどういうことですか?
これは、状態を切り替えるためのきっかけが1つ増えた、ということなのだ。
「きっかけ」ですか???
元のプログラムでは、「Aボタン」や「Bボタン」で状態が変わっていたのは覚えているね?
ボタンを押すことが「きっかけ」ということですね?
その通りだ。ところが、「徐々に減速」の状態では、ボタンはきっかけにならず、プロペラの減速が完了することがきっかけとなり、「停止」に状態が変わるのだ。
ということは、減速中はボタンを押しても、何も起こらない、ということですか?
うむ。今回は、そういうふうにプログラムを改造したのだよ。
なるほど・・・
「徐々に減速」の動きを行うための関数を追加
ところで、それぞれの状態の動きを行う部分を「関数」にまとめていたのは覚えているかい?
「State何とか」というやつですね?
うむ。そして、今回追加したのは、徐々に減速して停止を行っている最中ということで、「StateStopping」という名前にしてみた。
状態 | 番号 | 関数の名前 |
停止の状態 | 0 | StateStop |
セルモーターの状態 | 1 | StateCellMotor |
エンジンの状態 | 2 | StateEngine |
徐々に減速の状態 | 3 | StateStopping |
4つある関数のうち、一番名前が長いですね!
「徐々に減速」の状態に割り当てた番号は3、変数名はSTATE_STOPPINGとしたぞ。
- 3行目 STATE_STOP = 0
- 4行目 STATE_CELL_MOTOR = 1
- 5行目 STATE_ENGINE = 2
- 6行目 STATE_STOPPING =3
「関数」が追加されたら、呼び出す部分も追加されるのですか?
ヤ~。プログラムでは、下のようになるのだ。
(ドイツ語できたぞ??)
- 76行目 while True:
- 77行目 if state == STATE_CELL_MOTOR:
- 78行目 state = StateCellMotor()
- 79行目 elif state == STATE_ENGINE:
- 80行目 state = StateEngine()
- 81行目 elif state == STATE_STOPPING:
- 82行目 state = StateStopping()
- 83行目 else:
- 84行目 state = StateStop()
赤の部分に「StateStopping()」の呼び出しが追加されているのが分かるね?
ヤ~!
「徐々に減速」の状態の関数の内容
これまでの流れから言って、次は関数の中身の説明ですね?
うむ。「StateStopping」は、次のような動作をするように作られている。
- プロペラの回転数を徐々に遅くする。
- Aボタン、Bボタンが押された場合は現状維持とする。
- 減速が完了したら、「次は停止である」ということを知らせる。
そして、これが「StateStopping」の中身だ。
- 53行目 def StateStopping():
- 54行目 rpm = RPM_ENGINE
- 55行目 nextState = STATE_STOPPING
- 56行目 display.show(‘:’)
- 57行目 while True:
- 58行目 if button_a.get_presses():
- 59行目 nextState = STATE_STOPPING
- 60行目 if button_b.get_presses():
- 61行目 nextState = STATE_STOPPING
- 62行目 rpm = rpm – D_RPM
- 63行目 if rpm <= RPM_CELL:
- 64行目 nextState = STATE_STOP
- 65行目 break
- 66行目 pin1.write_analog(rpm)
- 67行目 sleep(200)
- 68行目 return nextState
回転数の設定値は、エンジンの状態の回転数RPM_ENGINEから徐々に小さくしていくことになる。まずは、54行目で回転数を保持するために「rpm」という変数を用意した。
「変数」は、数字を入れておく箱でしたね。
箱に入れてある数字を少しづつ小さくすることで、徐々に減速させるのだ。最初は高速回転しているので、この変数「rpm」に、RPM_ENGINEを入れておく。
ふむふむ。
この変数「rpm」に格納された値を、57行目以降のループの中の62行目で小さくする。
- 62行目 rpm = rpm – D_RPM
- 63行目 if rpm <= RPM_CELL:
- 64行目 nextState = STATE_STOP
- 65行目 break
- 66行目 pin1.write_analog(rpm)
- 67行目 sleep(300)
そして、66行目でマイクロビットからモーター駆動回路に出力するのだ。モーター駆動回路については、以前の記事をご覧ください。
ループの中にあるので、どんどん遅くなっていく、という訳ですね。
そうだね。また、67行目で少し待つことで、遅くなる具合を調整している。
「減速が完了したら・・・」の部分はどこですか?
それは63~65行目の部分だ。
- 63行目 if rpm <= RPM_CELL:
- 64行目 nextState = STATE_STOP
- 65行目 break
モーターの回転数がセルモーターの時の回転数「RPM_CELL」以下になったら、減速完了と判断して、「次は停止」というお知らせを用意して、ループを抜けるのだ。
「break」でループを抜けるのでしたね。
ループを抜けると、「return」があるので、この関数の呼び出しもとに「お知らせ」が届くことになるのだ。
- 68行目 return nextState
58行目から61行目は、ボタンについての部分ですね。後で問題が起こらないようにするために、ここでボタンが押されたかどうかを見ているのでしたね。
これは前回説明したところだね。詳しくは、前回の記事をご覧ください。
「エンジンの状態」の改造
改造はこれで全てですか?何か忘れているような気がしますが・・・
関数「StateEngine」の改造が残っているぞ。「Bボタン」を押した後に、「停止」の状態ではなく、「徐々に減速」の状態に切り替わるようにしないといけないのだ。
思い出した!そういえば、こんな表がありましたね。
状態 | プロペラの動き | Aボタン | Bボタン | プロペラが減速完了 |
停止 | 止まっている。 | セルモーターの状態になる。 | 停止の状態のまま | (減速しない) |
セルモーター | ゆっくり回る。 | エンジンの状態になる。 | 停止の状態になる | (減速しない) |
エンジン | 速く回る。 | エンジンの状態のまま | 徐々に減速の状態になる | (減速しない) |
徐々に減速 | 徐々に遅くなる。 | 徐々に減速のまま | 徐々に減速のまま | 停止の状態になる。 |
なんだか、大変そうな予感が・・・
いや、心配はいらない。「StateEngine」からのお知らせを「次は停止状態」から「次は徐々に減速」に変更すればよいのだ。
- 40行目 def StateEngine():
- 41行目 nextState = STATE_ENGINE
- 42行目 display.show(‘E’)
- 43行目 pin1.write_analog(RPM_ENGINE)
- 44行目 pin13.write_digital(1)
- 45行目 while True:
- 46行目 if button_a.get_presses():
- 47行目 nextState = STATE_ENGINE
- 48行目 if button_b.get_presses():
- 49行目 nextState = STATE_STOPPING
- 50行目 break
- 51行目 return nextState
「StateEngine」の中の49行目を変更するだけで良い。
なんと!意外と簡単でしたね!
「状態切り替え」方式のプログラムは改造しやすい。
プロペラのプログラムを改造してみたのだが、元のプログラムにはあまり手を加えていないことには気が付いたかな?
新しく作った関数「StateStopping」の呼び出し部分の追加と、後は「エンジン」の状態から切り替わるときのお知らせを変更したぐらいですね。
新たに関数「StateStopping」を作ることで、今回の改造はほとんど終わったと言えるだろう。
なるほど・・・何か秘訣があるのですか?
うむ。「状態切り替え方式」でプログラムを作っていることが秘訣なのだ。
そうだったのですか!でも、まだピンときませんが・・・
では、説明しよう。元のプログラムでは、三つの状態がある。そして、それぞれの動きを関数にまとめてある。
ここに、新しい動作を加える場合は、その動作を新しい状態として追加すればよいのだ。
追加した状態の動きを新しい関数にまとめて追加すればよく、状態の切り替わり方は「お知らせ」を変えるだけで済むのだ。
「徐々に減速」の状態の関数を作るのは大変ではないですか?
確かにそこは大変だが、他の状態の関数はほとんど手を加えていない。追加したい機能のみに集中してプログラムを改造することができるのだ。
ほかの部分をあまりいじらなくてよいのは助かりますね。
このように、「状態切り替え」方式でプログラムを作っておけば、後で改造するのが楽、ということは分かったかな?
ヤー!
まとめ
今回のまとめデース
- 「状態切り替え」方式のプログラムは改造しやすい。
- 状態を切り替えるきっかけはボタンでなくてもよい。
改造したプログラムの全文は、以下の記事をご覧ください。(新しいタブで開きます。)
【プログラミング応用】[4.1] プラモの飛行機のプロペラのプログラム:回転が徐々に遅くなって止まるバージョン
つづく
コメント