SSブログ

特定のDCCアドレスの列車を探す [python]

次に列車を探すルーチンを考えます。
ある変数に特定の列車の位置をトレース次々と代入していく方法があると思いますが、私の拙い頭では簡単なアルゴリズムが思いつかなかったので、列車を探しに行くようなアルゴリズムにしました。
BASICのfor nextループと同じような感じです。

for i in range(24):
___self.ichi = memories.getMemory(memoriList[i]).getValue()
___if self.ichi == "7":
______if (i == 0) and (memories.getMemory(memoriList[23]).getValue() == "7"):
_________i = 23
______self.singo = singoList[i]
______self.iro = signals.getSignalHead(self.singo).getAppearanceName()
______break

pythonでは左インデントでループの範囲を規定しますが、ここのページでは先頭のスペースは切り詰められるので、ここでは______で示しています。
iを0から23まで回します。24個の閉塞ブロックに渡ってメモリー値をスキャンしていって、途中で値“7”が出れば(最初のif のところ)、信号現示を読みに行き、breakでループを抜けます。
#DCCアドレス7の列車を探しに行くときの例です。

2つめのif のところ、i == 0のときだけ一番後を見に行き、そちらの値も7ならばi = 23とします。
メモリーリストは列車の進行方向とは逆向きに列べてあり、若いリストから見ていき、最初に”7”が出たところに先頭がいるはずです。これはうちのレイアウトが簡単なエンドレスだから出来ることだと思います。こうした理由は、ブロックをまたがって列車がいるときには’7’の値を持つメモリーが2つあり、後側の値を採用してしまうと信号現示が赤となるのを防ぐ手立てです。0番目でヒットしたときだけそのブロックの前は最後尾23ですので、それをifで見に行ってます。

信号現示を読み取れれば
if self.iro == 'Green':
self.sp = 1
elif self.iro == 'Yellow':
self.sp = 0.5
elif self.iro == 'Red':
self.sp = 0
self.throttle = self.getThrottle(7, False)
self.throttle.speedSetting = self.sp

とベタに書いて、列車を減速・停止させることが出来ます。
これを複数の列車に適応させていきます。

メモリーと信号のリスト [python]

さて、前回までにメモリーの読み出し、信号現示の読み出し、スロットルの制御が出来ることが分かりましたので、
特定の列車の位置を探し出し、信号現示に従って速度を制御するスクリプトを試作していきます。

まず、読み出すべきメモリー値や信号のSystem Nameを登録します。
pythonではリストとして変数を持てます。
例えば、メモリーのリストを
memoriList = ['IM:AUTO:0005', 'IM:AUTO:0004', 'IM:AUTO:0020', 'IM:AUTO:0019', 'IM:AUTO:0018', 'IM:AUTO:0014', 'IM:AUTO:0013', 'IM:AUTO:0012', 'IM:AUTO:0021', 'IM:AUTO:0022', 'IM:AUTO:0011', 'IM:AUTO:0010', 'IM:AUTO:0009', 'IM:AUTO:0023', 'IM:AUTO:0024', 'IM:AUTO:0001', 'IM:AUTO:0002', 'IM:AUTO:0003', 'IM:AUTO:0015', 'IM:AUTO:0016', 'IM:AUTO:0017', 'IM:AUTO:0008', 'IM:AUTO:0007', 'IM:AUTO:0006']
こんな感じで定義できます。

3つめを取り出すときは
a = memoriList[2]
です。(0からなので、3つめは2)

このメモリーリストに対して、参照すべき信号のリストを
singoList = ['IH:SE8C:"LT273";"LT274"', 'IH:SE8C:"LT275";"LT276"', 'IH:SE8C:"LT287";"LT288"', 'IH:SE8C:"LT285";"LT286"', 'IH:SE8C:"LT283";"LT284"', 'IH43', 'IH:SE8C:"LT261";"LT262"', 'IH:SE8C:"LT263";"LT264"', 'IH:SE8C:"LT301";"LT302"', 'IH:SE8C:"LT303";"LT304"', 'IH45', 'IH:SE8C:"LT265";"LT266"', 'IH:SE8C:"LT267";"LT268"', 'IH:SE8C:"LT305";"LT306"', 'IH:SE8C:"LT307";"LT308"', 'IH46', 'IH:SE8C:"LT257";"LT258"', 'IH:SE8C:"LT259";"LT260"', 'IH:SE8C:"LT277";"LT278"', 'IH:SE8C:"LT279";"LT280"', 'IH:SE8C:"LT281";"LT282"', 'IH44', 'IH:SE8C:"LT269";"LT270"', 'IH:SE8C:"LT271";"LT272"', 'IH47']
このように決めます。
メモリーは占有センサ(=閉塞区間)のブロックに対応してます。リストの同じ順番にその閉塞区間で見るべき先の信号機を対応させています。
IH44など、ダミーの信号機の部分は場内信号機で、場内の場合はどれか一つが緑現示なら緑、黄なら黄となるようにダミーを立てています。そのようなLogixを組んでいます。

たとえば、memoriList[10]の列車はsingoList[10]を参照すればいいようになってます。
ですので、ある列車がmemoriList[10]にいることが分かれば、singoList[10]の現示に従って速度を調節すれば良い訳です。

次回は、目的の列車がどこにいるかを探すルーチンです。
タグ:Python DCC

信号現示を読み取る [python]

次は信号現示を読み取ります。

信号現示を読み取るのも同じように
a = 'IH:SE8C:"LT273";"LT274"'
b = signals.getSignalHead(a).getAppearanceName()
で読み出せます。
もちろん変数を使わずそのまま引数に書いても大丈夫です。

ここも信号機はSystem Nameですので少々やっかいですが、定義してしまえばおしまいなので構わす進みます。
print bとすると、
Red
などと出力されます。

なかなか順調。
「メモリー値から列車の場所を特定、先の信号現示を読み出し、列車のスピードを変える。」
くらいはすぐに出来そうな雰囲気です。
そのためのコマンドは全て分かりましたので、あとはアルゴリズムを考えます。
オブジェクト指向など、全く理解しておりませんが、そのままベタで書いていきます。


タグ:DCC Python

ブロックトラッキングの値を読み取り、列車IDに。 [python]

つぎはある区間にいる列車IDを読み取って、
その列車のスピードを変更してみます。

self.a = memories.getMemory("IM:AUTO:0002").getValue()
self.b = int(self.a)
self.throttle = self.getThrottle(self.b, False)
self.throttle.speedSetting = 0.5

こんな感じです。2行目は文字変数を数値に変換しています。
getThrottleの第1引数にint(self.a)と直接書いても構いません。
なんかまどろっこしいですが、メモリー値はテキスト、getThrottleの引数は整数ですので、
こうなってしまいます。
もちろんspeedSettingの値は0から1までの数値ですので、変数を持たせられます。
上手く利用すれば閉塞区間によって最高速度をかえたりもできますね。

これを、2回目でご紹介したスクリプトのクラスの定義部分に書いて、
出来上がりです。

次は信号現示を読み取ります。

タグ:DCC Python

メモリー値を読み出す [python]

JMRI python scriptの2回目、メモリー値の取り出しです。

こちらはPanel Proを開いた状態で、そのままコマンドを入力してOKです。

めもり.png

System NameがIM:AUTO:0002のメモリー値を読むのは
memories.getMemory("IM:AUTO:0002").getValue()
です。
これを調べるのは骨が折れましたが、見つけられれば簡単でした。

出力させないと値が見えませんので、
print memories.getMemory("IM:AUTO:0002").getValue()

一旦変数に持たせても構いません。

a = memories.getMemory("IM:AUTO:0002").getValue()
print a

pythonは変数の方を指定せずにいきなり代入しても良いみたいです。
簡単ですね。

ただ、User Nameではうまく行きません。
たぶん日本語のLocaleを設定してないから、あるいはMemoryを示す接頭語がいるのか、でしょう。
ここは拘らずに、System Nameですませます。

つぎはある区間にいる列車IDを読み取って、
その列車のスピードを変更してみます。

タグ:Python DCC

JMRI python scriptに挑戦 [python]

前回まで、Logixを使って、ポイントや信号機、サウンドの制御をセッティングしてきました。

ここまでやっても、運転士が信号を見落とすと事故になります。
どうせなら、先の信号現示を判定して、強制的に減速、停止できれば良いなぁ、と思うのは自然です。

例えば「前方の閉塞信号機が赤現示のときに、その場所にいる列車を停止させる。」のような動きを設定したいのですが、JMRI Logixを使って、これはできません。
これはある意味デジタルコマンドコントロールの泣き所なんです。

アナログ方式だと、前方信号機に連動させて手前の区間の電圧を制御してやれば、どの列車でも減速→停止が可能なのですが、デジタルだとそこにいる列車のアドレスに対して、コマンドを送ってやらなければなりません。

制御すべき列車のIDはMemory Valueとして保持しておりますので、その値のIDに対してスロットルコマンドを送れば良いのですが、JMRIにはそのためのLogixがありません。何となく、簡単にできそうなんですが。。。ダメです。

そこで、私にとっては敷居の高い禁断の果実を取りに行くことになりました。Jython scriptに挑戦です。

BASIC, Fortran の世代(学生以来)なので、Pythonの流儀が全く分かりません。
そこで、サンプルスクリプトを解読しながらの試行錯誤です。
サンプルはJMRI/jythonのフォルダ内にあります。テキストで開いて、コメントを見ながら解読です。

スクリプト.png

Panel Pro ==> Panels ==> Script Entryで開くテキストフィールドにPython Scriptを打ち込んでみて、
動作を確認していきます。コマンドは打ち込まなくても、Loadボタンで上記JMRI/jythonフォルダから読み込めます。うまくいったスクリプトはセーブも出来ます。
複数行のスクリプトでも打ち込んでおいて、Executeすれば良いです。

結果はScript Outputで確認出来ます。エラーがあればメッセージが出ます。

勉強はLocoTest.pyやAutomatonExample.pyなどのコメントを読みながらです。

ここから以降はプログラムの全くの素人が系統的に学習した訳でなく、我流でサンプルスクリプトを解読、理解した範囲で書いてますので、間違っている場合は是非ご指摘ご教示くださいませ。

解読と試行錯誤の結果、例えばID3の列車のスピードを半分にするにはこれだけのscriptを書く必要があります。(と思います。)

1 import jarray
2 import jmri
3 class test(jmri.jmrit.automat.AbstractAutomaton) :
4
5 def init(self):
6 self.throttle = self.getThrottle(3, False)
7 self.throttle.speedSetting = 0.5
8 return
9 def handle(self):
10 return 0
11
12 a = test()
13 a.start()

(pythonは 左インデントに意味がありますので、ご注意を。ここのサイト、スペースを無視するので困ります。行番号は不要です。良い書き方ないでしょうか。)

スロットルを制御するコマンドは
jmri.jmrit.automat.AbstractAutomaton
で定義されていて、これらのコマンドをつかうには、
クラス(コマンド?)を定義して(3~10行)、実行(12~13行)します。

5〜8行、def init から returnまでは最初に一度だけ実行する部分、ここに全部書いてあります。実際に実行するのは2行だけなんです。
def handle から returnまでは、ルーチンに回す部分ですが、return 0で即終了です。return 1だと回り続けますし、判別式を入れておけば条件によって終了させることが出来ます。

自分の場所を読み取って、先の信号を読み取って、、、、結構大変そうです。。。。
タグ:Python DCC

Logixのおまとめ [DCC]

何回かに渡ってレイアウト制御のLogixを紹介して参りました。

おまとめ.png

まとめてみますと、
(1)赤枠、複数の場内信号機を束ねて、一つ手前の閉塞信号を制御するためのダミー信号機。
(2)オレンジ枠、これはご紹介してませんが、ヤードは一つのデコーダーアドレスで2つのポイントマシンを連動して(直列で)繋いでまして、それをパネル上の信号機と同期させるためのもの。
(3)黄色枠、東駅(パネル上の右側)の場内振り分け、列車種別を判定して必要があれば待避線に誘導。
(4)緑枠、中駅(パネル上の中央)の各場内への振り分け、空いている方にポイントを切り替え、特急は通過か停車を判定して振り替え、貨物列車は通過線へ。
(5)青枠、出発信号機制御用のセンサー管理。別の列車が通過中、出発中のときに誤って前方のポイントを切り替えないように。一番下、中駅1番線だけLRoutesから転送コピーしたために別のIDが付いています。
(6)紫枠、ブロックトラッキングが上手くワークしないために別途自作したもの。どの列車がどこにいるのかを記憶転送していきます。
(7)グレー枠、各駅の発車ベルを鳴らします。
(8)黒枠、各駅に退避するかどうかの判定、通過していてポイントを切り替えないようにするためのセンサーの設定。
(9)無枠、三分岐ポイントを設定したときに自動でセットされたセンサー群。

以上です。たくさんありますね。半年くらいかけて、試運転しながら(子供と遊びながら)、事故が起こったら改良、音を鳴らしたら面白そうなら追加、などなどしていったものです。
これらでやっているのはポイントの転換と出発信号機の制御だけです。

これを元に列車のコントロールをやっても、問題はありませんでしたので、ポイントの制御はこれでOKかと思っています。

Logixは基本的にif …then …のシンプルな構文で、if をネスト出来ない、elseが使えないのが難点です。これらが使えるともう少し楽にかけそうです。
場合分けを綺麗にやるにはjythonスクリプトで書けば良いのですが、リスナーを理解できてないのでハードルが高そうです。

タグ:DCC

オーディオファイルの登録 [DCC]

レイアウトに音が加わると楽しさが大きくなります。

うちでは、駅の接近放送と発車メロディー、バーチャルサウンドデコーダー(VSD)を使っています。
VSDはまた別の機会にご紹介しようかと思います。今回は発車メロディーの登録です。

前回までのLogixで列車の種別を判定し、到着ホームを振り分けていました。このときに鳴らす接近放送はLogixでサウンドwavファイルを直接指定するだけで、特に問題なく音を鳴らすことが出来ました。
一方で、発車メロディーはplay/stopを制御したり、ループさせたりしたいのですが、これをやるにはサウンドファイルを登録してやれねばなりません。

オーディオ.png

JMRI PanelProでポイントやセンサーなどと同じようにAudioから設定することが出来ます。
Audioでは3つのたぶがあります。一つ目はほとんどデフォルトのままで、使えると思います。(何をしたか覚えてません。)
2つめでAudio Buffer3つめでAudio Sourcesとなってますが、やってみないとよく分からないと思います。
Audio Bufferの方で、wavファイルを登録します。jmriで扱えるのはwav形式のみです。
Audio Sourcesは実際に鳴らすパラメーターです。ループの位置やゲイン、ベロシティーなどいろいろ設定できます。踏切の音などでは遠くで鳴っているようなエフェクトをつけられるように思いますが、ここでは何も設定していません。Audio Bufferとの関連づけだけです。

登録をしておけば、
発車メロディー.png
このようなLogixでAudio SourcesのIDを指定してやるとPlay/Stopをセンサーのオン・オフに合わせて切り替えられる、オンのままだとループするようになり、発車メロディーっぽく鳴らせます。

現状では5列車を同時に走らせてますので、結構やかましく、何を言っているのか分からないように鳴っちゃってますけど。。。
タグ:DCC

発車メロディー [DCC]

発車メロディーを流すLogixを紹介します。

発車メロディー.png

出発許可のためのセンサーボタンの脇にある小さなセンサーボタン。
これはオン/オフすることにより、発車メロディーをならすようにしています。
子供が、結構な聞き鉄さん、なんです。

トリガーは「アクティブになったとき」のみです。
真ん中のラジオボタン、トリガーの状態が変化したときのみ動作が重要です。

動作は
(1)トリガーが真になったときIAS4として登録されているサウンドをプレー。
サウンドの登録については次回にご紹介します。
(2)トリガーが偽になったときにサウンドストップ。
(3)トリガーが偽になったときに「4番線ドアーが閉まります。ご注意ください。」
(4)と(5)は自動運転をするときの調節用です。(出発信号機が青現示で列車がすぐに動き出してしまうのを防ぐために、発車ベルが鳴っている間発車を抑止するためのセンサーです。)

(1)と(3)でサウンド再生の仕方が異なります。(3)はwavファイルを直接再生、(1)は登録したサウンドを再生。(1)の方法ですとサウンドをループさせたり、途中で止めることが出来ます。

真ん中の駅は、土浦駅を意識して、かつて使われていた発車メロディーを使っています。上りは「きらきら星」、下りは「ロンド」。東駅(左側)はひたちの牛久駅のもので、上りと下り。

レイアウトで音が鳴ると、結構楽しいですよ。(PCのスピーカーから音が出ますけど。。。)

タグ:DCC

出発の指令Logix [DCC]

出発するときのLogixを紹介します。

出発2.png

図の青四角で囲まれたセンサー(ボタン)が出発信号機のプロテクトセンサーになっています。
これは通常のsimple signal logicとは別に追加したセンサーで、こいつがアクティブなときは例え前方のポイントが開通していて閉塞も空いていても赤現示のままとなります。

こいつをクリックしてアクティブ/イナクティブをトグルすることが出来ます。
今22番の列車が止まっている3番線の場合、左側の赤のボタンをクリックしてイナクティブにしてやれば、(条件R1)前方の信号機が反位の方に転換します。さらに前方が空いていれば出発信号機が(この場合は黄現示にかわるというわけです。
通常はこれで出発信号機が青現示になり、運転士が確認し出発進行となります。

さらに安全のために、たとえば後続の通過列車が4番線を通過しようとしているときに、3番線の出発信号機が切り替わらないようにしています。R2は4番線に既に出発指令が出ていないこと、R3は構造にの通過列車が接近していないこと、の2つの条件をアンドで追加しております。

R3のセンサーは、ID20以下の優等列車が前駅を出て、当駅を過ぎ去るまでアクティブになっているセンサーです。こいつがイナクティブでないと当駅に停車中の列車は出発できません。

出発1.png

これは指令が後続の列車を見落として、先行の列車を誤って発車させてしまい、スタックしてしまうことが多かったので追加設定したものです。最初から条件を考えて作ればもう少しスマートに作れるかと思いますが、その場の対策で追加追加を繰り返しているので、回りくどくなっているかもしれません。

(現在、ダイヤを組んでの自動運転になっていますが、今のところこのままで大きな支障はありません。)
タグ:DCC

この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。