基于MSPM0G3507的智能送药小车(21电赛F题,OPENMV循迹,K210识别数字,并行PID快速学习,小车转动返回)

一.项目要求

首先看一下基本的题目要求,只有明白了题目的基本要求才能够做出符合题目要求的作品。以下是相关的题目要求。

基本项的任务就是送药到近中远端三个不同的类别的病房,中端和远端的 病房号会随机变化,而近端病房的1,2号病房是不会改变的。发挥项目就是双车联动,到中端,远端病房送药,和基础项一样,病房号还是会随机变化。

二.具体方案描述

1本次项目我们所选取的模块

本项目由微处理器MSPM0G3507,编码器电机驱动,OPENMV、K210视觉处理单元,红外药品检测单元,ZIGBEE无限透传单元,OLED显示,红绿黄三色LED构成。系统运行由两部分组成:智能送药小车的单车控制部分和双车联动部分,小车的单车控制部分由K210识别目标病房,OPENMV识别指引线,通过红外传识别药品,通过串口收发送数据到MSPM0,由MSPM0控制电机的转动以此达到小车进入目标病房和取药回到起始点的目的,双车联动通过OPENMV记录路口数目,ZIGBEE互传,实时共享双车的实际位置达到主车控制从车到达目标病房的双车协调送取药目标任务。

2具体模块的分析

(1)视觉处理模块

        首先要明白运用视觉处理模块,到底要完成什么目标任务?第一个是巡线,第二个是识别字模,第三个是识别标志位。这些是本次任务所必须的功能模块。

首先说一下巡线,其实这个有蛮多种方式的。最简单是就是红外识别巡线,扫到为0 ,扫不到为1,由于MSPM0的io口所剩不多,这样一弄的画会浪费很多io口,导致后面的功能模块io口不够。所以最终没有选择红外。当时也想过用灰度识别巡线的,其原理是通过软件I2C给MSPM0发送数据,达到实时控制小车的运行轨迹。指示线识别的准确率也可达95%,软件I2C的操作难度较大。(对MSPM0这个板子熟悉度不高,I2C的操作难度较大,网上资料也较少)最终也没有用上这种方案。最是利用OPENMV视觉传感器对指示线识别,通过串口收发数据给MSPM0,达到实时PID调节控制小车的姿态和预期运动轨迹。指示线识别的准确率可达85%,满足本项目的各种标志位的识别,以及本项目所需的各种标志位之间互相协调。这样做的唯一缺点就是及其的受到光线影响,我们所调车的地方有两户窗户,无法拉上窗帘。由于晴天和阴天的时候太阳直射的光线,以及光照程度不同,这个会导致有时候要么巡线寻不准,或者之前反复确认的标志位,光照不同也不会十分的准确的。

再就是数字字模的识别,首先了解以下V831与K210之间的区别,以下为网上搜索所得:

K210和V831都是由海思(Hisilicon)公司生产的嵌入式处理器芯片,主要用于各种智能设备和物联网(IoT)应用。以下是关于它们的一些基本信息:

1.K210: 这是一款专为人工智能(AI)和机器学习(ML)设计的芯片,它集成了神经网络处理单元(NPU),拥有高效的性能来加速深度学习任务,比如图像识别、语音处理等。它适用于需要大量计算能力的应用场景,如智能摄像头、智能家居设备等。

2.V831: V831则属于华为LiteOS平台的低功耗微控制器(MCU),通常用于物联网设备,特别是对能耗和体积有较高要求的场景。它拥有基本的计算能力和足够的内存资源,适合于传感器数据采集、通信控制等简单功能。

也就是简单的一句话,V831比K210更加精准,所以这个根据所需要而定的,我觉得本项目的话K210就已经足够了。

(2)药品识别模块

方案一:利用HX771重力传感器识别药品,达到检测药品是否获取的目的。价格较低,精确度较低。

方案二:利用红外传感器检测药品。价格较低,精准度较高,环境要求较低。

一般情况下,用自己熟悉的模块,这个也就用红外了,没必要用压力传感器,就已经有不错的效果了。

(3)双车通信

方案一:利用ZIGBEE无限透传,进行双车数据互传,达到双车联动、实时数据共享的目的。传输速率有250Kbps范围较大、AES加密安全级别高。

方案二:利用蓝牙5.0达到双车通信的目的。传输速率2Mbps范围较小、通信安全级别较低。

个人喜好是zigbee的,用习惯了。这个蓝牙还是zigbee都是可以完成基本要求的。

(4)电机的选取

方案一:利用步进电机能够精准的控制小车的前进、后退、转向和自转,不需要过多的PID调制就可以达到所预期的目标速度和小车的运动轨迹,但对于本项目单个步进电机的体积过大,加上其他基本部件会超过项目要求的最大高度。

   方案二:利用霍尔编码器电机,通过编码器返还的实时脉冲数,通过PID的实时计算修正,达到小车的预期运动轨迹,体积适中,PID调节难度适中,能够满足本项目对电机控制的全部需求。

这个也是用自己熟练的配件,我选用的是带霍尔编码器的直流电机,这个型号的话基本上还是用自己熟练的配件,因为那个地方出现了什么问题你也能够一眼看出来。

系统框图如下。

         前车:                       

后车:

三.理论分析具体操作

1单车指定病房送药

本项目将八个病房分为近、中、远端三类病房,而实际操作中将其分为中端和远端两类病房,中端病房部分包括项目中的近端和中端病房。对中端病房送药,首先通过K210识别目标病房,OPENMV记录小车通过的路口数小于3判断小车需要送药的病房为中端病房,识别进目标病房并进入目标病房的目的,取药后通过进入目标病房记录的数据进行回到起点的目的。对远端病房送药,通过K210识别目标病房,OPENMV记录小车通过路口数大于2判断小车所需要送药的病房为远端病房,在最后第3个路口进行左转到达远端左方病房,如果目标病房在其中则进入,不在则自转到达右方远端进行最后的识别进入目标送药病房的目的,取药后通过进入目标病房记录的数据进行回到起点的目的。单车远端送药逻辑图如图所示

2双车联动送取药

一车中端病房送药,一车K210识别目标病房,到达目标病房后通过ZIGBEE命令二车开始运动到达目标病房的对立病房,当一车取药通过路口后,命令二车开始运动到达目标病房,取药后回到起点。

一车远端病房送药,一车K210识别目标病房,一车通过第三个路口后左转判断目标病房是否在左边,在左边直接到达目标病房并停车,不在左边病房则自转向右边巡逻,进入目标病房。一车停在目标药房,取药后命令二车快速启动运动到第三个路口,一车从第三路口出来,命令二车则向第三路一车的对立面避让,一车回到起点,二车继续向前运动识别目标病房并运动到目标病房进行取药。双车联动远端送取药逻辑图如图所示。

整体的功能模块软硬件的实现就如上所示了,剩下来就是在做这个项目的一些经验,以及遇到的一些困难了。

四.项目经验以及一些难点和一些方法的不同见解

1.动作的流畅性

在做这个项目的时候,感觉到最大的难点应该是每次动作的连贯性,也就是本次动作不影响上一次动作,和将要执行的动作。用通俗一点的话语就是你巡线的动作不能影响你左转或者右转进入病房的动作,你进入病房的动作不能影响你再次巡线,停车的动作。这一点在本个项目里面十分的重要的,你这一点完完全全的弄透彻了,你这个项目将会十分的顺畅,后面一系列的东西也将会不会出现什么不知名的bug。

2.左右转执行

再就是分左右转,我上面用的是比较K210返回字模的X坐标,其实你也可以用一个数组将你所识别过的数字存起来,让后对于是目标病房,并且在其同一路,两个字模进行比较,如果目标病房的X小于另外一个也就是左转,反之右转。

3.远端中端路口的识别

这个我上面是利用的数路口的,实际上这种方式十分依赖OPENMV的准确度,如果你路口数量孰不准确的情况下,你的远端近端也会分别不清楚。稳定性不是十分的高,针对这一点,后面也让我们走了很多弯路,有时候分别不清楚路口数,导致后面各种近端远端都是不太准确的,于是最后决定采用存数组的方式,也就是你识别了目标病房启动后,如果你接下所扫的4字模里面没有目标病房,那你的目标病房就是远端病房(这里不包括病房1,2)。这样一处理最后得到的结果是十分准确的。

五K210、openmv、MSPM0G3507的部分源码

   1.k210源码,字模识别

# generated by maixhub, tested on maixpy3 v0.4.8
# copy files to TF card and plug into board and power on
import sensor, image, lcd, time
import KPU as kpu
from machine import UART
import gc, sys
from fpioa_manager import fm

input_size = (224, 224)
labels = ['2', '1', '8', '3', '6', '7', '4', '5']
anchors = [2.25, 2.38, 2.06, 2.03, 2.47, 2.66, 2.38, 2.91, 2.72, 3.06]
fm.register(13, fm.fpioa.UART1_TX, force=True)
uart1 = UART(UART.UART1, 115200, 8, 0, 1, timeout=1000, read_buf_len=4096)
def sending_data(a,b,c,d):
    FH = bytearray([int(a*100),int(b),int(c),int(d),0x0d,0x0a])
    uart1.write(FH);
    print(FH)
def lcd_show_except(e):
    import uio
    err_str = uio.StringIO()
    sys.print_exception(e, err_str)
    err_str = err_str.getvalue()
    img = image.Image(size=input_size)
    img.draw_string(0, 10, err_str, scale=1, color=(0xff,0x00,0x00))
    lcd.display(img)

class Comm:
    def __init__(self, uart):
        self.uart = uart

    def send_detect_result(self, objects, labels):
        msg = ""
        for obj in objects:
            pos = obj.rect()
            p = obj.value()
            idx = obj.classid()
            label = labels[idx]
            msg += "{}:{}:{}:{}:{}:{:.2f}:{}, ".format(pos[0], pos[1], pos[2], pos[3], idx, p, label)
        if msg:
            msg = msg[:-2] + "\n"
        self.uart.write(msg.encode())

def init_uart():
    fm.register(10, fm.fpioa.UART1_TX, force=True)
    fm.register(11, fm.fpioa.UART1_RX, force=True)

    uart = UART(UART.UART1, 115200, 8, 0, 0, timeout=1000, read_buf_len=256)
    return uart

def main(anchors, labels = None, model_addr="/sd/m.kmodel", sensor_window=input_size, lcd_rotation=0, sensor_hmirror=False, sensor_vflip=False):
    sensor.reset()
    sensor.set_pixformat(sensor.RGB565)
    sensor.set_framesize(sensor.QVGA)
    sensor.set_windowing(sensor_window)
    sensor.set_hmirror(sensor_hmirror)
    sensor.set_vflip(sensor_vflip)
    sensor.run(1)

    lcd.init(type=1)
    lcd.rotation(lcd_rotation)
    lcd.clear(lcd.WHITE)

    if not labels:
        with open('labels.txt','r') as f:
            exec(f.read())
    if not labels:
        print("no labels.txt")
        img = image.Image(size=(320, 240))
        img.draw_string(90, 110, "no labels.txt", color=(255, 0, 0), scale=2)
        lcd.display(img)
        return 1
    try:
        img = image.Image("startup.jpg")
        lcd.display(img)
    except Exception:
        img = image.Image(size=(320, 240))
        img.draw_string(90, 110, "loading model...", color=(255, 255, 255), scale=2)
        lcd.display(img)

    uart = init_uart()
    #comm = Comm(uart)

    try:
        task = None
        task = kpu.load(model_addr)
        kpu.init_yolo2(task, 0.5, 0.3, 5, anchors) # threshold:[0,1], nms_value: [0, 1]
        while(True):
            img = sensor.snapshot()
            t = time.ticks_ms()
            objects = kpu.run_yolo2(task, img)
            t = time.ticks_ms() - t
            if objects:
                for obj in objects:
                    pos = obj.rect()
	    if obj.value()>0.75 :
                    	sending_data(obj.value(),obj.x(),5,labels[obj.classid()])
                    img.draw_rectangle(pos)
                    img.draw_string(pos[0], pos[1], "%s : %.2f" %(labels[obj.classid()], obj.value()), scale=2, color=(255, 0, 0))
                   #comm.send_detect_result(objects, labels)
            img.draw_string(0, 200, "t:%dms" %(t), scale=2, color=(255, 0, 0))
            img.draw_string(0, 2, "Upgrade to MaixCAM to use YOLOv8", scale=1.2, color=(255, 0, 0))
            img.draw_string(0, 30, "wiki.sipeed.com/maixcam", scale=1.2, color=(255, 0, 0))
           
            lcd.display(img)
    except Exception as e:
        raise e
    finally:
        if not task is None:
            kpu.deinit(task)


if __name__ == "__main__":
    try:
        # main(anchors = anchors, labels=labels, model_addr=0x300000, lcd_rotation=0)
        main(anchors = anchors, labels=labels, model_addr="/sd/model-136737.kmodel")
    except Exception as e:
        sys.print_exception(e)
        lcd_show_except(e)
    finally:
        gc.collect()

2.openmv源码

import pyb, sensor, image, math, time
from pyb import UART
from pyb import LED
import ustruct
import json
sensor.reset() #初始化
sensor.set_pixformat(sensor.RGB565)#565彩图 sensor.GRAYSCALE灰度图
sensor.set_framesize(sensor.QQVGA)
sensor.skip_frames(time = 10)
sensor.set_hmirror(True)
sensor.set_vflip(True)
sensor.set_auto_gain(False) # must be turned off for color tracking
sensor.set_auto_whitebal(False)#关闭白平衡
t=0
uart = UART(3, 115200)
uart.init(115200, bits=8, parity=None, stop=1)
clock = time.clock() #跟踪FPS帧率
state=0
biaozhi=0
roi1 =[
         (2,45,158,28),
         (1,83,30,25),
         (135,82,30,25),
         (24,16,26,23),
         (135,14,30,25)
      ]
#roil感兴区域
THRESHOLD =[(0, 100, 1, 127, 10, 127),
            (41, 0, -128, 7, -118, 108)
           ]  #阈值
while(True):
    cross=0       #十字路口标志位
    XXX=0
    left=0
    right=0
    cross_right=0
    clock.tick()  # 追踪两个snapshots()之间经过的毫秒数.
    img = sensor.snapshot()#将捕获的图片存于IMG
    #img = sensor.snapshot().binary([thresholds], invert=False, zero=True)
    for rec in roi1:
        img.draw_rectangle(rec, color=(0,230,0)) #绘制红色矩形 rce表示矩形
    blob1 = img.find_blobs([THRESHOLD[0]],pixels_threshold=200, area_threshold=150,merge=True, roi=roi1[0])#巡线
    blob2 = img.find_blobs([THRESHOLD[0]],pixels_threshold=200, area_threshold=50,merge=True, roi=roi1[1])#左下
    blob3 = img.find_blobs([THRESHOLD[0]],pixels_threshold=200, area_threshold=50,merge=True, roi=roi1[2])#右下
    blob5 = img.find_blobs([THRESHOLD[0]],pixels_threshold=300, area_threshold=50,merge=True, roi=roi1[3])#左上
    blob7 = img.find_blobs([THRESHOLD[0]],pixels_threshold=200, area_threshold=50,merge=True, roi=roi1[4])#右上
    for abc in blob1:
        img.draw_cross(abc.cx(), abc.cy())
        XXX=abc.cx()
    if blob5:
       cross=1
    if blob7:
        cross_right=1
    if(not blob3 and not blob2):
       state=1
    if (blob3 and blob2):
        if(state==1):
           biaozhi += 1
           state = 0
    if blob2:
        left=1
    if blob3:
        right=1
    data = bytearray([XXX,cross,biaozhi,left,right,cross_right,0x0d,0x0a])
    uart.write(data)

    print(XXX,cross,biaozhi,left,right,cross_right) #  巡线标志,cx,路口标志,t无效,路口数

3.MSPM0G3507部分源码

OLED部分:

#include "oled.h"
#include "stdlib.h"
#include "oledfont.h"  	 
#include "stdio.h"
#include "delay.h"

//	 
//±¾³ÌÐòÖ»¹©Ñ§Ï°Ê¹Óã¬Î´¾­×÷ÕßÐí¿É£¬²»µÃÓÃÓÚÆäËüÈκÎÓÃ;
//ALIENTEK STM32F407¿ª·¢°å
//OLED Çý¶¯´úÂë	   
//ÕýµãÔ­×Ó@ALIENTEK
//¼¼ÊõÂÛ̳:www.openedv.com
//´´½¨ÈÕÆÚ:2014/5/4
//°æ±¾£ºV1.0
//°æȨËùÓУ¬µÁ°æ±Ø¾¿¡£
//Copyright(C) ¹ãÖÝÊÐÐÇÒíµç×ӿƼ¼ÓÐÏÞ¹«Ë¾ 2014-2024
//All rights reserved									  
// 	  
 

//OLEDµÄÏÔ´æ
//´æ·Å¸ñʽÈçÏÂ.
//[0]0 1 2 3 ... 127	
//[1]0 1 2 3 ... 127	
//[2]0 1 2 3 ... 127	
//[3]0 1 2 3 ... 127	
//[4]0 1 2 3 ... 127	
//[5]0 1 2 3 ... 127	
//[6]0 1 2 3 ... 127	
//[7]0 1 2 3 ... 127 		   
uint8_t OLED_GRAM[128][8];	 

//¸üÐÂÏÔ´æµ½LCD		 
void OLED_Refresh_Gram(void)
{
	uint8_t i,n;		    
	for(i=0;i<8;i++)  
	{  
		OLED_WR_Byte (0xb0+i,OLED_CMD);    //ÉèÖÃÒ³µØÖ·£¨0~7£©
		OLED_WR_Byte (0x00,OLED_CMD);      //ÉèÖÃÏÔʾλÖáªÁе͵ØÖ·
		OLED_WR_Byte (0x10,OLED_CMD);      //ÉèÖÃÏÔʾλÖáªÁиߵØÖ·   
		for(n=0;n<128;n++)OLED_WR_Byte(OLED_GRAM[n][i],OLED_DATA); 
	}   
}
//ÏòSSD1306дÈëÒ»¸ö×Ö½Ú¡£
//dat:ҪдÈëµÄÊý¾Ý/ÃüÁî
//cmd:Êý¾Ý/ÃüÁî±êÖ¾ 0,±íʾÃüÁî;1,±íʾÊý¾Ý;
void OLED_WR_Byte(uint8_t dat,uint8_t cmd)
{		
	if(cmd)
		OLED_DC_1;
	else 
		OLED_DC_0;	  
	for(uint8_t i=0;i<8;i++)
	{			  
		OLED_D0_0;
		if(dat&0x80)OLED_D1_1;
		else OLED_D1_0;
		OLED_D0_1;
		dat<<=1;   
	}				 	  
	OLED_DC_1;   	  
} 	  	  
//¿ªÆôOLEDÏÔʾ    
void OLED_Display_On(void)
{
	OLED_WR_Byte(0X8D,OLED_CMD);  //SET DCDCÃüÁî
	OLED_WR_Byte(0X14,OLED_CMD);  //DCDC ON
	OLED_WR_Byte(0XAF,OLED_CMD);  //DISPLAY ON
}
//¹Ø±ÕOLEDÏÔʾ     
void OLED_Display_Off(void)
{
	OLED_WR_Byte(0X8D,OLED_CMD);  //SET DCDCÃüÁî
	OLED_WR_Byte(0X10,OLED_CMD);  //DCDC OFF
	OLED_WR_Byte(0XAE,OLED_CMD);  //DISPLAY OFF
}		   			 
//ÇåÆÁº¯Êý,ÇåÍêÆÁ,Õû¸öÆÁÄ»ÊǺÚÉ«µÄ!ºÍûµãÁÁÒ»Ñù!!!	  
void OLED_Clear(void)  
{  
	uint8_t i,n;  
	for(i=0;i<8;i++)for(n=0;n<128;n++)OLED_GRAM[n][i]=0X00;  
	OLED_Refresh_Gram();//¸üÐÂÏÔʾ
}
//»­µã 
//x:0~127
//y:0~63
//t:1 Ìî³ä 0,Çå¿Õ				   
void OLED_DrawPoint(uint8_t x,uint8_t y,uint8_t t)
{
	uint8_t pos,bx,temp=0;
	if(x>127||y>63)return;//³¬³ö·¶Î§ÁË.
	pos=7-y/8;
	bx=y%8;
	temp=1<<(7-bx);
	if(t)OLED_GRAM[x][pos]|=temp;
	else OLED_GRAM[x][pos]&=~temp;	    
}
//x1,y1,x2,y2 Ìî³äÇøÓòµÄ¶Ô½Ç×ø±ê
//È·±£x1<=x2;y1<=y2 0<=x1<=127 0<=y1<=63	 	 
//dot:0,Çå¿Õ;1,Ìî³ä	  
void OLED_Fill(uint8_t x1,uint8_t y1,uint8_t x2,uint8_t y2,uint8_t dot)  
{  
	uint8_t x,y;  
	for(x=x1;x<=x2;x++)
	{
		for(y=y1;y<=y2;y++)OLED_DrawPoint(x,y,dot);
	}													    
	OLED_Refresh_Gram();//¸üÐÂÏÔʾ
}
//ÔÚÖ¸¶¨Î»ÖÃÏÔʾһ¸ö×Ö·û,°üÀ¨²¿·Ö×Ö·û
//x:0~127
//y:0~63
//mode:0,·´°×ÏÔʾ;1,Õý³£ÏÔʾ				 
//size:Ñ¡Ôñ×ÖÌå 12/16/24
void OLED_ShowChar(uint8_t x,uint8_t y,uint8_t chr,uint8_t size,uint8_t mode)
{      			    
	uint8_t temp,t,t1;
	uint8_t y0=y;
	uint8_t csize=(size/8+((size%8)?1:0))*(size/2);		//µÃµ½×ÖÌåÒ»¸ö×Ö·û¶ÔÓ¦µãÕó¼¯ËùÕ¼µÄ×Ö½ÚÊý
	chr=chr-' ';//µÃµ½Æ«ÒƺóµÄÖµ		 
    for(t=0;t<csize;t++)
    {   
		if(size==12)temp=asc2_1206[chr][t]; 	 	//µ÷ÓÃ1206×ÖÌå
		else if(size==16)temp=asc2_1608[chr][t];	//µ÷ÓÃ1608×ÖÌå
		else if(size==24)temp=asc2_2412[chr][t];	//µ÷ÓÃ2412×ÖÌå
		else return;								//ûÓеÄ×Ö¿â
        for(t1=0;t1<8;t1++)
		{
			if(temp&0x80)OLED_DrawPoint(x,y,mode);
			else OLED_DrawPoint(x,y,!mode);
			temp<<=1;
			y++;
			if((y-y0)==size)
			{
				y=y0;
				x++;
				break;
			}
		}  	 
    }          
}
//m^nº¯Êý
uint32_t mypow(uint8_t m,uint8_t n)
{
	uint32_t result=1;	 
	while(n--)result*=m;    
	return result;
}				  
//ÏÔʾ2¸öÊý×Ö
//x,y :Æðµã×ø±ê	 
//len :Êý×ÖµÄλÊý
//size:×ÖÌå´óС
//mode:ģʽ	0,Ìî³äģʽ;1,µþ¼Óģʽ
//num:ÊýÖµ(0~4294967295);	 		  
void OLED_ShowNum(uint8_t x,uint8_t y,uint32_t num,uint8_t len,uint8_t size)
{         	
	uint8_t t,temp;
	uint8_t enshow=0;						   
	for(t=0;t<len;t++)
	{
		temp=(num/mypow(10,len-t-1))%10;
		if(enshow==0&&t<(len-1))
		{
			if(temp==0)
			{
				OLED_ShowChar(x+(size/2)*t,y,' ',size,1);
				continue;
			}else enshow=1; 
		 	 
		}
	 	OLED_ShowChar(x+(size/2)*t,y,temp+'0',size,1); 
	}
} 
//ÏÔʾ×Ö·û´®
//x,y:Æðµã×ø±ê  
//size:×ÖÌå´óС 
//*p:×Ö·û´®ÆðʼµØÖ· 
void OLED_ShowString(uint8_t x,uint8_t y,const uint8_t *p,uint8_t size)
{	
    while((*p<='~')&&(*p>=' '))//ÅжÏÊDz»ÊÇ·Ç·¨×Ö·û!
    {       
        if(x>(128-(size/2))){x=0;y+=size;}
        if(y>(64-size)){y=x=0;OLED_Clear();}
        OLED_ShowChar(x,y,*p,size,1);	 
        x+=size/2;
        p++;
    }  
	
}	


//³õʼ»¯SSD1306					    
void OLED_Init(void)
{ 
	OLED_RES_0;
    delay_ms(200);
	OLED_RES_1; 
					  
	OLED_WR_Byte(0xAE,OLED_CMD); //¹Ø±ÕÏÔʾ
	OLED_WR_Byte(0xD5,OLED_CMD); //ÉèÖÃʱÖÓ·ÖƵÒò×Ó,Õðµ´ÆµÂÊ
	OLED_WR_Byte(80,OLED_CMD);   //[3:0],·ÖƵÒò×Ó;[7:4],Õðµ´ÆµÂÊ
	OLED_WR_Byte(0xA8,OLED_CMD); //ÉèÖÃÇý¶¯Â·Êý
	OLED_WR_Byte(0X3F,OLED_CMD); //ĬÈÏ0X3F(1/64) 
	OLED_WR_Byte(0xD3,OLED_CMD); //ÉèÖÃÏÔʾƫÒÆ
	OLED_WR_Byte(0X00,OLED_CMD); //ĬÈÏΪ0

	OLED_WR_Byte(0x40,OLED_CMD); //ÉèÖÃÏÔʾ¿ªÊ¼ÐÐ [5:0],ÐÐÊý.
													    
	OLED_WR_Byte(0x8D,OLED_CMD); //µçºÉ±ÃÉèÖÃ
	OLED_WR_Byte(0x14,OLED_CMD); //bit2£¬¿ªÆô/¹Ø±Õ
	OLED_WR_Byte(0x20,OLED_CMD); //ÉèÖÃÄÚ´æµØַģʽ
	OLED_WR_Byte(0x02,OLED_CMD); //[1:0],00£¬ÁеØַģʽ;01£¬ÐеØַģʽ;10,Ò³µØַģʽ;ĬÈÏ10;
	OLED_WR_Byte(0xA1,OLED_CMD); //¶ÎÖض¨ÒåÉèÖÃ,bit0:0,0->0;1,0->127;
	OLED_WR_Byte(0xC0,OLED_CMD); //ÉèÖÃCOMɨÃè·½Ïò;bit3:0,ÆÕͨģʽ;1,Öض¨Òåģʽ COM[N-1]->COM0;N:Çý¶¯Â·Êý
	OLED_WR_Byte(0xDA,OLED_CMD); //ÉèÖÃCOMÓ²¼þÒý½ÅÅäÖÃ
	OLED_WR_Byte(0x12,OLED_CMD); //[5:4]ÅäÖÃ
		 
	OLED_WR_Byte(0x81,OLED_CMD); //¶Ô±È¶ÈÉèÖÃ
	OLED_WR_Byte(0xEF,OLED_CMD); //1~255;ĬÈÏ0X7F (ÁÁ¶ÈÉèÖÃ,Ô½´óÔ½ÁÁ)
	OLED_WR_Byte(0xD9,OLED_CMD); //ÉèÖÃÔ¤³äµçÖÜÆÚ
	OLED_WR_Byte(0xf1,OLED_CMD); //[3:0],PHASE 1;[7:4],PHASE 2;
	OLED_WR_Byte(0xDB,OLED_CMD); //ÉèÖÃVCOMH µçѹ±¶ÂÊ
	OLED_WR_Byte(0x30,OLED_CMD); //[6:4] 000,0.65*vcc;001,0.77*vcc;011,0.83*vcc;

	OLED_WR_Byte(0xA4,OLED_CMD); //È«¾ÖÏÔʾ¿ªÆô;bit0:1,¿ªÆô;0,¹Ø±Õ;(°×ÆÁ/ºÚÆÁ)
	OLED_WR_Byte(0xA6,OLED_CMD); //ÉèÖÃÏÔʾ·½Ê½;bit0:1,·´ÏàÏÔʾ;0,Õý³£ÏÔʾ	 
    OLED_Clear();	
	OLED_WR_Byte(0xAF,OLED_CMD); //¿ªÆôÏÔʾ	 
	
}  

void OLED_ShowDecimals(uint16_t x,uint16_t y,const uint8_t *p,uint8_t size,int get)
{
     uint8_t test[10];
  while((*p<='~')&&(*p>=' '))	
	{
  if(x>(128-(size/2))){x=0;y+=size;}
        if(y>(64-size)){y=x=0;OLED_Clear();}
        OLED_ShowChar(x,y,*p,size,1);	 
        x+=size/2;
        p++;
	}
    sprintf((char*)test,"%6i",get);
	printf("test is %sC\n",test);
	printf("\r\n");
	OLED_ShowString(x+2,y,test,size);
}

//x,y:Æðµã×ø±ê
//num :×Ö¿âÖеڼ¸¸öºº×Ö
//size:×ÖÌå´óС
//mode£ºÄ£Ê½0,·´°×ÏÔʾ;1,Õý³£ÏÔʾ	
void OLED_ShowCHinese(uint8_t x, uint8_t y,  uint8_t num, uint8_t size,uint8_t mode)
{
    uint8_t temp,t,t1;
    uint8_t y0=y;
	//uint8_t size = 16;
    uint8_t csize=(size/8 + ((size%8)?1:0)) * size;     //µÃµ½×ÖÌåÒ»¸ö×Ö·û¶ÔÓ¦µãÕó¼¯ËùÕ¼µÄ×Ö½ÚÊý              
    for(t=0;t<csize;t++)
       {  
		  //  ÎÒÖ»¶¨ÒåÁË16£¬12ºÅ×ÖÌå   ûÓÐÉùÃ÷ÆäËû×ÖÌå
        if(size==12)      temp = china_1212[num][t];    //µ÷ÓÃ1212×ÖÌå
        else if(size==16) temp = china_1616[num][t];    //µ÷ÓÃ1616×ÖÌå
        else return;                                //ûÓеÄ×Ö¿â
		for(t1=0;t1<8;t1++)
           {if(temp&0x80)OLED_DrawPoint(x,y,mode);
            else OLED_DrawPoint(x,y,!mode);
            temp<<=1;
            y++;
            if((y-y0)==size)
              {y=y0;
                x++;
                break;
               }
           }    
       }  	
}









4.其他部分(串口部分)

#include "usart2.h"




uint16_t USART2_RX_STA=0;       //½ÓÊÕ״̬±ê¼Ç	

uint8_t USART2_RX_BUF[USART2_REC_LEN];     //½ÓÊÕ»º³å,×î´óUSART_REC_LEN¸ö×Ö½Ú.


void uart2_init(void)
{
     NVIC_ClearPendingIRQ(UART2_INT_IRQn);
     NVIC_EnableIRQ(UART2_INT_IRQn);
}







void UART2_IRQHandler(void)
{
     uint8_t Res;
     switch(DL_UART_getPendingInterrupt(UART2))  //½ÓÊÕÖжÏ(½ÓÊÕµ½µÄÊý¾Ý±ØÐëÊÇ0x0d 0x0a½áβ)
	     {
		 case DL_UART_IIDX_RX:
			  Res =DL_UART_Main_receiveData(UART2);//(USART0->DR);	//¶ÁÈ¡½ÓÊÕµ½µÄÊý¾Ý

		 if((USART2_RX_STA&0x8000)==0)//½ÓÊÕδÍê³É
		   {
			if(USART2_RX_STA&0x4000)//½ÓÊÕµ½ÁË0x0d
			   {
				if(Res!=0x0a)USART2_RX_STA=0;//½ÓÊÕ´íÎó,ÖØпªÊ¼
				else USART2_RX_STA|=0x8000;	//½ÓÊÕÍê³ÉÁË 
			    } 
			else //»¹Ã»ÊÕµ½0X0D
			   {	
				if(Res==0x0d)USART2_RX_STA|=0x4000;
				else
				   {
					USART2_RX_BUF[USART2_RX_STA&0X3FFF]=Res ;
					USART2_RX_STA++;
					if(USART2_RX_STA>(USART2_REC_LEN-1))USART2_RX_STA=0;//½ÓÊÕÊý¾Ý´íÎó,ÖØпªÊ¼½ÓÊÕ	  
				    }		 
			    }
		    }
			 
		 default:
			  break;   		 
           } 
}

电赛智能送药小车使用了openMV进行数字识别和模板匹配。在工作过程中,参赛者手动将小车摆放在药房处,手持数字标号纸张由小车识别病房号,并将药品装载到小车上。小车检测到药品装载完成后自动开始运送,根据走廊上的标识信息自动识别、寻径将药品送到指定病房。在摄像头部分,openMV可以寻红线、识别十字和黑白色块,并将处理好的数据传回给单片机进行PID控制。同时,openMV还可以实现一排识别多个数字的模板匹配识别功能。\[1\]\[2\]\[3\] #### 引用[.reference_title] - *1* *3* [2021电赛F智能送药小车方案分析(openMV数字识别,红线循迹,STM32HAL库freeRTOS,串级PID快速学习,小车自动...](https://blog.csdn.net/cubejava/article/details/121274043)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [2021电赛F送药小车视觉部分的一种思路(双OpenMV法)](https://blog.csdn.net/DCCSDNDC/article/details/121249961)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

电赛张小七

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值