2021电赛D题长度、角度测量思路

        题目链接:基于互联网的摄像测量系统(D 题)-- 2021 年全国大学生电子设计竞赛_行走的皮卡丘-CSDN博客

        在这次电赛取得了安慰奖(省三等奖),但是在测试的时候,单摆长度和角度值精度都完美达标,同时思路也受到了评委老师的肯定。所以今天打算把测量思路分享出来。

        我们组选择的方案是:stm32f1战舰板(正点原子)+LCD+OV7725(因为三个设备需要和交换机相连接,战舰的板载资源恰好有对应的接口)。当时在网络上,交流群里流传了这题的做法,使用树莓派等设备是主流方案。但是碍于水平的有限,只掌握了stm32、51、模电数电的基本知识,只能硬着头皮选择stm32来做。于是任务就分成了两部分:三个单片机之间的通信和单摆(把题目的模型简化,其实就是个单摆)长度的测量,角度的话有了两个节点的长度然后tan就可求到。这篇文章主要解释长度的测量(电赛没有做出好成绩就是因为三个单片机之间的通信出了问题,当时老师认为通信能完成就能冲击国奖)。

        首先阅读完题目,联想到我们学习过的知识,可以判断该题目其实就是单摆。经过资料的查询得到了很关键的两个信息:1.单摆的长度只与单摆运动周期有关。2.在比赛条件下(室内)空气阻尼等因素对单摆周期影响不太大。所以测量单摆长度就转变成为了测量运动周期,然后就想到了只要知道单摆出现在同一点的时间便得到了周期,理论可行,就开始写代码。

//首先是两个for组成的循环,目的就是为了处理LCD上的图像,一个像素点一个像素点的处理。。
//把LCD上显示的图像(320*270)左上角为原点建立二维坐标系
for(i=0;iIDR&0XFF;	//读数据
				OV7725_RCK_H; 
				color<<=8;  
				OV7725_RCK_L;
				color|=GPIOC->IDR&0XFF;	//读数据
				OV7725_RCK_H; 
				gm_red = color>>11;//图像是RGB565格式,这样就得到了R红区的值
//这里用了二值化的处理,原因是没有掌握彩色图像的处理方式,缺点也很明显,就是需要很好的场地环境
//二值化处理后,激光笔(当时我们贴了纯黑色胶带)就是图像中的黑色部分,表现出来的就是一块黑色形状是矩形.
                 if(gm_red<=set_value) //set_value是设定值,也就是二值化的界限,当时在我们组自        
                                       //己的实验环境下理想值是7
				 {

					 color=0x0000;//黑色
				 }					 
				 else
				{			
					
					color=0xffff;//白色					
				}
      if(color==0x0000)//如果像素点是黑色
//这个if内部的代码,实际就是框住黑色区域,这个是题目基本要求之一
                                //(x1,y1),(x2,y2):矩形的对角坐标                            
//利用正点LCD的LCD_DrawRectangle(u16 x1, u16 y1, u16 x2, u16 y2)就可以在LCD上画出矩形,所以只要找到黑色区域的中点坐标(x,y)然后利用简单的加减法就可以在LCD上框出黑色矩形区域,也就是我们的激光笔,有了思路,开始写代码。
			{
					if(j<270)
					{	
				    biaozhi_x+=j;//将黑色像素点的X坐标累加
				    biaozhi++;
				    if(biaozhi>20)//如果这行出现了20个黑色像素点,则可以记录y值,这也是抗干扰的
                                  //的一种方法,可以消除频幕上黑色噪点的影响
				        {
							biaozhi_y+=i;//把y值记录下来
					        num++;	     //记录了多少次y值
						    biaozhi=0;   //将标志位清0
					    }
					}

			}
		
			else//如果是白色则不处理
			{
				biaozhi=0;
			}
			

			LCD->LCD_RAM=color;  //将图像还原到LCD
							
			}
		}
//自闭之路		
		POINT_COLOR=RED;//画笔用红色,红色醒目,我想得到一个红色的框		
        //下面的两行代码就是为了得到比较精准的黑色区域中点坐标(x,y),也是经过实验效果改进的
        biaozhi_x=biaozhi_x/(20*(num+6));
		biaozhi_y=biaozhi_y/num;

有了上面代码求出来黑色矩形区域(激光笔)中点坐标(biaozhi_x,biaozhi_y)就可以开始完成:1.视频上框选激光笔 2.测量出单摆的长度。

  if(biaozhi_x<10||biaozhi_y<10)//为了让框出现的自然,在x<10,y<10这块区域不显示红框
		{
	//也就是不处理,其实可以用一个if的,但是当时准备在这块区域显示length
		}
		else
		{
			int t=1;//t就是1
			k[0]=biaozhi_y;//这里在前面设定了一个数组:int k[2]={0},到了下面就知道它有什么用了
            //下面的函数也就是我们提到的画框函数,根据重点坐标画一个长100,宽20的矩形,这个也是调            
            //试出来可以框住黑色部分(激光笔)的大小
			LCD_DrawRectangle(biaozhi_x-50,biaozhi_y-10,biaozhi_x+50,biaozhi_y+10);
	      //下面的代码我认为是核心部分,因为对我们组这样三个基本跟着例程走的人是挑战,真正的挑战
            if(((k[1]-k[0])>t)&&n!=2&&n!=3)//在每一次处理LCD图像前,我们令k[1]=k[0],其实得到                                                                
                                           //的就是上一次biaozhi_y的值,将他们做差和t比较大小                                                                        
                                           //再根据多重if判断就可以得到周期!也就是把捕捉俩次                                  
			                               //到一个点的问题简化出来了
            {
				n=1;
				
			}
			if(n==1&&((k[1]-k[0])<(-1*t)))
			{
				n=2;
	
				shu=tim_a=0;//将tim_a清0,tim_a 在下面做出解释,其实就是得到时间
			}
				if(n==2&&((k[1]-k[0])>t))
			{
				y_min=biaozhi_y;
				x_min=biaozhi_x;
				n=3;

			}
						if(n==3&&((k[1]-k[0])<(-1*t)))
						{ 
							n=4;
							shu=tim_a;													
							y_max=biaozhi_y;
							x_max=biaozhi_x;
						}
			T=shu*0.01; //处理一下,这里的T和L都是doule型
			L=T*T*9.8/(4*3.1415926*3.1415926);  //放大了10*4,就是单摆公式
			L_chen=(u32) L;//ceil(L);
	    if(y_max>y_min)
			{chazhi_y=y_max-y_min;}
			else
			{chazhi_y=y_min-y_max;}
			//发送到终端与A的差值做比较,取差值大的数所对应的L为长度					                         
			 LCD_ShowxNum(120,120,(u32)chazhi_y,3,16,0);  //相当于求夹角中的x值。

这里贴一点补充:

void Timer1CountInitial(void)
 {
      //定时=36000/72000x2=0.001s=1ms;这个计算方法很好用,真的很好,算是为数不多的技巧吧。
        TIM_TimeBaseInitTypeDef    TIM_TimeBaseStructure;
	 	NVIC_InitTypeDef NVIC_InitStructure;

        RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
          
        TIM_TimeBaseStructure.TIM_Period = 10-1;//自动重装值(此时改为10ms)
		TIM_TimeBaseStructure.TIM_Prescaler = 720-1;//时钟预分频
		TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;//向上计数
		TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;//时钟分频1
		TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;            
		TIM_TimeBaseInit(TIM1,&TIM_TimeBaseStructure);
        
        TIM_ClearFlag(TIM1,TIM_FLAG_Update);
	    TIM_ITConfig(TIM1,TIM_IT_Update,ENABLE);  
        TIM_Cmd(TIM1, ENABLE);
	 
	NVIC_InitStructure.NVIC_IRQChannel = TIM1_UP_IRQn;  //TIM3中断
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;  //先占优先级0级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;  //从优先级3级
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
	NVIC_Init(&NVIC_InitStructure);  //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器 							
 }

 
 void TIM1_UP_IRQHandler(void)
 {        
     //TIM_TimeBaseStructure.TIM_Period = 100-1;//自动重装值(此时进中断的周期为100ms)
     if (TIM_GetITStatus(TIM1, TIM_IT_Update) != RESET)
     {  
     tim_a++;
         }        
    TIM_ClearITPendingBit(TIM1,TIM_IT_Update);
				 }

这个是单摆公式:L = T^{2}*g/4\pi ^{2}

        分享就止步于此,最后的测试效果,长度和角度的精确度很高,我记得当时老师说我们可能是湖北省精度最高的,老师要求摆长为100cm,底部角度35°,我们节点1测出99.997,节点二100.870,角度的出来是34.几,当时测量了几组,我只有这组(因为是第一组)记得非常清楚,当时老师就很吃惊,然后也表示了他的惋惜。很遗憾的是我在家里没有条件进行实验,开学过后,可能会将实验结果带给大家。

你可能感兴趣的:(stm32,嵌入式)