摘要:根据2021年全国大学生电子设计竞赛的题目要求,本次系统采用型号为MM32SPIN27PS的芯片设计了两台智能送药小车。系统硬件主要有中央处理器、图像采集电路、小车、电机驱动电路等四部分组成。两小车的供电采用电池供电,图像采集器选用OpenMV摄像头,在小车运行过程中实时拍摄并处理走廊上的拍摄到的数字图像信息,实时将信息传输给为MM32SPIN27PS中央芯片根据算法发出控制指令,控制小车按照指定要求要求运行,自动巡路。
关键字:智能小车、自动巡路、OpenMV、MM32SPIN27PS芯片
1、设计方案工作原理
1.1 预期实现目标定位
本次大赛中我组选择的题目为F:智能送药小车,需要设计并制作智能送药小车,能够在固定的时间内,在如下图所示的指定赛道内,根据走廊上的标识信息自动识别、寻径将药品送到指定病房,使得小车在点亮红色指示灯得时候,等待卸载药品;等病房处人工卸载药品后,小车自动熄灭红色指示灯,开始返回;小车自动返回到药房后,点亮绿色指示灯,从而模拟完成在医院药房与病房间药品的送取作业。
图1.1.1 模拟医院区域结构示意图
1.2 技术方案分析比较
(1)直流电动机驱动模块
方案一:基于L298N的驱动模块。L298N是一款接受高电压的电机驱动器,直流电机和步进电机都可以驱动。一片驱动芯片可同时控制两个直流减速电机做不同动作,在6V到46V的电压范围内,提供2安培的电流,并且具有过热自断和反馈检测功能,但是电流过大可能引起L298N的烧毁。
方案二:基于BTN7971B的驱动模块。BTN7971B用于电机驱动应用的集成大电流半桥。它包含一个p沟道高侧MOSFET和一个n沟道低侧MOSFET,其中一个封装有集成驱动芯片。由于p通道高压侧开关,因此无需电荷泵,从而将电磁干扰降至最低。BTN7971B提供了一个成本优化的解决方案,用于具有非常低的板空间消耗的受保护的大电流PWM电机驱动器。
分析比较:因为电池采用的是组委会提供的7.2V锂电池。L7805生成5V电源,采用HT7333-A生成3.3V电源为单片机供电。我们发现方案一中使用的L298N芯片驱动S380电机时由于内阻过大发热眼中,于是采用了方案二中驱动电流大,内阻小,驱动能力强的BTN7971B直流电机驱动芯片。
(2)巡线模块
方案一:基于光电对管原理的巡线模块。基于TCRT5000红外光电传感器设计的一款红外反射式光电开关。传感器采用高发射功率红外光电二极管和高灵敏度光电晶体管组成,输出的信号经施密特电路整形。
方案二:基于MT9V03X摄像头的巡线模块,最高分辨率为 752(H) x 480(V),以每秒60帧的形式输出。
分析比较:在实际操作中,基于方案一的巡线模块系统在巡线时并不稳定,识别红线也存在一定问题;而方案二使用MT9V03X摄像头巡线稳定。经过分析比较,为了能够更好的地识别红线进行自动寻路,我们小组选用了方案二。
(3)数字图像信息识别模块
方案一:基于K210的数字图像信息识别模块,其具有双核64位处理器,并自带独立FPU;有一块KPU用于神经网络加速单元;还有一块APU用于语音数据处理。。
方案二:基于OpenMV的数字图像信息识别模块。OpenMV摄像头是一款小巧,低功耗,低成本的电路板。帧数可在0~500帧以内随意切换,并且在此范围内的图像均很稳定
分析比较:在进行实验的过程中,我们发现K210运行同样的代码的时候运行速度和帧数不如OpenMV,导致其在识别数字信息的识别率差,而在采用OpenMV时并没有出现这些情况。综上,为了更好地实现模块功能,我们小组决定选用方案二。
2、核心部件电路设计
2.1 主控模块
MM32SPIN27PS使用高性能的 ARM® CortexTM-M0 为内核的 32 位微控制器,最高工作频率可达96MHz,内置高速存储器,丰富的增强型 I/O 端口和外设连接到外部总线。包含 2个 12 位的 ADC、5个比较器、4个运算放大器、1个16 位通用定时器、1个32 位通用定时器、3个16 位基本定时器、2个16 位高级定时器。还包含标准的通信接口:1个I2C接口、2个SPI 接口和 2个UART 接口。
图2.1.1 MM32SPIN27PS电路图
2.2驱动模块
驱动芯片我们采用的是BTN7971B,该芯片具有耐压值高,载流能力强等优点,而且电路比较简单。电源电压直接给驱动板供电,单片机输出PWM直接驱动电机,BTN7971B是一个内置半桥和MOS的栅极驱动器,所以使用两路BTN7971B就可以搭建一个完整的H桥从而使用PWM调整电机的速度以及正反。在三轮车上,转向是通过两个轮子的差速实现的,使用四个BTN7971B就能很好的控制两个电机实现一系列的操作。
图2. 2.1 BTN7971B驱动电路图
2.3稳压电路模块
考虑到一般通用的2S或者3S锂电池的电压在7.4V或11.1V,而MM32SPIN27PS以及需要使用到的外设是3.3V供电的,所以我们设计了两层降压电路,通过L7805将输入的电池电压降低到5V提供给一些大功率外设和需要5V供电的传感器,然后使用HT7333-A把5V的电压稳至3.3V提供给单片机和摄像头,HT7333-A可以提供300mA的电流,L7805可以提供800mA的电流,因此比较适合需要小功率线性稳压的使用场景。
图2. 3.1 稳压电源电路图
2.4双车通信模块
蓝牙无线转串口是双车通信模块的主要组成。通过蓝牙转串口模块能够实现蓝牙和串口(UART)之间的数据互传,从而实现两辆智能小车之间的协同工作。
图2.4.1 蓝牙转串口
3、系统软件设计分析
3.1系统总体工作流程
根据题目要求,我们设计了双送药小车的工作流程,如下图:
图 3.1.1 系统总体工作流程图
3.2主要模块程序设计
(1)主控模块
图3.2.1.1 主控程序代码
(2)图像识别模块
图3.2.2.1 图像识别代码
4、竞赛工作环境条件
4.1设计分析软件环境
keil uvision5 :单片机开发平台;
Altium Designer:PCB电路板设计。
4.2仪器设备硬件平台
可调直流电源、示波器、MM32SPIN27PS的芯片
4.3配套加工安装条件
焊台、焊锡、螺丝刀、万用表
4.4前期设计使用模块
焊台、焊锡、螺丝刀、万用表
5、作品成效总结分析
5.1小车速度测试
表1 速度测量测试
测量次数 |
起点 |
终点 |
时间/s |
1 |
药房 |
5号病房 |
18 |
2 |
药房 |
7号病房 |
19 |
3 |
药房 |
8号病房 |
20 |
4 |
药房 |
9号病房 |
17 |
5.2小车识别图像
表2 数字识别测量测试
测量次数 |
显示数字 |
测量数据 |
1 |
1 |
1 |
2 |
2 |
2 |
3 |
3 |
3 |
4 |
4 |
3 |
5 |
5 |
5 |
6 |
6 |
6 |
7 |
7 |
7 |
8 |
8 |
8 |
5.3系统测试性能指标
本系统成功通过MM32SPIN27PS主控芯片整合了速度方向控制,实现了小车的主控功能。然后通过OpenMV摄像头将信息传输到1.8寸TFT液晶显示屏上,进行周围环境的实时显示以及参数调整,做到了低误差,低延迟,符合题目要求。
本系统设计利用MT9V03X芯片,较为准确地检测走廊区域的红线并进行自动巡路,较为准确地识别病房的数字标号,并根据要求较为灵活地实现速度与方向的控制以及药物的装载与卸除。
5.4成效得失对比分析
本系统较为完整地完成了设计要求,本小组也通过这次比赛课题进一步掌握了利用单片机整合主控以及图像识别处理等功能。但在实验过程中,偶尔会出现轮胎不转动以及路线识别错误等情况,经过排查,是由于导线接触不良以及光线散播不均匀的原因导致的。
6、参考资料及文献
[1]张文青,龙奕帆.基于OpenMV视觉模块的智能小车巡线系统设计[J].集成电路应用,2021,38(10):232-233.
[2]郭荣毓,任姝静,陈宇航,王天阳,赵永亮.医疗辅助智能车的研究与设计[J].电子世界,2021(11):156-157.
[3] 胡寿松著,《自动控制原理(第6版)》,科学出版社,2015.12
[4] 董景新、赵长德、熊沈蜀、郭美凤,《控制工程基础(第2版)》,清华大学出版社,2001.8
附件:
1.竞赛相关图纸资料
2.重要程序段清单
void Img_Process(void)
{
if(mt9v03x_finish_flag)
{
#if 1 //图像处理
mt9v03x_finish_flag=0;
OSG_GetUseImg(mt9v03x_image, g_u8ImgAr); //剪切图像
g_stBorder.Threshold = OSG_GetThreshold(g_u8ImgAr);
OSG_GetBinaryImage(g_u8ImgAr, g_u8ImgAr, g_stBorder.Threshold); //获取二值化图像
OSG_GetBorder(g_u8ImgAr, &g_stBorder);
OSG_GetBorder_2(g_u8ImgAr, &g_stBorder);
OSG_GetTrackType(g_u8ImgAr, &g_stBorder, &g_stTrackType); //获取边界
OSG_GetLineError(&g_stBorder, &g_stLineError, &g_stTrackType); //根据中线计算偏差
//lcd_showint16(0,6,(int16)(g_stLineError.m_f32LineAllError));
#endif
Dir_Control();
}
}
void TIM14_IRQHandler (void)
{
uint32 state = TIM14->SR; // 读取中断状态
TIM14->SR &= ~state; // 清空中断状态
Time_counter++;
dir_R=gpio_get(C0)?-1:1;
dir_L=gpio_get(A8)?1:-1; // 清空中断状态
encounter_L = dir_L*tim_encoder_get_count(TIM_1); // 采集对应编码器数据
encounter_R = dir_R*tim_encoder_get_count(TIM_8); // 采集对应编码器数据
Total_encounter_L+=encounter_L;
Total_encounter_R+=encounter_R;
tim_encoder_rst(TIM_1);
tim_encoder_rst(TIM_8);
if(!Stop_Cross_Flag)
{
SpeedL = PID_Realize(&MOTOR_PID, MOTOR1,encounter_L, Aim_speed+DiffSpeed_);//-30
SpeedR = PID_Realize(&MOTOR1_PID,MOTOR1,encounter_R, Aim_speed-DiffSpeed_);// -30
Motor_Control(Motor_Left,SpeedL);//
Motor_Control(Motor_Right,SpeedR);//
}
else
{
if(Cross_Roads_Count)
{
}
}
}
void Turn_Angle(uint8 Dir,int16 angle)
{
Total_encounter_L=Total_encounter_R=0;
if(Dir==Right_Dir)
{
switch(angle)
{
case 90:
//Count_Dist(2590);
while(((ABS(Total_encounter_L)+ABS(Total_encounter_R)))<=1280||((
((ABS(Total_encounter_L)+ABS(Total_encounter_R))>1000))&&ABS(g_stLineError.m_f32LineAllError)>6))
{
Img_Process();
Speed_L = PID_Realize(&MOTOR_PID, MOTOR,encounter_L, 20);//-30
Speed_R = PID_Realize(&MOTOR1_PID,MOTOR,encounter_R, -20);// -30
Motor_Control(Motor_Left,Speed_L);//
Motor_Control(Motor_Right,Speed_R);//
}
//Speed_L = PID_Realize(&MOTOR_PID, MOTOR,encounter_L, 0);//-30
//Speed_R = PID_Realize(&MOTOR1_PID,MOTOR,encounter_R, 0);// -30
lcd_showint16(1,1,Guide);
Motor_Control(Motor_Left,0);//
Motor_Control(Motor_Right,0);//
systick_delay_ms(1000);
Total_encounter_L=0;
Total_encounter_R=0;
break;
case 180:
//Count_Dist(2590);
while((ABS(Total_encounter_L)+ABS(Total_encounter_R))<=2850)
{
Speed_L = PID_Realize(&MOTOR_PID, MOTOR,encounter_L, 40);//-30
Speed_R = PID_Realize(&MOTOR1_PID,MOTOR,encounter_R, -45);// -30
Motor_Control(Motor_Left,Speed_L);//
Motor_Control(Motor_Right,Speed_R);//
}
gpio_set(A7,0);
Motor_Control(Motor_Left,0);//
Motor_Control(Motor_Right,0);//
systick_delay_ms(1000);
gpio_set(A7,1);
Total_encounter_L=0;
Total_encounter_R=0;
break;
}
}
else if(Dir==Left_Dir)
{
switch(angle)
{
case 90:
//Count_Dist(2590);
//while((ABS(Total_encounter_L)+ABS(Total_encounter_R))<=1250)
while(((ABS(Total_encounter_L)+ABS(Total_encounter_R)))<=1280||((
((ABS(Total_encounter_L)+ABS(Total_encounter_R))>800))&&ABS(g_stLineError.m_f32LineAllError)>6))
{
Img_Process();
Speed_L = PID_Realize(&MOTOR_PID, MOTOR,encounter_L, -20);//-30
Speed_R = PID_Realize(&MOTOR1_PID,MOTOR,encounter_R, 20);// -30
Motor_Control(Motor_Left,Speed_L);//
Motor_Control(Motor_Right,Speed_R);//
}
{
Speed_L = PID_Realize(&MOTOR_PID, MOTOR,encounter_L, -45);//-30
Speed_R = PID_Realize(&MOTOR1_PID,MOTOR,encounter_R, 40);// -30
Motor_Control(Motor_Left,Speed_L);//
Motor_Control(Motor_Right,Speed_R);//
}
Motor_Control(Motor_Left,0);//
Motor_Control(Motor_Right,0);//
systick_delay_ms(1000);
Total_encounter_L=0;
Total_encounter_R=0;
break;
case 180:
//Count_Dist(2590);
gpio_set(A7,0);
Motor_Control(Motor_Left,0);//
Motor_Control(Motor_Right,0);//
while(!gpio_get(C3))
{}
while((ABS(Total_encounter_L)+ABS(Total_encounter_R))<=2850)
{
Speed_L = PID_Realize(&MOTOR_PID, MOTOR,encounter_L, -45);//-30
Speed_R = PID_Realize(&MOTOR1_PID,MOTOR,encounter_R, 40);// -30
Motor_Control(Motor_Left,Speed_L);//
Motor_Control(Motor_Right,Speed_R);//
}
Motor_Control(Motor_Left,0);//
Motor_Control(Motor_Right,0);//
systick_delay_ms(1000);
gpio_set(A7,1);
Total_encounter_L=0;
Total_encounter_R=0;
break;
}
}
}
3.作品照片