2005/9/8

目指せ完全自動運転(5)−信号を喚呼する  VRMスクリプト禅問答
難易度:☆☆☆☆☆

本日ご紹介するスクリプトは、いろいろな意味で完全自律型自動運転の「心臓部」となる部分です。ややこしいですが、意味するところを理解できれば他にもいろいろ応用の効く重要なテクニックが登場します。頑張って読んでください。
まずは、前回までの編成スクリプトにさらに以下のコードを加えてください。
[編成スクリプト]
//公開プロパティ
//点呼する信号
VarSignal StdObjSignal
//編成状態変数
Var VarActive
Var VarAutoMode
set VarActive 1
set VatAutoMode 1
//
//閉塞信号喚呼メソッド
//
BeginFunc StdMtdCheckSignal
//公開プロパティ読み出し
Var TmpSignal
mov TmpSignal StdObjSignal.StdVarSignalPosition
//信号喚呼
ifeq VarActive 1
ifeq TmpSignal 1
DrawMessage "「停止!!」"
endif
ifeq TmpSignal 3
DrawMessage "「注意!!」"
endif
ifeq TmpSignal 6
DrawMessage "「進行!!」"
endif
endif
//速度自動更新
ifeq VarAutoMode 1
ifeq TmpSignal 1
call this MtdSignalR
endif
ifeq TmpSignal 3
call this MtdSignalY
endif
ifeq TmpSignal 6
call this MtdSignalG
endif
endif
EndFunc
冒頭で宣言しているオブジェクト変数StdObjSignalの扱いかたが肝となります。ここに喚呼すべき信号が代入され、その信号オブジェクト(こちらを参照)の公開プロパティStdVarSignalPositionの値から灯火色を評価して編成速度を制御しよう、という戦略です。このオブジェクト変数に喚呼すべき信号を代入する方法は後述します。まずは、喚呼すべき信号が代入されている前提で読み進めてください。
続く2つの変数(VarActive,VarAutoMode)は将来の拡張のための仕掛けです。それぞれ「編成はアクティブか?」「自動運転モードか?」を0/1で記録します。今回は値を1に固定しています。

続くメソッドStdMtdCheckSignalが、言わばこの編成スクリプトにとっての「運転士の頭脳」に当たる部分となります。
まず、変数TmpSignalに信号オブジェクトの公開プロパティ=灯火色を取得します。続く「信号喚呼」の部分でifeq命令による条件分岐でDrawMessgae命令を使い分け「スクリプト運転士」にいわゆる「指差喚呼」をさせています。これにより、ビュワーのログウィンドウに信号に応じた喚呼メッセージが表示されます。
この部分全体をifeq VarActive 1〜endifが包んでいますが、これは現在選択している編成以外からの喚呼メッセージを抑止するための措置です。このシリーズで紹介しているスクリプトを組み込んだレイアウトで複数の編成を同時走行させると(と言うか、同時走行させないと意味がないんですが)ログウィンドウはそれぞれの編成からの喚呼メッセージで埋まってしまいます。そこで、現在選択している編成のみから喚呼メッセージが出るようにします。ただし、今回のスクリプトには編成の選択と連動する部分がありません。これは後日紹介します。そのための準備工事であるとご理解ください。
続く「速度自動更新」の部分は、構造的には「信号喚呼」の部分と基本的に同じです。異なるのは、DrawMessage命令ではなくcall命令で前回紹介した信号灯火色に応じた速度設定をおこなうメソッドが実行されることで、つまりこの部分が真の意味での自動運転の心臓部になります。
さらに、この部分を包んでいるifeq〜endifは変数VarAutoModeを参照しています。これはこの変数の値を0/1と変化させることで手動/自動運転を切り替えるための準備工事です。「完全自律型自動運転」とは言え、まったく自分が介入できないというのはつまらない話です。この変数により、たとえば自分が選択している編成のみが手動運転であり、他の編成はすべて自動運転で、事故を起こさないように信号(及び自動的におこなわれる喚呼)を守って運転する、といった遊び方が可能になります。

上掲スクリプトを編成スクリプトに加えることで、閉塞信号の灯火色に応じた自動運転が可能になることがご理解いただけたでしょうか。しかし、これはオブジェクト変数StdObjSignalに、正しく喚呼すべき信号が代入されていれば、の話です。以下、その方法を説明します。
閉塞信号の自動化にセンサーを使用しましたが、それに加えてもう1組のセンサーを信号の数だけ用意します。便宜上、前者を「閉塞センサー」、後者を「喚呼センサー」と呼ぶことにしましょう。レイアウターでレイヤー分けしておくと便利です。
[センサースクリプト]
//パートナー信号定義
VarSignal ObjPartnerSignal
get ObjPartnerSignal "{信号機の名前}"
//イベント定義
Var EventID
SetEventSensor MtdCheckSignal EventID
//
//信号点呼メソッド
//
BeginFunc MtdCheckSignal
//検知した編成を特定
VarTrain ObjTrain
GetSenseTrain ObjTrain
//編成に点呼すべき信号を知らせる
get ObjTrain.StdObjSignal ObjPartnerSignal
call ObjTrain StdMtdCheckSignal
EndFunc
上掲スクリプトをそれぞれの喚呼センサーに組み込んでください。このセンサーはその配置場所がミソです。閉塞信号を動作させる閉塞センサーよりも前、すなわち現実の鉄道を考えてみて運転士が信号を喚呼するであろう地点にセンサーを配置します。140mmレール2〜3本分前あたりが適当でしょうか。
当然のことながら{信号機の名前}の部分には、このセンサーの進行方向直後に設置されている信号機のオブジェクト名を補います。たとえば「SIGNAL4」の少し前にこの喚呼センサーがあるのであれば、ここは「SIGNAL4」に書き換えます。
さて、これで何が起こるでしょうか。ポイントはメソッドMtdCheckSignalの末尾2行です。直前のGetSenseTrain命令でオブジェクト変数ObjTrainにはセンサーを踏んだ編成が取得されています。つまり、このメソッドが実行された時点で、編成は信号を喚呼すべき地点に来ています。
続くget命令で「ObjTrain(すなわちセンサーを踏んでいる編成)のオブジェクト変数StdObjSignalに、自分(つまり喚呼センサー)が知っているオブジェクト変数ObjPartnerSignalの内容(つまり喚呼すべき信号の名前)を代入する」がおこなわれます。これで編成に「喚呼すべき信号」が伝わりました。
そしてcall命令で本稿前段で紹介した編成スクリプト中のメソッドStdMtdCheckSignalが実行されます。この時点で同メソッドが参照するStdObjSignalには喚呼すべき信号が記録されていますから、結果的に編成は目前に存在する信号の灯火色に応じて指差喚呼をおこない、そして速度を更新することになります。以降、編成が喚呼センサーを踏む都度、同じことが起こります。違うのは喚呼すべき信号が変わっていくことだけです。

長くなってきたので敢えてここで話を切ることにしましょう。まずは、本稿を改めて通読してその意味を咀嚼してください。明日、改めて一般論としてここで用いられているテクニックと、その応用の可能性を説明します。

質問やご要望があれば気軽にコメント欄に書き込んでください。特に「ここの意味がよくわからないのでもっと詳しく」という指摘は大歓迎です、なぜわかりにくいのかの理由が添えられていると特に。
0

コメントを書く

この記事にはコメントを投稿できません




teacup.ブログ “AutoPage”
AutoPage最新お知らせ