【项目背景】 
之前,有个网友咨询用行空板做视觉循迹车,我做了一个,给这个网友参考。之后这个车就被再“利用”了,今天再重新做一个。 
【项目设计】 
在佳立创重新打板,实现固定电机及电机驱动板。这个板子是为给micro:bit设计的小车底板,因行空行板也可使用这个电机驱动板,并且Mind+中Python模式下已有这个电机驱动板的扩展。 
【佳立创打板】 
电路比较简单,设置好布局和尺寸。 
 
 
 
【组装小车】 
电机驱动板使用3.7V锂电池供电。 
 
 
 
 
 
【程序编写】 
Mind+ Python模式下连接行空板编写程序。 
  
1.测试驱动电机使用pinpong库加载“DFMotor”驱动电机。 
			
			
			- 
 - #  -*- coding: UTF-8 -*-
 - 
 - # MindPlus
 - # Python
 - import time
 - from pinpong.board import Board
 - from pinpong.libs.microbit_motor import DFMotor
 - 
 - 
 - Board().begin()
 - p_dfr0548_motor_M1 = DFMotor(1)
 - p_dfr0548_motor_M2 = DFMotor(2)
 - p_dfr0548_motor_M3 = DFMotor(3)
 - p_dfr0548_motor_M4 = DFMotor(4)
 - p_dfr0548_motor_ALL = DFMotor(5)
 - 
 - while True:
 -     p_dfr0548_motor_M1.speed(200)
 -     p_dfr0548_motor_M1.run(p_dfr0548_motor_M1.CCW)
 -     p_dfr0548_motor_M2.speed(200)
 -     p_dfr0548_motor_M2.run(p_dfr0548_motor_M2.CCW)
 -     p_dfr0548_motor_M3.speed(200)
 -     p_dfr0548_motor_M3.run(p_dfr0548_motor_M3.CCW)
 -     p_dfr0548_motor_M4.speed(200)
 -     p_dfr0548_motor_M4.run(p_dfr0548_motor_M4.CCW)
 -     time.sleep(2)
 -     p_dfr0548_motor_ALL.stop()
 -     time.sleep(2)
 - 
 
  复制代码
  
 
2.行空板 Opencv 视频 识线加载opencv 库函数头文件”cv2“,开启USB摄像头采集图像,通过图像处理,获取目标颜色,进而识别目标”线“所在区域。 
- 
 - import cv2            # opencv 库函数头文件引用
 - import numpy as np    # python 库引用
 - import math           # python 库引用
 - 
 - import sys
 - class Twist:    
 -     x = 0
 -     z = 0
 -     linear=0
 -     angular=0
 - # 利用opencv 库函数,从摄像头采集一帧图像
 - cap = cv2.VideoCapture(0)
 - ret, cv_image = cap.read()
 - while ret:  
 -    ret, cv_image = cap.read()
 -    # 对图像进行裁剪
 -    height, width, channels = cv_image.shape
 -    descentre = 50
 -    rows_to_watch = 100
 -    crop_img = cv_image[(int((height)/4) + descentre):(int((height)/4)+ (descentre+rows_to_watch))][1:width]
 - 
 -    #将图像从RGB转换成HSV色彩空间
 -    hsv = cv2.cvtColor(crop_img,cv2.COLOR_RGB2HSV)
 -    # 设置需要提取的颜色的是绿色,然后确认其HSV空间下的范围
 -    lower = np.array([0,0,5])
 -    upper = np.array([180,255,100])
 - 
 -    #制作模板,绿色部分和其他颜色二值化后变为白色和黑色
 -    mask = cv2.inRange(hsv,lower ,upper)
 - 
 -    #将模板图像和原始图像按照 像素 位相与,提取出目标颜色
 -    res = cv2.bitwise_and(crop_img,crop_img,mask = mask)
 -    m = cv2.moments(mask,False)
 -    try:
 -         cx, cy = m['m10']/m['m00'], m['m01']/m['m00']
 -    except ZeroDivisionError:
 -         cx, cy = height/2, width/2
 -    height, width, channels = cv_image.shape
 -    error_x = cx - width / 2
 -    twist_object = Twist()
 -    # 设置固定的线速度值,单位 m/s
 -    twist_object.linear = 0.05
 - 
 -    # 将横向像素的偏差除以一个系数,该系数决定角速度值的大小,需要根据线速度和图像总宽度进行调节,才能达到比较好的循迹效果
 -    twist_object.angular = error_x / 80
 - 
 - # 二值化图像的像素统计,即统计目标颜色区域所占的像素个数
 -    no_red = cv2.countNonZero(mask)
 - 
 -    # 添加角速度和目标颜色像素判别,避免过大的角速度出现,防止车疯狂旋转
 -    if ((math.fabs(twist_object.angular)) < 10 and (no_red > 20000)):
 -         print(twist_object.angular)
 -    else:
 -       twist_object.linear = 0
 -       print("out of range")
 -       twist_object.angular = 0
 -    cv2.imshow("dst",mask)
 -    if cv2.waitKey(1) & 0xff== 27:
 -        break
 
  复制代码
  
 
3.完整程序 
- 
 - import cv2            # opencv 库函数头文件引用
 - import numpy as np    # python 库引用
 - import math           # python 库引用
 - 
 - import sys
 - import time
 - from pinpong.board import Board
 - from pinpong.libs.microbit_motor import DFMotor
 - 
 - 
 - Board().begin()
 - p_dfr0548_motor_M1 = DFMotor(1)
 - p_dfr0548_motor_M2 = DFMotor(2)
 - p_dfr0548_motor_M3 = DFMotor(3)
 - p_dfr0548_motor_M4 = DFMotor(4)
 - p_dfr0548_motor_ALL = DFMotor(5)
 - def speed(left,right):
 -     p_dfr0548_motor_M1.speed(left)
 -     p_dfr0548_motor_M1.run(p_dfr0548_motor_M1.CW)
 -     p_dfr0548_motor_M2.speed(left)
 -     p_dfr0548_motor_M2.run(p_dfr0548_motor_M2.CW)
 -     p_dfr0548_motor_M3.speed(right)
 -     p_dfr0548_motor_M3.run(p_dfr0548_motor_M3.CW)
 -     p_dfr0548_motor_M4.speed(right)
 -     p_dfr0548_motor_M4.run(p_dfr0548_motor_M4.CW)
 - def numberMap(x, in_min, in_max, out_min, out_max):
 -   return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min
 - num=30#电机启动基数
 - num2=100#电机调整范围
 - class Twist:    
 -     x = 0
 -     z = 0
 -     linear=0
 -     angular=0
 - # 利用opencv 库函数,从摄像头采集一帧图像
 - cap = cv2.VideoCapture(0)
 - ret, cv_image = cap.read()
 - while ret:  
 -    ret, cv_image = cap.read()
 -    # 对图像进行裁剪
 -    height, width, channels = cv_image.shape
 -    descentre = 50
 -    rows_to_watch = 100
 -    crop_img = cv_image[(int((height)/4) + descentre):(int((height)/4)+ (descentre+rows_to_watch))][1:width]
 - 
 -    #将图像从RGB转换成HSV色彩空间
 -    hsv = cv2.cvtColor(crop_img,cv2.COLOR_RGB2HSV)
 -    # 设置需要提取的颜色的是绿色,然后确认其HSV空间下的范围
 -    lower = np.array([0,0,5])
 -    upper = np.array([180,255,100])
 - 
 -    #制作模板,绿色部分和其他颜色二值化后变为白色和黑色
 -    mask = cv2.inRange(hsv,lower ,upper)
 - 
 -    #将模板图像和原始图像按照 像素 位相与,提取出目标颜色
 -    res = cv2.bitwise_and(crop_img,crop_img,mask = mask)
 -    m = cv2.moments(mask,False)
 -    try:
 -         cx, cy = m['m10']/m['m00'], m['m01']/m['m00']
 -    except ZeroDivisionError:
 -         cx, cy = height/2, width/2
 -    height, width, channels = cv_image.shape
 -    error_x = cx - width / 2
 -    twist_object = Twist()
 -    # 设置固定的线速度值,单位 m/s
 -    twist_object.linear = 0.05
 - 
 -    # 将横向像素的偏差除以一个系数,该系数决定角速度值的大小,需要根据线速度和图像总宽度进行调节,才能达到比较好的循迹效果
 -    twist_object.angular = error_x / 80
 - 
 - # 二值化图像的像素统计,即统计目标颜色区域所占的像素个数
 -    no_red = cv2.countNonZero(mask)
 - 
 -    # 添加角速度和目标颜色像素判别,避免过大的角速度出现,防止车疯狂旋转
 -    if ((math.fabs(twist_object.angular)) < 10 and (no_red > 20000)):
 -         print(twist_object.angular)
 -         if(twist_object.angular>0):
 -            left=int(numberMap(int(twist_object.angular*100),0,300,0,num2))
 -            print("left:"+str(left))
 -            speed(num-left,num+left)
 -         if(twist_object.angular<0):
 -            right=int(numberMap(int(abs(twist_object.angular)*100),0,300,0,num2))
 -            print("right:"+str(right))
 -            speed(num+right,num-right)
 - 
 -    else:
 -       twist_object.linear = 0
 -       print("out of range")
 -       twist_object.angular = 0
 -    #cv2.imshow("dst",mask)
 -    #if cv2.waitKey(1) & 0xff== 27:
 -        #break
 
  复制代码
  
  
 
  
【演示视频】 
 
 
 
 
 |