自動ブレーキの搭載

数日前に記事を書いた、移動するだけのTANKから、ギア比を大きくして速度を落とし、パワーを上げてみました。結果として、カーペットの上でも旋回するようになったのですが、ネコを追いかけるには全くスピードが足りなくなりました。ま、それは良いとして、早速自動ブレーキの実装をしていきたいと思います。

基本的な考え方

  • 前方40cm以内に障害物を検知したら、モーター正回転のpwmを落とす(0.3倍にする)
  • 前方20cm以内に障害物を検知したら、モーター正回転を禁止する。

この二つの動作を実装してみます。

また、モニターを見なくても挙動が分かるように、LEDを1つ繋ぎ、その明るさで40cm以内に障害物があるときは、暗く点灯し、20cm以内に障害物があるときはフル点灯させます。

このLED制御は、当然ながらPCA9685(4ch)を使う事にします。ラズパイでLチカをやる際には、ブレッドボードにLEDと抵抗を直列に繋ぐのですが、PCA9685を使うと、内部に既にLED用の抵抗が入っているので、pwmピンとGNDピンにLEDをダイレクトに繋いでOKなので楽ちんです。

LEDをPCA9685にダイレクトに接続

改造したプログラム

前回の移動するだけのプログラムは、ゲームパッドの入力にだけ従っていれば良かったので、ゲームパッドの入力があった場合のみpwm信号を送れば良かったのですが、自動ブレーキ実装となると、障害物検知でもpwm信号を送らなければいけないので、pwm信号を送る命令を一段外に置かなければいけません。

前回のプログラムがこちら。

import pygame
import Adafruit_PCA9685

# pygame初期設定
pygame.init()
joy = pygame.joystick.Joystick(0) # joystickのインスタンスを作成
joy.init() # joyの初期化

# PCA9685初期設定
pwm = Adafruit_PCA9685.PCA9685() # PCA9685のインスタンスを作成
pwm.set_pwm_freq(60) # PWMの周波数を60Hzに設定

motor = [0,0,0,0] # pwm各チャンネルに送るデータの格納場所を作成

while True:
    # ゲームパッドの入力があった場合
    if pygame.event.get():
        
        Acc = int((joy.get_axis(5) + 1)*2048) # RTの入力(-1 ~ 1)を0~4095に増幅
        hat = 2 + joy.get_hat(0)[0]+(joy.get_hat(0)[1] + 1) * 3 # hatを10キーの値に変換
        motor[0] = (hat == 8) + (hat == 7) + (hat == 4) + (hat == 9) * 0.3 # 右側正回転
        motor[1] = (hat == 2) + (hat == 1) + (hat == 6) # 右側逆回転
        motor[2] = (hat == 8) + (hat == 9) + (hat == 6) + (hat == 7) * 0.3 # 左側正回転
        motor[3] = (hat == 2) + (hat == 3) + (hat == 4) # 左側逆回転
        
        print(Acc,hat,motor); # 値の確認
        
        for n in range(4): # PWMの設定
            pwm.set_pwm(n, 0, int(motor[n]*Acc))

自動ブレーキを実装したプログラムがこちら。

import pygame
import Adafruit_PCA9685
from gpiozero import DistanceSensor

# pygame初期設定
pygame.init()
joy = pygame.joystick.Joystick(0) # joystickのインスタンスを作成
joy.init() # joyの初期化

# PCA9685初期設定
pwm = Adafruit_PCA9685.PCA9685() # PCA9685のインスタンスを作成
pwm.set_pwm_freq(60) # PWMの周波数を60Hzに設定

# 距離センサーの初期設定
sensor = DistanceSensor(23, 24) # 23:Echo  24:Trig
        
motor = [0,0,0,0] # pwm各チャンネルに送るデータの格納場所を作成
Acc = 0 # Accを定義しておく

while True:
    # 距離センサーの値を取得
    dist = round(sensor.distance*100,1)
    
    # ゲームパッドの入力があった場合
    if pygame.event.get():
    
        Acc = int((joy.get_axis(5) + 1)*2048) # RTの入力(-1 ~ 1)を0~4095に増幅
        hat = 2 + joy.get_hat(0)[0]+(joy.get_hat(0)[1] + 1) * 3 # hatを10キーの値に変換
        motor[0] = ((hat == 8) + (hat == 7) + (hat == 4) + (hat==9) * 0.2 ) # 右側正回転
        motor[1] = (hat == 2) + (hat == 1) + (hat == 6) # 右側逆回転
        motor[2] = ((hat == 8) + (hat == 9) + (hat == 6) + (hat==7) * 0.2 ) # 左側正回転
        motor[3] = (hat == 2) + (hat == 3) + (hat == 4) # 左側逆回転
    
    # PWM送信    
    for n in range(2): # 前進用PWMの設定
        # 40cm以上あればsafe=1, 20cm〜40cmならsafe=0.3, 20cm以下ならsafe=0
        safe = (dist >= 40) + (dist<40 and dist>20)*0.3
        # tale lampの輝度調整 20cm未満ならFull点灯、20cm以上40cm未満ならハーフ点灯
        tale = (dist < 20) * 3500 + (dist < 40) * 595
        
        ch=n*2
        pwm.set_pwm(ch, 0, int(motor[ch]*Acc*safe))
    
    for n in range(2): # 後進用PWMの設定
        ch=n*2+1
        pwm.set_pwm(ch, 0, int(motor[ch]*Acc))
    
    pwm.set_pwm(4, 0, tale) # テールランプの輝度設定
    print("前方距離",dist,"cm")

ちょっと解説すると、pwm送信を while True: 直下に置いているので、Accを定義しておかないとエラーになっちゃいます。従って、ループに入る前にAccを定義しました。

ループに入ると、まず変数 dist に距離センサーの値を小数第二位を四捨五入して代入します。

次にゲームパッドの入力チェックをしますが、何も入力がなければ、そのまま次のpwm送信になります。

pwm送信の前に、論理式を使って前方クリアなら1を、直前に障害物なら0を、近くに障害物なら、0.3を safe という変数に代入します。

taleは、テールランプLEDの輝度です。

そして、正回転と逆回転を分けて、pwm送信します。

最後に、テールランプのpwmを送信します。

テールランプが正しく機能しているかを確認した動画がこちら。

動画でLEDが点滅しているように見えるのは、フリッカー現象です。実際は点滅していません。

問題発生・・・。

さて、正常に動作しているように見えますが、これじゃダメでした。

実際に走らせてみたら、壁に激突しまくりでした。そうなんです。距離センサーの挙動が遅いのです。

これ、どうにかならないものかと、gpiozeroの距離センサーのマニュアルを見に行きました。

そしたら色々書いてあるじゃないですか!トリガーとエコー以外にもパラメーターを色々と設定出来るようでした。

echoエコーを入れるGPIOピン
triggerトリガーを出すGPIOピン
queue_lenキューの長さ。これが大きいと値が正確になる代わりに、反応が遅くなり、これが小さいと反応が早くなる代わりに、値がばらつく。初期値は9
max_distance検知する最大距離。初期値は1m
threshold_distanceスレッショルド距離。初期値は0.3m。この距離より近づくと、in_rangeイベントを発生させ、この距離より離れると、out_rangeイベントを発生させる。
partial初期値はFalse。これをTrueにすると、内部キューが一杯にならなくても値を取り出せる。すぐ値が欲しい時のみTrueにする。
pin_factory値は、FactoryかNoneのみ。Api-pinsを参照のこと。
距離センサーのパラメーター

ということで、とりあえず上のプログラムの15行目を次のように変えてみたところ、反応速度がだいぶ改善しました。

sensor = DistanceSensor(echo=23, trigger=24, quueue_len=5)

5より小さくすると不安定になるし、5より大きくすると反応が若干遅く感じるので、5が良いみたいです。

実際に走らせてみたのが次の動画です。

これで、壁に激突しなくなりましたが、音を反射しにくい柔らかい素材だとまぁ激突するんですけどね。

コメント

タイトルとURLをコピーしました