STM32F427主控(大疆A板)+K210视觉处理

一 任务及设计要求

1.1任务

模拟飞行器(如无人机、导弹等)在空中飞行过程中使用九轴加速度/陀螺仪/磁力计模块采集姿态信息;并且能使用摄像头模块采集图像信息实现目标检测功能,从而实现飞行器朝目标飞行或者对目标实施精确打击。

二 实现功能

2.1 stm32f427开发板板载陀螺仪最终温漂造成的yaw轴误差保持在1°/1min以内;
2.2 k210开发板能够较准确地识别目标,并将图像显示在LCD屏上;
2.3 k210开发板将目标在LCD屏的位置信息通过串口发送给stm32f427开发板;
2.4 若目标在LCD中心附近(即不需要飞行器调整姿态),则stm32f427开发板蜂鸣器响;

三 系统原理框图及模块作用

3.1 系统原理框图

STM32F427主控(大疆A板)+K210视觉处理_第1张图片

3.2 各模块作用

3.2.1 蜂鸣器模块
stm32f427开发板板载一个贴片蜂鸣器,该蜂鸣器需要使用PWM驱动,额定频率2700Hz,用以提示飞行器是否对准目标。

3.2.2 mpu6500+ist8310九轴陀螺仪传感器模块
stm32f427开发板集成一个IMU模块,由mpu6500陀螺仪和ist8310地磁传感器组成,用以采集姿态信息。

3.2.3 LCD模块
用以显示摄像头采集的图像,并将目标物体用矩形框标记,显示矩形框中心点坐标位置以及识别到的物体名称。

3.2.4 OV2640模块
用以采集图像,为k210目标检测提供信息。

四 硬件说明

4.1 硬件设计

4.2 硬件电路图及模块说明

4.2.1 蜂鸣器(STM32F4)
STM32F427主控(大疆A板)+K210视觉处理_第2张图片
开发板板载一个贴片蜂鸣器,需要使用PWM驱动,额定频率2700Hz。
4.2.2 IMU(STM32F4)
STM32F427主控(大疆A板)+K210视觉处理_第3张图片
STM32F427主控(大疆A板)+K210视觉处理_第4张图片
STM32F427主控(大疆A板)+K210视觉处理_第5张图片
开发板集成-一个IMU模块,其IMU由MPU6500陀螺仪和IST8310地磁传感器组成。为了解决陀螺仪温飘的问题,在MPU6500四周增加10颗加热电阻,可以通过PB5加热电阻控制管脚和MPU6500内部的温度传感器做恒温处理,加热温度一般控制在比电路板正常工作温度高15~20C为宜。10个加热电阻工作电压为24V,该电阻可以在1S内将IMU模块的温度从25C加热到50°C。板载IST8310的地址为: 0x0E。

五 程序流程图

5.1 主程序流程图

STM32F427主控(大疆A板)+K210视觉处理_第6张图片

5.2 模块流程图及其说明

5.2.1 串口中断及串口虚拟示波器(STM32F427)
STM32F427主控(大疆A板)+K210视觉处理_第7张图片
串口中断主要用于接收PC端发送的数据,并打印到串口助手上;并且同时要接收K210发送的8位大小的数据串(发送数据形式为:LCD屏上目标的三位数横坐标+‘/’+LCD屏上目标的三位数纵坐标+‘\n’),若当STM32F4串口接收完毕后,发生以下情况,则判断接收成功:
(1)接收到了8位数据
(2)接收到的第四位数据为‘/’
(3)接收到的第八位数据为‘\n’
以上条件缺一不可,若串口接收完毕后未能满足以上条件,则将数组清零重新接收。

串口虚拟示波器是一个PC端软件,有两种通讯协议 一种是CRC16另一种是ChkSum,我使用CRC16通信协议,简单的来说就是把我单片机的数据转换为示波器软件可以识别的形式读取并且显示。
STM32F427主控(大疆A板)+K210视觉处理_第8张图片
以上为串口示波器的界面展示,有四个通道对应四条波形,界面可以实现放大、缩小以及跟随波形等功能。
串口示波器下载请移步俺的博客:
https://blog.csdn.net/Junhanie/article/details/105851994
5.2.2 九轴陀螺仪传感器初始化
STM32F427主控(大疆A板)+K210视觉处理_第9张图片
MPU6500使用SPI通信,IST8310使用IIC通信。
设置的测量精度为gyro LPF 41Hz、 LPF 92Hz、±2000dps、±8G。

5.2.3 PWM+PID补偿温度误差调节姿态输出(定时器中断)
STM32F427主控(大疆A板)+K210视觉处理_第10张图片
PWM+PID补偿温度误差调节姿态输出主体程序放在定时器中断内,5ms进入一次中断。
其中,温度PID+PWM调节函数mpu_temp_control用于控制陀螺仪环境温度处于理想状态,缓解温漂对YAW轴造成的误差。在开发板第一次上电后,PWM输出以一个较大的值2000加热陀螺仪传感器模块周围电阻,当温度超过设定温度累计次数20次以上时,启动PID调节PWM输出,PID算法函数pid_calc输入量为实际温度和设定温度。
其中,一阶低通滤波函数first_order_filter_calc起到的作用是放缓PID输出数据的变化,避免因为个别数据误差较大导致PID输出量突然变化较大导致的温度不稳定情况,sampling_period采样周期为1,RC_time时间常数为100。根据一阶数字低通滤波算法Y(n)=T/(T+RCX(n))+RC/(T+RCY(n−1))改造first_order_filter_calc函数,RC即为RC_time,T即为sampling_period。

5.2.4 深度学习模型(yolov3)
STM32F427主控(大疆A板)+K210视觉处理_第11张图片
对我帮助很大的是B站up主理工男的春天,参考视频请见
https://www.bilibili.com/video/BV1tT4y1573S
up主有很多视频介绍yolo等算法入门,强烈推荐

yolo代码以及环境配置参考
https://github.com/TonyZ1Min/yolo-for-k210

下次再出一个博客谈谈俺踩过的坑

六 代码分析

6.1 STM32F4 定时器部分

因为我大部分处理数据的代码都是放在定时器中断里

#include "timer_pid.h"
#include "pid.h"
#include "delay.h"
#include "usart.h"
#include "init.h"
#include "filter.h"
#include "ahrs.h"
#include "mpu6500.h"
#include "imu.h"
#include "adc.h"
#include "global_def.h"

//5ms 
struct attitude mahony_atti;
struct ahrs_sensor mpu_sensor;
float mputemp2;

extern float mcu_temp;
extern pid_t pid_temp;
uint8_t mpu_count=0,mpu_flag=0;

static uint8_t first_temperate = 0;//µÚÒ»´ÎζÈÎȶ¨Ç° Îȶ¨ºóΪ1
static uint8_t second_temperate = 0;
const float *get_mcu_temp(void)
{
	return &mcu_temp;
}

const struct attitude* get_imu_data(void)
{
	return &mahony_atti;
}

/**
  * @brief ¶¨Ê±Æ÷1³õʼ»¯|Ħ²ÁÂÖpwmÊä³ö
  * @param  void
  * @retval void
  * @attention 
  */
void timer1_init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE);
	
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
	GPIO_InitTypeDef GPIO_InitStruct;
	TIM_OCInitTypeDef TIM_OCInitStruct;
	
	GPIO_PinAFConfig(GPIOA, GPIO_PinSource8, GPIO_AF_TIM1);
	GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_TIM1);
	GPIO_PinAFConfig(GPIOE, GPIO_PinSource13, GPIO_AF_TIM1);
	GPIO_PinAFConfig(GPIOE, GPIO_PinSource14, GPIO_AF_TIM1);
	
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9;
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;
	GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;
	GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
	GPIO_InitStruct.GPIO_Speed = GPIO_High_Speed;
	GPIO_Init(GPIOA, &GPIO_InitStruct);
	
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14;
	GPIO_Init(GPIOE, &GPIO_InitStruct);
	
	TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;
	TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_TimeBaseInitStruct.TIM_Period = 2500 - 1;	
	TIM_TimeBaseInitStruct.TIM_Prescaler = 180 - 1;
	TIM_TimeBaseInit(TIM1, &TIM_TimeBaseInitStruct);
	
	TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1;
	TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High;
	TIM_OCInitStruct.TIM_OCNPolarity = TIM_OCNPolarity_High;
	TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable; 
	TIM_OCInitStruct.TIM_OutputNState = TIM_OutputNState_Disable;
	TIM_OCInitStruct.TIM_OCIdleState = TIM_OCIdleState_Reset;
	TIM_OCInitStruct.TIM_OCNIdleState = TIM_OCNIdleState_Reset;	
	TIM_OCInitStruct.TIM_Pulse = 900;
	
	TIM_OC1Init(TIM1, &TIM_OCInitStruct);
	TIM_OC2Init(TIM1, &TIM_OCInitStruct);
	TIM_OC3Init(TIM1, &TIM_OCInitStruct);
	TIM_OC4Init(TIM1, &TIM_OCInitStruct);
	
	TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable);
	TIM_OC2PreloadConfig(TIM1, TIM_OCPreload_Enable);
	TIM_OC3PreloadConfig(TIM1, TIM_OCPreload_Enable);
	TIM_OC4PreloadConfig(TIM1, TIM_OCPreload_Enable);
	
	TIM_ARRPreloadConfig(TIM1, ENABLE);
	
	TIM_CtrlPWMOutputs(TIM1, ENABLE);
	
	TIM_Cmd(TIM1, ENABLE);
}

/**
  * @brief 定时器3初始化,PWM加热陀螺仪周围电阻
  * @param  void
  * @retval void
  * @attention 
  */
void timer3_init(void)
{	
		TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
    GPIO_InitTypeDef GPIO_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;
    TIM_OCInitTypeDef TIM_OCInitStructure;

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);

    GPIO_PinAFConfig(GPIOB, GPIO_PinSource5, GPIO_AF_TIM3);

    RCC_APB1PeriphResetCmd(RCC_APB1Periph_TIM3, ENABLE);
    RCC_APB1PeriphResetCmd(RCC_APB1Periph_TIM3, DISABLE);

    NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 5;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x00;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

	//TIM3_Int_Init(5000,1) rm????
    TIM_TimeBaseInitStructure.TIM_Period = 500 - 1;//50us
    TIM_TimeBaseInitStructure.TIM_Prescaler = 3 - 1;//90*1000000/9 = 10Mhz 0.1us
    TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;

//    TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);

    TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure);

    /* TIM3 */
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN;
    GPIO_Init(GPIOB, &GPIO_InitStructure);

    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
    TIM_OC2Init(TIM3, &TIM_OCInitStructure);

    TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);

    TIM_ARRPreloadConfig(TIM3, ENABLE);

    TIM_Cmd(TIM3, ENABLE);
}

void timer4_init(void)
{
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
	
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
	GPIO_InitTypeDef GPIO_InitStruct;
	TIM_OCInitTypeDef TIM_OCInitStruct;
	
	GPIO_PinAFConfig(GPIOD, GPIO_PinSource12, GPIO_AF_TIM4);
	GPIO_PinAFConfig(GPIOD, GPIO_PinSource13, GPIO_AF_TIM4);
	
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13;
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;
	GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;
	GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
	GPIO_InitStruct.GPIO_Speed = GPIO_High_Speed;
	GPIO_Init(GPIOD, &GPIO_InitStruct);
	
	TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;
	TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_TimeBaseInitStruct.TIM_Period = 20000;		
	TIM_TimeBaseInitStruct.TIM_Prescaler = 90 - 1;    //90 - 1
	TIM_TimeBaseInit(TIM4, &TIM_TimeBaseInitStruct);
	
	TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1;
	TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable; 
	TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High;
	
	TIM_OC1Init(TIM4, &TIM_OCInitStruct);
	TIM_OC2Init(TIM4, &TIM_OCInitStruct);
	
	TIM_OC1PreloadConfig(TIM4, TIM_OCPreload_Enable);
	TIM_OC2PreloadConfig(TIM4, TIM_OCPreload_Enable);
    
	TIM_ARRPreloadConfig(TIM4, ENABLE);
	TIM_Cmd(TIM4, ENABLE);
}
//定时器5  开启定时器中断
void timer5_init(void)
{
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5,ENABLE);
	
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
	NVIC_InitTypeDef NVIC_InitStruct;
	
	TIM_TimeBaseInitStruct.TIM_ClockDivision=TIM_CKD_DIV1;
	TIM_TimeBaseInitStruct.TIM_CounterMode=TIM_CounterMode_Up;
	TIM_TimeBaseInitStruct.TIM_Period=2000-1;
	TIM_TimeBaseInitStruct.TIM_Prescaler=90-1;
	TIM_TimeBaseInit(TIM5,&TIM_TimeBaseInitStruct);
	
	TIM_ITConfig(TIM5,TIM_IT_Update,ENABLE);
	TIM_Cmd(TIM5,ENABLE);
	
	NVIC_InitStruct.NVIC_IRQChannel=TIM5_IRQn;
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=1;
	NVIC_InitStruct.NVIC_IRQChannelSubPriority=2;
	NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE;
	NVIC_Init(&NVIC_InitStruct);
}
//蜂鸣器初始化
void Buzzer_Init(void)
{
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOH,ENABLE);
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM12,ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStruct;
	
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_6;
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF;
	GPIO_InitStruct.GPIO_Speed=GPIO_High_Speed;
	GPIO_InitStruct.GPIO_PuPd=GPIO_PuPd_UP;
	GPIO_InitStruct.GPIO_OType=GPIO_OType_PP;
	GPIO_Init(GPIOH,&GPIO_InitStruct);
			
}
//蜂鸣器PWM
void Buzzer_PWMSet(int16_t f)
{
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
	TIM_OCInitTypeDef TIM_OCInitStruct;
	
	GPIO_PinAFConfig(GPIOH, GPIO_PinSource6, GPIO_AF_TIM12);
	
	TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_TimeBaseInitStruct.TIM_Period = 1000000/f - 1;
	TIM_TimeBaseInitStruct.TIM_Prescaler = 90 - 1;
	TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;
	TIM_TimeBaseInit(TIM12, &TIM_TimeBaseInitStruct);
	
	TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1;
	TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable;
	TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_Low;
	TIM_OC1Init(TIM12, &TIM_OCInitStruct);
	
	TIM_OC1PreloadConfig(TIM12, TIM_OCPreload_Enable);

	TIM_ARRPreloadConfig(TIM12, ENABLE);
	
	TIM_Cmd(TIM12, ENABLE);		
	TIM_SetCompare1(TIM12, (1000000/f - 1)/2);
}
//
void Buzzer_Disable(void)
{
	TIM_Cmd(TIM12, DISABLE);	
}
// 蜂鸣器播放音乐
void Music_Play(uint8_t flag)
{
	int i;

	if(flag == 0)
	{
		Buzzer_PWMSet(D1);
		delay_ms(100);
		Buzzer_PWMSet(D5);
		delay_ms(100);
	}
	Buzzer_Disable();
}
float input=1000;
float d_yaw,lsat_d_yaw=0,final_yaw=0;
//定时器中断服务函数
void TIM5_IRQHandler(void)
{
	if(TIM_GetITStatus(TIM5,TIM_IT_Update)!=RESET)
	{
		
		mpu_get_data(&mpu_sensor);     //获取陀螺仪信息
		mahony_ahrs_updateIMU(&mpu_sensor,&mahony_atti);    //用mahony_ahrs算法解算
		mpu_get_temp(&mahony_atti.temp);				//imu temp get by mpu/return *tmp
		mcu_temp=get_temprate();		//获取A板CPU温度,用ADC测量
		mpu_temp_control(&mahony_atti.temp);
//		VisualScope_Output(mahony_atti.pitch,mahony_atti.roll ,mahony_atti.yaw, mahony_atti.temp);
//		d_yaw=(float)mahony_atti.yaw;   //ÕâÀï²ÉÓù̶¨Ê±¼ä¼õÈ¥Õâ¶Îʱ¼äÄÚµÄÎÂƯ²úÉúµÄyawÖáÆ«²îµÄÀÛ¼ÆÖµ£¬»ººÍËäÈ»²ÉÓÃÁ˼ÓÈÈÍÓÂÝÒÇÀ´Î¬³Öζȣ¬µ«ÊÇÈÔÈ»»áÓдó¸Å1¡ã/1minµÄÇé¿ö
//		mahony_atti.yaw=constrain_judge_motion(d_yaw,lsat_d_yaw,0.05); //±È½ÏÁ½´ÎÍÓÂÝÒÇyawÖáÊý¾ÝµÄ²îÖµ£¬ÈôСÓÚ0.05£¬Ôò¼ÆÈëÍÓÂÝÒÇÎó²î·¶³ë£¬ÀÛ¼ÆÆðÀ´£¬¹ýÒ»¶Îʱ¼ä¾Í¼õµôÕâЩÀۼƲîÖµ£¬0.05Õâ¸öÊýÖµÊÇÒª±£Ö¤A°åÕý³£Ô˶¯Ï£¬yawÖáÊýÖµ±ä»¯²»±»¼ÆÈëÎó²î
//		lsat_d_yaw=(float)mahony_atti.yaw;
//		printf("yaw angle is %f, pitch angle is %f, rol angle is %f\r\nmcu temp is %f, mpu temp is %f\r\n", 
//						mahony_atti.yaw, mahony_atti.pitch, mahony_atti.roll, mcu_temp, mahony_atti.temp);
		TIM_ClearFlag(TIM5,TIM_IT_Update);
	}
}

/**
  * @brief 陀螺仪温度控制函数,先以pwm比较值为2000加热陀螺仪,第一次达到设定温度后,启动pid
  * @param  void
  * @retval void
  * @attention 
  */
void mpu_temp_control(float *temp)
{	
	uint16_t temp_pwm;
	static uint8_t temp_conunt_time = 0 ,temp_conunt_time2=0;
	if (first_temperate==1)
	{	
		pid_temp.ilimit = 100;
		pid_temp.iout=60;
		pid_temp.p=80;
		pid_temp.i=0.3;
		pid_temp.d=100;
		pid_calc(&pid_temp,*temp,mahony_atti.set_temp);
		temp_pwm = constrain_float(pid_temp.out, 0, +1000);
		first_order_filter_calc(&all_control_s.first_order_lowpass_filter_imu,temp_pwm);
		temp_pwm=all_control_s.first_order_lowpass_filter_imu.out;
		imu_temp_pwm_set(temp_pwm);
	}
	else 
	{
		if (*temp > (mahony_atti.set_temp-1))
    {
      temp_conunt_time ++;
			if(temp_conunt_time > 20)
			{		
				first_temperate = 1;
			}
		}
		imu_temp_pwm_set(2000);
	}
}





6.2 串口中断部分

STM32用以接收K210发送的坐标数据,数据形式为图像在LCD屏上的x坐标+‘/’+y坐标+‘0x0d’,坐标都是3位数,百位小于一则补0如089,加起来总共8位数据,接收到的数据若第四位不是‘/’或者最后一位不是‘0x0d’则判断接收失败,失败的原因可能有串口与串口的连接线不等长或者信号干扰导致串口漏发、错位发送数据。


//USART2½ÓÊÜÖжÏ
void USART2_IRQHandler(void)
{
	u8 Res,len;
	if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)  // 接收中断,接收到的数据必须以0x0d,0x0a结尾
	{
		Res =USART_ReceiveData(USART2);//(USART2->DR);	//读取接收到的数据
		if((USART_RX_STA&0x8000)==0)//接收未完成
		{
			if(USART_RX_STA&0x4000)//接收到了0x0d
			{
				if(Res!=0x0a)USART_RX_STA=0;//接收错误,重新开始
				else USART_RX_STA|=0x8000;	//接收完成了
				
				len=USART_RX_STA&0x3fff;
				
				for(int t=0;t<len;t++)
				{
				USART2->DR=USART_RX_BUF[t];
				while((USART2->SR&0X40)==0);
				}
//				printf("%d%d%d  %d%d%d \r\n",USART_RX_BUF[0],USART_RX_BUF[1],USART_RX_BUF[2],USART_RX_BUF[4],USART_RX_BUF[5],USART_RX_BUF[6]);
				USART_RX_STA=0;
			}
			else //还没收到0x0d
			{	
				if(Res==0x0d)
				{
					USART_RX_STA|=0x4000;
				}
				else
				{
					USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;
					USART_RX_STA++;
					if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收
				}		 
			}
		} 
	}		
	//  the width limit of k210-lcd:320---------the high limit of k210-lcd:240
	if( (Res==0x0a)&&(USART_RX_BUF[3]!='/')&&(USART_RX_BUF[7]!='\n' ) )//判断接收到的数据是否符合协议,若否,则赋予所有数据为‘w’
	{
		 for(int count_r=0;count_r<8;count_r++)
		{
			USART_RX_BUF[count_r]='w';
		}
		USART_RX_STA=0;
	}
//	Music_Play(0);
	if((USART_RX_BUF[0]=='1')&&(USART_RX_BUF[4]=='1')&&(USART_RX_BUF[1]=='4')&&(USART_RX_BUF[5]=='4'))//判断是否在设定坐标左右,若是,则蜂鸣器响
	{
		Music_Play(0);
	}
}


6.3 K210部分

我使用了TF卡储存训练后转化为Kmodel文件的模型,然后K210串口与STM32F4串口相连,如何训练模型并且转化模型?请移步

B站up主理工男的春天
https://www.bilibili.com/video/BV1tT4y1573S

或者我只后有时间再详细写。。。写博客太累了吧。

from fpioa_manager import *
from fpioa_manager import fm
from machine import UART
from Maix import GPIO
import sensor
import image
import lcd
import time
import math
import KPU as kpu
#LCD初始化
lcd.init()
#摄像头初始化
sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QVGA)
sensor.set_windowing((224, 224))
sensor.set_hmirror(0)
sensor.run(1)
clock = time.clock()
#串口初始化
fm.register(board_info.PIN15,fm.fpioa.UART1_TX)
fm.register(board_info.PIN17,fm.fpioa.UART1_RX)
uart_A = UART(UART.UART1, 115200, 8, None, 1, timeout=1000, read_buf_len=4096)
classes = ['class_1']
#载入模型
task = kpu.load("/sd/test.kmodel")
anchor = (1, 1.2, 2, 3, 4, 3, 6, 4, 5, 6.5)
a = kpu.init_yolo2(task, 0.17, 0.3, 5, anchor)
info=kpu.netinfo(task)
while(True):
    clock.tick()
    img = sensor.snapshot()
    code = kpu.run_yolo2(task, img)
    print(clock.fps())
    if code:
        for i in code:
            #计算目标框的几何中心坐标
            center_x=(  i.x()+  (i.w()//2)  )
            center_y=(  i.y()+  (i.h()//2)  )
            #画框和十字
            a=img.draw_rectangle(i.rect())
            a=img.draw_cross(center_x,center_y)
            a = lcd.display(img)
            print(i.classid(),i.value())
            for i in code:
                #显示目标类别
                lcd.draw_string(i.x(), i.y(), classes[i.classid()], lcd.RED, lcd.WHITE)
                center_x_1=center_x//100
                center_y_1=center_y//100
                #判断坐标是否为3位十进制,若不是,则在数字前填充0直至3位十进制
                if center_x_1<1:
                    uart_A.write('0{}/'.format(center_x))
                else:
                    uart_A.write('{}/'.format(center_x))
                if center_y_1<1:
                    uart_A.write('0{}\n'.format(center_y))
                else:
                    uart_A.write('{}\n'.format(center_y))
    else:
        a = lcd.display(img)
uart_A.deinit()
del uart_A
a = kpu.deinit(task)



七 结果展示

7.1 陀螺仪效果展示

STM32F427主控(大疆A板)+K210视觉处理_第12张图片
上图为串口示波器界面,其中CH1通道1为陀螺仪PITCH轴数据、CH2通道2为陀螺仪ROLL轴数据、CH3通道为陀螺仪YAW轴数据、CH4通道为陀螺仪实时温度。
其中,横坐标为时间坐标轴(1min/30000),纵坐标为数值大小,串口示波器的数值表现形式均为整数形式,解释了曲线部分地方为锯齿状的原因。
STM32F427主控(大疆A板)+K210视觉处理_第13张图片
放大纵坐标轴对陀螺仪PITCH/ROLL/YAW轴数据进行观察,结合图 7.1可以发现在0-20000(时间坐标轴)时间段内,即大约40s内,陀螺仪传感器模块温度达到设定值,在此期间陀螺仪PITCH/ROLL/YAW轴数据抖动变化较明显。
而在大约50000-350000(1min40s-12min)温度稳定期间可以得出以下结果:
(1)陀螺仪PITCH数据(红色)稳定在2°-3°内
(2)陀螺仪ROLL数据(黄色)稳定在(-2)°-(-3)°内
(3)陀螺仪YAW数据(蓝色)稳定在1°-3°内,平均3min变化1°

7.2 STM32F4串口接收K210数据效果展示

若检测到目标(浣熊)则发送坐标信息给STM32F4,反之则正常显示摄像头图像
STM32F427主控(大疆A板)+K210视觉处理_第14张图片
上图为正确接收到正确数字的情况,通过debug观察USART_RX_BUF[]数组的内容[0]-[2]为x坐标,[4]-[6]为y坐标轴。[3]和[7]起到校验作用。
STM32F427主控(大疆A板)+K210视觉处理_第15张图片
上图为接收到错误数字的情况,将给SART_RX_BUF[]数组赋值为‘w’。
STM32F427主控(大疆A板)+K210视觉处理_第16张图片
若检测到目标(浣熊)则在LCD上以框的形式标记,反之则正常显示摄像头图像。效果展示以视频的形式展示,请参考附件视频。
检测15张照片,其中11张浣熊照片,2张猫照片,2张狗照片,能够稳定有效检测到9张浣熊照片,不稳定检测到2张浣熊照片,对其他照片没有反应。效果良好,影响效果的因素主要是摄像头像素太低,或者我没有调焦好

视频展示请参考
链接:https://pan.baidu.com/s/1Cwxrqzl-WnnkeEv9fUg3tA
提取码:iuvn

你可能感兴趣的:(STM32F427主控(大疆A板)+K210视觉处理)