关于TC264单片机与智能车摄像头循迹的一些学习心得

这篇具有很好参考价值的文章主要介绍了关于TC264单片机与智能车摄像头循迹的一些学习心得。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言:最近一段时间在准备智能车考核,其中要求使用TC264单片机实现PID控制的小车摄像头循迹。其中关于PID的部分在之前我已经上传过了,这篇文章主要讲怎么实现循迹与舵机的位置式PID调参和电机的增量式调参的一些心得。

一、摄像头循迹的实现

首先我们要明白英飞凌公司旗下的小钻风摄像头为灰度摄像头,它收集到的图像信号为黑白灰像素点构成的图像,那我们想要小车能识别到赛道边缘从而实现循迹,就需要对采集到的图像进行二值化处理。

那么什么叫二值化处理呢,下面我结合代码进行说明。

 int16 i,j;
 uint8 mt9v03x_image_2[120][188];
 uint8 th=100;//阈值大小


mt9v03x_init();//摄像头初始化
 tft180_set_dir(TFT180_CROSSWISE_180);                             // 需要横屏不然显示不下
 tft180_init();//屏幕初始化
 if(mt9v03x_finish_flag)//这个if函数内部进行对采集到的每一帧画面进行处理
      {
       erzhihua();//二值化函数
       tft180_displayimage03x((const uint8 *)mt9v03x_image_2, 160, 128);//将处理的图像显示出来
       mt9v03x_finish_flag = 0;
         }



void erzhihua(void)
{

//说明:摄像头采集的图像会以数组的形式存起来,其中数组的里面的数据为0~255的uint8型的数,其中0为黑色,255为白色
    int i,j;

    for(i=0;i<MT9V03X_H;i++)
    {
        for(j=0;j<MT9V03X_W;j++)
        {
            mt9v03x_image_2[i][j] = mt9v03x_image[i][j];
        }
    }//为防止二值化对原始数据的破坏导致不能正常输出,我们复制了一个一样的数组,对其进行操作


    for(i=0;i<MT9V03X_H;i++)//行遍历
    {
        for(j=0;j<MT9V03X_W;j++)//列遍历
        {
            if(mt9v03x_image[i][j]<th)
            {
                mt9v03x_image[i][j]=0;
                mt9v03x_image_2[i][j]=0; //小于阈值变黑
            }
            else
            {
                mt9v03x_image[i][j]=255;
                mt9v03x_image_2[i][j]=255; //大于阈值变白
            }
        }
    }
}

简单来说二值化就是将黑白灰图像转化为只有黑白的图像,通过阈值的合理设定达到对赛道边缘的黑化(便于检测)的效果。

但是二值化仅仅使得我们知道了赛道边缘在哪里,而我们想要小车保持在赛道中间,就需要控制转向的舵机的配合。具体怎么配合呢?

首先,我们要将得到二值化图像进行处理,将赛道的中线表示出来。此时这个所谓的赛道中线其实本质上就是一串数组对应的宽和高。通过将得到的赛道中线的水平位置与屏幕显示的中间位置(就是数组的宽的一半)进行比较,算出小车与赛道中心的误差值,将这个误差值输入到舵机的PID中进行计算,最后输出PWM给舵机进行转向。

具体代码如下

int16 i,j;
 uint8 mt9v03x_image_2[120][188];
 uint8 th=100;//阈值大小
 uint8 mid[120];
 int side_left[120],side_right[120];

void erzhihua(void)
{


    int i,j;

    for(i=0;i<MT9V03X_H;i++)
    {
        for(j=0;j<MT9V03X_W;j++)
        {
            mt9v03x_image_2[i][j] = mt9v03x_image[i][j];
        }
    }


    for(i=0;i<MT9V03X_H;i++)
    {
        for(j=0;j<MT9V03X_W;j++)
        {
            if(mt9v03x_image[i][j]<th)
            {
                mt9v03x_image[i][j]=0;
                mt9v03x_image_2[i][j]=0; //小于阈值变黑
            }
            else
            {
                mt9v03x_image[i][j]=255;
                mt9v03x_image_2[i][j]=255; //大于阈值变白
            }
        }
    }
}



void xunji_Mid(void)
{
    erzhihua();
    int i,j;

    for(i=0;i<MT9V03X_H;i++)
  {
      for(j=80;j<MT9V03X_W;j++)
      {
          if( mt9v03x_image_2[i][j]==0)
          { side_right[i]=j;break;}//从屏幕中间开始向两边寻找黑点,找到就记下黑点的横坐标
      }
      for(j=80;j>=0;j--)
      {
          if( mt9v03x_image_2[i][j]==0)
           { side_left[i]=j;break;}
      }
      mid[i]=(side_right[i]+side_left[i])/2;//赛道中线就是左右端点之和除以二

  }
    for(int i=120;i>70;i--)
        {
            feedbackpositionleijia+=mid[i];
        }
            feedbackposition= feedbackpositionleijia/50;//为提高数据可信度,减小误差,将50个中点求和算平均值
            feedbackpositionleijia=0;
}

void show_line(void)//将中线与边界线在显示的图像中描黑
{

    int i;
    for(i = MT9V03X_H-1; i >= 0; i--)
      {
          //画赛道中线
          if(mid[i] < MT9V03X_W && mid[i] >= 0)
          {
              mt9v03x_image_2[i][side_left[i]] = 0;
              mt9v03x_image_2[i][side_right[i]] = 0;
              mt9v03x_image_2[i][mid[i]] = 0;
          }
      }

}


if(mt9v03x_finish_flag)
         {
          erzhihua();
          xunji_Mid();
          show_line();
          tft180_displayimage03x((const uint8 *)mt9v03x_image_2, 160, 128);
          mt9v03x_finish_flag = 0;
         }//最后写在主函数循环里面的显示函数






       PID_Calc_wz(&mypid_Angle,targetposition,feedbackposition);//位置式pid控制舵机
       int angleout=dutyzhong+mypid_Angle.output;
       pwm_set_duty(SERVO_MOTOR_PWM,angleout);


int dutyzhong=720;//每个舵机的中点对应的PWM占空比不同,一般在750上下
float feedbackposition;
float feedbackpositionleijia;
float targetposition=80;//这里获取小车目标位置




void PID_Init(PID *pid,float p,float i,float d,int16 maxI,int16 maxOut)
{
    pid->kp=p;//调参只要改动这三个值就行p,i,d
    pid->ki=i;
    pid->kd=d;
    pid->maxIntegral=maxI;//最大积分
    pid->maxOutput=maxOut;//最大输出值
}

进行一次位置式pid计算
参数为(pid结构体,目标值,反馈值),计算结果放在pid结构体的output成员中
void PID_Calc_wz(PID *pid,float reference,float feedback)
{
    //更新数据
    pid->lastError=pid->error;//将旧error存起来
    pid->error=reference-feedback;//计算新error
    //计算微分
    int dout=(pid->error-pid->lastError)*pid->kd;//误差的差值乘以Kd
    //计算比例
    int pout=pid->error*pid->kp;//误差乘以Kp
    //计算积分
    pid->integral+=pid->error*pid->ki;//误差的累加乘以Ki
    //积分限幅
    if(pid->integral > pid->maxIntegral) pid->integral=pid->maxIntegral;//积分过大则限幅赋值为max
    else if(pid->integral < -pid->maxIntegral) pid->integral=-pid->maxIntegral;//积分过小则赋值为min
    //计算输出
    pid->output=pout+dout+pid->integral;//输出值为P+I+D
    //输出限幅
    if(pid->output > pid->maxOutput) pid->output=pid->maxOutput;
    else if(pid->output < -pid->maxOutput) pid->output=-pid->maxOutput;

    if(pid->output < pid->minOutput) pid->output=pid->minOutput;


}

二、PID调参技巧

位置式PID:先调Kp,使得波形在目标值上下震荡,且幅度会逐渐减小。接下来就加入Kd来对震荡进行抑制,当Kd过大时就没有抑制作用,反而会使得波形不停震荡。最后加入Ki来使得最后趋于稳定的值更趋近于目标值。当你初步调节完成PID参数时,先记录下此时的参数,然后回过去继续微调Kp、Kd、Ki。直到最后波形达到一定的响应与稳定时才算调好了参数。而且每个电机的PID参数不尽相同,需要单独调节。

增量式PID:因为由于公式为增量,Ki对应的量才是error,所以先调Ki。接着再调Kp,最后调Kp。具体调到什么情况合适参考位置式PID。

可以用vofa这个软件,用电脑这个上位机来实时显示波形,便于我们的调参。文章来源地址https://www.toymoban.com/news/detail-763803.html

到了这里,关于关于TC264单片机与智能车摄像头循迹的一些学习心得的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 关于单片机的前后台系统

    单片机裸机系统,通常又被称为前后台系统。 百度百科中,对前后台系统有一段解释: ​前后台系统,即计算机前后台系统,早期的嵌入式系统中没有操作系统的概念,程序员编写嵌入式程序通常直接面对裸机及裸设备,在这种情况下,通常把嵌入式程序分成两部分,即前

    2024年02月12日
    浏览(36)
  • 关于单片机的分频定时器的记录

    对于单片机的频率原来一直不太明白,现在在学习进行记录: 主频: 分频 对于分频来说,实际就是相当于间接降低这个主频,减少这个震荡次数,比如我分频系数为72,那么我就是1S产生的震荡次数就是72000000/72=1000000次,相当于用少的计数来对一秒钟进行计数。 那么有人好

    2024年04月09日
    浏览(47)
  • 关于单片机的时钟浅谈及STM32F103/F030单片机的内外时钟切换问题

    本文主要讲解单片机的时钟系统的相关知识,并进行超频测试,同时介绍如何在STM32F0单片机上进行内外时钟的切换,在不使用外部晶振或者外部晶振不启动时自动切换内部时钟的方法。 问题来源于群里的一次问答: 诚然,当使用固件库时,把外部晶振摘掉,系统确实会自动

    2024年02月15日
    浏览(42)
  • 单片机毕设 基于单片机的智能快递柜设计与实现

    Hi,大家好,这里是丹成学长,今天向大家介绍一个 单片机项目 基于单片机的智能快递柜设计与实现 大家可用于 课程设计 或 毕业设计 🧿 毕设项目分享:见文末! 一般来说,传统快递服务方式是人对人,即快递员进行揽件派送,与签收人进行面对面签收,确认无误后服务终

    2024年04月09日
    浏览(53)
  • 单片机设计基于51单片机的智能风扇控制系统设计与实现

      我们常见的电风扇一般只有四、五个风速档,用的是人工开关,而且并不是每个人家里都会有空调,或者在一些小型的工厂或者一些小型加工厂,这些地方都可能没有配备大型的中央空调系统这些东西,所以这些东西往往都会采用风扇这种小成本的东西来代替,但是不清楚

    2024年02月03日
    浏览(65)
  • 关于单片机的data, idata,xdata,code

    51单片机的运行内存分前128字节和后128字节。 前面128个字节好像cpu里的寄存器,读写非常快。 后面的128字节只能用指针访问。 单片机可以外扩运行内存条,外扩的这部分内存叫xdata。 就像搭建积木。以乐高积木举例:单片机是玩家,code区是说明书,data是积木块,单片机看着

    2024年02月07日
    浏览(37)
  • 单片机课设———基于51单片机的智能风扇控制器(汇编语言)

    说明:单片机芯片为AT89C52,使用普中开发板,用汇编语言编程。 系统实现了对风扇的控制: (1)控制器面板包括:启/停键、模式选择键、风速键和类型选择键。 (2)模式分为:手动模式和自动温控模式。在温控模式下,风速键和类型选择键无效。 (3)风速分为:强、中

    2024年02月11日
    浏览(51)
  • 【毕业设计】基于单片机的智能鱼缸系统设计与实现 - 嵌入式 物联网 stm32 51单片机 智能鱼缸

    Hi,大家好,今天向大家介绍一个 单片机项目, 大家可用于 课程设计 或 毕业设计 基于单片机的智能鱼缸系统设计与实现 🔥 项目分享与指导: https://gitee.com/sinonfin/sharing 近年以来,随着我国综合实力飞速飙升,人们对物质和精神生活质量的要求也不断提升,各式各样的智能

    2024年02月04日
    浏览(85)
  • 关于STM32单片机的I/O口配置

    最近在看数据手册的时候,发现在Cortex-M3里,对于GPIO的配置种类有8种之多: (1)GPIO_Mode_AIN 模拟输入  (2)GPIO_Mode_IN_FLOATING 浮空输入 (3)GPIO_Mode_IPD 下拉输入 (4)GPIO_Mode_IPU 上拉输入 (5)GPIO_Mode_Out_OD 开漏输出 (6)GPIO_Mode_Out_PP 推挽输出 (7)GPIO_Mode_AF_OD 复用开漏输出

    2024年02月10日
    浏览(42)
  • 基于单片机的智能小车设计

    随着科技的发展,智能机器人在日常生活中的应用越来越广泛。智能小车作为智能机器人的一种,具有便携性和多功能的特点,在教育、娱乐和工业等领域得到了广泛关注和应用。智能小车可以通过远程控制实现各种动作,如前进、后退、转弯等,并且可以通过搭载传感器实

    2024年02月06日
    浏览(55)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包