#|树莓派视觉小车 -- 小球追踪(颜色追踪)(OpenCV色彩空间HSV)

目录
效果展示
基础理论(HSV)
为什么用HSV空间而不是RGB空间?
HSV
1、Hue(色相)
2、Value(明度)
3、Saturation(饱和度)
一、初始化
滑动条初始化
1、创建回调函数
2、窗口设置(名称)
3、滑动条设置
代码
二、运动函数
三、舵机控制
四、在HSV空间下获取二值图
1、获取滑动条的值
2、转HSV(三通道:H、S、V)
3、转二值图(单通道阈值处理)
4、结合HSV三个通道,得到最终二值图
代码
五、图像处理(总)
1、打开摄像头
2、获取HSV色彩空间得到的二值图
3、高斯滤波
4、开运算去噪
5、闭运算
6、霍夫圆检测
原理
API
6-1、霍夫圆检测
6-2、获取圆心和半径坐标
6-3、画圆
代码
六、运动控制
总代码
效果展示#|树莓派视觉小车 -- 小球追踪(颜色追踪)(OpenCV色彩空间HSV)
文章图片
#|树莓派视觉小车 -- 小球追踪(颜色追踪)(OpenCV色彩空间HSV)
文章图片

#|树莓派视觉小车 -- 小球追踪(颜色追踪)(OpenCV色彩空间HSV)
文章图片

#|树莓派视觉小车 -- 小球追踪(颜色追踪)(OpenCV色彩空间HSV)
文章图片



基础理论(HSV)

为什么用HSV空间而不是RGB空间? 因为RGB通道并不能很好地反映出物体具体的颜色信息。
而HSV空间能够非常直观的表达色彩的明暗、色调、以及鲜艳程度,方便进行颜色之间的对比。
(RGB受光线影响很大,所以采取HSV)
这里用HSV的目的:得到合适的二值图。

HSV #|树莓派视觉小车 -- 小球追踪(颜色追踪)(OpenCV色彩空间HSV)
文章图片

Hue(H):色调、色相(具体的颜色)
Saturation(S):饱和度、色彩纯净度
Value(V):明度
Hue范围是[0,179],饱和范围是[0,255],值范围是[0,255]
(写代码的时候,犯了蠢错误:把3个都设成了179,发现死活调不对)

1、Hue(色相) Hue:色相(具体的颜色)
#|树莓派视觉小车 -- 小球追踪(颜色追踪)(OpenCV色彩空间HSV)
文章图片



2、Value(明度) 明度:色彩的明亮程度,单通道亮度(并不等同于整体发光量)。
(明度越高越白,越低越黑,一般提高明度会同时提高R、G、B三通道的数值)

3、Saturation(饱和度) Saturation:饱和度、色彩纯度(越低越灰,越高越纯)。
(一般调高饱和度会降低RGB中相对较低的数值,凸显主要颜色的纯度。 )
#|树莓派视觉小车 -- 小球追踪(颜色追踪)(OpenCV色彩空间HSV)
文章图片


B站视频讲解:
短动画慢语速1分钟讲清影视调色中色彩形成原理基础——RGB与HSV


一、初始化 滑动条初始化 这次的初始化,除了电机、舵机、窗口等等的初始化,还有滑动条的初始设置。
在创建滑动条之前:
1、需要设置窗口名称,不然窗口都没有,自然也就无法设置滑动条了。
2、创建回调函数。
1、创建回调函数
def nothing(*arg): pass

2、窗口设置(名称)
def Trackbar_Init(): # 1 create windows cv2.namedWindow('h_binary') cv2.namedWindow('s_binary') cv2.namedWindow('v_binary')

3、滑动条设置
# 2 Create Trackbar cv2.createTrackbar('hmin', 'h_binary', 6, 179, nothing) cv2.createTrackbar('hmax', 'h_binary', 26, 179, nothing) cv2.createTrackbar('smin', 's_binary', 110, 255, nothing) cv2.createTrackbar('smax', 's_binary', 255, 255, nothing) cv2.createTrackbar('vmin', 'v_binary', 140, 255, nothing) cv2.createTrackbar('vmax', 'v_binary', 255, 255, nothing) #创建滑动条滑动条值名称 窗口名称滑动条值 滑动条阈值 回调函数


代码
def Motor_Init(): global L_Motor, R_Motor L_Motor= GPIO.PWM(l_motor,100) R_Motor = GPIO.PWM(r_motor,100) L_Motor.start(0) R_Motor.start(0)def Direction_Init(): GPIO.setup(left_back,GPIO.OUT) GPIO.setup(left_front,GPIO.OUT) GPIO.setup(l_motor,GPIO.OUT)GPIO.setup(right_front,GPIO.OUT) GPIO.setup(right_back,GPIO.OUT) GPIO.setup(r_motor,GPIO.OUT)def Servo_Init(): global pwm_servo pwm_servo=Adafruit_PCA9685.PCA9685()def Trackbar_Init(): # 1 create windows cv2.namedWindow('h_binary') cv2.namedWindow('s_binary') cv2.namedWindow('v_binary') # 2 Create Trackbar cv2.createTrackbar('hmin', 'h_binary', 6, 179, nothing) cv2.createTrackbar('hmax', 'h_binary', 26, 179, nothing) cv2.createTrackbar('smin', 's_binary', 110, 255, nothing) cv2.createTrackbar('smax', 's_binary', 255, 255, nothing) cv2.createTrackbar('vmin', 'v_binary', 140, 255, nothing) cv2.createTrackbar('vmax', 'v_binary', 255, 255, nothing) #创建滑动条滑动条值名称 窗口名称滑动条值 滑动条阈值 回调函数def Init(): GPIO.setwarnings(False) GPIO.setmode(GPIO.BCM) Direction_Init() Servo_Init() Motor_Init() Trackbar_Init()# 回调函数 def nothing(*arg): pass




二、运动函数 常规操作:向前、向后、向左、向右、停止。
def Front(speed): L_Motor.ChangeDutyCycle(speed) GPIO.output(left_front,1)#left_front GPIO.output(left_back,0)#left_backR_Motor.ChangeDutyCycle(speed) GPIO.output(right_front,1)#right_front GPIO.output(right_back,0)#right_backdef Back(speed): L_Motor.ChangeDutyCycle(speed) GPIO.output(left_front,0)#left_front GPIO.output(left_back,1)#left_backR_Motor.ChangeDutyCycle(speed) GPIO.output(right_front,0)#right_front GPIO.output(right_back,1)#right_backdef Left(speed): L_Motor.ChangeDutyCycle(speed) GPIO.output(left_front,0)#left_front GPIO.output(left_back,1)#left_backR_Motor.ChangeDutyCycle(speed) GPIO.output(right_front,1)#right_front GPIO.output(right_back,0)#right_backdef Right(speed): L_Motor.ChangeDutyCycle(speed) GPIO.output(left_front,1)#left_front GPIO.output(left_back,0)#left_backR_Motor.ChangeDutyCycle(speed) GPIO.output(right_front,0)#right_front GPIO.output(right_back,1)#right_backdef Stop(): L_Motor.ChangeDutyCycle(0) GPIO.output(left_front,0)#left_front GPIO.output(left_back,0)#left_backR_Motor.ChangeDutyCycle(0) GPIO.output(right_front,0)#right_front GPIO.output(right_back,0)#right_back


三、舵机控制
def set_servo_angle(channel,angle): angle=4096*((angle*11)+500)/20000 pwm_servo.set_pwm_freq(50)#frequency==50Hz (servo) pwm_servo.set_pwm(channel,0,int(angle))

set_servo_angle(4, 110)#top servolengthwise #0:back180:front set_servo_angle(5, 90)#bottom servocrosswise #0:left180:right



四、在HSV空间下获取二值图 1、获取滑动条的值
# 1 get trackbar's value hmin = cv2.getTrackbarPos('hmin', 'h_binary') hmax = cv2.getTrackbarPos('hmax', 'h_binary') smin = cv2.getTrackbarPos('smin', 's_binary') smax = cv2.getTrackbarPos('smax', 's_binary') vmin = cv2.getTrackbarPos('vmin', 'v_binary') vmax = cv2.getTrackbarPos('vmax', 'v_binary')


2、转HSV(三通道:H、S、V)
# 2 to HSV hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV) cv2.imshow('hsv', hsv) h, s, v = cv2.split(hsv)

#|树莓派视觉小车 -- 小球追踪(颜色追踪)(OpenCV色彩空间HSV)
文章图片

#|树莓派视觉小车 -- 小球追踪(颜色追踪)(OpenCV色彩空间HSV)
文章图片

#|树莓派视觉小车 -- 小球追踪(颜色追踪)(OpenCV色彩空间HSV)
文章图片


3、转二值图(单通道阈值处理) 分别对H、S、V三个道阈值处理:
# 3 set threshold (binary image) # if value in (min, max):white; otherwise:black h_binary = cv2.inRange(np.array(h), np.array(hmin), np.array(hmax)) s_binary = cv2.inRange(np.array(s), np.array(smin), np.array(smax)) v_binary = cv2.inRange(np.array(v), np.array(vmin), np.array(vmax))# 5 Show cv2.imshow('h_binary', h_binary) cv2.imshow('s_binary', s_binary) cv2.imshow('v_binary', v_binary)

#|树莓派视觉小车 -- 小球追踪(颜色追踪)(OpenCV色彩空间HSV)
文章图片
#|树莓派视觉小车 -- 小球追踪(颜色追踪)(OpenCV色彩空间HSV)
文章图片
#|树莓派视觉小车 -- 小球追踪(颜色追踪)(OpenCV色彩空间HSV)
文章图片


4、结合HSV三个通道,得到最终二值图 对H、S、V三个通道与操作。(H&&S&&V)
# 4 get binary binary = cv2.bitwise_and(h_binary, cv2.bitwise_and(s_binary, v_binary))

#|树莓派视觉小车 -- 小球追踪(颜色追踪)(OpenCV色彩空间HSV)
文章图片


代码
# 在HSV色彩空间下得到二值图 def Get_HSV(image): # 1 get trackbar's value hmin = cv2.getTrackbarPos('hmin', 'h_binary') hmax = cv2.getTrackbarPos('hmax', 'h_binary') smin = cv2.getTrackbarPos('smin', 's_binary') smax = cv2.getTrackbarPos('smax', 's_binary') vmin = cv2.getTrackbarPos('vmin', 'v_binary') vmax = cv2.getTrackbarPos('vmax', 'v_binary')# 2 to HSV hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV) cv2.imshow('hsv', hsv) h, s, v = cv2.split(hsv)# 3 set threshold (binary image) # if value in (min, max):white; otherwise:black h_binary = cv2.inRange(np.array(h), np.array(hmin), np.array(hmax)) s_binary = cv2.inRange(np.array(s), np.array(smin), np.array(smax)) v_binary = cv2.inRange(np.array(v), np.array(vmin), np.array(vmax))# 4 get binary(对H、S、V三个通道分别与操作) binary = cv2.bitwise_and(h_binary, cv2.bitwise_and(s_binary, v_binary))# 5 Show cv2.imshow('h_binary', h_binary) cv2.imshow('s_binary', s_binary) cv2.imshow('v_binary', v_binary) cv2.imshow('binary', binary)return binary



五、图像处理(总) 1、打开摄像头
# 1 Capture the frames ret, frame = camera.read() image = frame cv2.imshow('frame', frame)

#|树莓派视觉小车 -- 小球追踪(颜色追踪)(OpenCV色彩空间HSV)
文章图片


2、获取HSV色彩空间得到的二值图 (Get_HSV(frame)其实就是步骤四的HSV处理)
# 2 get HSV binary = Get_HSV(frame)


3、高斯滤波
# 3 Gausi blur blur = cv2.GaussianBlur(binary,(9,9),0)


4、开运算去噪
# 4 Open kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9,9)) Open = cv2.morphologyEx(blur, cv2.MORPH_OPEN, kernel) cv2.imshow('Open',Open)

#|树莓派视觉小车 -- 小球追踪(颜色追踪)(OpenCV色彩空间HSV)
文章图片


5、闭运算
# 5 Close Close = cv2.morphologyEx(Open, cv2.MORPH_CLOSE, kernel) cv2.imshow('Close',Close)

#|树莓派视觉小车 -- 小球追踪(颜色追踪)(OpenCV色彩空间HSV)
文章图片


6、霍夫圆检测 原理
霍夫变换圆检测是基于图像梯度实现:
圆心检测的原理︰圆心是圆周法线的交汇处,设置一个阈值,在某点的相交的直线的条数大于这个阈值就认为该交汇点为圆心。
圆半径确定原理:圆心到圆周上的距离〔半径)是相同的,设置一个阈值,只要相同距离的数量大于该阈值,就认为该距离是该圆心的半径。

API
def HoughCircles(image: Any, method: Any, dp: Any, minDist: Any, circles: Any = None, param1: Any = None, param2: Any = None, minRadius: Any = None, maxRadius: Any = None) -> None

参数:
  • 第二个参数,int类型的method,即使用的检测方法,目前OpenCV中就霍夫梯度法一种可以使用,它的标识符为CV_HOUGH_GRADIENT,在此参数处填这个标识符即可。
  • 第三个参数,double类型的dp,用来检测圆心的累加器图像的分辨率于输入图像之比的倒数,且此参数允许创建一个比输入图像分辨率低的累加器。上述文字不好理解的话,来看例子吧。例如,如果dp= 1时,累加器和输入图像具有相同的分辨率。如果dp=2,累加器便有输入图像一半那么大的宽度和高度。
  • 第四个参数,double类型的minDist,为霍夫变换检测到的圆的圆心之间的最小距离,即让我们的算法能明显区分的两个不同圆之间的最小距离。这个参数如果太小的话,多个相邻的圆可能被错误地检测成了一个重合的圆。反之,这个参数设置太大的话,某些圆就不能被检测出来了。
  • 第五个参数,InputArray类型的circles,经过调用HoughCircles函数后此参数存储了检测到的圆的输出矢量,每个矢量由包含了3个元素的浮点矢量(x, y, radius)表示。
  • 第六个参数,double类型的param1,有默认值100。它是第三个参数method设置的检测方法的对应的参数。对当前唯一的方法霍夫梯度法CV_HOUGH_GRADIENT,它表示传递给canny边缘检测算子的高阈值,而低阈值为高阈值的一半。
  • 第七个参数,double类型的param2,也有默认值100。它是第三个参数method设置的检测方法的对应的参数。对当前唯一的方法霍夫梯度法CV_HOUGH_GRADIENT,它表示在检测阶段圆心的累加器阈值。它越小的话,就可以检测到更多根本不存在的圆,而它越大的话,能通过检测的圆就更加接近完美的圆形了。
  • 第八个参数,int类型的minRadius,有默认值0,表示圆半径的最小值。
  • 第九个参数,int类型的maxRadius,也有默认值0,表示圆半径的最大值。

6-1、霍夫圆检测
# 6 Hough Circle detect circles = cv2.HoughCircles(Close,cv2.HOUGH_GRADIENT,2,120,param1=120,param2=20,minRadius=20,maxRadius=0) #param2:决定圆能否被检测到(越少越容易检测到圆,但相应的也更容易出错)


6-2、获取圆心和半径坐标
# 1 获取圆的圆心和半径 x, y, r = int(circles[0][0][0]),int(circles[0][0][1]),int(circles[0][0][2]) print(x, y, r)


6-3、画圆
# 2 画圆 cv2.circle(image, (x, y), r, (255,0,255),5) cv2.imshow('image', image)

#|树莓派视觉小车 -- 小球追踪(颜色追踪)(OpenCV色彩空间HSV)
文章图片


代码
# 图像处理 def Image_Processing(): global h, s, v # 1 Capture the frames ret, frame = camera.read() image = frame cv2.imshow('frame', frame)# 2 get HSV binary = Get_HSV(frame)# 3 Gausi blur blur = cv2.GaussianBlur(binary,(9,9),0)# 4 Open kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9,9)) Open = cv2.morphologyEx(blur, cv2.MORPH_OPEN, kernel) cv2.imshow('Open',Open) # 5 Close Close = cv2.morphologyEx(Open, cv2.MORPH_CLOSE, kernel) cv2.imshow('Close',Close)# 6 Hough Circle detect circles = cv2.HoughCircles(Close,cv2.HOUGH_GRADIENT,2,120,param1=120,param2=20,minRadius=20,maxRadius=0) #param2:决定圆能否被检测到(越少越容易检测到圆,但相应的也更容易出错) # judge if circles is exist if circles is not None: # 1 获取圆的圆心和半径 x, y, r = int(circles[0][0][0]),int(circles[0][0][1]),int(circles[0][0][2]) print(x, y, r) # 2 画圆 cv2.circle(image, (x, y), r, (255,0,255),5) cv2.imshow('image', image) else: (x,y),r = (0,0), 0return (x,y), r



六、运动控制 根据检测到的圆,获取到的坐标、半径,进行运动控制。
这里可以做到跟踪小球,前进和后退相配合,“敌进我退,敌退我进”。
# 运动控制(这里可以做到跟踪小球,前景和后退相配合,“敌进我退,敌退我进”) def Move((x,y), r): low_xlimit = width/4 high_xlimit = 0.75 * width #low_ylimit = 3/4 * height ylimit = 0.75 * height print(high_xlimit, ylimit) # 没检测到,停止不动 if x==0: Stop() # 检测到在图片0.75以上的区域(距离正常) elif x>low_xlimit and xlow_xlimit and x=ylimit: Back(60) # 在左0.25区域,向左跟踪 elif xhigh_xlimit: Right(60)



总代码
#Ball Tracking(HSV) importRPi.GPIO as GPIO import time import Adafruit_PCA9685 import numpy as np import cv2#set capture window width, height = 320, 240 camera = cv2.VideoCapture(0) camera.set(3,width) camera.set(4,height) l_motor = 18 left_front=22 left_back=27r_motor = 23 right_front= 25 right_back=24def Motor_Init(): global L_Motor, R_Motor L_Motor= GPIO.PWM(l_motor,100) R_Motor = GPIO.PWM(r_motor,100) L_Motor.start(0) R_Motor.start(0)def Direction_Init(): GPIO.setup(left_back,GPIO.OUT) GPIO.setup(left_front,GPIO.OUT) GPIO.setup(l_motor,GPIO.OUT)GPIO.setup(right_front,GPIO.OUT) GPIO.setup(right_back,GPIO.OUT) GPIO.setup(r_motor,GPIO.OUT)def Servo_Init(): global pwm_servo pwm_servo=Adafruit_PCA9685.PCA9685()def Trackbar_Init(): # 1 create windows cv2.namedWindow('h_binary') cv2.namedWindow('s_binary') cv2.namedWindow('v_binary') # 2 Create Trackbar cv2.createTrackbar('hmin', 'h_binary', 6, 179, nothing) cv2.createTrackbar('hmax', 'h_binary', 26, 179, nothing) cv2.createTrackbar('smin', 's_binary', 110, 255, nothing) cv2.createTrackbar('smax', 's_binary', 255, 255, nothing) cv2.createTrackbar('vmin', 'v_binary', 140, 255, nothing) cv2.createTrackbar('vmax', 'v_binary', 255, 255, nothing) #创建滑动条滑动条值名称 窗口名称滑动条值 滑动条阈值 回调函数def Init(): GPIO.setwarnings(False) GPIO.setmode(GPIO.BCM) Direction_Init() Servo_Init() Motor_Init() Trackbar_Init()def Front(speed): L_Motor.ChangeDutyCycle(speed) GPIO.output(left_front,1)#left_front GPIO.output(left_back,0)#left_backR_Motor.ChangeDutyCycle(speed) GPIO.output(right_front,1)#right_front GPIO.output(right_back,0)#right_backdef Back(speed): L_Motor.ChangeDutyCycle(speed) GPIO.output(left_front,0)#left_front GPIO.output(left_back,1)#left_backR_Motor.ChangeDutyCycle(speed) GPIO.output(right_front,0)#right_front GPIO.output(right_back,1)#right_backdef Left(speed): L_Motor.ChangeDutyCycle(speed) GPIO.output(left_front,0)#left_front GPIO.output(left_back,1)#left_backR_Motor.ChangeDutyCycle(speed) GPIO.output(right_front,1)#right_front GPIO.output(right_back,0)#right_backdef Right(speed): L_Motor.ChangeDutyCycle(speed) GPIO.output(left_front,1)#left_front GPIO.output(left_back,0)#left_backR_Motor.ChangeDutyCycle(speed) GPIO.output(right_front,0)#right_front GPIO.output(right_back,1)#right_backdef Stop(): L_Motor.ChangeDutyCycle(0) GPIO.output(left_front,0)#left_front GPIO.output(left_back,0)#left_backR_Motor.ChangeDutyCycle(0) GPIO.output(right_front,0)#right_front GPIO.output(right_back,0)#right_backdef set_servo_angle(channel,angle): angle=4096*((angle*11)+500)/20000 pwm_servo.set_pwm_freq(50)#frequency==50Hz (servo) pwm_servo.set_pwm(channel,0,int(angle))# 回调函数 def nothing(*arg): pass# 在HSV色彩空间下得到二值图 def Get_HSV(image): # 1 get trackbar's value hmin = cv2.getTrackbarPos('hmin', 'h_binary') hmax = cv2.getTrackbarPos('hmax', 'h_binary') smin = cv2.getTrackbarPos('smin', 's_binary') smax = cv2.getTrackbarPos('smax', 's_binary') vmin = cv2.getTrackbarPos('vmin', 'v_binary') vmax = cv2.getTrackbarPos('vmax', 'v_binary')# 2 to HSV hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV) cv2.imshow('hsv', hsv) h, s, v = cv2.split(hsv)# 3 set threshold (binary image) # if value in (min, max):white; otherwise:black h_binary = cv2.inRange(np.array(h), np.array(hmin), np.array(hmax)) s_binary = cv2.inRange(np.array(s), np.array(smin), np.array(smax)) v_binary = cv2.inRange(np.array(v), np.array(vmin), np.array(vmax))# 4 get binary(对H、S、V三个通道分别与操作) binary = cv2.bitwise_and(h_binary, cv2.bitwise_and(s_binary, v_binary))# 5 Show cv2.imshow('h_binary', h_binary) cv2.imshow('s_binary', s_binary) cv2.imshow('v_binary', v_binary) cv2.imshow('binary', binary)return binary# 图像处理 def Image_Processing(): global h, s, v # 1 Capture the frames ret, frame = camera.read() image = frame cv2.imshow('frame', frame)# 2 get HSV binary = Get_HSV(frame)# 3 Gausi blur blur = cv2.GaussianBlur(binary,(9,9),0)# 4 Open kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9,9)) Open = cv2.morphologyEx(blur, cv2.MORPH_OPEN, kernel) cv2.imshow('Open',Open) # 5 Close Close = cv2.morphologyEx(Open, cv2.MORPH_CLOSE, kernel) cv2.imshow('Close',Close)# 6 Hough Circle detect circles = cv2.HoughCircles(Close,cv2.HOUGH_GRADIENT,2,120,param1=120,param2=20,minRadius=20,maxRadius=0) #param2:决定圆能否被检测到(越少越容易检测到圆,但相应的也更容易出错) # judge if circles is exist if circles is not None: # 1 获取圆的圆心和半径 x, y, r = int(circles[0][0][0]),int(circles[0][0][1]),int(circles[0][0][2]) print(x, y, r) # 2 画圆 cv2.circle(image, (x, y), r, (255,0,255),5) cv2.imshow('image', image) else: (x,y),r = (0,0), 0return (x,y), r# 运动控制(这里可以做到跟踪小球,前景和后退相配合,“敌进我退,敌退我进”) def Move((x,y), r): low_xlimit = width/4 high_xlimit = 0.75 * width #low_ylimit = 3/4 * height ylimit = 0.75 * height print(high_xlimit, ylimit) # 没检测到,停止不动 if x==0: Stop() # 检测到在图片0.75以上的区域(距离正常) elif x>low_xlimit and xlow_xlimit and x=ylimit: Back(60) # 在左0.25区域,向左跟踪 elif xhigh_xlimit: Right(60)if __name__ == '__main__': Init()set_servo_angle(4, 110)#top servolengthwise #0:back180:front set_servo_angle(5, 90)#bottom servocrosswise #0:left180:rightwhile 1: # 1 Image Process (x,y), r = Image_Processing()# 2 Move Move((x,y), r)# must include this codes(otherwise you can't open camera successfully) if cv2.waitKey(1) & 0xFF == ord('q'): Stop() GPIO.cleanup() break


这里的HSV是根据我自己当前的情况调节的,更改场景以后可能需要重新调节H、S、V三通道的阈值(max && min)
【#|树莓派视觉小车 -- 小球追踪(颜色追踪)(OpenCV色彩空间HSV)】基础的视觉检测+运动,没有太多的运动控制算法(PID等等都没有涉及到)。有好的想法和建议欢迎交流讨论,Thanks?(?ω?)?

    推荐阅读