赞
踩
二值化 - 边缘提取 - 特征识别 - 补线 - 中心巡线 - 偏差曲率计算
0 - 255 >> 0 - 1
1. 固定阈值
最简单,不用
2. 动态阈值
由近向远逐行迭代阈值
3. 大津法
灰度直方图找谷底
一行数据:
38 36 40 60 55 78 99 111 114 121 115 120 118 100 110 106 80 60 40 35 42 40 36
二值化:
0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0
边缘提取:
0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0
差比和计算偏差
if( (dat[0]-dat[1]/dat[0]+dat[1]) > 斜率阈值) dat[0]=1; dat[1]=1;
// DMA 模块+外部中断结合,自动读取摄像头数据 #define pixel_num 4800 uchar image[pixel_num]; // 图像数组 60*80 // 大津法二值化 uint huidu[256]={0}; // 灰度直方图数组 uchar YUZHI; // 二值化阈值 for(i=0; i<4800; i++) // 遍历所有像素 { huidu[image[i]]++; // 统计灰度直方图 } uint H1=0, H2=0; //第一、第二高峰海拔值 uchar D1=0, D2=0; // 第一、第二高峰位置 #define KUAN 30 // 山峰宽度阈值 for(i=0; i<256; i++) // 遍历灰度直方图 { if(huidu[i] > H1) // 寻找最大值 { H1 = huidu[i]; // 记录海拔 D1 = i; // 记录位置 } } bit OK = 0; // 是否找到第二高峰 /* 要找的是第二高峰,不是第二高点 */ for(i=H1-5; i>0; i-=5) // 向下切,找第二高峰 { for(j=0; j<256; j++) // 遍历这一行 { if(huidu[j] > i && abs(j-D1) > KUAN) // 有上面部分 { H2 = i; // 记录第二高峰海拔 D2 = j; // 记录第二高峰位置 OK = 1; // 标志置位 break; } } if(OK) break; // 如果找到,直接跳出 } uint H3=pixel_num; // 山谷海拔值 uchar D3=0; // 山谷位置 if(OK) // 已经找到2座山 { if(D1<D2) // 找山谷 { for(i=D1; i<D2; i++) { if(huidu[i]<H3) // 寻找最小值 { H3 = huidu[i]; D3 = i; } } } else { for(i=D2; i<D1; i++) { if(huidu[i]<H3) { H3 = huidu[i]; D3 = i; } } } YUZHI = D3; // 获取阈值 }
uchar image1[60][80]; // 二维图像临时处理数组1
uchar image2[60][80]; // 二维图像临时处理数组2
for(i=0; i<4800; i++)
{
if(image[i]>YUZHI) // 判断灰度值
{
image1[i/80][i%80] = 1; // 白点
}
else
{
image1[i/80][i%80] = 0; // 黑点
}
}
灵魂
通过关键点特征来判断
uchar AX, AY; // A点 uchar BX, BY; // B点 uchar CX, CY; // C点 uchar DX, DY; // D点 AY = 59; // 获取 AB 点 BY = 59; for(i=39; i>=1; i--) // 从中间向左找上升 { if(image1[59][i] - image1[59][i-1] == 1) // 找到上升沿 { AX = i; // A 横坐标 } } for(i=39; i<79; i++) // 从中间向右找上升沿 { if(image1[59][i] - image1[59][i+1] == 1) // 找到上升沿 { BX = i; // B 横坐标 } } CY = AY-1; // 迭代 C 点 CX = AX-1; // 去到下一行的边界黑点 for(i=CY; i>0; i--) // 由近及远 { for(j=CX; j<80; j++) // 由左向右 { if(image[i][j] == 1) // 找到白点 { CX = j-1; // 得到上一行黑点X位置 break; } } if(image1[i-1][CX] == 1) // 判断上方是否还有黑点 { CY = i; // 得到C点Y位置 break; } } DY = BY-1; // 迭代D点 DX = BX+1; // 得到下面一行黑点 for(i=DY; i>0; i--) // 由近及远 { for(j=DX; j>0; j--) { if(image1[i][j] == 1) // 找到白点 { DX = j + 1; break; } } if(image1[i-1][DX] == 1) { DY = i; break; } } if(abs(CY-DY)<10) && CY > 30 && DY > 30) // 初级判断十字路口 { uchar Y = min(CY, DY); // 获取CD高度较小值 uchar HEI = 0; // 十字路口上方区域黑点数量 for(i=Y; i>Y-10; i-=2) // Y抽点轮训 { for(j=10; j<70; j+=5) // X抽点轮训 { if(image1[i][j] == 0) // 如果有黑点 { HEI++; // 计数变量++ } } } if(HEI < 10) // 最终判断十字路口,并补线 { float K; // 补线斜率 K = (CX-AX)/(CY-AY); // 计算AC点斜率 for(i=CY; i>CY-20; i--) // 补AC延长2像素宽线 { image1[i][CX+(CY-i)*K] = 0; //把图像对应点涂黑 image1[i][(CX+(CY-i)*K)-1] = 0; } K = (DX-BX)/(DY-BY); // 计算AC点斜率 for(i=DY; i>DY-20; i--) // 补AC延长2像素宽线 { image1[i][DX+(DY-i)*K] = 0; //把图像对应点涂黑 image1[i][(DX+(DY-i)*K)-1] = 0; } } }
uchar ZHONGXIAN[60] = {39}; // 中线位置 uchar ZUO[60] = {0}; // 左线位置 ucahr YOU[60] = {79}; // 右线位置 // 先找最底下一行中心线 for(i=ZHONGXIAN[59]; i>=1; i--) // 从中间向左找上升 { if(image1[59][i] - image1[59][i-1] == 1) // 找到上升沿 { ZUO[59] = i; } } for(i=ZHONGJIAN[59]; i<79; i++) // 从中间向右找上升沿 { if(image1[59][i] - image1[59][i+1] == 1) // 找到上升沿 { YOU[59] = i; // 右线 } } ZHONGJIAN[59] = (ZUO[59] + YOU[59]) /2; // 最底下一行中心线位置找到
uchar ZHONGXIAN[60] = {39}; // 中线位置 uchar ZUO[60] = {0}; // 左线位置 ucahr YOU[60] = {79}; // 右线位置 for(i=59; i>=0; i--) // 向上迭代中心线 ,从靠近摄像头到远离摄像头 { for(j=ZHONGJIAN[i]; j>=1; j--) // 从中间向左找上升沿 { if(image1[i][j] - image1[i][j-1] == 1) // 找到上升沿 ZUO[i] = j; // 左线 } for(j=ZHONGJIAN[i]; j<79; j++) // 从中间向右找上升沿 { if(image1[i][j] - image1[i][j+1] == 1) // 找到上升沿 YOU[i] = j; // 右线 } ZHONGJIAN[i] = (ZUO[i] + YOU[i]) / 2; // 计算当前行中心点 }
for(j=ZHONGJIAN[i+1]; j<79; j++) // 从中间向右找上升沿
以上一次的中间值为这一行的中值向两侧寻找
避免如下情况,找不到中间值
2. 图像截取
图片拟出来的中点向上应该为白色;否则此时不为赛道。
if(h > 1 && ZHONGJIAN[h-1]==0) // 此时截止
可以通过求曲率,太复杂这里不展示
uchar QIANZHAN = 15; // 摄像头前瞻 uchar YUAN, ZHONG, JIN; // 中线所在位置 char ERR = 0; // 前瞻偏差 char YERR = 0; // 车身横向偏差 JIN = ZHONGJIAN[59]; ZHONG = ZHONGJIAN[59-QIANZHAN]; YUAN = ZHONGJIAN[59-QIANZHAN*2]; /* 分情况讨论,右负左正 */ if(YUAN<ZHONGJIAN && ZHONG < JIN) // 情况1 { ERR = ( (ZHONG - YUAN ) + (JIN - ZHONG) )/2; // 获取前瞻偏差 } else if(YUAN < ZHONG && ZHONG >= JIN) // 情况2 { ERR = JIN - ZHONG; // 获取前瞻偏差 } else if(YUAN >= ZHONG && ZHONG < JIN) // 情况3 { ERR = JIN - ZHONG; // 获取前瞻偏差 } else { ERR = ( (ZHONG - YUAN ) + (JIN - ZHONG) )/2; // 获取前瞻偏差 } YERR = JIN - 39; // 获取车身横向偏差
PID参数随机编撰
float KP = 1.0; // 方向控制前瞻比例系数
float KD = 1.0; // 方向控制前瞻微分系数
float YKP = 1.0; // 方向横向控制比例系数
float GYRO_Z; // 车身Z轴角速度
#define DUOji_ZHONGZHI 840 // 前轮正方向的舵机占空值
uint DUOJI_PWM; // 舵机PWM
GET_z = GET_GYRO(Z); // 获取车身Z轴角速度
DUOJI_PWM = DUOJI_ZHONGZHI + KP*ERR + YKP*YERR - KD*GYRO_Z; // 舵机参数计算
PWM_OUT(DUOJI_PWM); // 控制舵机
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。