本份文件仅整理个人认为有价值的代码部分,并不会将每一份代码的整体思路梳理一遍,而是注重于某一些单独的、可借鉴的方法。本文件按代码文件分类,方便读者根据压缩包文件找源代码阅读。
文件:2019四轮组代码
入环岛判断
根据圆环切点和拐点个数来判断圆环(目前不清楚如何判断切点)
可做辅助方法判断圆环
补线方法:粗暴简单,直接从储存右边界线的数组的第34个开始,每一行的值等于后一行的值减一(没懂为什么)
/****************圆环循迹****************************/
if(flagl_1 > 10 && flagl_2 == 0)
{
uint8 i = 33;
left[i+1] = 3;
for( ;i >= 18; --i)
{
left[i] = left[i+1] + 1;
//更新中值
mid[i] = (left[i] + right[i]) / 2;
}
}
//只看到右圆环第一个标志位时,右边丢线,补上
if(flagr_1 > 10 && flagr_2 == 0)
{
uint8 i = 33;
right[i+1] = 79;
for( ; i >= 18; --i)
{
right[i] = right[i+1] - 1;
//更新中值
mid[i] = (left[i] + right[i]) / 2;
}
}
//flagl_1是进环岛拐角标志位,flagl_2圆切点标志
文件:camera补线 》image
(注:此代码用的巡线方法与【左右巡线求中线】的方法类似,故不做记录
此代码直接记录图片坐标,对坐标进行处理,以下许多处理都建立在此基础上)
提取拐点
基本思路:以屏幕中间行的两个中点为起始点,分别向左右扫描(此代码包含于f__edge_sp_czj函数中)以此为一条直线,从指定行向下(or上)移动,若判断有跳变点,则判断为拐点
名 称: 提取“下拐左右”拐点函数
功能描述: 提取“下拐左右”拐点
入口参数: 图像数组
出口参数: 下左右拐点存在标志位 拐点坐标
备 注: 如果没找到下拐点 函数会强制返回一个固定的拐点 需要注意!!!
**********************************************************************************************/
void f__guaidian_X(uint8 img[][IMG_W_YS],uint8 *flg_l,zb_imgarr_t *gd_l,uint8 *flg_r,zb_imgarr_t *gd_r)
{
uint8 nomeaning_tmp_l; uint8 nomeaning_tmp_r;
zb_imgarr_t I_l; zb_imgarr_t I_r;
zb_imgarr_t I_last_l; zb_imgarr_t I_last_r;
uint8 l_sdkd; uint8 r_sdkd;
uint8 l_sdkd_last; uint8 r_sdkd_last;
uint8 l_jxz_flg=1; uint8 r_jxz_flg=1;//是否继续找标志位
uint8 RANGE=93-30;
*flg_l=0;
*flg_r=0;
//假如函数运行完没有找到下拐点 则会强制返回下面这个固定的拐点
(*gd_l).i=79;
(*gd_r).i=79;
(*gd_l).j=0 + 45;
(*gd_r).j=187 - 45;
I_last_l.i=79;
I_last_r.i=79;
f__edge_sp_czj(img[79],RANGE,&nomeaning_tmp_l,&(I_last_l.j),&nomeaning_tmp_r,&(I_last_r.j));//跳变点判断,返回参数:跳变点存在性及坐标
l_sdkd_last = 93 - I_last_l.j + 1;
r_sdkd_last = I_last_r.j - 94 + 1;
if(nomeaning_tmp_l==0)
{
l_jxz_flg=0;
}
if(nomeaning_tmp_r==0)
{
r_jxz_flg=0;
}
for( int16 Ci=78 ; Ci>=0 ; Ci=Ci-2 )
{
if(Ci==78)
{
if( (l_jxz_flg==0)&&(r_jxz_flg==0) ) break;
}
I_l.i=Ci;
I_r.i=Ci;
f__edge_sp_czj(img[Ci],RANGE,&nomeaning_tmp_l,&(I_l.j),&nomeaning_tmp_r,&(I_r.j));//跳变点判断,返回参数:跳变点存在性及坐标
l_sdkd=93 - I_l.j + 1;
r_sdkd=I_r.j - 94 + 1;
if( (l_jxz_flg==1)&&((*flg_l)==0)&&((l_sdkd-l_sdkd_last)>2) )
{
*flg_l=1;
(*gd_l).i=I_last_l.i;
(*gd_l).j=I_last_l.j;
}
if( (r_jxz_flg==1)&&((*flg_r)==0)&&((r_sdkd-r_sdkd_last)>2) )
{
*flg_r=1;
(*gd_r).i=I_last_r.i;
(*gd_r).j=I_last_r.j;
}
if( ((*flg_l)==1)&&((*flg_r)==1) ) break;
I_last_l=I_l;
I_last_r=I_r;
l_sdkd_last=l_sdkd;
r_sdkd_last=r_sdkd;
if(nomeaning_tmp_l==0)
{
l_jxz_flg=0;
}
if(nomeaning_tmp_r==0)
{
r_jxz_flg=0;
}
if( (l_jxz_flg==0)&&(r_jxz_flg==0) ) break;
}
/**********************************************************************************************
名 称: 提取“上拐左右”拐点函数
功能描述:
入口参数:
出口参数:
备 注:
**********************************************************************************************/
void f__guaidian_S(uint8 img[][IMG_W_YS],uint8 s_i,uint8 s_j_l,uint8 s_j_r,uint8 *flg_l,zb_imgarr_t *gd_l,uint8 *flg_r,zb_imgarr_t *gd_r)
{
zb_imgarr_t I_last_l; zb_imgarr_t I_last_r; //上次点
zb_imgarr_t I_l; zb_imgarr_t I_r; //本次点
uint8 szkd_last_l; uint8 szkd_last_r; //上次竖直方向宽度
uint8 szkd_l; uint8 szkd_r; //本次竖直方向宽度
uint8 j_left; uint8 j_right;
*flg_l=0;//上拐点存在标志位清零
*flg_r=0;//同上
//提取“上左”拐点
I_last_l.j = s_j_l;
I_last_l.i = f__edge_cz_cktdbqvi( img , s_j_l , s_i );
szkd_last_l = s_i - I_last_l.i + 1 ;
for( j_left=s_j_l+1 ; j_left<=(93+40) ; j_left+=2 )
{
I_l.j = j_left;
I_l.i = f__edge_cz_cktdbqvi( img , j_left , s_i );
szkd_l = s_i - I_l.i + 1;
if( ((*flg_l)==0)&&((szkd_l-szkd_last_l)>2) )
{
*flg_l=1;
(*gd_l).i = I_last_l.i;
(*gd_l).j = I_last_l.j;
}
if( (*flg_l)==1 ) break;
I_last_l=I_l;
szkd_last_l=szkd_l;
// zb_imgarr_t p;
// p.i=s_i;
// p.j=j_left;
// liang_dian_lian_xian(img,123,p,I_l);
}
//提取“上右”拐点
I_last_r.j = s_j_r;
I_last_r.i = f__edge_cz_cktdbqvi( img , s_j_r , s_i );
szkd_last_r = s_i - I_last_r.i + 1 ;
for( j_right=s_j_r-1 ; j_right>=(94-40) ; j_right-=2 )
{
I_r.j = j_right;
I_r.i = f__edge_cz_cktdbqvi( img , j_right , s_i );
szkd_r = s_i - I_r.i + 1;
if( ((*flg_r)==0)&&(szkd_r-szkd_last_r)>2 )
{
*flg_r=1;
(*gd_r).i = I_last_r.i;
(*gd_r).j = I_last_r.j;
}
if( (*flg_r)==1 ) break;
I_last_r=I_r;
szkd_last_r=szkd_r;
// zb_imgarr_t p;
// p.i=s_i;
// p.j=j_right;
// liang_dian_lian_xian(img,123,p,I_r);
}
}
十字补线
/**********************************************************************************************
名 称: 十字补线程序
功能描述:
入口参数:
出口参数:
备 注: 集成了 入十字 十字中 出十字 三个阶段的补线程序
**********************************************************************************************/
uint8 flg_msycsz;//马上要出十字标志位(每当判断出十字时,先将此标志位清零再执行下面的十字补线程序)
void shizi_bxcx(uint8 img_bin[][IMG_W_USED])
{
static uint8 msycsz_bx_start_i;
if( flg_msycsz==0 )
{
uint8 xgd_flag_l;//下左拐点是否找到标志位
uint8 xgd_flag_r;//下右拐点是否找到标志位
zb_imgarr_t xgd_l;//下左拐点
zb_imgarr_t xgd_r;//下右拐点
//先提取“下左右拐点”
f__guaidian_X(img_bin,&xgd_flag_l,&xgd_l,&xgd_flag_r,&xgd_r);
uint8 sgd_flag_l; //上左拐点是否找到标志位
uint8 sgd_flag_r; //上右拐点是否找到标志位
zb_imgarr_t sgd_l; //上左拐点
zb_imgarr_t sgd_r; //上右拐点
uint8 f_sgd_s_j_l; //找上拐点的左侧起始j值
uint8 f_sgd_s_j_r; //找上拐点的右侧起始j值
uint8 f_sgd_start_i; //查找找上左右拐点的起始i值
f_sgd_s_j_l = xgd_l.j-20; //计算上述j值
f_sgd_s_j_r = xgd_r.j+20; //同上
if( xgd_l.i69 )//当上左右拐点 低到一定程度时
{
flg_msycsz=1;
msycsz_bx_start_i=MIN(sgd_l.i,sgd_r.i)-5;
}
}
//十字补线
zb_imgarr_t zdd_l;//最底部left
zb_imgarr_t zdd_r;//最底部right
zdd_l.i=79;
zdd_r.i=79;
uint8 nomeaning_l;
uint8 nomeaning_r;
f__edge_sp_czj(img_bin[79],93,&nomeaning_l,&(zdd_l.j),&nomeaning_r,&(zdd_r.j));
if(sgd_flag_l==1)
{
liang_dian_lian_xian(img_bin,0,xgd_l,sgd_l);//上拐点找到了 上下拐点直接连线 补全十字边界
}
else
{
xiang_shang_hua_yan_shen_xian(img_bin,0,zdd_l,xgd_l);//上拐点没找到 画延伸线 补全十字边界
}
if(sgd_flag_r==1)
{
liang_dian_lian_xian(img_bin,0,xgd_r,sgd_r);//上拐点找到了 上下拐点直接连线 补全十字边界
}
else
{
xiang_shang_hua_yan_shen_xian(img_bin,0,zdd_r,xgd_r);//上拐点没找到 画延伸线 补全十字边界
}
//补完十字边界线后 再执行直道弯道补线程序 补出中线
zhidao_wandao_bxcx(img_bin,79,10);
//在图像上画出这几个拐点
if(sgd_flag_l==1) gd_hua_zfx(img_bin,200,sgd_l);
if(sgd_flag_r==1) gd_hua_zfx(img_bin,200,sgd_r);
if(xgd_flag_l==1) gd_hua_zfx(img_bin,100,xgd_l);
if(xgd_flag_r==1) gd_hua_zfx(img_bin,100,xgd_r);
}
else//flg_msycsz==1 马上要出十字了
{
zhidao_wandao_bxcx(img_bin,msycsz_bx_start_i,10);
}
入环岛判断
根据同一行赛道宽度是否增加而判断是否进入环形
宽度增加→环形
此方法有条件限制(比如要排除遇到十字的情况),但可以做辅助判断。
void huanxing_ru_bxcx(uint8 img_bin[][IMG_W_USED])
{
uint8 xgd_flag_l;//下左拐点是否找到标志位
uint8 xgd_flag_r;//下右拐点是否找到标志位
zb_imgarr_t xgd_l;//下左拐点
zb_imgarr_t xgd_r;//下右拐点
//提取环形下左右拐点
f__guaidian_X(img_bin,&xgd_flag_l,&xgd_l,&xgd_flag_r,&xgd_r);
zb_imgarr_t yxcb_r;//环形中心的圆形侧边 右
uint8 i_tmp;
uint8 i_tmp_last;
uint8 szkd;
uint8 szkd_last;
i_tmp_last = f__edge_cz_cktdbqvi( img_bin , xgd_l.j , xgd_l.i );
szkd_last = xgd_l.i - i_tmp_last + 1;
uint8 sfdz_flg=0;//宽度是否递增标志位
for( uint8 j_tmp=(xgd_l.j+1) ; j_tmp<=187 ; j_tmp++ )
{
i_tmp = f__edge_cz_cktdbqvi( img_bin , j_tmp , xgd_l.i );
szkd = xgd_l.i - i_tmp + 1;
if( (szkd-szkd_last)>0)
{
sfdz_flg=1;
}
else
{
sfdz_flg=0;
}
if( ((szkd-szkd_last)>4)||((sfdz_flg==1)&&(i_tmp==0)&&(j_tmp>75)) )
{
yxcb_r.i=i_tmp_last;
yxcb_r.j=j_tmp-1;
break;
}
i_tmp_last=i_tmp;
szkd_last=szkd;
}
补线思路:两点连线,
先找出两个拐点,然后根据斜率补线
SaiDao_type_m pdsdlx(uint8 img_bin[][IMG_W_USED])
{
SaiDao_type_m sdlx_return; //定义返回的赛道类型
zb_imgarr_t l_1_img,r_1_img; //定义第一行,第二行,第三行左右跳变沿图像坐标
zb_imgarr_t l_2_img,r_2_img;
zb_imgarr_t l_3_img,r_3_img;
zb_math_t l_1_math,r_1_math; //定义数学坐标
zb_math_t l_2_math,r_2_math;
zb_math_t l_3_math,r_3_math;
slope_t K_l_13,K_r_13; //定义左右斜率
slope_t K_l_12,K_r_12;
uint8 kuandu_last; //定义上一次最后一行宽度数据
uint8 kuandu; //定义最后一行宽度数据
static uint8 leixing_flag=0; //定义判断类型标志位
static uint8 chuhuanxing_flag=0; //定义出环形标志位
static uint8 shizi_huanxing_flag=0; //定义十字环形标志位
f__edge_sp_czj_plus(img_bin[Hang_1],93,&f_l_1,&left_1,&f_r_1,&right_1);//水平扫第一行
f__edge_sp_czj_plus(img_bin[Hang_2],93-30,&f_l_2,&left_2,&f_r_2,&right_2);//水平扫第二行
f__edge_sp_czj_plus(img_bin[Hang_3],93-50,&f_l_3,&left_3,&f_r_3,&right_3);//水平扫第三行
f__edge_sp_czj_plus(img_bin[Hang_end],93,&f_l_end,&left_end,&f_r_end,&right_end);//水平扫最后一行
l_1_img.i=Hang_1; // 左1图像坐标赋值 //给图像坐标赋值
l_1_img.j=left_1;
r_1_img.i=Hang_1; // 右1图像坐标赋值
r_1_img.j=right_1;
l_2_img.i=Hang_2; // 左2图像坐标赋值
l_2_img.j=left_2;
r_2_img.i=Hang_2; // 右2图像坐标赋值
r_2_img.j=right_2;
l_3_img.i=Hang_3; // 左3图像坐标赋值
l_3_img.j=left_3;
r_3_img.i=Hang_3; // 右3图像坐标赋值
r_3_img.j=right_3;
l_end_img.i=Hang_end; //最后一行图像坐标赋值
l_end_img.j=left_end;
r_end_img.i=Hang_end;
r_end_img.j=right_end;
l_1_math=covert_zb(l_1_img); //第一行,第三行左右跳变沿坐标转换用于计算斜率
r_1_math=covert_zb(r_1_img);
l_3_math=covert_zb(l_3_img);
r_3_math=covert_zb(r_3_img);
l_2_math=covert_zb(l_2_img); //第二行左右跳变沿坐标转换
r_2_math=covert_zb(r_2_img);
K_l_13=calcu_slope(l_1_math,l_3_math); //计算1_3左斜率
K_r_13=calcu_slope(r_1_math,r_3_math); //计算1_3右斜率
K_l_12=calcu_slope(l_1_math,l_2_math); //计算1_2左斜率 尚未使用
K_r_12=calcu_slope(r_1_math,r_2_math); //计算1_2右斜率
gpio_init (PTD15,GPO,1); //指示赛道类型的灯 D15为直道和弯道
gpio_init (PTE26,GPO,1); // E26为十字
shizi_huanxing_flag=huanxing_shizi(img_bin,79,7); //判断十字环形左右两边的点的个数
文件:freescale_9th》scr》app_image
十字判断
以每行点的总和来判断是否进入十字
(白块为1,黑块为0,每行规定点数之和可以反应本行黑白块的数量之比,若120个全为白点,则可判断进入十字)
/*************************************************************************
* 函数名称:Cross_judge
* 功能说明:判断有无十字
* 参数说明:无
* 函数返回:无
* 修改时间:2012-2-14 已测试
*************************************************************************/
void Cross_judge()
{
Cross_flag = 0;
for(int i=H+20;i>H-20;i--)
{
if(sum_OneRow(i)==0)
{
Cross_flag = 1;
black_centre = 160;
break;
}
}
}
//计算一行40个数的总和
int sum_OneRow(unsigned int hang)
{
int sum=0;
for(int i=0;i<40;i++)
sum+=Image_fire[hang][i];
return sum;
}
//计算一列240个数的总和
int sum_OneCol(unsigned int lie)
{
int sum=0;
for(int i=0;i<240;i++)
sum+=Image_fire[i][lie];
return sum;
}
巡线方法
找到起始点后,直接用一条斜率为2 的直线来寻找边界线(右边界线同理)
此种方法可以提高巡线的效率,但容易丢失边界点
//寻左边黑线
for(i = 200;i>=170;i=i-2)
{
//以斜率为2划线
Leftline2=Leftline2+2;
Leftline1=Leftline1+2;
//在 Leftline2 列上遇到黑色
if(Image_fire2Image(i,Leftline2) == 1)
{
Leftrow_pre = i;
Leftcol_pre = Leftline2;
Leftblack = 1;
break;
} //在 Leftline1 列上遇到黑色
else if(Image_fire2Image(i,Leftline1) == 1)
{
Leftrow_pre = i;
Leftcol_pre = Leftline1;
Leftblack = 1;
break;
} //都没遇到黑色,左边没寻到黑线
else
{
Leftblack = 0;
}
}
文件:motor
本代码对坐标轴进行处理
整体特点:针对不同路况使用不同速度,并有相应速度记录
环岛拐点判断
基本思路:判断一个点左右是否都是白块,以及列数限定
void ChangzhidaoRuHuan()
{
int i;
LeftRoundInflectionPointRow=0;//记录拐点的行
LeftRoundInflectionPointCol=0;//记录拐点的列
LeftRoundInflectionPointFlag=0;
for(i=40;i>18;i--)
{
if(i<55&&LeftEdge[i+2]!=0&&LeftEdge[i+1]!=0&&LeftEdge[i]!=0
&&(LeftEdge[i-1]==0&&LeftEdge[i-2]==0)&&LeftEdge[i]>10&&LeftEdge[i]-LeftEdge[i+1]>=0&&
RightEdge[i-3]<79&&RightEdge[i-5]<79)
{
if(LeftEdge[i]>=0&&img[i-3][LeftEdge[i]]==255
&&img[i-4][LeftEdge[i]]==255 )
{
//找到右边线有拐点
LeftRoundInflectionPointRow=i;//记录拐点的行
LeftRoundInflectionPointCol=LeftEdge[i];//记录拐点的列
LeftRoundInflectionPointFlag=1;
break;//退出for
}
}
}
}
文件:Freescale-master》app》LandzoCamera
寻找拐点
/*查找左边沿开始,找到边沿后跳出循环*/
for(temp0=centre_lastblack;temp0>5;temp0--) //检测左侧边沿
{
for(temp1=temp0; temp1>( temp0-LINEWITH );temp1--) //寻找一点附近的几个点是否能构成下降沿
{
if(temp1<=0) break; //不满足实际条件跳出循环
temp_value1= point[temp0]-point[temp1]; //比较白点附近的几个值
if(temp_value1>=LEFTLINEFAV) //判断是否满足要求
{
leftcount++; //满足条件计数值加一
// testarry[temp0]=temp_value1;
}
else
{
leftcount=0;
// testarry[temp0]=0;
}
}
if(leftcount >= LINEWITH-1 ) //判断是否满足边沿条件
{
leftedge = temp0 ; //满足条件边沿找到
leftflag=0; //找到边沿,标志位置1
break;
}
else
{
leftcount=0; //未找到清除计数
leftflag=1; //标志位清除
leftedge=5; // 边沿赋值
}
if(leftedge<5) leftedge = 5;
}
/*左边沿查找结束*/