目录
1:写在前面
感谢四天三夜以来,我们所做的所有努力!
2:思路
3: 实现过程
简易版图像腐蚀和膨胀
首先要对图像进行处理:
4:总结
1:写在前面
感谢四天三夜以来,我们所做的所有努力!
讨论一天才确定下来选题导致时间十分紧张。开始的方案是想用树莓派,考虑到学习时长太高,遂还是选择了学了半年的32。选材上对我们产生了不小的限制,存储空间小,精确度低,很多问题第一次见。作为才升入大二的我们简直是难以攻克。
好险是负责通信的队友有重大突破,我们有了克服的动力。感谢大家的不放弃,明年,后年,我相信会有更好的结局。
结果:我们最后对角度的误差控制在1cm以内,角度误差在0.1°。是个非常不错的结果。
2:思路
五版方案一改再改,大量的数据跑不起来。
我是组内负责图像处理和长度,角度计算的。所有这篇文章分享的也仅仅是我这一部分。
最开始面临的第一个问题就是:我难以找到图像的中心
出现的问题还有:框住了但是框的不稳,经常晃动
容易受外界环境干扰
读取到的数据经常出错
再结合题目:1.框住激光笔轮廓
2.测量长度
3.测量角度
所以我的思路大概是,先想办法对图像进行处理,得到中心点坐标。
长度使用:l=T^2*g/4Π^2
一个关于单摆的公式,可以完美的解决长度计算问题。
角度使用:argtan x/y
利用投影的知识,将两个摄像头(A,B)读取到的x值分别当作投影面的x,y。通过反正切函数来计算。
3: 实现过程
简易版图像腐蚀和膨胀
为了得到最重要的东西:一个准确的中心点数据
首先要对图像进行处理:
我开始得到的图像抖动明显,受光线影响较大。受光线影响,那就是需要调节阈值的问题了。
if(KEY0==0)
{
delay_ms(190);
chen=chen+1;
}
if(KEY1==0)
{
delay_ms(190);
chen=chen-1;
}
//按键来对阈值做调节
if(gm_red<=chen)
{
color=0x0000;//黑色
}
else
{
color=0xffff;//白色
}
会注意到,我对阈值判断时选择了红色,而不是常用的
#define GRAY_2_RGB565(gray) ((u16)((((u8)(gray)>>3)<<11)|(((u8)(gray)>>2)<<5)|((u8)(gray)>>3)))
这是因为,我需要判断的物体颜色的单一的,不需要去平衡着计算。
然后我面对的就是框不住的问题。因为环境中颜色相同的物体较多且杂乱,为了减少这些干扰,我首先想到的是对图像做背景扣除。
可是第一个问题就是,存储不了这么多数据。第二个问题是,每次比较需要大量的时间,导致图像卡顿。
然后我就想到了常用的图像腐蚀和膨胀:
//src原图大小为Wid*Hei
//图像膨胀
void DilateFilter(unsigned char* src, int Wid, int Hei)
{
int i, j, k, l, m;
int max=0;
l=0;
for (i = 0; i < Wid; i++)
{
for (j = 0; j < Hei; j++)
{
for(m=-3;m<=3;m++){
for(k=-3;k<=3;k++){
if(*(src+i+m+(j+k)*Wid)>max){
max=*(src+i+m+(j+k)*Wid);
}
}
}
temp_IMG[i+j*Wid]=max;
max=0;
}
}
memcpy(src,temp_IMG,sizeof(unsigned char)*Wid*Hei);
}
//图像腐蚀
void ErodeFilter(unsigned char* src, int Wid, int Hei)
{
int i, j, k, l, m;
int min=255;
l=0;
for (i = 0; i < Wid; i++)
{
for (j = 0; j < Hei; j++)
{
for(m=3;m<=3;m++){
for(k=-3;k<=3;k++){
if(*(src+i+m+(j+k)*Wid)<min){
min=*(src+i+m+(j+k)*Wid);
}
}
}
temp_IMG[i+j*Wid]=min;
min=255;
}
}
memcpy(src,temp_IMG,sizeof(unsigned char)*Wid*Hei);
}
//此处为作者xiaoyang0307文章中的代码
因为作者选用的是stm32+ov7725的方案,还是遇到了存储空间不够的问题,本来是想在这个代码基础上减少行列数。但是灵光一闪:
已知我读取数据是以打点的形式由上到下,由左到右进行的。我需要读取的激光笔是一段连续的数据,而外界数据连续度和激光笔连续度不同,那么我利用连续的长度为判读依据,进行保留和删除,不就得到激光笔的形状了吗!
//标志位
if(color==0x0000)
{
if(j<270)
{
biaozhi_x+=j;
biaozhi++;
if(biaozhi>20)
{
biaozhi_y+=i;
num++;
biaozhi=0;
}
}
}
else
{
biaozhi=0;
}
至此为止,已经解决了百分之六十的问题,剩下的就是对数据的读取了。
长度L:
直接读取长度显然是不可取的。根据单摆的公式,我得知,我现在只需要得到周期就可以算出值了。
通过对单摆的现象分析,在摆动过程中一直有个规律:假设我们的单摆从左到右,那么我们下一帧的数据比上一帧大,到最高点时,会出现一个左边到右边的跳变。以这一点为起始点记做t1。然后激光笔继续摆动,下一帧数据会比上一帧小,直到另一个最高点处,发生跳变,记录此时为t2,然后下一帧数据比上一帧大,直到回到最开始的跳变点,记作t3。我们的周期就是t3-t1的值。
if(biaozhi_x<10||biaozhi_y<10)
{
}
else
{
k[0]=biaozhi_y;
LCD_DrawRectangle(biaozhi_x-50,biaozhi_y-10,biaozhi_x+50,biaozhi_y+10);
if(((k[1]-k[0])>t)&&n!=2&&n!=3)
{
n=1;
}
if(n==1&&((k[1]-k[0])<(-1*t)))
{
n=2;
shu=tim_a=0;
}
if(n==2&&((k[1]-k[0])>t))
{
y_max=biaozhi_y;
x_max=biaozhi_x;
n=3;
}
if(n==3&&((k[1]-k[0])<(-1*t)))
{
n=4;
shu=tim_a;
//tim_a=0;
y_min=biaozhi_y;
x_min=biaozhi_x;
// shu=TIM1->CNT;
}
这里注意到,我对一段连续值进行了去除处理,这里就是上文提到的“看不见的白点”
不过这个纯属编者对现象做出的判断,理论支持较少。
对周期的读取使用了定时器的功能,因为和通信有不少冲突,最终选择了高级定时器1,如果只是进行图像处理的读者可以使用其他定时器。
角度:
角度的算法上文也提到过,通过我们以上的处理,这一步就十分容易了。
我们需要得到x值,也就是激光笔从左边最高点到右边最高点的距离。
jiajiao=atan((double)141/152)*180/3.1415926;
printf("%lf",jiajiao);
LCD_ShowString(30,180,200,16,16,"jiajia: 00度");
LCD_ShowxNum(30+10*8,180,jiajiao,2,16, 0X80); //显示小数部分
至此,该题需要的图像部分已经结束。希望这篇文章能给你帮助!
由于编者水平有限,本篇文章中很多问题的叙述并不清楚,如有不明白的地方欢迎大家给我留言。
4:总结
以实际现象为核心,有目的的改变。
本次题目难度确实有点超出我的预期,准备工作做的并不充分。
很多问题选择的方案并不合理,浪费了很多时间。
调试时太过于盲目,没有想清楚逻辑就去更改。
但是本次电赛还是让我受益良多,凌晨四点的学校,忙里偷闲看了场edg的比赛,深夜的加餐,还有为每一个突破而由衷的喜悦。
再次感谢四天三夜以来大家的努力,我们明年再见!