LoginSignup
6
5

More than 3 years have passed since last update.

画像処理によるライントレースカー製作記

Last updated at Posted at 2020-05-05

ラズベリーパイとWEBカメラを組み合わせて、ライントレースカーを製作しました。
ベース車両の製作記はこちらです。ハード編ソフト編

経緯

ライントレースカーと言えば、照度センサーなどを使うのが一般的な方法と思います。
自分も最初はそれを考えていたのですが、部品調達やハードをいじるのが面倒だったので、既存のハードが使えないかと考え、昔買ったWEBカメラを使って、画像処理でやってみることにしました。

完成形

完成したライントレースカーが動作しているところです。

ソースコード

モーター制御のところ関数にできますよね…

import cv2
import time
import RPi.GPIO as GPIO
import pigpio

#モーターの正転/逆転制御GPIO
GPIO.setwarnings(False) 
GPIO.setmode(GPIO.BCM)
GPIO.setup(17, GPIO.OUT)
GPIO.setup(27, GPIO.OUT)

#モーター回転速度制御GPIO(PWM)
gpio_pin0 = 12
gpio_pin1 = 13
pi = pigpio.pi()
pi.set_mode(gpio_pin0, pigpio.OUTPUT)

#モーター制御パラメータ
duty1 = 4  #直進時のDuty比
duty2 = 4  #旋回時のDuty比
freq = 700
sleep_time1 = 0.05  #直進時のモーター動作時間
sleep_time2 = 0.15  #旋回時のモーター動作時間

#カメラ設定
camera = cv2.VideoCapture(0)   
th = 50    # 二値化の閾値
i_max = 255

#カメラ画像のトリミングサイズ設定
trim_y = 180
trim_h = 30

#左ブロックエリア設定
LB_x1,LB_x2 = 200,210
LB_y1,LB_y2 = 0,trim_h

#右ブロックエリア設定
RB_x1,RB_x2 = 400,410
RB_y1,RB_y2 = 0,trim_h


while True:
    ret, frame = camera.read()    #フレームを取得
    frame = frame[trim_y:trim_y + trim_h,]  #画像をトリミング
    frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) #グレースケール化
    ret, frame = cv2.threshold(frame, th, i_max, cv2.THRESH_BINARY_INV) #二値化(白黒反転)
    cv2.rectangle(frame,(LB_x1,LB_y1),(LB_x2,LB_y2),(0,0,255),1) #左ブロックエリア描画
    cv2.rectangle(frame,(RB_x1,RB_y1),(RB_x2,RB_y2),(0,0,255),1) #右ブロックエリア描画

    LB = frame[LB_y1:LB_y2,LB_x1:LB_x2] #左ブロックエリアのフレームをセット
    RB = frame[RB_y1:RB_y2,RB_x1:RB_x2] #右ブロックエリアのフレームをセット

    Det_LB = cv2.countNonZero(LB) #左ブロックエリアの白ピクセルカウント
    Det_RB = cv2.countNonZero(RB) #右ブロックエリアの白ピクセルカウント
    print("Det_LB: " +str(Det_LB) +" Det_RB: " +str(Det_RB))

    cv2.imshow('camera', frame)             # フレームを画面に表示

    # キー操作があればwhileループを抜ける
    if cv2.waitKey(1) & 0xFF == ord('q'):
        print("エスケープが押されました")
        break

    #左右のブロックエリアへの接触を検出したら、停止線と判定して停止する
    elif Det_LB > 0 and Det_RB > 0:
        print("停止線への接触を検出しました")        
        GPIO.output(17, GPIO.HIGH)
        GPIO.output(27, GPIO.HIGH)
        pi.hardware_PWM(gpio_pin0, freq, 000000)
        pi.hardware_PWM(gpio_pin1, freq, 000000)
        break

    #LB接触→右旋回
    elif Det_LB > 0:
        print("左ブロックエリアへの接触を検出しました")      
        GPIO.output(17, GPIO.HIGH)
        GPIO.output(27, GPIO.LOW)
        pi.hardware_PWM(gpio_pin0, freq, duty2*100000)
        pi.hardware_PWM(gpio_pin1, freq, duty2*100000)
        time.sleep(sleep_time2)
        pi.hardware_PWM(gpio_pin0, freq, 000000)
        pi.hardware_PWM(gpio_pin1, freq, 000000)

    #RB接触→左旋回
    elif Det_RB > 0:
        print("右ブロックエリアへの接触を検出しました")
        GPIO.output(17, GPIO.LOW)
        GPIO.output(27, GPIO.HIGH)
        pi.hardware_PWM(gpio_pin0, freq, duty2*100000)
        pi.hardware_PWM(gpio_pin1, freq, duty2*100000)
        time.sleep(sleep_time2)
        pi.hardware_PWM(gpio_pin0, freq, 000000)
        pi.hardware_PWM(gpio_pin1, freq, 000000)

    #ブロックエリアへの接触なし→前進
    else :
        GPIO.output(17, GPIO.HIGH)
        GPIO.output(27, GPIO.HIGH)
        pi.hardware_PWM(gpio_pin0, freq, duty1*100000)
        pi.hardware_PWM(gpio_pin1, freq, duty1*100000)
        time.sleep(sleep_time1)
        pi.hardware_PWM(gpio_pin0, freq, 000000)
        pi.hardware_PWM(gpio_pin1, freq, 000000)

# 撮影用オブジェクトとウィンドウの解放
camera.release()
cv2.destroyAllWindows()

解説及び所感

Untitled.png
上の写真のように、ブロックエリアという架空のエリアを設定して、その中にラインが入ったことを検出して、車両の向きを補正しています。
ラインの検出にはcountNonZeroというメソッドを使っています。
これは、黒ではないピクセルの数をカウントするというメソッドです。
これを使うために、取得画像を二値化する時に白黒を反転させ、黒いラインが白くなるようにしています。

画像をトリミングしているのもポイントかもしれません。
画像処理のライントレースカーの事例を調べていたら、そういうやり方が出ていたので、パクらせて頂きました。

あとはひたすら、地道な調整の世界でした。

sleep_time1 = 0.05  #直進時のモーター動作時間
sleep_time2 = 0.15  #旋回時のモーター動作時間

直進時と旋回時で、モーターの動作時間を変えたりしているのがポイントです。
直進が速いと、ラインを踏み越えてしまったりします。
他にも、ブロックエリアの位置や広さを変えるなど、調整事項は色々ありそうです。
正直、まだ完璧ではないです。

たぶんですが、ライントレースカーでは、センサーを極力車両の中央に、タイヤの軸に近いところに設置するほうが、制御が楽なんだと思います。
今回の車両ではカメラをフロント部分に設置したのですが、旋回した時に結構ブレるので、それで制御が難しくなっていると思います。

やっぱり、カメラはこのような用途より、前方を撮影してレーンキープ用に使うというのが理にかなっているんだろうな。
その場合、道の勾配で見え方が変わったりするから、カメラ二つでステレオ的な処理が必要になるのか。画像処理は奥が深いです。

参考サイト

1.Python/OpenCVでWebカメラの情報をリアルタイム表示
2.Python/OpenCVで画像の二値化をする方法
3.Raspberry Pi + Python 3 に OpenCV 3 をなるべく簡単にインストールする
4.ラズパイでpython3にopencvを入れたらエラーが出た【対処法】
5.Python,OpenCVで二値画像から白と黒の面積比を算出

6
5
0

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
6
5