Nゲージの模型をラズパイで動かす “らずてつ”その4――タイミングを合わせて車両を走らせる:名刺サイズの超小型PC「ラズパイ」で遊ぶ(第55回)
鉄道模型の運転をラズパイで制御する仕組みに挑戦します。
第52回から第53回、第54回とラズパイを使って列車を動かし、ポイントを切り替える方法についてご紹介してきました。次はいよいよ、自動運転の“はじめの一歩”を踏み出してみましょう。
線路の上の模型を往復させる
まずは写真のような線路を組みました。平行に線路が引かれており、その真ん中にポイントを設けて、上の線と下の線を行き来できるようにしてあります。
まずは前後に動かしてみましょう。第53回でも述べたように、列車の進む方向を決めてPWMを動作させればOKです。プログラムとしては以下のようになります。
#!/usr/bin/env python # -*- coding: utf-8 -*- import time import RPi.GPIO as GPIO # GPIOの設定 GPIO.setwarnings(False) GPIO.setmode(GPIO.BCM) motor_r = 20 # 右向き motor_l = 21 # 左向き pwm = 12 # PWM GPIO.setup(motor_r, GPIO.OUT) GPIO.setup(motor_l, GPIO.OUT) GPIO.setup(pwm, GPIO.OUT) pwm_m = GPIO.PWM(pwm, 100) pwm_m.start(0) GPIO.output(motor_r, 0) GPIO.output(motor_l, 0) time.sleep(1) # 右向き pwm_m.ChangeDutyCycle(0) GPIO.output(motor_r, 1) GPIO.output(motor_l, 0) print ("go right") pwm_m.ChangeDutyCycle(30) # ここの「30」を変えると最大速度が変化する time.sleep(3) # ここの数値を変えると走る距離が変化する pwm_m.ChangeDutyCycle(0) # 動作停止 print ("stop") GPIO.output(motor_r, 1) GPIO.output(motor_l, 1) time.sleep(3) # 左向き GPIO.output(motor_r, 0) GPIO.output(motor_l, 1) print ("go left") pwm_m.ChangeDutyCycle(30) # ここの「30」を変えると最大速度が変化する time.sleep(3) # ここの数値を変えると走る距離が変化する pwm_m.ChangeDutyCycle(0) GPIO.output(motor_l, 0) # 動作停止 print ("stop") GPIO.output(motor_r, 1) GPIO.output(motor_l, 1) pwm_m.stop() GPIO.cleanup() jidoutest.py
円周の線路を回っている場合と違うのは、端点があることです。そこにぶつからないように稼働させる時間を調節して動かす必要があります。このため円周の時はデューティー比を40に設定しましたが、少し速すぎるので30に落としています。
上のプログラムを記述したらコンソールから起動させます。
$ python jidoutest.py
模型が左から右へと1往復します。
ポイントを使った移動
続いてポイントを使った移動について進めていきます。基本的な考えは上の往復動作と同じですが、ポイントがあるので、それを切り替えつつ進むようにプログラムを記述する必要があります。そのためにはこれまでと同様に「進む」−「戻る」−「ポイントを切り替え」−「進む」……のように延々とプログラムを記述してもよいのですが、それだとあまりにも膨大な行数となりますし、もしミスがあった場合、どこに原因があるのか分かりにくくなってしまいます。
そこで今回用いたのは「関数」です。右に進む、左に進むといった内容を関数として定義し、実行時にはその関数名だけ並べればよいようにしておくと、ミスがあったときのデバッグも楽になります。
定義した関数は以下の7個です。
- 右に進む
def go_right(): pwm_m.ChangeDutyCycle(0) GPIO.output(motor_r, 1) GPIO.output(motor_l, 0) print ("go right") pwm_m.ChangeDutyCycle(30) # ここの「40」を変えると最大速度が変化する time.sleep(3.5) # ここの数値を変えると走る距離が変化する pwm_m.ChangeDutyCycle(0) return
- 左に進む
def go_left(): GPIO.output(motor_r, 0) GPIO.output(motor_l, 1) print ("go left") pwm_m.ChangeDutyCycle(30) # ここの「40」を変えると最大速度が変化する time.sleep(3.5) # ここの数値を変えると走る距離が変化する pwm_m.ChangeDutyCycle(0) GPIO.output(motor_l, 0) return
- ポイント1をカーブ側に
def point1_curve(): GPIO.output(straight_1,0) GPIO.output(curve_1,1) print("Point1 Curve") pwm1.ChangeDutyCycle(100) time.sleep(0.05) pwm1.ChangeDutyCycle(0) return
- ポイント2をカーブ側に
def point2_curve(): GPIO.output(straight_2,0) GPIO.output(curve_2,1) print("Point2 Curve") pwm2.ChangeDutyCycle(100) time.sleep(0.05) pwm2.ChangeDutyCycle(0) return
- ポイント1を直線側に
def point1_straigh(): GPIO.output(straight_1,1) GPIO.output(curve_1,0) print("Point1 Straight") pwm1.ChangeDutyCycle(100) time.sleep(0.05) pwm1.ChangeDutyCycle(0) return
- ポイント2を直線側に
def point2_straight(): GPIO.output(straight_2,1) GPIO.output(curve_2,0) print("Point2 Straight") pwm2.ChangeDutyCycle(100) time.sleep(0.05) pwm2.ChangeDutyCycle(0) return
- 停止する
def stop(): print ("stop") GPIO.output(motor_r, 1) GPIO.output(motor_l, 1) pwm_m.stop() return
これを踏まえたプログラムが以下のようになります。右から左へスタートした模型が一度止まり、ポイントを切り替えて下の線路に移動。下の線路で左に行って右に戻ったらポイントを切り替えて上の線路へ。そこから右に走ってスタート地点に戻る、という動きにしました。なおポイントについては2つとも同じ動作となりますので、まとめてしまってもよいでしょう。このほか脱線や、プログラムを途中で止めたりした場合に備えて、ポイントをストレートに切り替える動作を最初に入れています。
#!/usr/bin/env python # -*- coding: utf-8 -*- import time import RPi.GPIO as GPIO import threading # GPIOの設定 GPIO.setwarnings(False) GPIO.setmode(GPIO.BCM) # 模型 motor_r = 20 # 右向き motor_l = 21 # 左向き pwm = 12 # PWM # ポイント1 straight_1 = 27 # pin11 curve_1 = 22 # pin13 pwm_1 = 17 # ポイント2 straight_2 = 24 # pin11 curve_2 = 25 # pin13 pwm_2 = 23 # 模型 GPIO.setup(motor_r, GPIO.OUT) GPIO.setup(motor_l, GPIO.OUT) GPIO.setup(pwm, GPIO.OUT) pwm_m = GPIO.PWM(pwm, 100) pwm_m.start(0) # ポイント1 GPIO.setup(straight_1, GPIO.OUT) GPIO.setup(curve_1, GPIO.OUT) GPIO.setup(pwm_1, GPIO.OUT) pwm1 = GPIO.PWM(pwm_1, 100) pwm1.start(0) # ポイント2 GPIO.setup(straight_2, GPIO.OUT) GPIO.setup(curve_2, GPIO.OUT) GPIO.setup(pwm_2, GPIO.OUT) pwm2 = GPIO.PWM(pwm_2, 100) pwm2.start(0) # 初期設定 GPIO.output(motor_r, 0) GPIO.output(motor_l, 0) GPIO.output(straight_1,0) GPIO.output(curve_1,0) GPIO.output(straight_2,0) GPIO.output(curve_2,0) time.sleep(1) # 右に進む def go_right(): pwm_m.ChangeDutyCycle(0) GPIO.output(motor_r, 1) GPIO.output(motor_l, 0) print ("go right") pwm_m.ChangeDutyCycle(30) # ここの「40」を変えると最大速度が変化する time.sleep(3.5) # ここの数値を変えると走る距離が変化する pwm_m.ChangeDutyCycle(0) return # 左に進む def go_left(): GPIO.output(motor_r, 0) GPIO.output(motor_l, 1) print ("go left") pwm_m.ChangeDutyCycle(30) # ここの「40」を変えると最大速度が変化する time.sleep(3.5) # ここの数値を変えると走る距離が変化する pwm_m.ChangeDutyCycle(0) GPIO.output(motor_l, 0) return # ポイント1をカーブ側に def point1_curve(): GPIO.output(straight_1,0) GPIO.output(curve_1,1) print("Point1 Curve") pwm1.ChangeDutyCycle(100) time.sleep(0.05) pwm1.ChangeDutyCycle(0) return # ポイント2をカーブ側に def point2_curve(): GPIO.output(straight_2,0) GPIO.output(curve_2,1) print("Point2 Curve") pwm2.ChangeDutyCycle(100) time.sleep(0.05) pwm2.ChangeDutyCycle(0) return # ポイント1を直線側に def point1_straigh(): GPIO.output(straight_1,1) GPIO.output(curve_1,0) print("Point1 Straight") pwm1.ChangeDutyCycle(100) time.sleep(0.05) pwm1.ChangeDutyCycle(0) return # ポイント2を直線側に def point2_straight(): GPIO.output(straight_2,1) GPIO.output(curve_2,0) print("Point2 Straight") pwm2.ChangeDutyCycle(100) time.sleep(0.05) pwm2.ChangeDutyCycle(0) return def stop(): print ("stop") GPIO.output(motor_r, 1) GPIO.output(motor_l, 1) pwm_m.stop() pwm1.stop() pwm2.stop() return # 運転スタート # ポイントを直線側に切り替え point1_straigh() point2_straight() time.sleep(1) # プログラムスタート # 左に動く go_left() time.sleep(2) # ポイント切り替え point1_curve() point2_curve() time.sleep(1) # 右に動く go_right() time.sleep(2) # ポイント切り替え point1_straigh() point2_straight() time.sleep(1) # 左に動く go_left() time.sleep(2) # 右に動く go_right() time.sleep(2) # ポイント切り替え point1_curve() point2_curve() time.sleep(1) # 左に動く go_left() time.sleep(2) # ポイント切り替え point1_straigh() point2_straight() time.sleep(1) # 右に動く go_right() time.sleep(2) # 終了させる stop() GPIO.cleanup() jidouunten.py
このパターンで動かした例が以下の動画です。
なおこれは1パターンしか動かしていませんが、「while True:」文を使って無限ループさせることもできます。その場合は以下のようにすればよいでしょう。
#!/usr/bin/env python # -*- coding: utf-8 -*- import time import RPi.GPIO as GPIO import threading # GPIOの設定 GPIO.setwarnings(False) GPIO.setmode(GPIO.BCM) # 模型 motor_r = 20 # 右向き motor_l = 21 # 左向き pwm = 12 # PWM # ポイント1 straight_1 = 27 # pin11 curve_1 = 22 # pin13 pwm_1 = 17 # ポイント2 straight_2 = 24 # pin11 curve_2 = 25 # pin13 pwm_2 = 23 # 模型 GPIO.setup(motor_r, GPIO.OUT) GPIO.setup(motor_l, GPIO.OUT) GPIO.setup(pwm, GPIO.OUT) pwm_m = GPIO.PWM(pwm, 100) pwm_m.start(0) # ポイント1 GPIO.setup(straight_1, GPIO.OUT) GPIO.setup(curve_1, GPIO.OUT) GPIO.setup(pwm_1, GPIO.OUT) pwm1 = GPIO.PWM(pwm_1, 100) pwm1.start(0) # ポイント2 GPIO.setup(straight_2, GPIO.OUT) GPIO.setup(curve_2, GPIO.OUT) GPIO.setup(pwm_2, GPIO.OUT) pwm2 = GPIO.PWM(pwm_2, 100) pwm2.start(0) # 初期設定 GPIO.output(motor_r, 0) GPIO.output(motor_l, 0) GPIO.output(straight_1,0) GPIO.output(curve_1,0) GPIO.output(straight_2,0) GPIO.output(curve_2,0) time.sleep(1) # 右に進む def go_right(): pwm_m.ChangeDutyCycle(0) GPIO.output(motor_r, 1) GPIO.output(motor_l, 0) print ("go right") pwm_m.ChangeDutyCycle(30) # ここの「40」を変えると最大速度が変化する time.sleep(3.5) # ここの数値を変えると走る距離が変化する pwm_m.ChangeDutyCycle(0) return # 左に進む def go_left(): GPIO.output(motor_r, 0) GPIO.output(motor_l, 1) print ("go left") pwm_m.ChangeDutyCycle(30) # ここの「40」を変えると最大速度が変化する time.sleep(3.5) # ここの数値を変えると走る距離が変化する pwm_m.ChangeDutyCycle(0) GPIO.output(motor_l, 0) return # ポイント1をカーブ側に def point1_curve(): GPIO.output(straight_1,0) GPIO.output(curve_1,1) print("Point1 Curve") pwm1.ChangeDutyCycle(100) time.sleep(0.05) pwm1.ChangeDutyCycle(0) return # ポイント2をカーブ側に def point2_curve(): GPIO.output(straight_2,0) GPIO.output(curve_2,1) print("Point2 Curve") pwm2.ChangeDutyCycle(100) time.sleep(0.05) pwm2.ChangeDutyCycle(0) return # ポイント1を直線側に def point1_straigh(): GPIO.output(straight_1,1) GPIO.output(curve_1,0) print("Point1 Straight") pwm1.ChangeDutyCycle(100) time.sleep(0.05) pwm1.ChangeDutyCycle(0) return # ポイント2を直線側に def point2_straight(): GPIO.output(straight_2,1) GPIO.output(curve_2,0) print("Point2 Straight") pwm2.ChangeDutyCycle(100) time.sleep(0.05) pwm2.ChangeDutyCycle(0) return # 停止させる def stop(): print ("stop") GPIO.output(motor_r, 1) GPIO.output(motor_l, 1) pwm_m.stop() pwm1.stop() pwm2.stop() return # ポイント切り替え point1_straigh() point2_straight() time.sleep(1) # 無限ループで走らせる try: while True: # 左に動く go_left() time.sleep(2) # ポイント切り替え point1_curve() point2_curve() time.sleep(1) # 右に動く go_right() time.sleep(2) # ポイント切り替え point1_straigh() point2_straight() time.sleep(1) # 左に動く go_left() time.sleep(2) # 右に動く go_right() time.sleep(2) # ポイント切り替え point1_curve() point2_curve() time.sleep(1) # 左に動く go_left() time.sleep(2) # ポイント切り替え point1_straigh() point2_straight() time.sleep(1) # 右に動く go_right() time.sleep(2) except KeyboardInterrupt: # 終了させる print ("stop") stop() GPIO.cleanup() jidoumugen.py
ここまでで何となく自動運転っぽいものが出来上がりましたが、適当な秒数を設定しているため、ずーっと運転していくと止まることができずにエンドレールへぶつかってしまうこともあるでしょう。そこで次回からは赤外線センサーを使って列車の位置を計測し、ポイントの切り替え動作や停止/折り返し動作ができるようにする方法を考えていきます。
関連記事
- Nゲージの模型をラズパイで動かす “らずてつ”その1――鉄道模型とラズパイをつなぐ
今回から鉄道模型の運転をラズパイで制御する仕組みに挑戦します。 - 部屋の二酸化炭素濃度を測定しよう ラズパイでCO2センサーを作る
ラズパイを使ってCO2を計測し、部屋の換気をする目安にしてみましょう。 - 550円の「Raspberry Pi Pico」でIoT その1:気温と湿度、気圧を測定する
独自開発のチップ「RP2040」を搭載したマイコンボード「Raspberry Pi Pico」でセンサーデバイスをテスト。 - ラズパイで「Windows 10 on ARM64」を動かす(オーバークロック編)
ラズパイにARMプロセッサ向けの「Windows 10 on ARM64」をインストールしてみた。今回はオーバークロックで快適動作を実現。 - ラズパイで「Windows 10 on ARM64」を動かす(インストール編)
ラズパイにARMプロセッサ向けの「Windows 10 on ARM64」をインストールしてみた。 - ラズパイで「Windows 10 on ARM64」を動かす(事前準備編)
ラズパイにARMプロセッサ向けの「Windows 10 on ARM64」をインストールしてみた。 - ラズパイ一体型キーボード「Raspberry Pi 400」レビュー 特例制度のWeb申請で“技適なし”を回避した
かつての“マイコン”を思わせるラズパイ一体型キーボード「Raspberry Pi 400」のレビューのファーストインプレッションをお届け。海外製品を日本国内で使うために気を付けるべきポイントも紹介。 - ラズパイ一体型キーボード「Raspberry Pi 400」の性能は? ベンチマークテストで従来モデルと比べた
かつての“マイコン”を思わせるラズパイ一体型キーボード「Raspberry Pi 400」でベンチマークテストを実施。その実力とは? - ラズパイで気温と湿度を測定、LINEで通知を受け取る 〜前編〜
今回は温湿度センサーモジュールとラズパイを組み合わせ、LINEと連携させる方法を紹介します。 - ラズパイを無線LANルーター化する 〜アクセスポイント編〜
家庭内で無線LANルータを使っている人は多いかと思います。今回はそのルーター機能をラズパイに持たせて、ラズパイを使った無線LANルーターの構築方法についてご紹介します。 - 本格的な撮影が楽しめる公式カメラモジュール「Raspberry Pi High Quality Camera」を試す
小さなマイクロコンピュータ「Raspberry Pi」(通称ラズパイ)で作る、自分だけのガジェット。Raspberry Pi公式のカメラとして本格的な撮影が楽しめる「Raspberry Pi High Quality Camera」を紹介します。 - ラズパイで新型コロナウイルス解析に参加する 〜Ubuntu Serverの構築編〜
小さなマイクロコンピュータ「Raspberry Pi」(通称ラズパイ)で作る、自分だけのガジェット。今回はラズパイで。 - ラズパイでスマートスピーカーを作る 「アレクサ!あしたの天気は?」 〜ソフトウェア準備編〜
小さなマイクロコンピュータ「Raspberry Pi」(通称ラズパイ)で作る、自分だけのガジェット。今回は米Amazonが開発したAIアシスタント「Alexa」をラズパイで使えるようにしてみます。 - これが“らずキャン△”だ! キャンプ場で役立つ「気温・湿度・気圧センサー」の作り方
小さなマイクロコンピュータ「Raspberry Pi」(通称ラズパイ)で作る、自分だけのガジェット。まずはキャンプ場で気温、湿度、気圧を測れるデバイスを作ってみます。 - ラズパイで「YouTube」と「Amazon プライムビデオ」を見る
小さなマイクロコンピュータ「Raspberry Pi」(通称ラズパイ)で作る、自分だけのガジェット。音楽や動画のメディアサーバ化するために入れたOS「OSMC」で、YouTubeやAmazon プライムビデオなどを楽しんでみましょう。
Copyright © ITmedia, Inc. All Rights Reserved.