送藥小車代碼倉庫:https://gitee.com/lcsc/medical_car
更好的觀看體驗請去:https://lceda001.feishu.cn/wiki/ZDYbwqDfCiwVlckUEcScF0KSnRh
送藥小車立創開源平臺資料:https://oshwhub.com/li-chuang-kai-fa-ban/21-dian-sai-f-ti-zhi-neng-song-yao-xiao-che
K210功能實現代碼講解
具體實現代碼如下(在2_Code->application->sensor->k210->pyconde->main.py
):
# Hello World Example
#
# Welcome to the CanMV IDE! Click on the green run arrow button below to run the script!
import sensor, image, time, lcd, struct, ustruct
import gc
from machine import UART,Timer,PWM
from board import board_info
from fpioa_manager import fm
import KPU as kpu
from Maix import GPIO,utils
import gc
import machine
lcd.init() # Init lcd display
lcd.clear(lcd.RED) # Clear lcd screen.
sensor.reset(freq=22000000, dual_buff=1) # 設置攝像頭頻率 24M 開啟雙緩沖模式 會提高幀率 但內存占用增加
sensor.set_auto_exposure(0) # 設置自動曝光
sensor.set_auto_gain(False) # 顏色跟蹤必須關閉自動增益
sensor.set_auto_whitebal(False) # 顏色跟蹤必須關閉白平衡
sensor.set_pixformat(sensor.RGB565) # Set pixel format to RGB565 (or GRAYSCALE)
sensor.set_framesize(sensor.QVGA) # Set frame size to QVGA (320x240)
#kpu = KPU()
#kpu.load_kmodel("/sd/KPU/mnist/uint8_mnist_cnn_model.kmodel")
sensor.skip_frames(time = 2000) # Wait for settings take effect.
clock = time.clock() # Create a clock object to track the FPS.
sensor.set_auto_exposure(0) # 設置自動曝光
sensor.set_vflip(1)
#______________________________________________________________________________________________________________________________
#打印內存分配情況
print(utils.gc_heap_size())
print("stack mem:"+str(gc.mem_free() / 1024)) # stack mem
print("heap mem:"+str(utils.heap_free() / 1024)) # heap mem
#第一次用執行一次下面這兩個語句
#utils.gc_heap_size(800*1024)
#machine.reset()
#______________________________________________________________________________________________________________________________
#程序運行選擇
is_need_debug = 0
is_patrol_line = 0
is_upacker_debug = 0
is_stack_heap_mem_debug = 0
is_upacker_recive_debug = 1
is_findflob_debug = 0
#______________________________________________________________________________________________________________________________
#程序運行狀態
work_mode = 0 #0是巡線模式,1是數字識別模式
#______________________________________________________________________________________________________________________________
#串口配置區
fm.register(6, fm.fpioa.UART1_TX, force=True)
fm.register(7, fm.fpioa.UART1_RX, force=True)
k210_uart = UART(UART.UART1, 115200, 8, 0, 0, timeout=1000, read_buf_len=4096)
uart_test_write_str = '1260808878'
#______________________________________________________________________________________________________________________________
#按鍵蜂鳴器配置區
#注冊IO,注意高速GPIO口才有中斷
fm.register(35, fm.fpioa.GPIO0)
fm.register(16, fm.fpioa.GPIOHS0)
#構建案件對象
KEY=GPIO(GPIO.GPIOHS0, GPIO.IN, GPIO.PULL_UP)
#PWM通過定時器配置,接到IO15引腳
tim = Timer(Timer.TIMER1, Timer.CHANNEL0, mode=Timer.MODE_PWM)
beep = PWM(tim, freq=1000, duty=0, pin=9)
#按鍵標志位
key_node = 0
key_press_long = 0
#中斷回調函數
def fun(KEY):
global work_mode,key_node,key_press_long
temp_count = 0
time.sleep_ms(10) #消除抖動
while KEY.value()== 0:
key_node = 1
time.sleep_ms(10) #長按延時
#長按檢測計數
temp_count=temp_count+1
print(temp_count)
if temp_count >= 50:
beep.duty(50)
time.sleep_ms(500)
beep.duty(0)
time.sleep_ms(100)
print(temp_count)
key_node = 0
key_press_long = 1
#開啟中斷,下降沿觸發
KEY.irq(fun, GPIO.IRQ_FALLING)
#______________________________________________________________________________________________________________________________
#要傳給梁山派的數據
#work_mode:0是巡線模式,1是數字識別模式
#recognition:0是未知(巡線狀態下),1是藥房門口區域(停車線),十字路口和T字路口由
class uart_send_data_t:
def __init__( self, work_mode=0, recognition=0,top_block_offset=0,center_block_offset=0,left_block_offset=0,right_block_offset=0,left_number = 0,right_number = 0):
self.work_mode = work_mode
self.recognition = recognition
self.top_block_offset = top_block_offset
self.center_block_offset = center_block_offset
self.left_block_offset = left_block_offset
self.right_block_offset = right_block_offset
self.left_number = left_number
self.right_number = right_number
global_uart_send_data = uart_send_data_t()
#______________________________________________________________________________________________________________________________
#感興趣區配置
roi_area_width = 30
roi_area_color = (0, 0, 200)
roi_area_thickness = 2
left_roi = [50,40,roi_area_width,sensor.height()-40]
right_roi = [sensor.width()-roi_area_width-50,40,roi_area_width,sensor.height()-40]
top_roi = [0,10,sensor.width(),roi_area_width]
center_roi = [0,int(sensor.height()/2)-int(roi_area_width/2),sensor.width(),roi_area_width]
#bottom_roi = [0,sensor.height()- roi_area_width,sensor.width(),roi_area_width]
black_block_roi = [0,100,sensor.width(),100]
def draw_roi(img):
img.draw_rectangle(left_roi, color = roi_area_color, thickness = roi_area_thickness, fill = False)
img.draw_rectangle(right_roi, color = roi_area_color, thickness = roi_area_thickness, fill = False)
img.draw_rectangle(top_roi, color = roi_area_color, thickness = roi_area_thickness, fill = False)
img.draw_rectangle(center_roi, color = roi_area_color, thickness = roi_area_thickness, fill = False)
img.draw_rectangle(black_block_roi, color = (0, 255, 0), thickness = roi_area_thickness, fill = False)
return
#______________________________________________________________________________________________________________________________
#尋找色塊區配置
red_threshold =[12, 68, 11, 63, 5, 81]
black_threshold =[0, 50, -24,-1, -18, 6]
class red_blob_location_t:
def __init__( self, x=0, y=0,area = 0):
self.x = x
self.y = y
self.area = area
#______________
#左部blob位置信息
left_blob_location = red_blob_location_t()
left_pixels_threshold = 20 #若一個色塊的像素數小于 pixel_threshold ,則會被過濾掉。
left_area_threshold = 20 #若一個色塊的邊界框區域小于 area_threshold ,則會被過濾掉。
left_x_stride = 20 #是查找某色塊時需要跳過的x像素的數量。找到色塊后,直線填充算法將精確像素。 若已知色塊較大,可增加 x_stride 來提高查找色塊的速度。
left_y_stride = 20 #是查找某色塊時需要跳過的y像素的數量。找到色塊后,直線填充算法將精確像素。 若已知色塊較大,可增加 y_stride 來提高查找色塊的速度。
#______________
#右部blob位置信息
right_blob_location = red_blob_location_t()
right_pixels_threshold = 20 #若一個色塊的像素數小于 pixel_threshold ,則會被過濾掉。
right_area_threshold = 20 #若一個色塊的邊界框區域小于 area_threshold ,則會被過濾掉。
right_x_stride = 20 #是查找某色塊時需要跳過的x像素的數量。找到色塊后,直線填充算法將精確像素。 若已知色塊較大,可增加 x_stride 來提高查找色塊的速度。
right_y_stride = 20 #是查找某色塊時需要跳過的y像素的數量。找到色塊后,直線填充算法將精確像素。 若已知色塊較大,可增加 y_stride 來提高查找色塊的速度。
#______________
#頂部blob位置信息
top_blob_location = red_blob_location_t()
top_pixels_threshold = 20 #若一個色塊的像素數小于 pixel_threshold ,則會被過濾掉。
top_area_threshold = 20 #若一個色塊的邊界框區域小于 area_threshold ,則會被過濾掉。
top_x_stride = 15 #是查找某色塊時需要跳過的x像素的數量。找到色塊后,直線填充算法將精確像素。 若已知色塊較大,可增加 x_stride 來提高查找色塊的速度。
top_y_stride = 20 #是查找某色塊時需要跳過的y像素的數量。找到色塊后,直線填充算法將精確像素。 若已知色塊較大,可增加 y_stride 來提高查找色塊的速度。
#______________
#中部blob位置信息
center_blob_location = red_blob_location_t()
center_pixels_threshold = 20 #若一個色塊的像素數小于 pixel_threshold ,則會被過濾掉。
center_area_threshold = 20 #若一個色塊的邊界框區域小于 area_threshold ,則會被過濾掉。
center_x_stride = 20 #是查找某色塊時需要跳過的x像素的數量。找到色塊后,直線填充算法將精確像素。 若已知色塊較大,可增加 x_stride 來提高查找色塊的速度。
center_y_stride = 20 #是查找某色塊時需要跳過的y像素的數量。找到色塊后,直線填充算法將精確像素。 若已知色塊較大,可增加 y_stride 來提高查找色塊的速度。
blob_location=[0,0]
def find_blob(img):
global global_uart_send_data
#top_blob_location.x = -1
#top_blob_location.y = -1
#center_blob_location.x = -1
#center_blob_location.y = -1
left_blob_location.x = -1
left_blob_location.y = -1
right_blob_location.x = -1
right_blob_location.y = -1
center_blob_location.area = 0
for blob in img.find_blobs([red_threshold], roi = left_roi, x_stride = left_x_stride, y_stride = left_y_stride, pixels_threshold=left_pixels_threshold, area_threshold=left_area_threshold, merge=False, margin=10):
img.draw_rectangle(blob.rect())
img.draw_cross(blob.cx(), blob.cy())
left_blob_location.x = blob.cx()
left_blob_location.y = blob.cy()
left_blob_location.area = blob.area()
for blob in img.find_blobs([red_threshold], roi = right_roi,x_stride = right_x_stride, y_stride = right_y_stride, pixels_threshold=right_pixels_threshold, area_threshold=right_area_threshold, merge=True, margin=10):
img.draw_rectangle(blob.rect())
img.draw_cross(blob.cx(), blob.cy())
right_blob_location.x = blob.cx()
right_blob_location.y = blob.cy()
right_blob_location.area = blob.area()
for blob in img.find_blobs([red_threshold], roi = top_roi,x_stride = top_x_stride, y_stride = top_y_stride, pixels_threshold=top_pixels_threshold, area_threshold=top_area_threshold, merge=True, margin=10):
img.draw_rectangle(blob.rect())
img.draw_cross(blob.cx(), blob.cy())
#print("-------"+str(blob.area()))
top_blob_location.x = blob.cx()
top_blob_location.y = blob.cy()
top_blob_location.area = blob.area()
for blob in img.find_blobs([red_threshold], roi = center_roi,x_stride = center_x_stride, y_stride = center_y_stride, pixels_threshold=center_pixels_threshold, area_threshold=center_area_threshold, merge=True, margin=10):
img.draw_rectangle(blob.rect())
img.draw_cross(blob.cx(), blob.cy())
center_blob_location.x = blob.cx()
center_blob_location.y = blob.cy()
center_blob_location.area = blob.area()
#print(blob.pixels())
#for blob in img.find_blobs([black_threshold], roi = black_block_roi, pixels_threshold=30,merge=True,margin=50):
#img.draw_rectangle(blob.rect())
#img.draw_cross(blob.cx(), blob.cy())
##print(blob.count())
#if(blob.count() >= 5):
#print("tinche"+str(blob.count()))
#global_uart_send_data.recognition = 1
#else:
#global_uart_send_data.recognition = 0
if(center_blob_location.area == 0):
print("tinche")
global_uart_send_data.recognition = 1
else:
global_uart_send_data.recognition = 0
return
#______________________________________________________________________________________________________________________________
#色塊連線區配置
lines_color = (0, 200, 0)
lines_thickness = 1
def draw_lines(img):
img.draw_line(top_blob_location.x, top_blob_location.y, center_blob_location.x, center_blob_location.y, color = lines_color, thickness = lines_thickness)
img.draw_line(left_blob_location.x, left_blob_location.y, right_blob_location.x, right_blob_location.y, color = lines_color, thickness = lines_thickness)
return
#______________________________________________________________________________________________________________________________
#upacker python實現代碼 :https://github.com/aeo123/upacker/blob/master/python/upacker.py
#介紹請看這里:https://github.com/aeo123/upacker
class Upacker():
def __init__(self):
self._STX_L = 0x55
self._MAX_PACK_SIZE = 1024 + 4 + 128
self._calc = 0
self._check = 0
self._cnt = 0
self._flen = 0
self._state = 0
self._data = bytearray()
def _decode(self, d):
if (self._state == 0 and d == self._STX_L):
self._state = 1
self._calc = self._STX_L
elif self._state == 1:
self._flen = d & 0xff
self._calc ^= d & 0xff
self._state = 2
elif self._state == 2:
self._flen |= (d & 0xff) < 8
self._calc ^= d & 0x3F
# 數據包超長得情況下直接丟包
if ((self._flen & 0x3FFF) > self._MAX_PACK_SIZE):
self._state = 0
return -1
else:
self._data = bytearray(self._flen & 0x3FFF)
self._state = 3
self._cnt = 0
elif self._state == 3:
header_crc = ((d & 0x03) < 4) | ((self._flen & 0xC000) >> 12)
self._check = d
if (header_crc != (self._calc & 0X3C)):
self._state = 0
return -1
self._state = 4
self._flen &= 0x3FFF
elif self._state == 4:
self._data[self._cnt] = d
self._cnt += 1
self._calc ^= d
if self._cnt == self._flen:
self._state = 0
#接收完,檢查check
if ((self._calc & 0xFC) == (self._check & 0XFC)):
return 0
else:
return -1
else:
self._state = 0
return 1
# 解包
def unpack(self, bytes_data, callback):
ret = 0
for i in bytes_data:
ret = self._decode(i)
if ret == 0:
callback(self._data)
if(is_upacker_debug):
print(self._data)
elif ret == -1:
# callback(None)
print("err")
# 打包
def enpack(self, data):
tmp = bytearray(4)
tmp[0] = 0x55
tmp[1] = len(data) & 0xff
tmp[2] = (len(data) >> 8) & 0xff
crc = tmp[0] ^ tmp[1] ^ tmp[2]
tmp[2] |= (crc & 0x0c) < 4
tmp[3] = 0x03 & (crc >> 4)
for i in range(len(data)):
crc ^= data[i]
tmp[3] |= (crc & 0xfc)
frame = struct.pack("BBBB%ds" % len(data), tmp[0], tmp[1], tmp[2],
tmp[3], data)
#python3.5之前bytes數據沒有hex()屬性,所以修改了下面這個
#print(frame.hex())
if(is_upacker_debug):
print(''.join(map(lambda x:('' if len(hex(x))>=4 else '/x0')+hex(x)[2:],frame)))
return frame
#____________
#串口命令切換模式
uart_cmd_need_change_mode = 0
def print_hex(bytes):
global work_mode,uart_cmd_need_change_mode
hex_byte = [hex(i) for i in bytes]
if is_upacker_recive_debug:
print("-----"+" ".join(hex_byte))
if bytes[0] == 0x00:
work_mode = 0
if bytes[0] == 0x01:
work_mode = 1
uart_cmd_need_change_mode = 1
#if __name__ == '__main__':
#buf = bytearray([0x00, 0x01, 0x02,0x03,0x77])
#pack = Upacker()
#pkt = pack.enpack(buf)
#pack.unpack(pkt, print_hex)
#______________________________________________________________________________________________________________________________
#upacker python實現代碼結束
def upacker_init():
pack = Upacker()
return pack
yzh = 10
#______________________________________________________________________________________________________________________________
#發送數據到MCU,gd32是小端字節序
#pack各字母對應類型
#x pad byte no value 1
#c char string of length 1 1
#b signed char integer 1
#B unsigned char integer 1
#? _Bool bool 1
#h short integer 2
#H unsigned short integer 2
#i int integer 4
#I unsigned int integer or long 4
#l long integer 4
#L unsigned long long 4
#q long long long 8
#Q unsilong long long 8
#f float float 4
#d double float 8
#s char[] string 1
#p char[] string 1
#P void * long
def send_data_to_mcu(pack,global_uart_send_data):
hex_data = ustruct.pack("
關鍵的注釋都添加了,可以幫助理解代碼,這段代碼同時實現了尋紅線和數字識別,但是還做不到同時運行,在尋紅線的時候沒法數字識別,這兩個狀態之間的切換可以通過長按K210上的用戶按鍵或者由立創梁山派來控制切換。當送藥小車到達數字識別處時就控制K210進入數字識別模式,識別到數字后再控制K210進入尋紅線模式。
首先初始化LCD屏幕、配置攝像頭、配置UART通信,并配置一個具有中斷的按鈕。然后,定義了感興趣區域(ROIs)和一些用于在圖像中查找色塊的參數(具體可以去MaixPy的API文檔中查找)。
設置了在圖像中檢測色塊和在里面繪制連線的參數。共定義了四個感興趣區域(ROI):左側、右側、頂部和中心。每個ROI都有自己的色塊檢測參數,如pixel_threshold、area_threshold、x_stride和y_stride。這些參數用于過濾掉過小的色塊或者通過跳過一些像素來提高檢測速度,避免一些微小噪聲小色塊的影響。
find_blob
函數處理輸入圖像(img),使用img.find_blobs
方法在每個ROI中檢測紅色的色塊。然后在色塊周圍繪制矩形并在其中心繪制十字標記來方便調試。檢測到的色塊的位置和面積信息存儲在left_blob_location、right_blob_location、top_blob_location和center_blob_location結構中。如果在中心ROI中沒有檢測到色塊,則將global_uart_send_data.recognition
變量設置為1,否則設置為0,當紅色線沒了的時候就代表該停車了。draw_lines
函數用于在上下和左右ROI的紅色塊之間繪制連線(上下左右ROI都識別到紅色塊就是一個交叉的十字),方便調試使用。
定義了一個名為send_data_to_mcu
的函數,用于將數據發送到立創梁山派。這里使用了ustruct.pack
函數將數據打包為字節序列,然后調用pack.enpack
方法將數據打包,最后通過k210_uart.write
將數據發送出去。
定義了一個名為on_timer
的定時回調函數,用于定時發送串口數據。在這個函數中,根據圖像中的各個位置計算出相應的偏移量,并將這些偏移量存儲在global_uart_send_data
對象中。然后,調用send_data_to_mcu
函數將數據發送到立創梁山派的串口里面。然后,配置定時器0通道0,設置定時器的模式、周期、單位、回調函數等參數,并啟動定時器。
進行數字識別的相關配置,用init_yolo2
加載模型并初始化KPU
。
在主循環中,根據工作模式選擇執行巡線模式還是數字識別模式。在巡線模式下,調用find_blob
、draw_roi
、draw_lines
等函數來處理攝像頭采集到圖像,并將處理后的圖像顯示在LCD上方便調試。在數字識別模式下,使用KPU
運行模型并獲取識別結果,將識別到的數字繪制在圖像上,并將處理后的圖像顯示在LCD上。最后,程序會讀取來自立創梁山篇串口發送來的數據,并使用pack.unpack
方法解包數據。如果有數據,會執行解包,解包成功后會更新K210模式控制的標志位(巡線模式還是數字識別模式)。
審核編輯 黃宇
-
開發板
+關注
關注
25文章
5033瀏覽量
97380 -
代碼
+關注
關注
30文章
4780瀏覽量
68529
發布評論請先 登錄
相關推薦
評論