赞
踩
本人大四学生,电赛生涯已经走到尽头,一路上踩过不少坑,但运气也不错拿了两年省一,思来想去,决定开始写博客,将电赛经验分享一二,能力有限,高手轻喷。
往期的博客讲述了 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就会被打印一次。
首先还是定义一个电机类,来保存电机的信息,推荐大家养成这个习惯,对大家有好处。
# 定时器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左右,表示直走。
此时两个灯亮的程度基本相当,呈现黄色,说明逻辑正确。
可以看到位置差不多在280附近的时候,motor1的占空比为87,motor2的占空比为12,表示右转。
此时绿灯较亮,绿灯是13号引脚,被电机2占空比控制,因为灯是低电平点亮,因此电机2占空比为12,绿灯较亮,说明逻辑正确。
可以看到位置差不多在47附近的时候,motor1的占空比为14,motor2的占空比为85,表示左转。
此时红灯较亮,红灯是14号引脚,被电机1占空比控制,因为灯是低电平点亮,因此电机1占空比为14,红灯较亮,说明逻辑正确。
小车的测试等待我回学校后更新!
完整源码如下所示,大家可以复制该源码,进行测试,下一次学习笔记将会记录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- ") # 打印红色色块参数 #__________________________________________________________________
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。