ラズパイ&ブレッドボードで遊ぶ(3)~鉄道模型の車速計測

マスコン風のパワーパックを作りたい、ということを最終目標にして、まずは Raspberry Pi Pico でどんなことができるのか、と、ブレッドボードを使っていろいろと遊んでいます。

前回はモータードライバーとを組み合わせて、鉄道模型のレールにPWM制御を行うことで、車両の速度をコントロールするところまでを試してみました。

今回は、目標到達までの道のりに必須ではないんですが、前々から気になっていたことをやってみたいと思います。

それは、既にタイトルで書いていますが、「鉄道模型の車速を測る」です。

どのデバイスで計測するか

そもそも、なぜリアルな速度を計測したかったのかというと、適正なスケールスピードで列車を走らせたかったから。それに尽きます。

PWM波形を生成して車両のモーターに与える電圧を変化させるのに、0km/hから100km/hまで一定の加速度でスピードを上げる制御を、PWMのデューティー比を比例的に上げるだけでいいのか、というのも気になっていました。

そのあたりの検証用のツールとして使用したいと考えています。

それを実現するにあたり、何を用意しないといけないか、です。

最初に考えたのは、赤外線遮断による通過検知のシステムです。送信機側から照射される赤外線が受信機側で検知できなくなると遮断を検知。でも、やたらと大がかりなんですよね。ならば、と、フォトリフレクタの利用も考えてみたんですが、反射させる色によって精度が変わるようで、「使えるのかなぁ」という不安が。

考えていたのは線路脇にフォトリフレクタを配置し、車両の通過を検知させるというもの。2ヶ所に設置して、検知の時間差で速度が割り出せるはず、と。

車両を無加工で行うには最善なのかと思っています。が、反射しない色の車両だと(実際に使用していないので、どの程度「反射しない」のかは不明です)、テスト用車両側面に反射用に白いテープを貼るなどの加工が必要かもしれません。

それくらいの小加工がアリなら、試してみたいと思ったのが「磁気センサ(ホールIC)」です。

磁気センサ/Melexis US1881

Melexis Microelectronic Systems 社製の US1881LUA-AAA-000-BU という品番のもの。

これで何ができるのかというと、写真の上面側に磁石のN極が近付くと出力端子が HI になり、S極が近付くと LO になる、というものです。

この出力、磁気が弱くなってもラッチしてくれる、という優れモノなんですが通過検知には若干使いにくい機構です。

というのも、車両にN極の磁石を積み、線路上に仕込んだ磁気センサを通過したら、初回は L → H に変化があるものの、通過し終わっても(磁気を検知しなくなっても)ラッチしているので H のままです。次回、同じセンサを通過しても、変化が発生しない、ということです。(以下、車両図示の矢印は進行方向を示し、グラフは右側に向かって時間経過を表します)

じゃぁどうするのか、というと、次回計測のためにS極を設けて L にしておく、ということになります。

これで、2ヶ所の磁気センサを、それぞれ立ち上がりエッジでタイマをカウントすれば目的は達成できそうです。

これでOKな感じがしますが、車両が逆向きに走った場合はどうでしょうか。

2回目に逆方向に走ると、立ち上がりエッジが発生しません。

どちらの向きに走行しても立ち上がりエッジが発生するように、車両の外側にS極、内側にN極を置くと、解決です。

が、ここまで考えた時に、S/N/N/Sの順で並べれば、必ず立上がりエッジ、立下りエッジが発生するので、これを利用すれば、センサは2ヶ所設置せずに1ヶ所で済むのでは?と。

S/N、N/Sで必ず両エッジが発生するので、立上りエッジから立下りエッジ間の時間を計測すれば、S/NとN/Sの距離は不変なので速度は確定するはずです。

というわけで、これで行きたいと思います。

磁気センサを使ってみる

実験用に、小さなブレッドボードを使ってみます。

最初の配線 間違ってます

同じブレッドボード上でも全然問題ないんですが、センサの向きを変えたりする必要があるかな、という配慮です。

メーカーサイトから US1881_rev015.pdf をダウンロードし、一部を引用しています。

最初、最左端列のピン番号とName列(Function列)を見て、左からVDD、状態出力、GNDとつないでしまったんですが、これは誤り。見るのは2列目の「UA Pin No」です。なので、左から、VDD、GND、状態出力が正しいです。

肝心のセンサーは写ってませんが上の写真と同様です

こちらが修正後の配線です。

センサ ピン番号機能接続先
1VDD(電源)ブレッドボードの+
2GNDブレッドボードの-(GND)
3出力Raspberry Pi Pico GP22

Raspberry Pi Pico の端子は、GPIOであればどれでも構いません。

GP22にエッジ割り込みを設定して、HIでLED点灯、LOでLED消灯というサンプルプログラムを作ってみました。

import machine
(省略)
led_pin = machine.Pin(19, machine.Pin.OUT)
# 磁気センサー用
sen_pin = machine.Pin(22, machine.Pin.IN, machine.Pin.PULL_UP)
(省略)
# コールバック関数
def edge_callback(pin):
    if sen_pin.value() == 1:
        led_pin.on()
    else:
        led_pin.off()
(省略)
# 割り込み設定
sen_pin.irq(trigger=machine.Pin.IRQ_RISING | machine.Pin.IRQ_FALLING, handler=edge_callback)
(省略)

while True:
    (省略)
    time.sleep(0.1)

全体プログラムからの抜粋ですので、上記だけでは動かないかもしれません(未確認)。

手元に、磁石が無かったので、ダイソーでカラーマグネット15個入りを購入。このプラスチック部分を外して使用します。

とりあえずのお試しという感じで、脇に転がっていたリレーラーに貼り付けてみました(笑)

反応は速いですね。それと、ラッチが効いていることも確認できます。

線路への取り付け

ICそのものは小さくて薄いので、線路上に取り付けられるかな、と考えています。

取り付けるのは、140mmの直線レール。このサイズは、どのようなレイアウトにも取り込みやすいですからね。70mmでもいいかも、です。

計算されたかのように、PC枕木の隙間にフィットしてますね。そこまで考慮して選定しません。。

動画は撮ってませんが、この上を車両を通過させて床下機器と接触しないことも確認してます。すべての車両を確認したわけではありませんが、大丈夫でしょう、きっと。

TOMIXのレールは、D.C.フィーダー取り付け用にレールと道床の間に隙間(差し込み口)が開けられています。D.C.フィーダーは2股ですが、差し込み口は3ヶ所開いているので、3つの端子をそれぞれの口から出してみます。

これをコードにはんだ付けして、レールと接触しそうな部分をコーティング。

コーティングといっても、レールと接触しないようにマスキングテープを貼り付けただけです。

これで、線路側の加工は完了です。

車両側の加工

車両側は、ひとまず最適と思った車両に。

それがこれ。

TOMIXのコキ106です。片側は機関車に牽引できるようTNカプラーに、反対側は、まだほぼすべてのコキ104/106/107がアーノルドカプラーのままなので、それに合わせてます。というか、無加工です。

で、磁石は線路に面した側を、左からS極、N極、N極、S極として載せます。S極/N極は隣り合わせると引っ張り合うので楽ですね。

カッティングシートのメモリは1cmですのでS極/N極、N極/S極の「接合箇所の距離を10cm」にして、マスキングテープで固定しました。

そもそも、円形の磁石で、どのタイミングで検知するのかは厳密には調べてませんが、N極/S極とも同じ形状なので、どちらに走っても接合箇所からズレる距離(タイミング)は同じかな、と。

確認用プログラムでの検証

エッジ割込みが出来ることはLED点灯/消灯で確認できていますので、割り込み処理部分だけ。

# 立上がり→立下りエッジ間の時間を測定するコールバック関数
def edge_callback(pin):
    global start_time
    if sen_pin.value() == 1:
        led_pin.on()
        start_time = utime.ticks_us()
    else:
        led_pin.off()
        end_time = utime.ticks_us()
        edge_duration = (end_time - start_time) / 1000000  # マイクロ秒から秒に変換
        speed = (((10 * 150 ) / ( 100 * 1000)) / edge_duration ) * 3600 # 10cm移動を150倍のスケール換算して速度算出
        print("エッジ間の時間: {:.6f} 秒".format(edge_duration))
        print("換算速度: {:.6f} km/h".format(speed))

では、さっそく。

EF66に牽かせてみます。

赤いコードのひとつ左(枕木間のスペース)にセンサーがありますので、反応速度は良好だと思います。

動画から、LED点灯/消灯の直前直後をキャプチャしてみました。

立上り(N→S)直前
立上り(N→S)直後
立下り(S→N)直前
立下り(S→N)直後

立下り検知で速度が確定しますので、Thonnyのシェル画面には、次のように表示されます。

動画撮影時の記録は残せてなかったので、別のタイミング(もう少し高速で走行しているとき)のものです。

エッジ間の時間: 1.547561 秒
換算速度: 34.893616 km/h

動画撮影時の時間から想像して作成してみますと、こんな感じになったはずです。小数点3桁以降は空想です。

エッジ間の時間: 2.201325 秒
換算速度: 24.530680 km/h

まぁ、そんなもんかぁ、という感じですね。実車(模型)の速さではないですよ。150倍したスケールスピードです。

全長20.4mのコキ106が1両通過するのに3.13秒(動画から計測)かかっていますので、そんな感じです。

動画と対になった記録が残せていなかったのでちょっと残念ですが、視点を車両のそばに落として眺めてみると(その視点から撮影すると)、速度の体感値は概ね一致します。そりゃそうですが。。

見えてきた問題点

今回の目的のひとつはPWMのデューティー比と速度が比例するかを確認したい、というものだったのですが、同じデューティー比でも、速度がバラバラだということが、このツールから見えてきました。

勾配区間は問題外として、車輪に抵抗が増えるカーブ区間をも排除して、全車両が平らな直線を走る区間で速度を計測すれば、デューティー比が同じならほぼ同じような速度が出るのかと思っていたんですが、高速から低速まで、結構バラバラです。

点線はExcelで求めた線形近似線

PWMの設定値を25300(最大65535なので約38.6%)とした場合に、93.05km/hから100.15km/hまでの幅が出ています。実はもっと幅があったんですが、平均値を出すために上側下側ともにカットしました。

ちなみに、これはTOMIXのEF66(7143 JR EF66-0形電気機関車(後期型・特急牽引機・グレー台車))に、試験車両と、他に12ftコンテナ満載のコキ104/106/107の4両で、牽引車両は5両で確認しています。

速度からデューティー比を求めるために、グラフの縦軸・横軸を入れ替えてみます。

全体としては、直線の一次方程式で表されそうな感じですが、このバラつきがネックになりそうですね。

この時点での線形近似式は、 y = 144.59x + 10552

ところが、切片は停止状態(速度は 0km/h)のデューティー比になるんですが、調査時の限界値11450よりも下回っています。これはどういうことかというと、上記の式で速度→デューティー比変換を行うと、実際には、デューティー比を上げていっても、しばらく動き出さない、ということになってしまいます。

でも問題はそれだけではなく、そもそも停止状態から車両が動き出す限界のデューティー比もバラバラ(測るたびに値が変わる)だったんです。

実は、こちらの方が問題は大きく、スロースタート、スローストップの制御を難解にさせています。

停止→発車のデューティー比と、走行→停車のデューティー比が違うので、「停止」の捉え方が崩れ始めています。悩ましいところですね。

というわけで、今回は、鉄道模型の速度を測定するためのツール作成の話でした。

Raspberry Pi Picoなので…

最後に、割り込み処理を作成した際に困ったことを記しておきます。

Pythonで割り込み処理を行おうとすると、このようにする例がよく見られると思います。

import RPi.GPIO as GPIO
(省略)
input_pin = 17
GPIO.setup(input_pin, GPIO.IN)

def edge_callback(channel):
    if GPIO.input(channel) == GPIO.HIGH:
        led_pin.on()
    else:
        led_pin.off()
(省略)
GPIO.add_event_detect(input_pin, GPIO.BOTH, callback=edge_callback)
(省略)

ところが、これを Thonnyで実行しようとすると、

ImportError: no module named 'RPi' 

と、エラーになります。

追加でライブラリのインストールをしないといけないのか、とか、いろいろと考えてみたんですが、結論としては、Raspberry Pi Pico(MicroPython 環境)では RPi.GPIO ライブラリは使えないようです。

なので、先述のような machine を使用したエッジ判定と割り込み処理で実現しています。

この調査に無駄な時間を使ってしまったため、参考になればと記載しておきます。