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

# プラレールの自動制御

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

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

# インフラ編
## 配線図

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

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/224979/15bc3c41-1414-9b54-3cce-c257a2e8bbde.png)

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

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

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/224979/3894fa7f-08a3-32dc-bffc-f892cf8d8c90.png)

## ポイント

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

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

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

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

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

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/224979/667a3571-69f6-5d8b-ad5e-93ec494573d6.png)

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

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

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

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

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/224979/76ee2f5a-a7dc-8e20-efef-6d822b3809cb.png)

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

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/224979/5e2c5324-16bc-6b9a-ea7b-ff2c9947b930.png)


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

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/224979/706bdf2c-8ab5-1e74-c772-1357a27eafab.png)

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

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/224979/dcfd1ab5-7bed-aa51-4d34-21e89f25910a.png)

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

### 編成長の識別
光センサが２つ同時に隠れるか否かにより、２両編成か３両編成かを判定しています。

２両編成の場合
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/224979/a0fed322-1d53-7ecd-44b1-44200dea3f37.png)

３両編成の場合
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/224979/eeaae7a3-8746-71da-424a-0cde453778c5.png)


## Raspberry Pi

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

- ポイント用サーボモータ：３個
- ストップレール用サーボモータ：２個
- 光センサ：４個

接続すると、このようになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/224979/68250542-d40c-688c-8bcb-6489b8e19ac1.png)


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

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

# ソフトウェア編

続いて、ソフトウェア編です。
- [光センサーCdSセルでLEDをコントロールする（RaspberryPi3）](https://hombre-nuevo.com/microcomputer/raspberrypi/raspberrypi0041/)
- [wiringPi: Access GPIO pins on Raspberry Pi via wiringPi library](https://hackage.haskell.org/package/wiringPi)
- [Raspberry Piでサーボモーターを回す](https://qiita.com/undo0530/items/51cc446eefec6c91c26d)

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

```py
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](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/224979/368168b2-3df0-46a4-41ee-cbee0ce821fb.png)

また、光センサは下図のwiringpiピン番号に配線されています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/224979/eccfcdb6-0063-d504-944e-9fab5d3ce877.png)

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

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

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

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

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

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

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

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

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

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