エンジニア ろっきーの探究ノート

"不"を"したい"にひっくり返すモノ/コトづくり(H/W, S/W, IoTシステム, アイデア発想)技術を探究します。

ラズパイ× tensorflowでプラレール自動運転(2) 学習用の写真を撮りまくる

tensorflowで駅の写真を学習・推論させ、プラレールをラズパイで自動運転する第2回!学習用の写真を撮るプログラムを2種類、作成しました。

1.どんな写真を学習させたらいいか

カメラを取り付けた様子がこちら。

f:id:rockhack_design:20220403193147j:plain
この瞬間をとらえて停車させたい

写真を撮ると、こんな感じ。

f:id:rockhack_design:20220403193558j:plain
今回はこの方向での進入時のみを学習させる

背景に写っている物を誤認識されたくないので、駅が映り込む右下1/4の部分のみトリミングして取り込むことにする(←精度向上のためコレが大事でした!)。

2.走りながら写真を撮るプログラム

import tkinter as tk
import picamera, time, shutil
import RPi.GPIO as GPIO
from PIL import Image

# 写真ファイル名の連番スタート値を設定
count_0 = 0

# 使用するピン番号を設定(MAX14870)
GPIO.setmode(GPIO.BOARD)
DIR_1 = 18
DIR_2 = 22
EN_N = 29
PWM1 = 32
PWM2 = 33

# 各ピンを出力ピンに設定
GPIO.setup(DIR_1, GPIO.OUT, initial=GPIO.LOW)  # LOW:forward
GPIO.setup(DIR_2, GPIO.OUT, initial=GPIO.LOW)
GPIO.setup(EN_N, GPIO.OUT, initial=GPIO.LOW)  # Low active
GPIO.setup(PWM1, GPIO.OUT, initial=GPIO.LOW)
GPIO.setup(PWM2, GPIO.OUT, initial=GPIO.LOW)

p1 = GPIO.PWM(PWM1, 3000)
p2 = GPIO.PWM(PWM2, 3000)

p1.start(0)
p2.start(0)

p1.ChangeDutyCycle(6)
p2.ChangeDutyCycle(6)

try:
    while (1):
        with picamera.PiCamera() as camera:
            count_0 += 1
            camera.resolution = (640, 480)
            camera.rotation = 180     # カメラが上下反対なので反転する
            #camera.start_preview()
            time.sleep(0.2)
            #camera.stop_preview()
            camera.capture("pic_" + str(count_0) + '.jpg')
            
            # 右下1/4部分のみトリミングする
            im = Image.open('/home/pi/Desktop/pic_' + str(count_0) + '.jpg')    
            im_crop = im.crop((320, 240, 640, 480))
            im_crop.save('/home/pi/Desktop/pic_' + str(count_0) + '.jpg', quality=95)
            
            filename = 'pic_' + str(count_0) + '.jpg'
            source = r'/home/pi/Desktop/'+filename
            destination = r'/home/pi/Desktop/case0/'+filename
            shutil.move(source, destination)
                
except KeyboardInterrupt:                
# PWM を停止
    p1.stop()
    p2.stop()
  • PWMのDuty比は6という小さな値でゆっくり走らせる。
  • 右下1/4をトリミングした後、デスクトップにある「case0」フォルダに移動させる。
  • 写真を撮るごとに変数count_0がインクリメントされ、ファイル名の連番になる。
  • 動作を止めるには「Ctrl + C」

というプログラムです。 また、駅が映り込んだ瞬間のデータを増やすべく止まって撮るバージョンがこちら。

import tkinter as tk
import picamera, time, shutil
from PIL import Image

count_0 = 0
count_1 = 0
count_2 = 0

class Application(tk.Frame):

    def __init__(self, master=None):
        super().__init__(master)
 
        # ウィンドウの設定
        self.master.title("gather picture")
 
        self.pack()
        self.create_widget()
 
    def create_widget(self):
 
        self.label1 = tk.Label(self,text="3 case")
        self.label1.pack()

        def button0_click(): # button0をクリックした時の処理
            with picamera.PiCamera() as camera:
                global count_0
                count_0 += 1
                camera.resolution = (640, 480)
                camera.rotation = 180
                camera.start_preview()
                time.sleep(1)
                camera.stop_preview()
                camera.capture("pic_0" + str(count_0) + '.jpg')
            
                im = Image.open('/home/pi/Desktop/pic_0' + str(count_0) + '.jpg')    
                im_crop = im.crop((320, 240, 640, 480))
                im_crop.save('/home/pi/Desktop/pic_0' + str(count_0) + '.jpg', quality=95)
                
                filename = 'pic_0' + str(count_0) + '.jpg'
                source = r'/home/pi/Desktop/'+filename
                destination = r'/home/pi/Desktop/case0/'+filename
                shutil.move(source, destination)
                
        def button1_click(): # button1をクリックした時の処理
            with picamera.PiCamera() as camera:
                global count_1
                count_1 += 1                
                camera.resolution = (640, 480)
                camera.rotation = 180
                camera.start_preview()
                time.sleep(1)
                camera.stop_preview()
                camera.capture("pic_1" + str(count_1) + '.jpg')
                
                im = Image.open('/home/pi/Desktop/pic_1' + str(count_1) + '.jpg')    
                im_crop = im.crop((320, 240, 640, 480))
                im_crop.save('/home/pi/Desktop/pic_1' + str(count_1) + '.jpg', quality=95)
                
                filename = 'pic_1' + str(count_1) + '.jpg'
                source = r'/home/pi/Desktop/'+filename
                destination = r'/home/pi/Desktop/case1/'+filename
                shutil.move(source, destination)                
                
        def button2_click(): # button2をクリックした時の処理
            with picamera.PiCamera() as camera:
                global count_2
                count_2 += 1
                camera.resolution = (640, 480)
                camera.rotation = 180
                camera.start_preview()
                time.sleep(1)
                camera.stop_preview()
                camera.capture("pic_2" + str(count_2) + '.jpg')
                
                im = Image.open('/home/pi/Desktop/pic_2' + str(count_2) + '.jpg')    
                im_crop = im.crop((320, 240, 640, 480))
                im_crop.save('/home/pi/Desktop/pic_2' + str(count_2) + '.jpg', quality=95)

                filename = 'pic_2' + str(count_2) + '.jpg'
                source = r'/home/pi/Desktop/'+filename
                destination = r'/home/pi/Desktop/case2/'+filename
                shutil.move(source, destination)                  
                
        self.button0 = tk.Button(self,text="case 0", command=button0_click, width=30, heigh=3)
        self.button0.pack() # button0ウィジェット配置
        self.button1 = tk.Button(self,text="case 1", command=button1_click, width=30, heigh=3) 
        self.button1.pack() # button1ウィジェット配置
        self.button2 = tk.Button(self,text="case 2", command=button2_click, width=30, heigh=3) 
        self.button2.pack() # button2ウィジェット配置
        
if __name__ == "__main__":
    root = tk.Tk()
    app = Application(master=root)
    app.mainloop()
  • 3つのボタンを配置し、分類させたいケースに合わせて格納フォルダを分けられる。
  • 撮った瞬間に1秒だけプレビューを表示する。
  • 各フォルダに格納時、右下1/4をトリミングする。
  • 写真を撮るごとに変数count_0がインクリメントされ、ファイル名の連番になる。

実行時の様子がこちら。

f:id:rockhack_design:20220403195810p:plain
学習させたいケース別に写真を撮る場合

ケースを分けられるようにしたのは、前方に他の車両がいて減速させるケースなどの実装も見込んでのことですが、駅があるときの停車だけを考える今回は、駅があるとき・ないときをそれぞれ20~30ずつ集めました。

f:id:rockhack_design:20220403200100p:plain
駅があるときー、ないときー。

では学習させてラズパイで実行(推論)してみよー。