私たちは、無人運転がどのように色を認識するのかを学び、無人車両が色のターゲットを追跡する能力のデモンストレーションを行いました。現実に戻りますが、無人車両の速度制御は非常に重要であることがわかりました。これには、信号待ちなどの安全上の問題が含まれます。前のセクションでは、他の車両を追い越すために減速または加速するなどの一般的な状況が反映されていませんでした。ここでは、無人運転で自動的かつスムーズに加減速する方法を学びます。まずは動画を見てみましょう:無人運転の自動速度調整
1. カメラをインスタンス化する
関連するライブラリをインポートし、カメラを初期化します。インスタンス化エラーがある場合は、無人車両のカメラのリアルタイム撮影画像と関連するウィジェットの操作を参照してください。
from jetbotmini import Camera
from jetbotmini import bgr8_to_jpeg
from jetbotmini import Robot
import torch
import torchvision
import cv2
import traitlets
import ipywidgets.widgets as widgets
from IPython.display import display
import numpy as np
camera = Camera.instance(width=300, height=300)
global color_lower
global color_upperv
color_lower=np.array([100,43,46])
color_upper = np.array([124, 255, 255])
2. ロボットのインスタンス化
次に、ロボットをインスタンス化し、PID 制御アルゴリズムをインポートして、無人車両の速度を制御します。
ここで PID についてもう 1 つ紹介します。
PID (比例-積分-微分) は、産業プロセスや機械の動作を制御するために一般的に使用されるコントローラー アルゴリズムです。PID コントローラーは、実際の値と目標値の差を測定し、比例 P、積分 I、微分 D のこの差を最小限に抑えます
PID コントローラーの目的は、制御信号を調整することによって、実際の出力値を期待される出力値と一致させることです。制御信号は実際の出力値と期待される出力値の差である誤差を計算することによって得られます
robot = Robot()
import PID
follow_speed = 0.5
turn_gain = 1.7
follow_speed_pid_model = 1
#follow_speed_pid = PID.PositionalPID(3, 0, 0)
follow_speed_pid = PID.PositionalPID(1.5, 0, 0.05)
turn_gain_pid = PID.PositionalPID(0.15, 0, 0.05)
なお、実際に設定するシーンに応じて、PIDは必ずしも出現する必要はなく、PIとPDの組み合わせでもよいし、Pの比率だけでもよい。
3. PIDアルゴリズム
PID には比例、積分、微分があることがわかっており、PID コントローラーはこれら 3 つの係数を調整することで応答速度、安定性、精度のバランスをとります。3 つの機能は次のとおりです。
スケール係数:制御信号の強度を調整して、システムがエラーに迅速に応答できるようにします。
積分係数:システムの定常状態誤差を減らし、システムが期待値をより正確に追跡できるようにします。
微分係数:システムの動的パフォーマンスを向上させ、システムが変化に迅速に応答できるようにするために使用されます。
この記事では位置 PID が使用されます。コードは次のとおりです。
PID.py
class PositionalPID:
def __init__(self, P, I, D):
self.Kp = P
self.Ki = I
self.Kd = D
self.SystemOutput = 0.0
self.ResultValueBack = 0.0
self.PidOutput = 0.0
self.PIDErrADD = 0.0
self.ErrBack = 0.0
#设置PID控制器参数
def SetStepSignal(self,StepSignal):
Err = StepSignal - self.SystemOutput
KpWork = self.Kp * Err
KiWork = self.Ki * self.PIDErrADD
KdWork = self.Kd * (Err - self.ErrBack)
self.PidOutput = KpWork + KiWork + KdWork
self.PIDErrADD += Err
self.ErrBack = Err
#设置一阶惯性环节系统 其中InertiaTime为惯性时间常数
def SetInertiaTime(self, InertiaTime,SampleTime):
self.SystemOutput = (InertiaTime * self.ResultValueBack + SampleTime * self.PidOutput) / (SampleTime + InertiaTime)
self.ResultValueBack = self.SystemOutput
インクリメンタル PID もあります。PID の詳細については、興味のある方は、PID 駆動アルゴリズム制御と Jetbotmini でのコード実装を参照してください。
4. 色の検出と追跡
先ほど移植した色検出と追跡に加えて、PID 制御アルゴリズムを使用して、無人車両が検出対象の速度を変更することもでき、無人車両が検出対象に近づくと速度が遅くなり、距離が離れるほど速度が速くなります (もちろん、この速度も制限速度内です)。
image_widget = widgets.Image(format='jpeg', width=300, height=300)
display(widgets.VBox([
widgets.HBox([image_widget]),
]))
width = int(image_widget.width)
height = int(image_widget.height)
def execute(change):
global follow_speed
global turn_gain
global follow_speed_pid
target_value_speed = 0
#-------------颜色识别处理------------------------------
frame = camera.value
frame = cv2.resize(frame, (300, 300))
frame_=cv2.GaussianBlur(frame,(5,5),0)
hsv=cv2.cvtColor(frame,cv2.COLOR_BGR2HSV)
mask=cv2.inRange(hsv,color_lower,color_upper)
mask=cv2.erode(mask,None,iterations=2)
mask=cv2.dilate(mask,None,iterations=2)
mask=cv2.GaussianBlur(mask,(3,3),0)
cnts=cv2.findContours(mask.copy(),cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)[-2]
#-------------------------------------------------------
# 检测到目标
if len(cnts)>0:
cnt = max (cnts,key=cv2.contourArea)
(color_x,color_y),color_radius=cv2.minEnclosingCircle(cnt)
if color_radius > 10:
# 将检测到的颜色标记出来
cv2.circle(frame,(int(color_x),int(color_y)),int(color_radius),(255,0,255),2)
#控制将机器人向前移动,并控制成比例的目标与中心的x距离
center = (150 - color_x)/150
#跟随速度PID调节
follow_speed_pid.SystemOutput = 90000 * (color_radius/200)
follow_speed_pid.SetStepSignal(10000)
follow_speed_pid.SetInertiaTime(0.2, 0.1)
#转向增益PID调节
turn_gain_pid.SystemOutput = center
turn_gain_pid.SetStepSignal(0)
turn_gain_pid.SetInertiaTime(0.2, 0.1)
#将转向增益限制在有效范围内
target_value_turn_gain = 0.2 + abs(turn_gain_pid.SystemOutput)
if target_value_turn_gain < 0:
target_value_turn_gain = 0
elif target_value_turn_gain > 2:
target_value_turn_gain = 2
#将输出电机速度保持在有效行驶范围内
target_value_speed = 0.8 + follow_speed_pid.SystemOutput / 90000
target_value_speedl = target_value_speed - target_value_turn_gain * center
target_value_speedr = target_value_speed + target_value_turn_gain * center
if target_value_speedl<0.3:
target_value_speedl=0
elif target_value_speedl>1:
target_value_speedl = 1
if target_value_speedr<0.3:
target_value_speedr=0
elif target_value_speedr>1:
target_value_speedr = 1
robot.set_motors(target_value_speedl, target_value_speedr)
# 没有检测到目标
else:
robot.forward(float(follow_speed))
#robot.stop()
# 更新图像显示至小部件
image_widget.value = bgr8_to_jpeg(frame)
execute({'new': camera.value})
ここでの実現原理は、下図に示すように、検知対象が遠ざかるとマークの円が小さくなり、半径が小さくなり、同様に距離が近づくと円が大きくなり、円の半径が大きくなり、半径の大きさに応じて車両の速度が制御されます。
5. リアルタイム更新
次に、camera.observeを使用して実行コールバック関数を呼び出し、リアルタイムで画面を更新することで、カラー ターゲットを追跡する効果が得られます。
camera.unobserve_all()
camera.observe(execute, names='value')
カメラを切断してロボットを停止する必要がある場合は、次のコードを実行します。
import time
camera.unobserve_all()
time.sleep(1.0)
robot.stop()
上記から、前のセクションと比較して、キー アプリケーションと PID 制御メソッドを追加することによって最適化が行われていることがわかります。この記事では、位置 PID (Positional PID) が使用されており、コードの残りの部分は前のセクションと同じです。これにより、無人車両は走行中に障害物に遭遇した際にスムーズに減速し、加速が必要な場合には急加速することがなくなり、乗り心地が大幅に向上します。