2021年F题-智能送药小车+队名奇葩能得奖队 - 嘉立创EDA开源硬件平台

编辑器版本 ×
标准版 Standard

1、简单易用,可快速上手

2、流畅支持300个器件或1000个焊盘以下的设计规模

3、支持简单的电路仿真

4、面向学生、老师、创客

专业版 professional

1、全新的交互和界面

2、流畅支持超过3w器件或10w焊盘的设计规模,支持面板和外壳设计

3、更严谨的设计约束,更规范的流程

4、面向企业、更专业的用户

标准版 2021年F题-智能送药小车+队名奇葩能得奖队

简介:参加2021年电赛国赛的F题:智能送药小车。图像处理部分为搭载OPEMMV的H750核心板,主控制器为F401单片机,已经电机驱动、电源模块等部分。

开源协议: Public Domain

(未经作者授权,禁止转载)

创建时间: 2021-12-15 16:05:41
更新时间: 2024-04-06 20:26:57
描述
### 项目简介 参加2021年电赛国赛的F题:智能送药小车。图像处理部分为搭载OPEMMV的H750核心板,主控制器为F401单片机,已经电机驱动、电源模块等部分。 本开源工程开源电机驱动电路及PCB,已经对图像处理和控制的方案开源。 我们来看一下题目基本要求: ![wCB6ApoNmQwUzfLSfMdUpMTwQY86HPLl7ChtwSJk.png](//image.lceda.cn/pullimage/ZXRMD1igHFtvo5NkrqGZPkimLm2ACAIE3iForHjl.png) ![QQ截图20211217133723.png](//image.lceda.cn/pullimage/KxZhKgJNhkZXYVskwkt8xCd3KuTBpEido0BOmBvO.png) 分析:要实现智能小车自动识别病房号以及路面信息,小车就需要具备图像识别模块,我们这里选择的是搭载OPENMV的H750单片机核心板(图像处理部分具体分析见后文)。图像处理的结果通过串口通信的方式与主控单片机通信,根据题目要求控制智能小车移动。 另外,每一个要求都对完成时间进行了规定,使用速度是决胜的关键。我们队选择的是“三轮车”结构:两个主驱动轮以及一个被动的万向轮,这种结构可以让小车运动更加灵活,在直角转弯和原地返回部分更加有优势。驱动两个直流电机的驱动模块为DRV8701模块,具体电路及分析见下文。 - - - ### 项目功能介绍 * 小车电源部分使用12V航模电池,通过DCDC降压模块降压到3.3V给单片机等模块供电; * 使用DRV8701电机驱动模块(具体见工程文件); * 图像处理部分用的是搭载OPENMV的H750核心板,识别方案为模板匹配; ### 电路讲解 电机驱动部分     我们选择的驱动芯片是**德州仪器**的DRV8701这一款有刷直流电机全桥栅极驱动器,它具有输入电压宽、控制方便灵活、封装体积小等优点。 DRV8701配合使用4个 QN3109 N沟道MOS管组成的H桥电路以及一些外围电路,就可实现单路直流电机控制,实物图见下图。 这个原理图是根据DRV8701官方数据手册进行绘制的,电机斩波电流约为3.25A。在锂电池供电(约12V)下,能够很好地驱动型号为MG513的直流电机。 ![QQ截图20211215164002.png](//image.lceda.cn/pullimage/n3DRYn33Wfcd2Nrm9xvjprkWwSmam2zdGuFTmAgY.png) 在本系统中,总电源来自于一块锂电池(电压约12V),单片机和电机驱动等外围电路工作需要约3.3V工作电压,这就需要一个降压模块。 同时,由于需要给两块单片机和两个电机驱动等模块供电,对降压模块工作电流和工作稳定性提出了不小的要求。 多番挑选后,我们选择了SY8303这颗DCDC降压芯片,它具有输入电压宽、开关频率高、封装体积小等优点。SY8303降压模块电路实物图见下图。 ![QQ截图20211215164238.png](//image.lceda.cn/pullimage/QILfsMLfSi1UVwoxszHJy5rf7oNaW6i1xQPRMhO6.png)~~~~ - - - ### 软件 主控程序部分: 基础部分单车送药模式与发挥部分双车送药模式下主控单片机程序流程图如下图所示。 ![QQ截图20211215164635.png](//image.lceda.cn/pullimage/XGOC8YUYgWRGkJfEpObZhSftdOxCpWru6nGGagQF.png)     虽然题目中提出了车身投影不能压线的要求,但是事实上只要能够实现车体依照红实线进行循迹即可获得不压线的效果,因此单片机的第一个任务就是能够进行沿线循迹;其次,视觉部分是单片机的眼睛,在传感器有限的情况下必须对视觉所能提供的所有信息进行及时且充分有效的利用,故数据传输也是单片机的一大重要任务;此外,在调试过程中需要进行一些调试日志的输出、调试数据的打印,这也是单片机的重要任务。     总结一下单片机的这些任务主要分为两类:一类是复杂的前台循线任务,另一类是后台的数据传输任务(包括与视觉部分和上位机进行数据传输)。     首先是前台任务:整个完成任务的过程其实就是不断在多个不同的复杂状态之间切换的过程,因此整体的前台业务程序采用了有限状态机的程序架构,以便于更方便的展现和实现各个状态之间的关系以及各阶段的任务内容。     接下来是后台任务:后台任务除了数据传输还有一些重要控制量的更新。鉴于后台任务多、周期性强,我们对一个定时器的定时资源进行了复用,采用时间片轮询的程序架构,实现了一个简单的“阉割版操作系统”,然后将不同的工作分别放到各自的任务中,在定时器中进行不同任务的时间片轮询。 图像识别程序部分: 图像负责循迹,数字识别,判断转向方向等,具体流程图如下图所示 ![QQ截图20211215165019.png](//image.lceda.cn/pullimage/GzhMYfAuwXZVuOvs6UILiaPBXCPDlcwDI9O3BOYc.png) 1)自动循迹     直线循迹主要是对赛道中心红色直线做边缘检测以及霍夫直线检测(处理后的效果如图6所示),检测到直线后计算直线的中心x坐标相对于图像中心坐标的偏移量,发送给单片机,单片机将偏移量通过PID运算后作用到两轮速度上,通过两轮差速实现小车旋转,自动跟随红实线方向前行,从而实现自动寻径。     循迹部分的第二个部分是对直角十字路口的识别,基本步骤和直线识别一致,最后比较两直线的夹角以及两直线的交点,为数字识别做基础,十字识别效果如下图所示 ![QQ截图20211215165144.png](//image.lceda.cn/pullimage/N5eUgnQjA08xkeqUtrah5vY6NAplTGADvB48fScP.png) 2)数字识别     本系统中的数字识别采用的是OpenMV中的模板匹配功能,它能自动匹配与模板图片大小和角度基本一致的图案。在确认病房号后,事先储存在单片机flsah里的对应数字图片将作为模板图片。在遇到十字路口时将摄像头拍到的画面与目标数字进行模板匹配,若能成功匹配则记录下对应数字的位置,和十字中心X坐标对比,从而判断出小车的转弯方向,然后向单片机发送转弯命令。数字识别效果如图(以识别数字4为例)所示。 ![QQ截图20211215165235.png](//image.lceda.cn/pullimage/Loe8lrparS8hGBcdtmJ6YqZnjrEaLGdij3l88PVU.png) 图像处理部分数字识别部分代码: ``` C def find_single_num(img,num,y_thre):   #寻找数字     if(num==3):         for temp in template3:             r3=img.find_template(temp, 0.7, step=1, search=SEARCH_EX) #, roi=(10, 0, 60, 60))             if(r3):                 if(r3[1]+r3[3]/2>y_thre):                     black_num=0                     for x in range(r3[0],r3[0]+r3[2]):                         if img.get_pixel(x,int(r3[1]+r3[3]/2))<40:                             black_num+=1                     if(black_num>2):                         img.draw_rectangle(r3)                         return r3[0]+r3[2]/2     elif(num==4):         for temp in template4:             r4=img.find_template(temp, 0.7, step=1, search=SEARCH_EX)#, roi=(10, 0, 60, 60))             if(r4):                 if(r4[1]+r4[3]/2>y_thre):                     black_num=0                     for x in range(r4[0],r4[0]+r4[2]):                         if img.get_pixel(x,int(r4[1]+r4[3]/2))<40:                             black_num+=1                     if(black_num>2):                         img.draw_rectangle(r4)                         return r4[0]+r4[2]/2     elif(num==5):         for temp in template5:             r5=img.find_template(temp, 0.7, step=1, search=SEARCH_EX) #, roi=(10, 0, 60, 60))             if(r5):                 if(r5[1]+r5[3]/2>y_thre):                     black_num=0                     for x in range(r5[0],r5[0]+r5[2]):                         if img.get_pixel(x,int(r5[1]+r5[3]/2))<40:                             black_num+=1                     if(black_num>2):                         img.draw_rectangle(r5)                         return r5[0]+r5[2]/2     elif(num==6):         for temp in template6:             r6=img.find_template(temp, 0.7, step=1, search=SEARCH_EX) #, roi=(10, 0, 60, 60))             if(r6):                 if(r6[1]+r6[3]/2>y_thre):                     black_num=0                     for x in range(r6[0],r6[0]+r6[2]):                         if img.get_pixel(x,int(r6[1]+r6[3]/2))<40:                             black_num+=1                     if(black_num>2):                         img.draw_rectangle(r6)                         return r6[0]+r6[2]/2     elif(num==7):         for temp in template7:             r7=img.find_template(temp, 0.7, step=1, search=SEARCH_EX) #, roi=(10, 0, 60, 60))             if(r7):                 if(r7[1]+r7[3]/2>y_thre):                     black_num=0                     for x in range(r7[0],r7[0]+r7[2]):                         if img.get_pixel(x,int(r7[1]+r7[3]/2))<40:                             black_num+=1                     if(black_num>2):                         img.draw_rectangle(r7)                         return r7[0]+r7[2]/2     elif(num==8):         for temp in template8:             r8=img.find_template(temp, 0.7, step=1, search=SEARCH_EX) #, roi=(10, 0, 60, 60))             if(r8):                 if(r8[1]+r8[3]/2>y_thre):                     black_num=0                     for x in range(r8[0],r8[0]+r8[2]):                         if img.get_pixel(x,int(r8[1]+r8[3]/2))<40:                             black_num+=1                     if(black_num>2):                         img.draw_rectangle(r8)                         return r8[0]+r8[2]/2     return 0 ``` - - - ### 图像处理部分十字路口识别部分代码: ``` C def check_cross(img):  #十字路口识别     left_y=0     right_y=0     for blob in img.find_blobs([(100,200)], pixel_threshold=500,merge=True, margin=5,roi=(0,0,60,120)):         img.draw_rectangle(blob.rect())         left_y=blob.cy()         img.draw_cross(blob.cx(), blob.cy())     for blob in img.find_blobs([(100,200)], pixel_threshold=500,merge=True, margin=5,roi=(100,0,60,120)):         img.draw_rectangle(blob.rect())         right_y=blob.cy()         img.draw_cross(blob.cx(), blob.cy())     if(left_y and right_y):         if(math.fabs(left_y-right_y)<20):             return 1     return 0 find_cross_flag=1 ``` - - - ### 图片 测试场地如下图: ![QQ截图20211215165345.png](//image.lceda.cn/pullimage/3fl9x7sPeKC9VPSU8L8BizYS4swrEBMHbg1vzL14.png) 小车整体照片如下图: ![QQ截图20211215165418.png](//image.lceda.cn/pullimage/cOfjndneya5buJxXxQPKQezPde6NRps9og7L1UC8.png) 测试视频见附件,项目早期拍摄,有瑕疵。
设计图
原理图
1 /
PCB
1 /
未生成预览图,请在编辑器重新保存一次
工程视频/附件
序号 文件名称 下载次数
1

测试视频_x264.mp4

2107
侵权投诉
相关工程
换一批
加载中...
添加到专辑 ×

加载中...

温馨提示 ×

是否需要添加此工程到专辑?

温馨提示
动态内容涉嫌违规
内容:
  • 153 6159 2675

服务时间

周一至周五 9:00~18:00
  • 技术支持

support
  • 开源平台公众号

MP