摄像头对于颜色的识别,我们在上一篇文章中有具体的介绍,并介绍了OpenCV中的一些常见知识点,这里我们来对颜色识别在无人驾驶中,做一个具体应用。
有兴趣的可以先看下本人拍摄的一个视频:无人车识别颜色并跟踪
通过视频我们可以看到无人车,会跟着自己设定的颜色而行驶,包括转弯的实现。那么无人车是如何识别颜色并跟踪的呢?其中的转弯又是怎么做到的呢?我们先来看下代码,代码就是很好的解释:
1、无人驾驶代码
from jetbotmini import Camera
from jetbotmini import bgr8_to_jpeg
import cv2
import numpy as np
import torch
import torchvision
import traitlets
import ipywidgets.widgets as widgets
from IPython.display import display
from jetbotmini import Robot
# 实例化摄像头
camera = Camera.instance(width=300, height=300)
# 蓝色上下限数组
color_lower = np.array([100,43,46])
color_upper = np.array([124, 255, 255])
# 初始化无人车驱动电机实例
robot = Robot()
# 图片、速度进度条、转弯进度条组件的显示
image_widget = widgets.Image(format='jpeg', width=300, height=300)
speed_widget = widgets.FloatSlider(value=0.4, min=0.0, max=1.0, description='speed')
turn_gain_widget = widgets.FloatSlider(value=0.5, min=0.0, max=2.0, description='turn gain')
display(widgets.VBox([widgets.HBox([image_widget]),speed_widget,turn_gain_widget]))
width = int(image_widget.width)
height = int(image_widget.height)
center_x = 0
def execute(change):
# -----这块属于对图像的处理与颜色检测,上篇文章有详细介绍------
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)
# 偏离中心位置来判断是否转弯
# [-1,1]
center_x = (150 - color_x)/150
robot.set_motors(
float(speed_widget.value + turn_gain_widget.value * center_x),
float(speed_widget.value - turn_gain_widget.value * center_x)
)
# 没有检测到就停止
else:
pass
robot.stop()
# 更新图像显示到Image组件
image_widget.value = bgr8_to_jpeg(frame)
将摄像头初始化,并且定义了一个能够检测到颜色的方法,除了识别颜色之外,我们还做了一个颜色目标位置在摄像头左右的偏移量,这个是用来给左右马达更新不同的速度值的。这个就是为什么无人车可以进行转弯的原因了,目标的中心位置在图像中心位置的偏离值,我们将其添加到马达的速度中去,左右马达一个是加另一个是减,这样就形成了差速!
接下来我们调用这个方法:execute({'new': camera.value})。
当然这种调用只能检测到一帧,我们需要的是实时更新摄像头的图像,实时的去跟踪变化的场景。
我们使用observe方法来实时处理:
camera.unobserve_all()
camera.observe(execute, names='value')
这样就可以实时检测蓝颜色目标,如果检测到目标,就会跟随目标行驶,如果没有检测到,无人车将会停止。
2、无人车停止
虽然上面代码可以在没有检测到目标的时候,无人车会自动停止,但有时候我们也想要强制停止无人车,如下:
import time
camera.unobserve_all()
time.sleep(1.0)
robot.stop()
我们可以看到上面的代码整体跟颜色识别章节差不多,区别在于多了一个无人车的加入,更准确来讲是多了左右两个轮子(马达),这两个轮子是差速轮,意思就是各自拥有一个马达驱动,速度可以不同(速度一样就是直行),这样才能够转弯和漂移等操作,根据识别到的颜色的中心位置来更新左右马达的速度,这样就有了跟踪的效果。
其中如何驱动无人车的更多详细介绍可以查阅:Jetson Nano驱动机器人的左右两路电机
3、camera.observe
observe(handler, names=traitlets.All, type='change')
设置一个在trait改变时调用的处理程序
其中handler是回调函数,调用的就是上面定义的execute函数,handler(change),其中change是一个字典,所以这也是调用execute时,参数是字典的原因{'new': camera.value}
在type为change的情况下,有以下几个键:
所以这里就做到了一个类似死循环的效果,可以对摄像头的每一帧进行更新。
4、camera.unobserve_all
camera.unobserve_all(name=traitlets.All)
删除指定名称的任何类型的trait更改处理程序。如果未指定名称,删除所有trait通知器。或者通俗来讲就是关闭视频流,释放资源的意思。
所以我们看到在camera.observe和robot.stop()之前都先做一步释放资源的处理以及停止无人车。
5、CSI摄像头
这里对摄像头做一个附加解释,随着人工智能的发展,自动驾驶,智能家居等应用都离不开摄像头,而一款低功耗低成本高清晰的摄像头就显得尤为重要了。一般大家见到的摄像头是USB接口,而这里是使用CSI接口协议的摄像头,如图:
可以看到这里是15针的排线,不是常见的USB接口,CSI接口是主机处理器与摄像头模块之间的高速串行接口,最关键点是能耗低,这也导致了现在手机高清摄像头的普及。