Skip to content

Latest commit

 

History

History
192 lines (107 loc) · 29.7 KB

algorithm.md

File metadata and controls

192 lines (107 loc) · 29.7 KB

bve-autopilot アルゴリズム解説

この文書は bve-autopilot プラグインのアルゴリズム設計の解説です。開発者が本プラグインのコードを読解するための手掛かりとなることを意図しています。また、他の TASC/ATO プラグインの開発をしようとする人にとっても参考になるでしょう (ただし全てを自分で成し遂げたい人にとってはネタバレになるのでお帰りください)。

ここでは単位の変換や等加速度運動の計算式に関する一通りの理解を前提としますのでそれらがおぼつかない人は先に勉強してから来てください。

TASC

減速度を一定に保つこと

TASC の目標は、ブレーキを制御して列車を所定の停止位置にできるだけ正確に止めることです。副次的な目標としては、できるだけブレーキを滑らかにかつ緩やかに制御するということが挙げられるかもしれません。この目標を達成するために最も重要な秘訣は、ブレーキの減速度をなるべく一定に保つことです。以下にその理由と方法を順に述べます。

BVE の車両プラグインは車両の減速度を自由に変化させることはできず、あくまでもブレーキ指令のノッチを BVE 本体に伝えることができるだけです。プラグインがブレーキ指令を変更しても、ブレーキの減速度が実際に変化するまでには多少の時間差があります。またブレーキシリンダーの圧力やモーターの電流が一瞬でパッと変化するということはないので、減速度も多少の時間をかけてなだらかに変化します。もしプラグインが減速度を特定のタイミングで変化させようとする場合、このようなブレーキの応答の遅れを見越して早めにブレーキ指令を変化させるということをする必要がありますが、そのような計算を行うことはとても難しいのが現実です。特に、ブレーキ指令を変化させたあと減速度が変化する前に次のブレーキ指令を計算しなければならないという事実が状況を非常に複雑にしています。したがって、減速度を特定のタイミングで変化させようとしてもタイミングがずれてしまうことが多く、停止直前にこれが起きると停止位置がずれる要因になります。つまり、減速度が変化するタイミングがずれると困るような変化のさせ方をしてはいけないのです。

逆に考えると、減速度が変化するタイミングが多少前後しても問題ないやり方なら減速度をうまく変化させることができます。そのもっとも単純なやり方は「目標となる減速度を決めて、その減速度に徐々に近付くように減速度を変化させてゆく」ことです。減速度を一気に変化させるのではなくある程度の時間をかけて変化させるので、減速度を変化させるタイミングを気にする必要がなくなります。

このやり方で実際に列車を止めるには、等減速度運動による減速パターンを目標とします。以下のような基準でブレーキ指令を計算します:

  • 列車の現在位置・速度が減速パターンに一致しているなら、そのまま減速パターンの減速度で減速し続けるようにします。
  • 列車が減速パターンを超えている (速度が出過ぎている) なら減速パターンの減速度より強く減速し、列車の動きが減速パターンに戻るように仕向けます。
  • 列車が減速パターンよりゆっくり走っているならば、減速パターンの減速度より弱く減速し、列車の動きが減速パターンに近付くように仕向けます。

ここで、減速パターンとずれているときにどれほど減速度を強く (あるいは弱く) するかを計算するにあたっては、列車が減速パターンに徐々に近付くように仕向けなくてはいけません。また、どのみち列車の動きには多かれ少なかれ誤差が出るので、減速パターンから外れたときに減速パターンに戻るように仕向ける必要もあります。

bve-autopilot プラグインでは、以下の式を基本の考え方としています。

  • 出力減速度 = 期待減速度 - (期待速度 - 現在速度) / 2秒

ただし

  • 出力減速度 = プラグインが BVE 本体に伝えるブレーキ指令の減速度
  • 期待減速度 = 目標とする減速パターンの減速度
  • 期待速度 = 現在位置において減速パターンに沿って減速していた場合の理想的な速度

です。この式は、単純に説明すれば「2 秒後に列車が減速パターンに到達するような減速度をブレーキ指令として出力する」という意味になります。ただし、期待速度と現在速度は刻々と変化しその度に出力減速度はこの式で再計算されることに注意してください。常にその時から 2 秒後に減速パターンに到達するように仕向けることで、減速パターンに近付くほど減速パターンへ近付く速さが緩やかになり、減速パターンに徐々に近付く効果が得られます。

bve-autopilot プラグインで実際に使っている式は、上記の式に少し手を加えて

  • 出力減速度 = 期待減速度 * (現在速度 / 期待速度) - (期待速度 - 現在速度) / 2秒

となっています。この変形の理由はソースコード内にコメントしてありますが、どのみち (現在速度 / 期待速度) の値はおおよそ 1 なので、あまり気にしなくて構いません。

停止直前の挙動

列車が停止する直前は上の計算式をそのまま使うと急激に出力減速度が変化することがあるので、別途対策が必要となります。

どうして出力減速度が急激に変化するのか見てみましょう。期待減速度が 2.50 km/h/s で停止位置まで残り 0.05 m だとすると、その位置での期待速度は約 0.95 km/h となります。現在速度はこれを少し上回る 1.2 km/h だとしましょう。この時、上に式に従うと出力減速度は約 3.29 km/h/s と計算されます。では 3.29 km/h/s でここから 0.15 秒間減速し続けたとします (実際にはブレーキ応答の遅れやその他もろもろの誤差の影響でそのような理想的な動きにはなりませんが、ここでは計算を分かりやすくするためにそういうことにしておきます)。そうすると列車は 0.15 秒間で停止位置まで残り約 0.01 m のところまで進み、現在速度は約 0.71 km/h となります。ここでもう一度期待速度と出力減速度を計算すると、それぞれ約 0.43 km/s と約 4.25 km/h/s となります。わずか 0.15 秒の間に、出力減速度が約 1.3 倍に上昇 (3.29 km/h/s → 4.25 km/h/s) してしまいました。実際の運転でこのようなことをすれば、列車が停止する寸前に突然急ブレーキがかかることになります。

そもそも上の出力減速度を計算する式は、出力減速度が段々と期待減速度に近付いてゆくことを意図していたはずです。それなのに、最初 3.29 km/h/s だった出力減速度は期待減速度 2.50 km/h/s に近付くように減少するどころかむしろ 4.25 km/h/s に上昇してしまっています。なぜうまくいかないのでしょうか?

出力減速度の計算式の意味をもう一度考え直してみましょう。この式は、「2 秒後に列車が減速パターンに到達するような減速度をブレーキ指令として出力する」ものでした。では、今回の場合の 2 秒後とは一体何でしょうか。列車は停止位置まで残り 0.05 m まで迫っており、速度は 1.2 km/h まで落ちています。ここから 2 秒後、列車はもう停止してしまっています! 上の計算式は減速度を変化させてゆくのに十分な時間の余裕があるときにはうまく機能しますが、時間がないときにそれを何とかする力はないのです。

この問題に対する解決策として、bve-autopilot プラグインでは上の式の他に以下の式を計算に組み込んでいます。

  • 列車が減速パターンを超えている (速度が出過ぎている) とき → その時点の列車の位置と停止目標位置の中点を求め、その中点で減速パターンに戻るような減速度を出力します。これで、停止位置を行き過ぎそうになるのをある程度防ぎます。
  • 列車が減速パターンよりゆっくり走っているとき → その時点の位置と速度から単純に等減速度運動で減速して行く場合の減速度と上の計算式の出力減速度を比較し、弱い方の減速度を出力します。これで、不必要に手前に止まってしまうことを防ぎます。

行き過ぎと衝撃緩和

また、上の計算式は停止位置を行き過ぎたときのことを何も考えていないという点も見逃せません。等減速度運動をベースにした計算では、停止位置をわずかでも行き過ぎると期待速度が存在しなくなるので出力減速度を計算することができなくなります。絶対に停止位置を行き過ぎないようにするということは不可能なので、行き過ぎたときにブレーキをどのように制御するかはこれまでとは全く別の方法で考える必要があります。

bve-autopilot プラグインでは、停止直前に (停止位置とは無関係に、列車の速度のみに基づいて) ブレーキをわざと弱めるようにしています。これは停止時の衝撃を緩和するのが主目的ですが、停止位置を行き過ぎたときにブレーキをどうするかいろいろ考えなくて済むというメリットもあります。やり方としては、減速度の変化が一定の速度となるように (つまり列車の動きが等加加速度運動となるように) ブレーキを緩めています。

ただし、実物の TASC では停止直前にブレーキを弱めるのは私の知る限り行われていないようです。実物の動作に近付けたい場合は、停止直前は残り距離に関係なくブレーキ力を一定に保つとそれらしい動作になるのではないかと思われます。

不連続な計算式を避けること

一つの値を求めるために複数の計算式を組み合わせる場合は、値の不連続な変化を避けることが重要です。例えば以下の擬似コードは出力減速度が不連続に変化する悪い例です:

if (現在速度 < 5 km/h)
    出力減速度 = 現在速度 * 現在速度 / 2 / 残り距離
else
    出力減速度 = 期待減速度 * (現在速度 / 期待速度) - (期待速度 - 現在速度) / 2秒

このコード例だと、現在速度が 5 km/h 以上から 5 km/h 未満に変わる瞬間に出力減速度の計算式が全く異なるものに変わるので、その瞬間に出力減速度の値がガクッと急激に変化する可能性があります。既に述べたように、減速度を急激に変化させようとしてもブレーキの応答の遅れが災いして車両の挙動は安定しません。したがって、このような急激な (不連続な) 変化は避ける必要があります。

If-else 文で二つ以上の式からどれか一つの式の値を選ぶようなアルゴリズムにすると不連続な変化が発生しやすくなります。bve-autopilot プラグインでは max, min 関数で複数の式から値を選ぶようにして連続性を保っています。上のコード例は以下のようになります:

減速度a = 現在速度 * 現在速度 / 2 / 残り距離
減速度b = 期待減速度 * (現在速度 / 期待速度) - (期待速度 - 現在速度) / 2秒
出力減速度 = min(減速度a, 減速度b)

低速からの停止

ここまでの話では期待減速度をどのようにして決めるか特に触れませんでした。初速が比較的高い場合は 2.5 km/h/s とか 3.0 km/h/s とか適当に決めればよいでしょう。しかし初速が低い場合 (約 10 km/h 以下) は期待減速度も小さくする必要があります。初速が低いのに大きな期待減速度で減速しようとすると、減速度を期待減速度に徐々に近付けてゆく十分な時間の余裕がなく、減速度を急激に変化させざるを得なくなり停止位置がずれやすくなります。bve-autopilot プラグインでは、初速を 8 秒で割ったものを期待減速度の上限としています。例えば現在速度が 10 km/h ならば期待減速度は 10 km/h ÷ 8 秒 = 1.25 km/h/s となります。

ATO

ATC 路線

ATC 路線向けに ATO を実装する場合、ATC そのものを実装する必要はありませんが結局は ATC に近い何かを実装することになります。基本的な動作は以下のようになるでしょう。

  • 列車の速度が ATC の信号が示している制限速度よりだいぶ低いなら、制限速度に近付くまで力行する。
  • 列車の速度が制限速度に近付いたら惰行する。
  • 列車の速度が制限速度を超え(そうになっ)たらブレーキをかける。
  • 列車が駅に近付いたら TASC を使用して停車する。

おまけとして、ATC の前方予告機能によりそろそろ制限速度が下がることが分かっている場合は早めに力行をやめたりブレーキをかけたりしても良いでしょう。この場合は、プラグインの動作としては前方の信号の状態を受信するために路線に設置された地上子から情報を受け取ることになります。

bve-autopilot プラグインでは、後述の非 ATC 路線への対応や TASC とのアルゴリズム共通化も考慮して減速パターンの実装を一般化してあります。単なる TASC の減速パターンは特定の地点に列車を停止させる等減速度運動ですが、ATO では所定の制限速度まで速度を落とすことが目標となるので、減速パターンの目標速度を 0 km/h だけでなく自由に設定できるようにしておくことが必要です。また、制限速度まで減速したときに一気にブレーキを緩めてもすぐに減速は終わらず不必要に速度が下がりすぎてしまうので、目標速度に近付くにつれて減速度が弱くなるようにしています。より具体的に言うと、最初は TASC と同様に等減速度運動で減速しますが、途中から等加加速度運動に移行し最終的にはだいぶ弱い減速度で減速を終えるようにしています。ATO では TASC の停車と違って減速が終わる地点の正確性はあまり求められないので、減速度を途中で変化させていっても問題はありません。(速度オーバーを避けるために、少し早めに減速が完了するように目標位置をやや手前にしておきます。)

非 ATC 路線

実物の ATO は ATC をベースに作られますが、bve-autopilot プラグインでは ATC を使用していない路線でも ATO を使用できるようにしてあります。この場合、列車を走らせる速度を ATC とは別のやり方で決める必要が出てきます。

まず、カーブや分岐器を通過するときに速度を落とす必要があるかもしれません。ATC では信号の制限速度に従っていればそれだけでよかったのですが、非 ATC 路線では基本的に運転士が一つ一つの制限速度を頭で覚えて (または線路脇の標識に書いてある制限速度を見て) 速度を調節することになっています。BVE 本体からプラグインに直接制限速度の情報を渡す仕組みは無いので、bve-autopilot プラグインでは路線に専用の地上子を設置してそこから制限速度の情報を受け取るようになっています。もちろん、減速が間に合うように地上子は十分手前に設置されている必要があります。

非 ATC 路線でのもう一つの課題は、信号を守ることです。ATC では信号の指示する速度が下がってからそれに合わせてブレーキをかければよかった (つまり一時的に速度オーバーとなることが許されている) のですが、非 ATC 路線では信号のある位置に列車が到達する前に信号の指示する速度まで列車を減速させなくてはいけません。

一方、プラグインが受け取れる信号の状態は以下の二つに限られています。

まず、プラグインは列車が今走っている閉塞の信号の状態を BVE 本体から教えてもらうことができます。走行中に現在閉塞の信号の状態が変わった時は、リアルタイムで新しい状態を BVE 本体から受け取ることもできます。これは ATC を実装しようとするときには役立つのですが、非 ATC 路線において知りたいのは現在位置よりも先にある閉塞の信号の状態なので、この情報を直接役立てることはできません。

プラグインが受け取れるもう一つの情報は、路線に設置された地上子から送られてくる情報です。これは以下のように動作します:

  • 列車が地上子のある位置を通過するときに、その地上子と対応させられている閉塞までの残り距離とその閉塞の信号の状態を受け取れます。

これはかなり制約の多い仕様です。まず、情報が送られてくるのは地上子を通過したときだけなので、情報が欲しいときに列車が地上子を通過するように路線データ内の地上子の位置を調整しておく必要があります。また、情報を受け取った後に信号が変わってもリアルタイムで新しい情報を受け取ることができないので、途中で信号が変わりそうなケースでは信号が変わった後に情報を受け取れるように地上子を増やす必要があります。とはいえ、地上子から情報を受け取れればどの地点までにどれだけ速度を落とせばよいかが分かるので、ATO を実装することは可能となります。

先にある閉塞の信号の状態を直接リアルタイムで受け取ることはできないものの、現在位置の閉塞の信号の状態はリアルタイムに受け取れるので、そこからある程度推測することは可能です。bve-autopilot プラグインでは以下のような仕組みを実装しています。

  1. 現在の閉塞の信号の状態から、先行列車が何閉塞先にいるかを計算する。
  2. 先行列車のいる閉塞が分かったら、そこから前方の信号の状態を計算する。

これを行うには、「先行列車が何閉塞先にいる時にその閉塞の信号の状態が何になるか」の対応表が必要です。残念ながらこの対応は BVE 本体から直接受け取ることはできないので、路線データ内に専用の地上子を追加してプラグインに対応を教えてやる必要があります。

発展的なブレーキ制御

ここからは、より自然で正確なブレーキングを行うための工夫の話です。

減速度からノッチへの変換

ここまではプラグインがブレーキ指令として出力する減速度をどのように計算するかを説明してきました。しかし、実際にプラグインが BVE 本体に対して出力するのは何段かに区切られたブレーキのノッチの強さです。従って、減速度をノッチにどのように変換するかを考えなくてはいけません。

bve-autopilot プラグインのデフォルトの動作は以下の性質を仮定して減速度をノッチに変換しています。

  • 常用ブレーキの最大の減速度は 3 km/h/s とする。
  • 各ノッチごとにブレーキの強さは等差で強くなってゆく。(例えば B6 ノッチは B3 ノッチの 2 倍の減速度になる)

これで、TASC/ATO として一応最低限の動作はするようになります。

最大減速度が 3 km/h/s というのは大抵の車両においては実際よりも小さすぎます (普通の車両の最大減速度は 3.5 から 4.0 km/h/s の辺りが多いでしょう) ので、上記の仮定による計算ではプラグインが出力したい減速度よりも強いブレーキのノッチが選択されてしまいます。とはいえ想定よりも強いブレーキがかかって速度が落ちすぎた場合はブレーキを弱めるような計算式を組んでいるのですから、大きな問題はありません。弱いブレーキでそのまま減速してゆき、最終的にきちんと止まってくれます。強いて言えば、プラグインが計算に使用している期待減速度よりも弱いブレーキでの減速が続くので、ブレーキをかけ始めてから停止するまでの時間が長くなります。これが気になるのであれば、設定ファイルから本当の最大減速度を設定することでより正確なブレーキ調整ができます。

空気ブレーキと電気ブレーキ

一般的に電車では空気ブレーキと電気ブレーキの二種類のブレーキが使われています。(車種によっては空気ブレーキだけ搭載している場合もありますが。)

空気ブレーキは、圧縮空気の圧力を利用して摩擦で車輪の回転を止める仕組みのブレーキです。空気ブレーキの減速度は制輪子が車輪を押さえ付ける力に比例し、その力はブレーキシリンダーに込められた空気の圧力に比例します。ブレーキシリンダーの圧力はブレーキ指令の圧力に (おおよそ) 比例します。ブレーキシリンダー圧力は BVE 本体からプラグインに随時伝えられます。

電気ブレーキは、モーターで運動エネルギーを電気エネルギーに変換することで車両を減速させる仕組みのブレーキです。一般論としては、列車の速度が遅くなるにつれて電気エネルギーに変換できる運動エネルギーが減っていって減速力が落ちるので、最終的に列車を止めるには空気ブレーキが必要です。BVE では電気ブレーキの減速力とモーターの電流はそれぞれ速度の関数として車両データ側で定義されます。一般に減速力は電流と電圧の積に比例すると考えられますが、車両データが実際にそのようになっているかはプラグインからは分かりません。

ブレーキの利き具合への追随

実際のブレーキの減速度に応じてブレーキ指令を調節することは可能でしょうか。それができれば、設定ファイルでブレーキの最大減速度を設定しておく必要もなくなるはずです。結論を先に言うと、bve-autopilot プラグインでは空気ブレーキが動作しているときのみ調節を行っています。多くの車両では空気ブレーキと電気ブレーキを併用しているので、調節はほとんど行われません。空気ブレーキのみを搭載している車両でブレーキをかける場合や、速度が落ちて空気ブレーキしか働かなくなった場合にだけ調節を行います。このため設定ファイルで減速度を設定する機能も併用しています。

電気ブレーキが働いている間にブレーキの減速度とブレーキ指令の関係を推定するのは至難の業です。プラグインが把握できる情報は実際の減速度とモーターの電流量くらいしかありません。しかし減速度と電流の関係は車両データ側で自由に設定でき、単純な比例関係になるとは限らないので、減速度と電流の関係性はプラグインからは推測できません。またブレーキ指令のノッチと電流の関係もプラグインからは分かりません。さらに、電気ブレーキが抵抗制御方式の場合は電流と減速度が波を打つように目まぐるしく変わりますが、そのような変化をプラグイン内で予想してブレーキ指令を調節することは全く現実的ではありません。

したがって、bve-autopilot プラグインでは空気ブレーキのみが動作しているときに限ってブレーキ指令を調節することにしました。具体的には以下のようにして計算を行います。

  1. 実際の減速度とブレーキシリンダー圧力の比を求める。
  2. ブレーキシリンダー圧力とブレーキ指令のノッチの比を求める。
  3. 上の二つの比をかけて減速度とブレーキ指令ノッチの比を出す。

ブレーキシリンダーの圧力変化は減速度を直ちに変化させるので、減速度とブレーキシリンダー圧力の比は常時リアルタイムで計算・更新して構いません (ただしブレーキを一切かけていないときを除く)。一方、ブレーキ指令のノッチを変化させてもブレーキシリンダー圧力が変化するまで時間差があるので、ブレーキ指令ノッチを変化させた直後 (ブレーキシリンダー圧力の変化が終わるまで) はブレーキシリンダー圧力とブレーキ指令ノッチの比の計算結果に反映しないようにします。

なお、ブレーキ指令のノッチが同じでも乗客の乗車率によって減速度とブレーキシリンダー圧力との比は変化します (また応荷重制御が働くとブレーキシリンダー圧力自体も変わります) ので、駅停車後にまで計算結果を持ち越すのは注意が必要です。

抑速ブレーキ

bve-autopilot プラグインでは、抑速ブレーキは一切使っていません。既に述べたように、期待速度と現在速度とのずれに応じてブレーキを調節する計算式を使っているので、それの他に速度を維持するための仕組みは要りません。ただし、抑速ブレーキを使う方が下り勾配で速度がより自然に維持されるという向きもあるので将来的に採用する可能性はあります。

Pressure rates

bve-autopilot プラグインではデフォルトで「各ノッチごとにブレーキの強さは等差で強くなってゆく」という仮定を置いていますが、車両データによってはこれは正しくありません。各ノッチのブレーキの強さは車両パラメーターファイルPressureRates という設定値で決められます。値が等差数列になっていない場合、プラグインがかけようとしているブレーキの減速度と実際の減速度がずれてしまいます。

このずれは多くの場合無視できる程度の誤差ですが、PressureRates に従ってブレーキのノッチを選択すればより正確な減速度が得られます。プラグインから PressureRates の値を特定するのは容易ではないので、bve-autopilot プラグインでは設定ファイルに PressureRates の値を書くことでこれを利用できるようになっています。

拡張ブレーキ指令

運転士が手動で操作する常用ブレーキのノッチ段数は一般的な車両では 5~8 段ですが、実物の TASC/ATO では 15 段とか 31 段とかのより多くのノッチ段数が使用されている場合があります。bve-autopilot プラグインでは、拡張ブレーキ指令の機能を使用すると手動のブレーキのノッチ段数を変えないまま TASC/ATO のブレーキ段数を増やすことができます。

車両パラメーターファイルで PressureRates に設定する各ノッチの減速力の数列において、非常ブレーキのノッチの後にさらに数字を定義すると、手動のブレーキ操作では使用できないノッチを作り出すことができます。プラグインから BVE 本体に送るブレーキ指令でそのノッチを選択することでそれを動作させることができます。減速力を小刻みに変化させながらたくさんのノッチを定義することで、実物の TASC/ATO のように細かなブレーキの制御ができるようになります。

なお、拡張ブレーキ指令でノッチ段数を増やすことでブレーキの強さの変化がより滑らかになり乗り心地が多少向上しますが、TASC の停止位置の精度を向上させる効果はほとんどありません。(まともな性能特性の車両であればノッチが 7 段程度でも十分正確な位置に止まるはずです)

勾配

別ページに勾配に関する計算の解説があります。