2023年电赛---运动目标控制与自动追踪系统(E题)OpenMV方案

前言

(1)废话少说,很多人可能无法访问GitHub,所以我直接贴出可能要用的代码。此博客还会进行更新,先贴教程和代码

(2)视频教程: https://singtown.com/learn/49603/

(3)有些人肯定说红点太小了,OpenMV不行精度不够,看这个C站上做出来的阈值代码:https://blog.csdn.net/weixin_52385589/article/details/126334744

(4)赛事方肯定不可能直接让你上现成的玩意,不然比赛的意义在哪里 ?我看了一下网上的一些讨论。肯定是要用摄像头的,K210好像有官方库,K210的同学可以了解一下。

(5)OpenMV的话需要多训练,建议最好做灯光补偿,毕竟OpenMV对灯光要求很高,环境光线一点要稳定!!!

硬件注意事项

接线问题

(1)我们这里是使用的OpenMV的P8和P7引脚。所以建议引脚如下图引出。

(2)舵机一般是5V供电,所以注意VCC是供5V的电压。而右边那两个GND和VCC是连接OpenMV的。如果右边的VCC是连接OpenMV的VIN引脚,就可以供5V电。如果是连接OpenMV的3.3V引脚,iu只能供3.3V电压。否则OpenMV会被烧掉!

(3)舵机的VCC要直接连接电池,因为如果通过OpenMV连接,OpenMV的输出电流太小,带不动舵机!

出现Frame capture has timed out. 帧捕获超时了

(1)如果是我贴出来的代码,出现这个bug。你重新开机启动应该就可以了。

(2)如果还不可以,就说明你OpenMV坏了。要快点换一个。

main.py

代码

py 复制代码
import sensor, image, time

from pid import PID
from pyb import Servo  #从内置pyb导入servo类,也就是舵机控制类

pan_servo=Servo(1)  #定义两个舵机,对应P7引脚
tilt_servo=Servo(2) #定义两个舵机,对应P8引脚

pan_servo.calibration(500,2500,500)
tilt_servo.calibration(500,2500,500)

red_threshold  = (13, 49, 18, 61, 6, 47)  #设置红色阈值

pan_pid = PID(p=0.07, i=0, imax=90) #PID参数,只需要调整P量即可,设置P7引脚的PI值
tilt_pid = PID(p=0.05, i=0, imax=90) #PID参数,只需要调整P量即可,设置P8引脚的PI值
#pan_pid = PID(p=0.1, i=0, imax=90)#在线调试使用这个PID
#tilt_pid = PID(p=0.1, i=0, imax=90)#在线调试使用这个PID

sensor.reset() # 初始化摄像头传感器
sensor.set_pixformat(sensor.RGB565) # 使用 RGB565 彩图
sensor.set_framesize(sensor.QQVGA) # 使用 QQVGA 分辨率
sensor.skip_frames(10) #跳过几帧,让新的设置生效。
sensor.set_auto_whitebal(False) # 因为是颜色识别,所以需要把白平衡关闭
clock = time.clock() # 追踪帧率,影响不大

#__________________________________________________________________
#定义寻找最大色块的函数,因为图像中有多个色块,所以追踪最大的那个
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

#__________________________________________________________________
while(True):
    clock.tick() # 跟踪快照()之间经过的毫秒数。
    img = sensor.snapshot() # 截取一张图片

    blobs = img.find_blobs([red_threshold]) #识别红色阈值
    if blobs:   #如果找到红色色块
        max_blob = find_max(blobs)  #调用上面自定义函数,找到最大色块
        pan_error = max_blob.cx()-img.width()/2
        tilt_error = max_blob.cy()-img.height()/2

        print("pan_error: ", pan_error)

        img.draw_rectangle(max_blob.rect()) # 在找到最大色块画一个矩形框
        img.draw_cross(max_blob.cx(), max_blob.cy()) # cx, cy

        pan_output=pan_pid.get_pid(pan_error,1)/2
        tilt_output=tilt_pid.get_pid(tilt_error,1) #上面两个都说进行PID运算
        print("pan_output",pan_output)
        pan_servo.angle(pan_servo.angle()+pan_output) #将最终值传入两个舵机中,追踪目标
        tilt_servo.angle(tilt_servo.angle()-tilt_output)# 因为两个舵机方向和摆放位置不同,所以一个是+一个是-

舵机控制

舵机选择

(1)Servo是舵机控制类。因为我们使用from pyb import Servo直接从pyd导入了servo类。所以可以直接写成Servo()。

(2)引脚对应关系:Servo(1)------P7,Servo(2)------P8,Servo(3)------P9。

(3)

<1>因此我们可以知道,pan_servo.calibration就是对P7进行相应的控制,因为pan_servo=Servo(1)。

<2>tilt_servo.calibration就是对P8控制,因为tilt_servo=Servo(2)

舵机脉冲设置

(1)pan_servo.calibration(500,2500,500) , 这个其实就是设置舵机的脉宽的。因为我们知道,常见的舵机周期都是20ms,脉宽都是再500us到2500us之间。

(2)所以这里的第一个参数是500,第二个参数是2500。第三个参数是舵机0°位置,根据下图可以知道,也是500。最后两个值是可以填可不填的,个人不建议填写。


颜色阈值设置

(1)因为我们要追踪红色,所以是使用red_threshold = (13, 49, 18, 61, 6, 47) 进行设置阈值。

(2)颜色阈值设置教程:OpenMV颜色阈值设置

PID的P和I参数设置

(1)注意,因为云台是比较稳定的,不要求高反应速度,所以我猜测只使用了PI ,而没有使用PID。因此我们可以看到pan_pid和tilt_pid只有P和I两个参数。

(2)I的参数不需要进行调整!imax是用于积分限幅的,这个也别更改!,如果你的云台抖动厉害,说明P过大了,需要调小。

(3)如果你感觉你云台反应太慢,就需要调高P值。

(4)最佳的P值是,你云台有抖动的前一个值。这个才是P的最优值。但是我认为,P没必要太大,因为云台还是比较稳的。

(5)所以说,只需要调整P值,I值和imax值不需要进行调整!!!

帧率禁用

(1)当OpenMV连接电脑端IDE的时候,运行帧率和不连接电脑端IDE是不一样的。因为我们连接上电脑端IDE的时候,OpenMV会向电脑端IDE传输数据,所以会导致帧率下降。

(2)帧率下降,会导致我们实际脱机跑的时候,PID参数和连接上电脑端IDE时候的PID参数不一样。

(3)所以我们需要点击电脑端右上角的禁用,或者是Disable。被禁用之后,我们的效果就是脱机之后真实运行效果。

pid.py

(1)这里面的代码我们不需要管,就算PID的代码。

(2)再次强调,这里请别擅自更改,出现问题自己负责。

py 复制代码
from pyb import millis
from math import pi, isnan
 
class PID:
    _kp = _ki = _kd = _integrator = _imax = 0
    _last_error = _last_derivative = _last_t = 0
    _RC = 1/(2 * pi * 20)
    def __init__(self, p=0, i=0, d=0, imax=0):
        self._kp = float(p)
        self._ki = float(i)
        self._kd = float(d)
        self._imax = abs(imax)
        self._last_derivative = float('nan')
 
    def get_pid(self, error, scaler):
        tnow = millis()
        dt = tnow - self._last_t
        output = 0
        if self._last_t == 0 or dt > 1000:
            dt = 0
            self.reset_I()
        self._last_t = tnow
        delta_time = float(dt) / float(1000)
        output += error * self._kp
        if abs(self._kd) > 0 and dt > 0:
            if isnan(self._last_derivative):
                derivative = 0
                self._last_derivative = 0
            else:
                derivative = (error - self._last_error) / delta_time
            derivative = self._last_derivative + \
                                     ((delta_time / (self._RC + delta_time)) * \
                                        (derivative - self._last_derivative))
            self._last_error = error
            self._last_derivative = derivative
            output += self._kd * derivative
        output *= scaler
        if abs(self._ki) > 0 and dt > 0:
            self._integrator += (error * self._ki) * scaler * delta_time
            if self._integrator < -self._imax: self._integrator = -self._imax
            elif self._integrator > self._imax: self._integrator = self._imax
            output += self._integrator
        return output
    def reset_I(self):
        self._integrator = 0
        self._last_derivative = float('nan')

关于C站那位做出结果的阈值代码注意事项

图像类型

(1)sensor.set_pixformat设置的图像类型不同。

<1>我拿官方代码和上面贴出来的那个代码进行了对比,发现了一件事情。他的sensor.set_pixformat是设置成GRAYSCALE图像,而我们这里是设置成RGB565图像。

<2>将图像画质设置成GRAYSCALE好处在于,如果只有两个颜色,更容易进行分辨,而且每个像素所占空间也减少了,可以适当的增加分辨率。但是也有缺点,如果是多颜色识别就会出现问题。

<3>本题只有三种颜色,底板白色,追踪的绿点,被追踪的红点。因为只需要追踪一个颜色,所以各位可以尝试将 sensor.set_pixformat(sensor.RGB565) 改成 sensor.set_pixformat(sensor.GRAYSCALE)

图像分辨率

(2)图像分辨率,sensor.set_framesize(sensor.QQVGA)

<1>官方例程的分辨率是QQVGA,像素点是160x120。而那个博客的分辨率是QVGA,像素点是 320x240。

<2>更高的分辨率,识别效果更好,但是分辨率也别无脑调高,否则颜色阈值方便会很难设置。

跳过帧数

(3)跳过帧数,sensor.skip_frames(10)

<1>这个就是给OpenMV初始化一个缓冲时间,他那边是设置的跳过900张图片。官方例程是跳过10个图片。

<2>这个选取看你自己,我感觉没必要900,太多了。

曝光度设置

(4)sensor.set_auto_exposure(False, 1000)

<1>这个是用于设置曝光度的,曝光过度照片看起来会过亮,曝光不足图片看起来会太暗。

<2>官方例程没有调用这个函数,说明曝光是一直打开的。

<3>C站那位是设置的每1ms曝光一次。你们可以根据自己测试结果来设置。

白平衡和自增益

(5)sensor.set_auto_whitebal(False)和sensor.set_auto_gain(False)

<1>按理来说颜色识别的话,这两个都需要关闭的。C站另外那个博客就是都关闭了。但是官方只关闭了set_auto_whitebal()白平衡。

<2>我个人建议还是先都关闭

E题官方解释

1问:E题可以使用openmv吗?

答:E题只禁止笔记本和台式机。其他任意

2问:E题目标运动追踪系统能用树莓派不?

答:同上。

3问:E题,云台和屏幕的高度关系是怎么样的?

答:自定。

4问:请问e题基本要求(1)进行自动复位时屏幕的位置是一直固定吗?

答:当然,只有绿色激光笔可以移动。

5问:E题中主板能用stm32的战舰精英mini版吗?E题中基本要求一测试完之后是否可以复位?

6问:请问E题运动目标控制与自动追踪系统中的屏幕,对材质有什么特殊要求吗?

答:白色。

7问:E题 1. 摄像头模块的摆放位置是否有要求,题目中未提及;2. 现场测试的屏幕材料是否确定,可否提供材料名称?

答:屏幕可自带。其他自定。

8问:E题:封装时屏幕和靶纸要一起封入带去吗?还是测试时由组委会提供所有队伍统一使用的?

答:封箱后自己带。赛区不提供屏幕和靶纸。

9问:E题中的两种激光笔测试开始后是一直要亮着吗?

答:自定。

10问:E题的基础部分第一问里边的"在任意位置按下按键可以复位"任意位置需要我们自己能达到还是说只需要把重点放在能复位?

答:题目已经说的很清楚,光斑处在任意位置都可通过复位功能回到原点

11问:E题屏幕高度有要求么?

答:自定。

12问:E题基础部分第一问的"任意位置都可以复位"我们需要自己写程序让激光笔达到人意位置然后再按下按键复位还是说到哪个位置不需要管只要能做到复位就可以了?

答:题目已经说的很清楚,光斑处在任意位置都可通过复位功能回到原点。

13问:测试场地屏幕是否与外界环境线条分界明显?

答:没有要求。

相关推荐
温柔的男孩像海洋丶20 分钟前
vscode的keil assistant 中搜索不到全局变量
ide·vscode·单片机
沐欣工作室_lvyiyi34 分钟前
基于单片机的多功能智能小车(论文+源码)
stm32·单片机·嵌入式硬件·毕业设计·单片机毕业设计
Auc241 小时前
使用scrapy框架爬取微博热搜榜
开发语言·python
梦想画家1 小时前
Python Polars快速入门指南:LazyFrames
python·数据分析·polars
程序猿000001号1 小时前
使用Python的Seaborn库进行数据可视化
开发语言·python·信息可视化
鸿喵小仙女1 小时前
C# WPF读写STM32/GD32单片机Flash数据
stm32·单片机·c#·wpf
API快乐传递者1 小时前
Python爬虫获取淘宝详情接口详细解析
开发语言·爬虫·python
公众号Codewar原创作者1 小时前
R数据分析:工具变量回归的做法和解释,实例解析
开发语言·人工智能·python
lucy153027510792 小时前
MCU 功耗基准测试
科技·单片机·嵌入式硬件·智能家居·信号处理·工控主板
FL16238631292 小时前
python版本的Selenium的下载及chrome环境搭建和简单使用
chrome·python·selenium