車両ベースのプログラム例
車両ベース上にある複数の入出力デバイスを使用した、総合的なプログラムを作成しましょう。
プログラムの概要
今回は、次のような動作をするプログラムを作ることにします。
動作概要
- 前進状態から始まり、マイク入力で音を検知したら停止する。
- 停止中にマイク入力で音を検知したら再び前進する。
-
走行中に左側の障害物センサーが反応したら、その後 前面障害物センサーが反応するまで左回りに旋回し続ける。前面で反応したら、再び前進する。
(左側に壁などの障害物を検出すると、その方向に転換して進むイメージ) - 右側の障害物センサーが反応した場合も、同様の動作をする。
- ヘッドライトLEDは、停止中は緑色、前進中は白色、左右いずれかに旋回中は赤色に点灯する。
使用する車両ベース上デバイス
- 入力
- マイク
- 障害物センサー (前面、左側面、右側面の3つ)
- 出力
- ヘッドライトLED
- モーター
マイク検知
回路上で音圧を検知したとき基板上の黄緑LED[MIC](LD4) が点灯します。
検知時間が短いとプログラムの入力が読み取れないことがあります。その場合は、
-
マイクの感度を上げる
音センサ感度調整ボリウム (VR7) を時計回しに回す
※ 感度を上げすぎると自身の走行音に反応するので回しすぎないよう注意してください。 -
検知信号のパルス長を長くする
基板上の「センサ検知パルス長の切替ジャンパー (JP1)」を「L」側に短絡ソケットを差し替えます。
作成方針
これらの入出力装置は、いずれも確認や操作に要する時間は短いため、簡単な条件であれば、基本的には前節までに説明した永久ループによるプログラミングで対応できます。 処理の流れは、次のようになります。
# (A) 初期設定 while True: # (B) 入力装置の読み取り # (C) 入力結果や現在および過去の状態から、出力内容や次の状態を決定 # (D) 出力の反映 # (E) 次回ループまで短時間待機
以上の概要を整理すると、このプログラムでは、モーターの動作を基準にした状態が「停止」「前進」「左旋回」「右旋回」の4つに分かれています。それぞれの状態間の関係について図にまとめると下記のようになります。
このような図のことを「状態遷移図」と呼びます。
(実際には、図中に遷移を伴わないものを含めてすべての入力を網羅するのが通例ですが、下図では説明を単純化するために省略しています)
プログラム説明
上記の状態遷移図に基づいたプログラムの例を下記 CODE9 に示し、続いて各部の説明を行います。
CODE9(A) の初期設定には、永久ループに入る前に変数の設定を行います。
- 7行目:車両ベース制御用ライブラリモジュール WR2WD のインスタンスを生成します。
-
10行目:ここでは、状態管理用として文字列 (
str
) 型のstate
という変数を定義し、初期状態を示す'advance'
を代入しています。状態管理変数に代入する値は、文字列のほかにも数値など作成者が管理しやすいと思うものでかまいません。
以後、本プログラムでは各状態を次の文字列で割り当てています。停止 'stop'
前進 'advance'
左旋回 'turn_left'
右旋回 'turn_right'
- 13行目・20行目:その次の
led_color
、motor
は、各状態に応じたヘッドライトLEDとモーター出力内容を表す辞書オブジェクトです。これらは定数として扱い、以後上書きされず読み取りのみが行われます。こういった定数の定義もこのタイミングで行います。 - 28行目:
mic
はループの初回で参照されるため、ここで宣言と初期化を行っています。
(B) では、条件判定に用いるセンサー類の入力データを取ります。
- 34行目:マイク入力は、オフからオンになった瞬間を検出するため、前回ループの値をあわせて管理しています。(前章の説明を参照してください)
previous_mic
が前回結果、mic
が今回結果を示しています。それぞれ、オフのときFalse
、オンのときTrue
になります。 - 37行目:障害物センサーおよび底面センサーの4入力を一括で取得する関数を呼び出します (このプログラムでは底面センサーは使用しません)。取得結果は辞書形式で返却されます。
例:{ 'top': True, 'left': False, 'right': False, 'bottom': False }
- 39行目:上記のマイクおよびセンサーの読み取り結果を、動作確認のために出力エリアにプリントする処理を付け加えています。各センサーがオンの時に頭文字のアルファベットが表示され、オフの時はハイフン (
'-'
) が代わりに表示されます。条件判断や出力デバイスの操作を行うものではないので、最終的な挙動には影響しません。
(C) では、状態遷移図で表した内容をもとに、各種センサー入力と現在状態の組合せから条件判定を行い、出力と次状態の変更を行うための記述をします。
- 47行目:次回ループ時の状態を仮決定する際の変数を作成します。今後 (C) パート内で、状態遷移を行う場合には、この
next_state
に対して上書きを行います。現状態state
で初期化しているため、状態遷移の条件に当てはまらなかった場合はそのまま現状態を維持します。 - 49行目:マイク入力がオフからオンになったタイミングで、「停止」と「前進」状態を相互に切り替える処理を記述しています。
- 57行目:前進状態で左右いずれかの側面センサーがオンになった場合の条件を記述しています。
- 64行目:左右旋回中状態で、前面センサーがオンになった場合の条件を記述しています。ここでの
state[:4]
とは、スライスを利用してstate
の先頭4文字のみを取り出す方法です。処理内容が同一である'turn_left'
と'turn_right'
の判定を1つにまとめるため、先頭4文字が'turn'
と一致することを条件に判定を行っています。
(D) では、状態遷移によって得られた次状態の確定と出力制御を行います。
- 69行目:(C) パートで確定した仮の次状態を、
state
に反映させるために代入を行います。 - 71行目・73行目:新しい状態に応じて、ヘッドライトLEDおよびモーター動作を制御する関数をそれぞれ呼び出します。LEDとモーター状態を示す引数は、13行目・20行目でそれぞれ状態名をキーにして辞書形式で定義済みであるため、それらから値を読み出します。
(E) では、次回ループまでの待ち時間を指定して待機するため、time
モジュールの sleep
関数を呼び出します。
- ここでの待機時間が1ループあたりの所要時間、および入出力1サイクルの頻度に影響します。そのため、(B)(C)(D) 各パートに極端に長時間を要する処理があれば全体処理のタイミングに大きく影響してしまいます。ループ内をなるべく短時間で終わる処理にとどめることが、スムーズなプログラムの動作のために望ましいと言えます。
長時間かかる処理を行いたい場合は、Python標準モジュールであるthreading
などを使うことが有効です。