前言
首先要实现小车寻迹,需将图像进行二值化处理显示在LCD屏上,然后调节阈值,找出所需要循迹的线条。最后,自定义一个中心点为目标坐标,寻找计算出线条的中心点为当前坐标根据当前坐标与目标坐标的偏差来判断直行,还是左右转向。
(注:免费源码链接,请点赞关注私信)
(注:本人采用的的小车是前驱舵机转向,后驱提供动力。例程中转向有单独的函数块,可以根据自己的需求进行更改)
一、图像二值化处理,在我上期文章有介绍
二、设立一个目标中心坐标,计算找出当前中心坐标
1:目标中心点
目标中心点可根据自己的LCD尺寸大小选择,我选择的是240320的LCD,程序中显示的图像大小为240100,目标中点设置的为(120,50),图像大小可根据自己的需要进行更改。
ov2640_outsize_set(240,100); /* 图像显示大小 240*100 */
(注:改变显示大小的同时也需改变存储摄像头数据数组的大小。)
2:当前中心坐标
找出第90行和第96行X的最小坐标和最大坐标,这里的坐标指的是线的边缘跳跃点,这样就相当于知道了四个点的坐标了。
if(i==90) //第90行 找出图像的跳跃边线,计算中心点
{
if( Xs_max<j ) Xs_max=j;
if( Xs_min>j ) Xs_min=j;
}
if(i==96) //第96行 找出图像的跳跃边线,计算中心点
{
if( Xx_max<j ) Xx_max=j;
if( Xx_min>j ) Xx_min=j;
}
X_he=(Xs_max+Xs_min+Xx_max+Xx_min)/4; //计算X坐标 当前中心点
lcd_fill_circle(120,50,2,RED); //绘制一个实心圆点,显示目标中心点坐标
lcd_fill_circle(X_he,93,2,GREEN); //绘制一个实心圆点,显示当前中心点坐标
也可以不选择某几行来找中心点而是根据所有像素点来计算一个中心点,这样后者没有前者这个方便理解。
三、判断状态
根据当前中心点与目标中心点的偏移位置来执行相应的转向即可。由于我的采用的是前驱舵机转向所以就要对转向的角度进行一个合理的计算,防止小角度的路线转向角度过大,从而影响小车行使的稳定性。不了解舵机的话可以观看江科大stm32 的视频里面有使用教程。
和我一样使用舵机的话可以参考下面为右行的程序:
void you(void)
{
if( X_he<115 ) // 舵机转向角度大小限制 ,78为直行。
{
rpwmval=78+(115-X_he)/2; //两个中心点偏差转向角度为1度,可提高小车转向的稳定性
if(rpwmval>=128) rpwmval=128;
__HAL_TIM_SET_COMPARE(&g_tim14_pwm_chy_handle, TIM_CHANNEL_1 , 550);//修改TIM14PWM占空比调节后驱转速
__HAL_TIM_SET_COMPARE(&g_tim4_pwm_chy_handle, GTIM_TIM4_PWM_CHY, rpwmval/180*2000+550);//修改TIM4PWM占空比调节前驱转向
}
}
X_he<115 是防止在小角度偏差进行转向,这样可以提高小车的稳定性。
偏差位置和角度比可根据自我调试情况进行修改。
四、部分程序源码
/****************************************************** 直行 ******************************************************************/
void zhi(void)
{
if( X_he>=115 && X_he<125)
{
rpwmval=78;
__HAL_TIM_SET_COMPARE(&g_tim14_pwm_chy_handle, TIM_CHANNEL_1 , 450);
__HAL_TIM_SET_COMPARE(&g_tim4_pwm_chy_handle, GTIM_TIM4_PWM_CHY, rpwmval/180*2000+550);//修改TIM14PWM占空比调节前驱转向
}
}
/****************************************************** 右转 ******************************************************************/
void you(void)
{
if( X_he<115 )
{
rpwmval=78+(115-X_he)/2;
if(rpwmval>=128) rpwmval=128;
__HAL_TIM_SET_COMPARE(&g_tim14_pwm_chy_handle, TIM_CHANNEL_1 , 550);//修改TIM14PWM占空比调节后驱转速
__HAL_TIM_SET_COMPARE(&g_tim4_pwm_chy_handle, GTIM_TIM4_PWM_CHY, rpwmval/180*2000+550);//修改TIM4PWM占空比调节前驱转向
}
}
/****************************************************** 左转 ******************************************************************/
void zuo(void)
{
if( X_he>125 && X_he<=240)
{
rpwmval=78-(X_he-125)/2;
if(rpwmval<=28) rpwmval=28;
__HAL_TIM_SET_COMPARE(&g_tim14_pwm_chy_handle, TIM_CHANNEL_1 , 550);
__HAL_TIM_SET_COMPARE(&g_tim4_pwm_chy_handle, GTIM_TIM4_PWM_CHY, rpwmval/180*2000+550);//修改TIM14PWM占空比调节前驱转向
}
}
/****************************************************** 停止 ******************************************************************/
void ting(void)
{
if(rpwmval!=78)
__HAL_TIM_SET_COMPARE(&g_tim14_pwm_chy_handle, TIM_CHANNEL_1 , 550);
else
{
if(X_he==250)
__HAL_TIM_SET_COMPARE(&g_tim14_pwm_chy_handle, TIM_CHANNEL_1 , 1000);
}
}
/**************************************************** 图像二值化显示 ************************************************************/
void er_zhi(void)
{
uint16_t gray=0;
uint8_t i=0, j=0;
lcd_set_cursor(0,0);
lcd_write_ram_prepare();
for(i=0;i<Y;i++)
{
for(j=0;j<X;j++)
{
if(j==(X-1))
{
lcd_set_cursor(0,i+1);
lcd_write_ram_prepare(); //开始写入GRAM
}
gray=((rgb_buf[i][j]>>11)*19595+((rgb_buf[i][j]>>5)&0x3f)*38469 +(rgb_buf[i][j]&0x1f)*7472)>>16;
if(gray>=threshold) //这里是图像黑白二值化
{
LCD->LCD_RAM=WHITE;
}
else
{ //选取图像两行,找中心坐标,减少计数量
if(i==90) //第90行 找出图像的跳跃边线,计算中心点
{
if( Xs_max<j ) Xs_max=j;
if( Xs_min>j ) Xs_min=j;
}
if(i==96) //第96行 找出图像的跳跃边线,计算中心点
{
if( Xx_max<j ) Xx_max=j;
if( Xx_min>j ) Xx_min=j;
}
LCD->LCD_RAM=BLACK;
}
}
}
X_he=(Xs_max+Xs_min+Xx_max+Xx_min)/4; //计算X坐标 当前中心点
Xs_max=0;
Xs_min=500;
Xx_max=0;
Xx_min=500;
lcd_fill_circle(120,50,2,RED); //绘制一个实心圆点,显示目标中心点坐标
lcd_fill_circle(X_he,93,2,GREEN); //绘制一个实心圆点,显示当前中心点坐标
lcd_show_num(0,290,X_he,8,16,RED);
}
五、程序效果展示
ov2640寻迹小车
总结
本期内容就到这里了,后期还会分享相关内容的学习,有感兴趣的小伙伴可以关注一下博主。如果还有不懂得可以下载程序完整的源码参考stm32f407+ov2640小车寻迹。