【K210】K210学习笔记四——定时器的使用

【K210】K210学习笔记四——定时器的使用

  • 前言
  • 定时器
    • 定时器配置
    • 定时器使用方法
    • 测试
  • 小车简单巡线
    • PWM配置
    • 控制小车巡线的方法
    • 测试
  • 完整源码

前言

本人大四学生,电赛生涯已经走到尽头,一路上踩过不少坑,但运气也不错拿了两年省一,思来想去,决定开始写博客,将电赛经验分享一二,能力有限,高手轻喷。
往期的博客讲述了 K210 的感光元件模块 sensor 的配置,机器视觉模块 image 中部分函数的使用(目前是用 find_blobs 函数实现一些寻找不同颜色的目标点,寻找不同颜色的线,后面会更新更多 image 模块中的函数使用方法),以及按键、LCD、LED的使用。

sensor 的学习笔记传送门
【K210】K210学习笔记一——sensor
image 的学习笔记传送门
【K210】K210学习笔记二——image
按键、LCD、LED的使用 的学习笔记传送门
【K210】K210学习笔记三——按键、LCD、LED的使用

本文着重于 K210 的定时器配置及使用方法,结合往期代码,做一个简单的巡线小车。无需将目标信息传输到STM32,仅用K210上的定时器输出PWM即可完成。主要讲一下是怎么样使用定时器的,另外跟随目标的逻辑是怎么样的。若要将识别信息传输至STM32,可看本人往期博客,了解串口发送与接收,传送门如下。

串口通信 传送门
【串口通信】K210与STM32串口通信、K210与OpenMV串口通信

定时器

定时器配置

配置定时器需要导入一些模块,这里连同PWM的模块也一起导入,后面的配置就不需要导入了。

from machine import Timer, PWM                              # 从 machine 模块中导入 定时器模块 Timer 脉宽调制模块 PWM

与往期一样的,定义一个类来保存定时器的一些属性,我个人喜欢定义类来保存,因为这样可以提高效率,提高代码可读性,变量名称看起来不会很怪,可以一下子知道这个变量是什么。

#__________________________________________________________________
# 定时器的使用
# 定义定时器属性类
class timer_property():
    cnt     = 0                                             # 定时器计数值
    cnt_max = 0                                             # 定时器计数值上限
    period  = 0                                             # 定时器周期
    freq    = 0                                             # 定时器频率
    

然后就是实例化类,我是打算将定时器0配置为一个计数器,每次触发定时器中断,这个计数器的值加1,加到上限值,就将计数器的值置0,这样我们就可以在 while 循环中,判断这个计数器的值是否等于0,从而控制一些不需要高频率执行的函数的执行周期(比如各种打印信息) ,这种方法会比之前博文中使用的计数器自增控制周期的方法 更准确。但会存在一定的误差的,这是因为是在 while 循环中做的判断,while 循环的周期是不一定的(周期就是fps 比如fps等于20 则代表一秒会有20个循环),如果图像识别的速度很慢(数据量大),那么 while 循环的周期就很长,可能一秒就只有几次。

# 定时器0 配置_______________________________________________________
# 定时器0 实例化类
timer0 = timer_property()                                   # 实例化定时器属性类 timer_property() 为 timer0
timer0.cnt_max = 9                                          # 设定 定时器0 的计数值上限为 9
timer0.period = 100                                         # 设定 定时器0 的周期为 100

我在回调函数中只是做一个计数器值自增的操作,你也可以将需要间隔一定时间做的函数放到回调函数中,但需要注意的是,这个函数千万不能占用太长的时间,否则得不偿失。这也就是为什么我只在回调函数中做一个简单的计数器值自增,将实际要控制执行周期的函数放到 while 循环中的原因。

# 定时器0 定义回调函数
def timer0_back(tim0):
    if timer0.cnt < timer0.cnt_max:                         # 若 定时器0 的计数值小于 定时器0 的计数值上限
        timer0.cnt = timer0.cnt + 1                         # 计数值自增
    else:
        timer0.cnt = 0                                      # 超出计数值上限 则计数值重置为0
        

然后就是定时器的初始化,这里配置的是定时器0,通道0,模式为周期性(模式有一次性、周期性、PWM,一般都是周期性或PWM,一次性使用的很少),周期的单位为ms,周期为设定值 timer0.period这个变量在之前被设置为100,也就是说,定时器0的周期是100ms。

# 定时器0 初始化
tim0 = Timer(Timer.TIMER0,                                  # 定时器编号 定时器0
            Timer.CHANNEL0,                                 # 定时器通道 通道0
            mode = Timer.MODE_PERIODIC,                     # 定时器模式 周期性
            unit = Timer.UNIT_MS,                           # 定时器周期单位 ms
            period = timer0.period,                         # 定时器周期 timer0.period 若 unit 为 Timer.UNIT_MS 则周期为 timer0.period ms
            callback = timer0_back)                         # 定时器触发中断后执行的回调函数 timer0_back
            

定时器使用方法

使用方法就很简单了,在 while 循环中,做一个判断,当定时器0的计数器值被重置为0的时候,执行函数即可。我这里控制的是打印各参数信息的函数,因为我不需要这些参数发送的太快,发送太快我也看不清。你可以用它来控制你想控制的其他函数,比如可以用来控制串口发送,如果这里加一句串口发送函数的话,那么这个串口发送函数就是1秒发送一次。

    if timer0.cnt == 0:                                     # 如果 timer0.cnt 等于 0 此步骤的目的是控制打印周期 不要打印的太快
        print_sensor()                                      # 打印sensor参数
        print_blobs_property(black,"Black-")                # 打印黑色色块参数
        print_blobs_property(red,  "Red-  ")                # 打印红色色块参数
        

测试

将K210连接到MaixPy IDE,然后运行即可,在串行终端可以看到这些参数差不多间隔1s就会被打印一次。
【K210】K210学习笔记四——定时器的使用_第1张图片

小车简单巡线

PWM配置

首先还是定义一个电机类,来保存电机的信息,推荐大家养成这个习惯,对大家有好处。

# 定时器1 配置_______________________________________________________
# 电机类定义
class motor_property():
    motor1      = 0                                         # 电机1 占空比
    motor2      = 0                                         # 电机2 占空比
    motor3      = 0                                         # 电机3 占空比
    motor4      = 0                                         # 电机4 占空比

    motor1_pin  = 0                                         # 电机1 引脚
    motor2_pin  = 0                                         # 电机2 引脚
    motor3_pin  = 0                                         # 电机3 引脚
    motor4_pin  = 0                                         # 电机4 引脚

    control_x   = 0                                         # 被控坐标 x
    control_y   = 0                                         # 被控坐标 y
    

然后是实例化电机类,这里我只写了电机1和电机2,如果你要做四驱小车,可以再写电机3和电机4。 然后PWM输出引脚(也就是电机所接的引脚)我是设置电机1的引脚为14,电机2的引脚为13。这个是K210开发板上红灯和绿灯的引脚,主要是当前放暑假在家,手头上没有小车,所以这里先用LED来观察一下效果,看看逻辑是否正确,回校后我会在小车上进行测试,并更新此博文

# 实例化电机类
motor = motor_property()                                    # 实例化电机类 motor_property() 为 motor
motor.motor1 = 50                                           # 电机1的占空比 初始设置为 50%
motor.motor2 = 50                                           # 电机2的占空比 初始设置为 50%
motor.motor1_pin  = 14                                      # 电机1的引脚 14为红灯引脚 这里先用灯的亮灭观察效果
motor.motor2_pin  = 13                                      # 电机2的引脚 13为绿灯引脚 这里先用灯的亮灭观察效果

然后实例化一个定时器属性类,保存一下PWM占空比的频率信息。这里设置是1K,如果你需要改成其他数值也可以,比如10K。

# 定时器1 实例化类
timer1 = timer_property()                                   # 实例化定时器属性类 timer_property() 为 timer1
timer1.freq = 1000                                          # 设定 定时器1 的频率为 1000

然后就是PWM定时器的设置,这里我用的是定时器1,我这里只设置了两个通道,如果你要做四驱车,就复制一下这个代码,改个名字即可

# 定时器1 通道0 初始化
tim1_ch0 = Timer(Timer.TIMER1,                              # 定时器编号 定时器1
                 Timer.CHANNEL0,                            # 定时器通道 通道0
                 mode = Timer.MODE_PWM)                     # 定时器模式 PWM

# 定时器1 通道1 初始化
tim1_ch1 = Timer(Timer.TIMER1,                              # 定时器编号 定时器1
                 Timer.CHANNEL1,                            # 定时器通道 通道1
                 mode = Timer.MODE_PWM)                     # 定时器模式 PWM
                 

然后是创建电机对象,需要注意的就是这里要跟上面的那些参数名字对应的上。

# 创建对象 电机1 通道为 定时器1的通道0 频率为 定时器1的频率 占空比为 电机1的占空比 引脚为 电机1的引脚
motor1 = PWM(tim1_ch0, freq = timer1.freq, duty = motor.motor1, pin = motor.motor1_pin)

# 创建对象 电机2 通道为 定时器1的通道1 频率为 定时器1的频率 占空比为 电机2的占空比 引脚为 电机2的引脚
motor2 = PWM(tim1_ch1, freq = timer1.freq, duty = motor.motor2, pin = motor.motor2_pin)

控制小车巡线的方法

控制小车巡线就太简单了,将摄像头识别到的坐标,和要被控制到的坐标值传入即可,转换成0到50之间的值,然后控制电机转速即可。这不单单是巡线可以这样做,你也可以控制小车跟随其他目标,比如你可以拿一个红色的纸,放到摄像头的左边,小车就会往左边开。

# 定义电机占空比控制函数
def motor_control(motor, x):
    val = 0
    if x < motor.control_x:                                 # 若 当前坐标 小于 被控坐标x 即当前状态小车在目标的 左边
        val = (motor.control_x - x) * 0.3125                # 获取坐标差值 并转换为 0~50 之间的值
        motor.motor1 = 50 - val                             # 减小 电机1 占空比 电机1为左电机 使小车右转
        motor.motor2 = 50 + val                             # 增大 电机2 占空比 电机2为右电机 使小车右转

    elif x > motor.control_x:                               # 若 当前坐标 大于 被控坐标x 即当前状态小车在目标的 右边
        val = (x - motor.control_x) * 0.3125                # 获取坐标差值 并转换为 0~50 之间的值
        motor.motor1 = 50 + val                             # 增大 电机1 占空比 电机1为左电机 使小车左转
        motor.motor2 = 50 - val                             # 减小 电机2 占空比 电机2为右电机 使小车左转

    motor.motor1 = int(motor.motor1)                        # 将 电机1占空比 转换为 整数
    motor.motor2 = int(motor.motor2)                        # 将 电机1占空比 转换为 整数
    

具体实现是这样实现的,首先会拍摄一张图片,然后在这张图片中找红色色块,获取红色色块的坐标值,然后设定将该坐标值控制到160。通过电机占空比控制函数获取占空比,将占空比重新装载即可,逻辑就是。
比如我识别到红色色块的坐标为20,而我要控制它走到160的位置,20说明小车当前在该红色目标的右边,那么我就要控制它左转,同样的,如果识别到的坐标是300,那就说明小车在目标左边,右转即可,如此一来便可完成巡线或者是目标跟踪。

#__________________________________________________________________
# 主函数
while(True):

    clock.tick()                                            # 跟踪运行时间

    img = sensor.snapshot()                                 # 拍摄一张照片

    #opv_find_blobs(black,1)                                # 找黑色色块 led标志为1 表示黑色
    opv_find_blobs(red,2)                                   # 找红色色块 led标志为2 表示红色

    point_control(key)                                      # 按键控制下的目标点获取函数

    lcd.display(img)                                        # LCD 显示图像
    lcd_key()                                               # LCD 显示按键信息及目标点信息

    #led_control(red.led_flag)                              # LED 标记色块识别情况

    motor.control_x = 160                                   # 控制目标处于 x轴中心点 160
    motor_control(motor,red.cx)                             # 电机占空比控制函数获取电机控制占空比

    motor1.duty(motor.motor1)                               # 将获取到的电机1占空比 装载
    motor2.duty(motor.motor2)                               # 将获取到的电机2占空比 装载

    if timer0.cnt == 0:                                     # 如果 timer0.cnt 等于 0 此步骤的目的是控制打印周期 不要打印的太快
        print_sensor()                                      # 打印sensor参数
        print_blobs_property(black,"Black-")                # 打印黑色色块参数
        print_blobs_property(red,  "Red-  ")                # 打印红色色块参数

#__________________________________________________________________

测试

这部分先用LED灯的情况来模拟,后面会装到小车上进行测试,后续会更新此博文。
可以看到位置差不多在160附近的时候,motor1和motor2的占空比为50左右,表示直走。
【K210】K210学习笔记四——定时器的使用_第2张图片
此时两个灯亮的程度基本相当,呈现黄色,说明逻辑正确。
【K210】K210学习笔记四——定时器的使用_第3张图片

可以看到位置差不多在280附近的时候,motor1的占空比为87,motor2的占空比为12,表示右转。
【K210】K210学习笔记四——定时器的使用_第4张图片
此时绿灯较亮,绿灯是13号引脚,被电机2占空比控制,因为灯是低电平点亮,因此电机2占空比为12,绿灯较亮,说明逻辑正确。
【K210】K210学习笔记四——定时器的使用_第5张图片

可以看到位置差不多在47附近的时候,motor1的占空比为14,motor2的占空比为85,表示左转。
【K210】K210学习笔记四——定时器的使用_第6张图片
此时红灯较亮,红灯是14号引脚,被电机1占空比控制,因为灯是低电平点亮,因此电机1占空比为14,红灯较亮,说明逻辑正确。
【K210】K210学习笔记四——定时器的使用_第7张图片
小车的测试等待我回学校后更新!

完整源码

完整源码如下所示,大家可以复制该源码,进行测试,下一次学习笔记将会记录K210串口的配置,虽然往期教程已经做过了,但下期我将会重制一下,争取让代码更加好用,我们下期再见~!
等下一期做完串口,K210这一系列可能不会日更,后期我会着手于写K210跑训练模型,以及怎么训练模型的教程,难度比现在来得大,因此更新的速度会放缓
但我一定会保持质量,做到句句有注释,句句有原因,思路清晰, 喜欢我的教程的,希望能换到你们的一个关注。

# Timer_V1.0 - By: FITQY - 周二 8 月 23 日 2022
#__________________________________________________________________
# 导入模块
import sensor, time, image                                  # 导入感光元件模块 sensor 跟踪运行时间模块 time 机器视觉模块 image
import utime                                                # 导入延时模块 utime
from fpioa_manager import fm                                # 从 GPIO 模块中导入 引脚注册模块 fm
from Maix import GPIO                                       # 从 Maix 模块中导入 模块 GPIO
import lcd                                                  # 导入 LCD 模块
from machine import Timer, PWM                              # 从 machine 模块中导入 定时器模块 Timer 脉宽调制模块 PWM

#__________________________________________________________________
# 感光元件设置
sensor.reset()                                              # 重置并初始化感光元件 默认设置为 摄像头频率 24M 不开启双缓冲模式
#sensor.reset(freq=24000000, dual_buff=True)                # 设置摄像头频率 24M 开启双缓冲模式 会提高帧率 但内存占用增加

sensor.set_pixformat(sensor.RGB565)                         # 设置图像格式为 RGB565 (彩色) 除此之外 还可设置格式为 GRAYSCALE 或者 YUV422
sensor.set_framesize(sensor.QVGA)                           # 设置图像大小为 QVGA (320 x 240) 像素个数 76800 K210最大支持格式为 VGA

sensor.set_auto_exposure(1)                                 # 设置自动曝光
#sensor.set_auto_exposure(0, exposure=120000)               # 设置手动曝光 曝光时间 120000 us

sensor.set_auto_gain(0, gain_db = 12)                       # 设置画面增益 17 dB 影响实时画面亮度
sensor.set_auto_whitebal(0, rgb_gain_db = (0,0,0))          # 设置RGB增益 0 0 0 dB 影响画面色彩呈现效果 在 K210 上无法调节增益 初步判定是感光元件 ov2640 无法支持

#sensor.set_contrast(0)                                     # 设置对比度 0 这个参数无法读取 且调这个参数对画面似乎不会产生影响 暂时注释
#sensor.set_brightness(0)                                   # 设置亮度 0 这个参数无法读取 且调这个参数对画面似乎不会产生影响 暂时注释
#sensor.set_saturation(0)                                   # 设置饱和度 0 这个参数无法读取 且调这个参数对画面似乎不会产生影响 暂时注释

sensor.set_vflip(1)                                         # 打开垂直翻转 如果是 01Studio 的 K210 不开启会导致画面方向与运动方向相反
sensor.set_hmirror(1)                                       # 打开水平镜像 如果是 01Studio 的 K210 不开启会导致画面方向与运动方向相反

sensor.skip_frames(time = 2000)                             # 延时跳过2s 等待感光元件稳定

#__________________________________________________________________
# 创建时钟对象
clock = time.clock()                                        # 创建时钟对象 clock

#__________________________________________________________________
# 打印sensor参数
def print_sensor():
    print("Exposure: "+str(sensor.get_exposure_us()))       # 打印 曝光时间
    print("Gain: "+str(sensor.get_gain_db()))               # 打印 画面增益
    print("RGB: "+str(sensor.get_rgb_gain_db()))            # 打印 RGB 增益

#__________________________________________________________________
# 目标点输入类 举例 对标 2022 年 TI 杯送货无人机 中的目标点输入部分
class point_input():
    point1  = 0                                             # 目标点 1
    point2  = 0                                             # 目标点 2
    cross   = 0                                             # 穿圈模式标志位
    send    = 0                                             # 目标点发送标志位

point = point_input()                                       # 实例化目标点输入类 point_input() 为 point

# 按键控制下的目标点获取函数
def point_control(ckey):
    if ckey.control == 1:                                   # 按键确认及发送控制标志位为1 即 按键3 按下
        ckey.control = 0                                    # 重置标志位
        if ckey.cs == 0:                                    # 如果当前为模式 0
            point.send = 1                                  # 目标点发送标志置为 1 串口开始发送

        elif ckey.cs == 1:                                  # 如果当前为模式 1
            point.point1 = ckey.cinput                      # 将按键输入值赋值给目标点 1

        elif ckey.cs == 2:                                  # 如果当前为模式 2
            point.point2 = ckey.cinput                      # 将按键输入值赋值给目标点 2

        elif ckey.cs == 3:                                  # 如果当前为模式 3
            point.cross = ckey.cinput                       # 将按键输入值赋值给 穿圈模式标志位

    if ckey.csflag == 1:                                    # 如果检测到按键模式切换
        ckey.csflag = 0                                     # 重置按键模式切换标志位
        ckey.cinput = 0                                     # 重置按键输入值

#__________________________________________________________________
# 按键的使用
# 定义按键控制类
class key_control():                                        # 定义按键控制类
    cnt     = 0                                             # 按键计数值
    cs      = 0                                             # 按键模式选择标志位
    csmax   = 0                                             # 按键模式上限
    csflag  = 0                                             # 按键模式切换标志位
    cinput  = 0                                             # 按键输入值保存位
    control = 0                                             # 按键确认及发送控制标志位

# 实例化按键类
key = key_control()                                         # 实例化按键控制类 key_control() 为 key
key.csmax = 4                                               # 按键模式上限为 4 即最多有 4 个模式

# 注册按键引脚
fm.register(16, fm.fpioa.GPIOHS0, force = True)             # 配置 16 脚为 KEY0 使用高速 GPIO 口 强制注册
fm.register(18, fm.fpioa.GPIOHS1, force = True)             # 配置 18 脚为 KEY1 使用高速 GPIO 口 强制注册
fm.register(19, fm.fpioa.GPIOHS2, force = True)             # 配置 19 脚为 KEY2 使用高速 GPIO 口 强制注册
fm.register(20, fm.fpioa.GPIOHS3, force = True)             # 配置 20 脚为 KEY3 使用高速 GPIO 口 强制注册

# 创建按键对象
KEY0 = GPIO(GPIO.GPIOHS0, GPIO.IN, GPIO.PULL_UP)            # 创建按键对象 KEY0
KEY1 = GPIO(GPIO.GPIOHS1, GPIO.IN, GPIO.PULL_UP)            # 创建按键对象 KEY1
KEY2 = GPIO(GPIO.GPIOHS2, GPIO.IN, GPIO.PULL_UP)            # 创建按键对象 KEY2
KEY3 = GPIO(GPIO.GPIOHS3, GPIO.IN, GPIO.PULL_UP)            # 创建按键对象 KEY3

# 中断回调函数 KEY0 控制按键模式选择
def key0_switch(KEY0):
    utime.sleep_ms(10)                                      # 延时 10ms 消除按键抖动
    if KEY0.value() == 0:                                   # 确认 按键0 按下
        key.csflag = 1                                      # 标记按键模式切换
        if key.cs < key.csmax:                              # 控制按键模式选择 自增
            key.cs = key.cs + 1
        else:                                               # 若达到上限 则重新从 0 开始
            key.cs = 0

# 中断回调函数 KEY1 按键输入值自增
def key1_switch(KEY1):
    utime.sleep_ms(10)                                      # 延时 10ms 消除按键抖动
    if KEY1.value() == 0:                                   # 确认 按键1 按下
        key.cinput = key.cinput + 1                         # 按键输入值自增

# 中断回调函数 KEY2 按键输入值自减
def key2_switch(KEY2):
    utime.sleep_ms(10)                                      # 延时 10ms 消除按键抖动
    if KEY2.value() == 0:                                   # 确认 按键2 按下
        key.cinput = key.cinput - 1                         # 按键输入值自减

# 中断回调函数 KEY3 按键确认及发送控制标志位
def key3_switch(KEY3):
    utime.sleep_ms(10)                                      # 延时 10ms 消除按键抖动
    if KEY3.value() == 0:                                   # 确认按键按下
        key.control = 1                                     # 按键确认及发送控制标志位

# 开启中断 下降沿触发
KEY0.irq(key0_switch, GPIO.IRQ_FALLING)                     # 开启 按键0 外部中断 下降沿触发
KEY1.irq(key1_switch, GPIO.IRQ_FALLING)                     # 开启 按键1 外部中断 下降沿触发
KEY2.irq(key2_switch, GPIO.IRQ_FALLING)                     # 开启 按键2 外部中断 下降沿触发
KEY3.irq(key3_switch, GPIO.IRQ_FALLING)                     # 开启 按键3 外部中断 下降沿触发

#__________________________________________________________________
# LCD 的使用
# LCD 初始化
lcd.init()                                                  # lcd初始化

# LCD 按键信息及目标点信息显示函数
def lcd_key():
    lcd.draw_string(0, 0,  "key_cs: "+str(key.cs), lcd.BLUE, lcd.WHITE)
    lcd.draw_string(0, 15, "cinput: "+str(key.cinput), lcd.BLUE, lcd.WHITE)
    lcd.draw_string(0, 30, "point1: "+str(point.point1), lcd.BLUE, lcd.WHITE)
    lcd.draw_string(0, 45, "point2: "+str(point.point2), lcd.BLUE, lcd.WHITE)
    lcd.draw_string(0, 60, "cross : "+str(point.cross), lcd.BLUE, lcd.WHITE)
    lcd.draw_string(0, 75, "red_cx: "+str(red.cx), lcd.BLUE, lcd.WHITE)
    lcd.draw_string(0, 90, "motor1: "+str(motor.motor1), lcd.BLUE, lcd.WHITE)
    lcd.draw_string(0, 105,"motor2: "+str(motor.motor2), lcd.BLUE, lcd.WHITE)
    lcd.draw_string(0, 120,"FPS   : "+str(clock.fps()), lcd.BLUE, lcd.WHITE)

#__________________________________________________________________
# LED 的使用
# 注册LED引脚
fm.register(14, fm.fpioa.GPIO2, force = True)               # 配置 14 脚为 LED_R 强制注册
fm.register(13, fm.fpioa.GPIO1, force = True)               # 配置 13 脚为 LED_G 强制注册
fm.register(12, fm.fpioa.GPIO0, force = True)               # 配置 12 脚为 LED_B 强制注册

# 创建LED对象
LED_R = GPIO(GPIO.GPIO2, GPIO.OUT)                          # 创建 LED_R 对象
LED_G = GPIO(GPIO.GPIO1, GPIO.OUT)                          # 创建 LED_G 对象
LED_B = GPIO(GPIO.GPIO0, GPIO.OUT)                          # 创建 LED_B 对象

# LED控制函数
def led_control(led_flag):                                  # LED控制函数 根据传入 led_flag 点亮对应的灯
    if led_flag == 0:                                       # 传入参数为 0 所有灯打开
        LED_R.value(0)
        LED_G.value(0)
        LED_B.value(0)

    elif led_flag == 1:                                     # 传入参数为 1 所有灯关闭
        LED_R.value(1)
        LED_G.value(1)
        LED_B.value(1)

    elif led_flag == 2:                                     # 传入参数为 2 红灯常亮
        LED_R.value(0)
        LED_G.value(1)
        LED_B.value(1)

    elif led_flag == 3:                                     # 传入参数为 3 绿灯常亮
        LED_R.value(1)
        LED_G.value(0)
        LED_B.value(1)

    elif led_flag == 4:                                     # 传入参数为 4 蓝灯常亮
        LED_R.value(1)
        LED_G.value(1)
        LED_B.value(0)

    else:                                                   # 其他情况 紫灯
        LED_R.value(0)
        LED_G.value(1)
        LED_B.value(0)

#__________________________________________________________________
# 定时器的使用
# 定义定时器属性类
class timer_property():
    cnt     = 0                                             # 定时器计数值
    cnt_max = 0                                             # 定时器计数值上限
    period  = 0                                             # 定时器周期
    freq    = 0                                             # 定时器频率

# 定时器0 配置_______________________________________________________
# 定时器0 实例化类
timer0 = timer_property()                                   # 实例化定时器属性类 timer_property() 为 timer0
timer0.cnt_max = 9                                          # 设定 定时器0 的计数值上限为 9
timer0.period = 100                                         # 设定 定时器0 的周期为 100

# 定时器0 定义回调函数
def timer0_back(tim0):
    if timer0.cnt < timer0.cnt_max:                         # 若 定时器0 的计数值小于 定时器0 的计数值上限
        timer0.cnt = timer0.cnt + 1                         # 计数值自增
    else:
        timer0.cnt = 0                                      # 超出计数值上限 则计数值重置为0

# 定时器0 初始化
tim0 = Timer(Timer.TIMER0,                                  # 定时器编号 定时器0
            Timer.CHANNEL0,                                 # 定时器通道 通道0
            mode = Timer.MODE_PERIODIC,                     # 定时器模式 周期性
            unit = Timer.UNIT_MS,                           # 定时器周期单位 ms
            period = timer0.period,                         # 定时器周期 timer0.period 若 unit 为 Timer.UNIT_MS 则周期为 timer0.period ms
            callback = timer0_back)                         # 定时器触发中断后执行的回调函数 timer0_back

# 定时器1 配置_______________________________________________________
# 电机类定义
class motor_property():
    motor1      = 0                                         # 电机1 占空比
    motor2      = 0                                         # 电机2 占空比
    motor3      = 0                                         # 电机3 占空比
    motor4      = 0                                         # 电机4 占空比

    motor1_pin  = 0                                         # 电机1 引脚
    motor2_pin  = 0                                         # 电机2 引脚
    motor3_pin  = 0                                         # 电机3 引脚
    motor4_pin  = 0                                         # 电机4 引脚

    control_x   = 0                                         # 被控坐标 x
    control_y   = 0                                         # 被控坐标 y

# 实例化电机类
motor = motor_property()                                    # 实例化电机类 motor_property() 为 motor
motor.motor1 = 50                                           # 电机1的占空比 初始设置为 50%
motor.motor2 = 50                                           # 电机2的占空比 初始设置为 50%
motor.motor1_pin  = 14                                      # 电机1的引脚 14为红灯引脚 这里先用灯的亮灭观察效果
motor.motor2_pin  = 13                                      # 电机2的引脚 13为绿灯引脚 这里先用灯的亮灭观察效果

# 定时器1 实例化类
timer1 = timer_property()                                   # 实例化定时器属性类 timer_property() 为 timer1
timer1.freq = 1000                                          # 设定 定时器1 的频率为 1000

# 定时器1 通道0 初始化
tim1_ch0 = Timer(Timer.TIMER1,                              # 定时器编号 定时器1
                 Timer.CHANNEL0,                            # 定时器通道 通道0
                 mode = Timer.MODE_PWM)                     # 定时器模式 PWM

# 定时器1 通道1 初始化
tim1_ch1 = Timer(Timer.TIMER1,                              # 定时器编号 定时器1
                 Timer.CHANNEL1,                            # 定时器通道 通道1
                 mode = Timer.MODE_PWM)                     # 定时器模式 PWM

# 创建对象 电机1 通道为 定时器1的通道0 频率为 定时器1的频率 占空比为 电机1的占空比 引脚为 电机1的引脚
motor1 = PWM(tim1_ch0, freq = timer1.freq, duty = motor.motor1, pin = motor.motor1_pin)

# 创建对象 电机2 通道为 定时器1的通道1 频率为 定时器1的频率 占空比为 电机2的占空比 引脚为 电机2的引脚
motor2 = PWM(tim1_ch1, freq = timer1.freq, duty = motor.motor2, pin = motor.motor2_pin)

# 定义电机占空比控制函数
def motor_control(motor, x):
    val = 0
    if x < motor.control_x:                                 # 若 当前坐标 小于 被控坐标x 即当前状态小车在目标的 左边
        val = (motor.control_x - x) * 0.3125                # 获取坐标差值 并转换为 0~50 之间的值
        motor.motor1 = 50 - val                             # 减小 电机1 占空比 电机1为左电机 使小车右转
        motor.motor2 = 50 + val                             # 增大 电机2 占空比 电机2为右电机 使小车右转

    elif x > motor.control_x:                               # 若 当前坐标 大于 被控坐标x 即当前状态小车在目标的 右边
        val = (x - motor.control_x) * 0.3125                # 获取坐标差值 并转换为 0~50 之间的值
        motor.motor1 = 50 + val                             # 增大 电机1 占空比 电机1为左电机 使小车左转
        motor.motor2 = 50 - val                             # 减小 电机2 占空比 电机2为右电机 使小车左转

    motor.motor1 = int(motor.motor1)                        # 将 电机1占空比 转换为 整数
    motor.motor2 = int(motor.motor2)                        # 将 电机1占空比 转换为 整数

#__________________________________________________________________
# 寻找色块
# 定义类
class color_property():
    cx                      =  0                            # 色块 x轴 中心坐标
    cy                      =  0                            # 色块 y轴 中心坐标
    flag                    =  0                            # 色块标志位 1 找到 0 未找到
    color                   =  0                            # 色块颜色标志位 例如 你可以用 1 来表示 黑色
    density                 =  0                            # 色块密度比 反映色块锁定程度 值越大 锁定程度越好
    pixels_max              =  0                            # 色块像素最大值
    led_flag                =  0                            # LED标志位 方便调试用

    color_threshold         = (0, 0, 0, 0, 0, 0)            # 色块颜色阈值
    color_roi               = (0,0,320,240)                 # 色块寻找区域(感兴趣区域)
    color_x_stride          =  1                            # 色块 x轴 像素最小宽度 色块如果比较大可以调大此参数 提高寻找速度
    color_y_stride          =  1                            # 色块 y轴 像素最小宽度 色块如果比较大可以调大此参数 提高寻找速度
    color_pixels_threshold  =  100                          # 色块 像素个数阈值 例如调节此参数为100 则可以滤除色块像素小于100的色块
    color_area_threshold    =  100                          # 色块 被框面积阈值 例如调节此参数为100 则可以滤除色块被框面积小于100的色块
    color_merge             =  True                         # 是否合并寻找到的色块 True 则合并 False 则不合并
    color_margin            =  1                            # 色块合并间距 例如调节此参数为1 若上面选择True合并色块 且被找到的色块有多个 相距1像素 则会将这些色块合并

# 实例化类
# 黑色
black = color_property()
black.color_threshold         = (0, 50, -10, 10, -10, 10)
black.color_roi               = (0,0,320,240)
black.color_x_stride          =  1
black.color_y_stride          =  1
black.color_pixels_threshold  =  100
black.color_area_threshold    =  100
black.color_merge             =  True
black.color_margin            =  1

# 红色
red   = color_property()
red.color_threshold           = (0, 100, 20, 127, -10, 127)

#red.color_roi                = (0,0,320,240)
red.color_roi                 = (0,110,320,20)

red.color_x_stride            =  1
red.color_y_stride            =  1

#red.color_pixels_threshold   =  100
#red.color_area_threshold     =  100
red.color_pixels_threshold    =  10
red.color_area_threshold      =  10

red.color_merge               =  True
red.color_margin              =  1

# 绿色 预留
green = color_property()

# 蓝色 预留
blue  = color_property()

# 定义寻找色块函数
def opv_find_blobs(color,led_flag):
    color.pixels_max = 0                                    # 重置 色块 最大像素数量
    color.flag       = 0                                    # 重置 色块 标志位
    color.led_flag   = 0                                    # 重置 led 标志位

    for blobs in img.find_blobs([color.color_threshold],    # 色块颜色阈值
    roi = color.color_roi,                                  # 色块寻找区域(感兴趣区域)
    x_stride = color.color_x_stride,                        # 色块 x轴 像素最小宽度 色块如果比较大可以调大此参数 提高寻找速度
    y_stride = color.color_y_stride,                        # 色块 y轴 像素最小宽度 色块如果比较大可以调大此参数 提高寻找速度
    pixels_threshold = color.color_pixels_threshold,        # 色块 像素个数阈值 例如调节此参数为100 则可以滤除色块像素小于100的色块
    area_threshold = color.color_area_threshold,            # 色块 被框面积阈值 例如调节此参数为100 则可以滤除色块被框面积小于100的色块
    merge = color.color_merge,                              # 是否合并寻找到的色块 True 则合并 False 则不合并
    margin = color.color_margin):                           # 色块合并间距 例如调节此参数为1 若上面选择True合并色块 且被找到的色块有多个 相距1像素 则会将这些色块合并
        img.draw_rectangle(blobs[0:4])                      # 圈出找到的色块
        if color.pixels_max < blobs.pixels():               # 找到面积最大的色块
            color.pixels_max = blobs.pixels()
            color.cx = blobs.cx()                           # 将面积最大的色块的 x轴 中心坐标值 赋值给 color
            color.cy = blobs.cy()                           # 将面积最大的色块的 y轴 中心坐标值 赋值给 color
            color.flag = 1                                  # 标志画面中有找到色块
            color.density = blobs.density()                 # 将面积最大的色块的 色块密度比 赋值给 color
            color.led_flag = led_flag                       # 将控制led颜色的标志位的值 赋值给 color

    if color.flag == 1:                                     # 标记画面中被找到的最大色块的中心坐标
        img.draw_cross(color.cx,color.cy, color=127, size = 15)
        img.draw_circle(color.cx,color.cy, 15, color = 127)

# 定义打印色块参数函数
def print_blobs_property(color,name):
    print(name,"cx:",color.cx,"cy:",color.cy,"flag:",color.flag,"color:",color.color,"density:",color.density,"led_flag:",color.led_flag)

#__________________________________________________________________
# 调试区
led_control(1)                                              # 关闭一下所有灯 再进入 while 循环 使显示结果正确

#__________________________________________________________________
# 主函数
while(True):

    clock.tick()                                            # 跟踪运行时间

    img = sensor.snapshot()                                 # 拍摄一张照片

    #opv_find_blobs(black,1)                                # 找黑色色块 led标志为1 表示黑色
    opv_find_blobs(red,2)                                   # 找红色色块 led标志为2 表示红色

    point_control(key)                                      # 按键控制下的目标点获取函数

    lcd.display(img)                                        # LCD 显示图像
    lcd_key()                                               # LCD 显示按键信息及目标点信息

    #led_control(red.led_flag)                              # LED 标记色块识别情况

    motor.control_x = 160                                   # 控制目标处于 x轴中心点 160
    motor_control(motor,red.cx)                             # 电机占空比控制函数获取电机控制占空比

    motor1.duty(motor.motor1)                               # 将获取到的电机1占空比 装载
    motor2.duty(motor.motor2)                               # 将获取到的电机2占空比 装载

    if timer0.cnt == 0:                                     # 如果 timer0.cnt 等于 0 此步骤的目的是控制打印周期 不要打印的太快
        print_sensor()                                      # 打印sensor参数
        print_blobs_property(black,"Black-")                # 打印黑色色块参数
        print_blobs_property(red,  "Red-  ")                # 打印红色色块参数

#__________________________________________________________________

你可能感兴趣的:(单片机,学习,嵌入式硬件,图像处理)