LoginSignup
45
26

More than 3 years have passed since last update.

ラズパイによるプラレール自動制御

Last updated at Posted at 2019-12-26

プラレールの自動制御

大学生にもなって、文化祭で鉄道研究部の展示を見てからというもの、プラレールにはまっています。

既に多くの方がプラレールの自動制御を行っていますが、今回は、Raspberry Pi による自動制御で、
「通過待ち」や「行先分類」などを行ってみます。

インフラ編

配線図

これはかなり悩みました。部屋の広さと相談し、今回は下の図のような配線図にしました。

image.png

複線のレイアウトです。
Sと書かれている個所は、ストップレールです。
車両を一時停止させることができます。

実際のレールで作ると、このようになります。

image.png

ポイント

ポイントの種類

今回、ポイントを3種類に分類しました。

  • サーボモータ付きポイント
    今回の自動制御において、中心となるサーボモータ付きのポイントです。Raspberry Pi から自由自在に制御することができます。

  • スプリングポイント(発条転轍器)(輪ゴムで方向を固定したポイントのことです。)
    必ず決められた方向に列車を導くポイントです。直前にどの方向から列車が来たかに関わらず、決められた方向に列車を誘導します。

  • 元々のポイント
    手を一切加えていないポイントです。分岐線の合流箇所で使用しています。

先ほどの配線図の各ポイントを次のように分類します。

image.png

赤:サーボモータ付きポイント
緑:スプリングポイント
青:元々のポイント

なにも印のついてないポイントは、サーボモータの個数の都合上、今回は見送りました。

輪ゴム・サーボモータの取り付け

ポイントに、輪ゴム・サーボモータを取り付けます。
下の画像のように、ポイントレールの裏面に輪ゴムを取り付けることで、進行方向を常に固定することができました。

image.png

さらに、反対側に引っ張るための糸を取り付け、サーボモータで引っ張ることで、進行方向を変更できるようになります。

image.png

ストップレール

続いて、ストップレールです。
ストップレールの黄色いレバーの部分に、サーボモータをセロハンテープで固定しました。

image.png

光センサ

列車の存在確認及び、列車の編成長の識別のため、光センサとしてCdSセルを使用しています。

image.png

レール接続部分に裏側から光センサを取り付けることで、レールを加工せずとも光センサを取り付けることができました。

編成長の識別

光センサが2つ同時に隠れるか否かにより、2両編成か3両編成かを判定しています。

2両編成の場合
image.png

3両編成の場合
image.png

Raspberry Pi

ここまでに作成した下記のものを接続しました。

  • ポイント用サーボモータ:3個
  • ストップレール用サーボモータ:2個
  • 光センサ:4個

接続すると、このようになります。
image.png

モータや抵抗の接続は、ソフトウェア編に記載した記事を参考に行いました。
もう少しきれいに見えるよう、コーディングの技術を上達させたいですね。

どのモータがどのピンに接続されているかは、下記のソフトウェア編に記載します。

ソフトウェア編

続いて、ソフトウェア編です。
- 光センサーCdSセルでLEDをコントロールする(RaspberryPi3)
- wiringPi: Access GPIO pins on Raspberry Pi via wiringPi library
- Raspberry Piでサーボモーターを回す

といった記事を参考に、次のようなプログラムを書きました。

import RPi.GPIO as GPIO
import wiringpi as w
import time

GPIO.setmode(GPIO.BCM)

w.wiringPiSetup()
#w.pipMode(22, 0)
#w.pipMode(23, 0)
#w.pipMode(24, 0)
#w.pipMode(25, 0)

gp_out_2 = 2
gp_out_3 = 3
gp_out_4 = 4
gp_out_17 = 17
gp_out_27 = 27
GPIO.setup(gp_out_2, GPIO.OUT)
GPIO.setup(gp_out_3, GPIO.OUT)
GPIO.setup(gp_out_4, GPIO.OUT)
GPIO.setup(gp_out_17, GPIO.OUT)
GPIO.setup(gp_out_27, GPIO.OUT)

servo_2 = GPIO.PWM(gp_out_2, 50)
servo_3 = GPIO.PWM(gp_out_3, 50)
servo_4 = GPIO.PWM(gp_out_4, 50)
servo_17 = GPIO.PWM(gp_out_17, 50)
servo_27 = GPIO.PWM(gp_out_27, 50)
servo_2.start(0)
servo_3.start(0)
servo_4.start(0)
servo_17.start(0)
servo_27.start(0)

wait=0

servo_2_off = 0
servo_3_off = 0
servo_27_off = 0
servo_4_on = 0
servo_4_off = 0
servo_17_on = 0
servo_27_on = 0
nocount=0
servo_17.ChangeDutyCycle(2.5)

while True:
    print('loop')
    print(w.digitalRead(29))
    print(w.digitalRead(23))
    print(w.digitalRead(24))
    print(w.digitalRead(25))
    time.sleep(0.1)
    servo_2_off=servo_2_off-1
    servo_27_off=servo_27_off-1
    servo_27_on=servo_27_on-1
    servo_4_off=servo_4_off-1
    servo_4_on=servo_4_on-1
    servo_17_on=servo_17_on-1
    servo_3_off=servo_3_off-1
    wait=wait-1
    nocount=nocount-1

    if(servo_2_off==0):
        servo_2.ChangeDutyCycle(7.25)
    if(servo_3_off==0):
        servo_3.ChangeDutyCycle(7.25)
    if(servo_27_off==0):
        servo_27.ChangeDutyCycle(7.25)
    if(servo_27_on==0):
        servo_27.ChangeDutyCycle(2.5)
    if(servo_4_off==0):
        servo_4.ChangeDutyCycle(7.25)
    if(servo_4_on==0):
        servo_4.ChangeDutyCycle(2.5)
    if servo_17_on == 0:
        servo_17.ChangeDutyCycle(2.5)

    if w.digitalRead(24) == 0:
        wait=30
    if w.digitalRead(29) == 0:
        if w.digitalRead(23) == 0:
            print('3 cars')
            nocount=15
            if wait > 0:
                servo_3.ChangeDutyCycle(2.5)
                servo_3_off=30
            else:
                servo_4_on = 70
                servo_4_off = 80
        else:
            if nocount < 0:
                print('2 cars')
                servo_2.ChangeDutyCycle(2.5)
                servo_2_off = 30
                servo_27_on = 20
                servo_27_off = 50
                servo_4_on = 40
                servo_4_off = 60
    if w.digitalRead(25) == 0:
        servo_17.ChangeDutyCycle(7.25)
        servo_17_on = 70


for i in range(1):
    servo_2.ChangeDutyCycle(2.5)
    servo_3.ChangeDutyCycle(2.5)
    servo_4.ChangeDutyCycle(2.5)
    servo_17.ChangeDutyCycle(2.5)
    servo_27.ChangeDutyCycle(2.5)
    time.sleep(5)

#    time.sleep(0.5)
    servo_2.ChangeDutyCycle(7.25)
    servo_3.ChangeDutyCycle(7.25)
    servo_4.ChangeDutyCycle(7.25)
    servo_17.ChangeDutyCycle(7.25)
    servo_27.ChangeDutyCycle(7.25)
    time.sleep(0.5)

#    servo.ChangeDutyCycle(12)
#    time.sleep(0.5)

#    servo.ChangeDutyCycle(7.25)
#    time.sleep(0.5)

servo_2.stop()
GPIO.cleanup()

複数の列車を運行するため、ポイントやストップレールの制御を約0.1秒ごとのclock式としました。

各サーボモータは下図のピン番号のように配線されています。

image.png

また、光センサは下図のwiringpiピン番号に配線されています。
image.png

なお、これらのセンサは次のような働きをしています。

番号 役割
24 通過待ちをするか否かの判定
23 3両編成か2両編成かの判定
29 列車接近検知
25 合流箇所における列車接近の検知

判定基準

2両編成の場合

最も早い車両を2両編成にしているため、2両編成の場合には通過待ちはありません。
29番センサに列車が検知された瞬間に、23番センサに列車が検知されていない場合は、2両編成です。
2両編成の場合は、問答無用に退避せず、分岐を右側に曲がるように誘導します。

3両編成通過待ちなし

29番センサに列車が検知された瞬間に、23番センサでも列車が検知された場合は、3両編成です。最後に24番センサを列車が通過してから3秒以上経過していれば、通過待ちはしません。ここで判定基準を3秒としているのは、3秒以上前の場合は、自分自身の通貨である可能性があるからです。ポイントの状態は変えず、列車を直進させます。また、通過待ち中の列車がいる可能性を考慮し、3秒後にストップレールを「進む」とし、

3両編成通過待ちあり

29番センサに列車が検知された瞬間に、23番センサでも列車が検知された場合は、3両編成です。最後に24番センサを列車が通過してから3秒以内であれば、通過待ちをするようにします。

走行動画

これらがどのような動きをするか、YouTubeに動画をアップしましたので、よろしければご覧ください。

2編成の通過待ち及び行先分岐
https://youtu.be/2Cd-xgwB5wY

3編成の通過待ち及び行先分岐
https://youtu.be/wQ6l39p_Mcw
※3編成だと、現状の設備・プログラムだと、しばらくたつと追突してしまいます。もう少し信号設備を増やすか、プログラムを改良する必要がありそうです。

今後の展望

  • プラレール車体へのカメラの搭載
  • 信号装置の追加
  • 単線入れ替えの導入
  • カメラ画像による識別
  • 行先表示パネルの設置

などができればより面白くなるかなと思います。
まあ、部屋の広さ的に、線路の拡張は厳しそうですけど。

45
26
6

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
45
26