K210追小球程序与STM32最小系统板通信(自主学习)

本人先通过学习OPENMV,再延申到K210中。(主要是OPENMV我还没买屏幕,但是K210有)在OPENMV官网中,有相关追小球的函数,但是是用OPENMV单片机来进行调试的。在网上找到的相关源码,加载到OPENMV后发现帧数很慢。

学校实训的要求是要做出能追一定物体的平衡小车,我的小车的机构通过STM32F1Z8T6来进行控制,K210进行识别传输实时数据。

1,通过函数img.draw_rectangle(b[0:4]) ;可以得到画出目标的矩形框中的中心点X轴坐标和矩形框的大小SIZE。这两个数据前者可以用来判断转向环,后者可以用来判断前进和后退。

          x_pos = b[5]#中心位置
          Size = b.area()

通过实验测试,我得到了X:2~320范围,SIZE:2000~15000 

2,但是K210给32发送的是字符串,并且字符串的位数不确定,导致32用于接收到的字符串所存放的数组长度不确定,无法正常的转化数据。

为了方便的使用,我们在K210的发送函数中修改。

将X和SIZE字符串强制转化为固定位数的(在PYthon环境中)

          X = '%03d' % x_pos
          S  = '%05d' % Size

并设置发送出的帧头和帧尾给最终发送的数据DATA

 DATA = 'x' + X + S + 's'

以上情况是当K210'看到'目标的时候进行的操作.

3,若没有找到目标的时候呢??

我是这样操作的:

直接给他X=目标值;SIZE=目标值;  发送到32内部,分别进行了PID计算

在PID程序中 X是转向环的测量值 SIZE是追求环的测量值;

当测量值=期望值的时候,PID相当于一直相等(我的偏见理解就是不计算)

此时就只剩下 直立环和速度环

4,回到之前的问题  用K210的屏幕有什么用呢?

为了更好更方便的调试,我们直接把X和SIZE的数据直接在屏幕LCD中显示出来.

          img.draw_string(2,2, ("X:%03d" %(b[5])), color=(255,255,255), scale=2)
          img.draw_string(2,25, ("S:%04d" %(b.area())), color=(255,255,255), scale=2)

这样在调转向环和追求环的PID中的P时候就会方便很多.

以上放置源码:(针对有屏幕的K210)

import sensor, lcd, time
import sensor, time
from machine import UART,Timer
from fpioa_manager import fm
from Maix import GPIO
from fpioa_manager import fm
fm.register(12, fm.fpioa.GPIO0)
LED_B = GPIO(GPIO.GPIO0, GPIO.OUT) #构建LED对象
LED_B.value(0) #点亮LED
#摄像头初始化
sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QVGA)
sensor.set_vflip(1) #后置模式,所见即所得
sensor.run(1)
sensor.skip_frames(30)
from machine import UART,Timer
from fpioa_manager import fm

#映射串口引脚
fm.register(7 , fm.fpioa.UART1_RX, force=True)
fm.register(30, fm.fpioa.UART1_TX, force=True)

#初始化串口
uart = UART(UART.UART1, 115200, read_buf_len=4096)
#lcd初始化
lcd.init()

clock=time.clock()


color_threshold = (23, 100, 31, 51, 6, 43)
size_threshold = 2000
max_Size = 10
def find_max(blobs):
    max_size=0
    for blob in blobs:
        if blob[2]*blob[3] > max_size:
            max_blob=blob
            max_size = blob[2]*blob[3]
    return max_blob

LED_B.value(0) #点亮LED
while(1):
    clock.tick()
    img = sensor.snapshot()
    blobs = img.find_blobs([color_threshold],area_threshold=50,pixels_threshold=50)
    if blobs:
      #for b in blobs:
          b = find_max(blobs)
          img.draw_rectangle(b[0:4])  # circle
          img.draw_cross(b[5], b[6], color=(0, 0, 255))
          x_pos = b[5]#中心位置
          Size = b.area()
          X = '%03d' % x_pos
          S  = '%05d' % Size
          DATA = 'x' + X + S + 's'
          img.draw_string(2,2, ("X:%03d" %(b[5])), color=(255,255,255), scale=2)
          img.draw_string(2,25, ("S:%04d" %(b.area())), color=(255,255,255), scale=2)
          uart.write(DATA)
          print(DATA)
    else
          uart.write('x')
          uart.write('1')
          uart.write('6')
          uart.write('0')
          uart.write('0')
          uart.write('2')
          uart.write('0')
          uart.write('0')
          uart.write('0')
          uart.write('s')
          img.draw_string(2,2, ("X:%03d" %(160)), color=(255,255,255), scale=2)
          img.draw_string(2,25, ("S:%04d" %(2000)), color=(255,255,255), scale=2)
    lcd.display(img)     #LCD显示图片

在以上的函数中  我们会发现 定义了一个函数:find max

为什么要定义这个函数呢??

因为  当我们要求找到最大的目标色块,给它进行画框.不然会出现很多目标小点点.多个目标矩形框会导致串口终端发送的数值有不寻常的跳变.

下面针对没有屏幕的OPENMV:

OPENMV中与K210有些不同在于:

(1) 串口的设置  OPENMV的串口是固定的,不需要引脚映射.但是K210需要.

(2) 在OPENMV中,最大的色块可以直接通过Bolb[]来确定,不需要find max函数建立.

OPENMV是在官网的例程程序中修改的(确保帧速,不让他这么卡)

uart = UART(3, 115200)
uart.init(115200, bits=8, parity=None, stop=1, timeout_char=1000) # 使用给定参数初始化

# Only blobs that with more pixels than "pixel_threshold" and more area than "area_threshold" are
# returned by "find_blobs" below. Change "pixels_threshold" and "area_threshold" if you change the
# camera resolution. "merge=True" merges all overlapping blobs in the image.

while(True):
    clock.tick()
    img = sensor.snapshot()
    for blob in img.find_blobs([thresholds[threshold_index]], pixels_threshold=200, area_threshold=200, merge=True):
        # These values depend on the blob not being circular - otherwise they will be shaky.
        if blob.elongation() > 0.5:
            img.draw_edges(blob.min_corners(), color=(255,0,0))
            img.draw_line(blob.major_axis_line(), color=(0,255,0))
            img.draw_line(blob.minor_axis_line(), color=(0,0,255))
        # These values are stable all the time.
        #画框
        img.draw_rectangle(blob.rect())
        #画十字
        img.draw_cross(blob.cx(), blob.cy())
        # Note - the blob rotation is unique to 0-180 only.
        #特征点识别
        img.draw_keypoints([(blob.cx(), blob.cy(), int(math.degrees(blob.rotation())))], size=20)
        #返回x轴中心点位置,blob.cx() 返回色块的外框的中心x坐标(int),也可以通过blob[5]来获取
        x = blob[5]
        #blob.area() 返回色块的外框的面积。应该等于(w * h)
        s = blob.area()
        #发送数据
        #data = bytearray([0x11,x,s,0x00])
        X = '%03d'%(x)
        S = '%05d'%(s)
        data = 'x' + X  + S+ 's'
        #串口发送
        uart.write(data)
   # print(clock.fps())
        print(data)