名刺サイズの超小型PC「ラズパイ」で遊ぶ(第55回)
Nゲージの模型をラズパイで動かす “らずてつ”その4――タイミングを合わせて車両を走らせる
第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
ここまでで何となく自動運転っぽいものが出来上がりましたが、適当な秒数を設定しているため、ずーっと運転していくと止まることができずにエンドレールへぶつかってしまうこともあるでしょう。そこで次回からは赤外線センサーを使って列車の位置を計測し、ポイントの切り替え動作や停止/折り返し動作ができるようにする方法を考えていきます。
Copyright © ITmedia, Inc. All Rights Reserved.
名刺サイズの超小型PC「ラズパイ」で遊ぶ
今やスマートフォンアプリで何でも完結できてしまう時代。しかし、一から自分の手でデバイスを作り上げ、試行錯誤しながらアイデアを具現化する楽しさは格別です。この連載では、テーマや用途にあわせてマイクロコンピュータ(マイコン)と呼ばれる名刺サイズの超小型PC「Raspberry Pi」(通称ラズパイ)の活用方法から具体的な工作手順までを紹介します。
この記事の著者
関連記事
こんなメディアも見られています
ITmedia NEWSに関連する情報をお探しであれば、こちらのメディアもお役に立てるかもしれません。
SpecialPR
本日の新着記事
アクセスランキング
-
1
「Xが情報収集に役立たない……」熊本地震で不満の声続出 「Twitterを返して」
-
2
メルカリ、梨の転売疑惑に「盗品の出品は確認されず」 誹謗中傷には利用制限も
-
3
「こんなのに追われたら……」急斜面もやすやす爆走、中国製の車輪付き四足ロボのデモ動画が話題 最高時速20km超
-
4
検索結果に「詐欺ではありません」と表示させる詐欺手口、警視庁が注意喚起 AI要約も餌食に
-
5
はてな、11億円流出の調査報告書を公開 偽警察、口外禁止、残業・休出200時間超、孤立……ほころびが連鎖
-
6
Z世代に聞く次の流行、「AIイラスト」が1位に 「Claude Code」も上位
-
7
携帯各社、熊本の一部で通信障害 震度7の地震で 他社回線につながる「JAPANローミング」開始【追記】
-
8
東野圭吾さん死去、Xなどで追悼相次ぐ 「ガリレオ」など数々の人気作、エンジニアから転身
-
9
NVIDIAやMicrosoftなど30社超、オープンAIの防御ツール共同開発の「Open Secure AI Alliance」設立
-
10
農水省の“クソダサ”ポスター話題 「AIよりよっぽど良い」の声も 担当者に狙いを聞いた
ITmedia NEWS SNS
インフォメーション
注目情報をチェック
ITmediaNEWSをフォロー
あなたにおすすめの記事PR