草庐IT

全国大学生智能汽车大赛(一):摄像头识别赛道代码

liutangplease 2024-07-02 原文

全国大学生智能汽车大赛(一):摄像头识别赛道代码

全国大学生智能汽车大赛(二):摄像头识别赛道代码

全国大学生智能汽车大赛(三):上下位机通信协议及代码

        这些代码是我在大二时参加智能车竞赛时编写的程序,仅供参考。

        代码内容涉及二值化、大津法(相关内容可以参考我的另一篇博文)等。

        智能汽车基于先进的自动化控制技术以传感器信号检测处理为指引,驱动电机实现特定轨迹的高速稳定行驶。目前智能汽车技术在交通运输、智能驾驶等方面有着广阔的应用前景与发展空间。智能车的方案设计基本相似,整体的稳定性和高速行驶对控制系统的设计要求很高,尤其是面对复杂路况时赛道的识别、转向控制和车速控制是系统设计的难点。

/**********************************************
 * @file       		摄像头
 * @Target core		TC264D
 * @date       		2021-5-19
 * @note		
 **********************************************/

#include "headfile.h"
double deviation1; //摄像头获得的偏差

/************************************
* 函数名称:otsu(uint16 column, uint16 row)
* 功能说明:求阈值大小
* 参数说明:
* 函数返回:阈值大小
* 修改时间:2021/5/12 Wed
************************************/
uint16 otsu(uint16 column, uint16 row)
{
    uint16 i,j;
    uint32 Amount = 0;
    uint32 PixelBack = 0;
    uint32 PixelIntegralBack = 0;
    uint32 PixelIntegral = 0;
    int32 PixelIntegralFore = 0;
    int32 PixelFore = 0;
    float OmegaBack, OmegaFore, MicroBack, MicroFore, SigmaB, Sigma; //类间方差;
    uint16 MinValue, MaxValue;
    uint16 Threshold = 0;
    uint8 HistoGram[256];

    for (j = 0; j < 256; j++)  HistoGram[j] = 0; //初始化灰度直方图

    for (j = 0; j < row; j++)
    {
        for (i = 0; i < column; i++)
        {
            HistoGram[mt9v03x_image[j][i]]++; //统计灰度级中每个像素在整幅图像中的个数
        }
    }

    for (MinValue = 0; MinValue < 256 && HistoGram[MinValue] == 0; MinValue++) ;        //获取最小灰度的值
    for (MaxValue = 255; MaxValue > MinValue && HistoGram[MinValue] == 0; MaxValue--) ; //获取最大灰度的值

    for (j = MinValue; j <= MaxValue; j++)    Amount += HistoGram[j];                   //像素总数

    PixelIntegral = 0;
    for (j = MinValue; j <= MaxValue; j++)
    {
        PixelIntegral += HistoGram[j] * j; //灰度值总数
    }
    SigmaB = -1;
    for (j = MinValue; j < MaxValue; j++)
    {
        PixelBack = PixelBack + HistoGram[j];                   //前景像素点数
        PixelFore = Amount - PixelBack;                         //背景像素点数
        OmegaBack = (float)PixelBack / Amount;                  //前景像素百分比
        OmegaFore = (float)PixelFore / Amount;                  //背景像素百分比
        PixelIntegralBack += HistoGram[j] * j;                  //前景灰度值
        PixelIntegralFore = PixelIntegral - PixelIntegralBack;  //背景灰度值
        MicroBack = (float)PixelIntegralBack / PixelBack;       //前景灰度百分比
        MicroFore = (float)PixelIntegralFore / PixelFore;       //背景灰度百分比
        Sigma = OmegaBack * OmegaFore * (MicroBack - MicroFore) * (MicroBack - MicroFore); //计算类间方差
        if (Sigma > SigmaB) //找出最大类间方差以及对应的阈值
        {
            SigmaB = Sigma;
            Threshold = j;
        }
    }
    return Threshold;                        //返回最佳阈值;
}

/************************************
  * @brief    二值化
  * @param    mode  :  0:大津法阈值    1:平均阈值
  * @return
  * @note     Get_01_Value(0); //使用大津法二值化
  * @date     2021/5/12 WED
 ************************************/
uint8 Pixle[MT9V03X_H][MT9V03X_W];            //存放二值化后图像
void binaryzation(uint8 mode)
{
  int i = 0,j = 0;
  uint32 Threshold;
  uint32  tv=0;
  if(mode)
  {
      //累加
      for(i = 0; i <MT9V03X_H; i++)
      {
          for(j = 0; j <MT9V03X_W; j++)
          {
              tv+=mt9v03x_image[i][j];         //累加
          }
      }
      Threshold=tv/MT9V03X_H/MT9V03X_W;        //求平均值,光线越暗越小,全黑约35,对着屏幕约160,一般情况下大约100
      Threshold=Threshold*7/10+10;             //此处阈值设置,根据环境的光线来设定
  }
  else
  {
      Threshold = otsu(MT9V03X_W,MT9V03X_H);   //大津法阈值
//    Threshold = (uint8_t)(Threshold * 0.5) + 70;
  }

  for(i = 0; i < MT9V03X_H; i++)
  {
    for(j = 0; j < MT9V03X_W; j++)
    {
      if(mt9v03x_image[i][j] >Threshold)       //数值越大,显示的内容越多,较浅的图像也能显示出来
        Pixle[i][j] =0xff;
      else
        Pixle[i][j] =0x00;
    }
  }
}

/************************************
  * @brief    过滤噪点
  * @param
  * @return
  * @note
  * @date     2021/5/13 Thur
 ************************************/
void Pixle_Filter(void)
{
    int nr; //行
    int nc; //列

    for(nr=1; nr<MT9V03X_H-1; nr++)
    {
        for(nc=1; nc<MT9V03X_W-1; nc=nc+1)
        {
            if((Pixle[nr][nc]==0xff)&&(((Pixle[nr-1][nc]==0xff)&&(Pixle[nr+1][nc]==0xff))||((Pixle[nr][nc+1]==0xff)&&(Pixle[nr][nc-1]==0xff))))
            {
                Pixle[nr][nc]=0xff;
            }
            else if((Pixle[nr][nc]==0x00)&&(((Pixle[nr-1][nc]==0x00)&&(Pixle[nr+1][nc]==0x00))||((Pixle[nr][nc+1]==0x00)&&(Pixle[nr][nc-1]==0x00))))
            {
                Pixle[nr][nc]=0x00;
            }
        }
    }
}

/************************************
  * @brief     摄像头获取偏差
  * @param     start_row开始列,end_row终止列,step检测步值
  * @return    camera_deviation偏差
  * @note
  * @date      2021/5/15 Sat
 ************************************/

typedef struct   //定义一个结构体,用于放置所用点的所在列数以及标志符
{
    int16 column;
    int16 flag;
}usefulpoint;
usefulpoint jump_left[use_h_max], jump_right[use_h_max], midpoint[use_h_max];//定义每一行左,右跳变点,赛道中点
double camera_deviation(int16 start_row, int16 end_row, int16 step)
{
    int16 column, row, num_left = 0, num_right = 0,num_mid = 0;     //定义检测偏差中所需的中间变量及标志
    double deviation = 0;
    //参数初始化
    for(row = 0; row>= end_row; row++)
    {
        jump_left[row].flag = 0;
        jump_right[row].flag = 0;
        midpoint[row].flag = 0;
    }
    //跳变点检测
    for (row = start_row; row < end_row; row=row+step)
    {
        //左侧跳变点检测
        for (column = 0; column < MT9V03X_W; column++)
        {
            if ( (Pixle[row-1][column]==0xff) && (Pixle[row][column]==0xff) && (Pixle[row+1][column]==0x00) && (Pixle[row+2][column]==0x00))
            {
                jump_left[row].column = column;    //记录左侧跳变点列数
                jump_left[row].flag = 1;           //标记为左侧跳变点
                num_left++;                        //左侧跳变点个数增加1
                break;                             //找到左侧跳变点,跳出循环
            }
        }
        //右侧跳变点检测
        for (column = MT9V03X_W; column > 0; column++)
        {
            if ( (Pixle[row-2][column]==0x00) && (Pixle[row-1][column]==0x00) && (Pixle[row][column]==0xff) && (Pixle[row+1][column]==0xff))
            {
                jump_right[row].column = column;    //记录右侧跳变点列数
                jump_right[row].flag = 1;           //标记为右侧跳变点
                num_right++;                        //右侧跳变点个数增加1
                break;                              //找到右侧跳变点,跳出循环
            }
        }
    }
        if(num_left<=4)   return -10;               //采集到的图像中只有右侧赛道边缘
        if(num_right<=4)  return 10;                //采集到的图像中只有左侧赛道边缘
        //计算赛道中点
        for (row = start_row; row < end_row; row=row+step)
        {
            if((jump_left[row].flag)&&(jump_right[row].flag))
            {
                midpoint[row].column = (jump_left[row].column+jump_right[row].column)/2;  //计算赛道中点所在列
                if(midpoint[row].column <= MT9V03X_W)            midpoint[row].flag = 1;  //标记为赛道中点
                num_mid++;                                                                //赛道中点个数增加1
            }
        }
        //计算偏差1.0
        for (row = start_row; row < end_row; row=row+step)
        {
            if(midpoint[row].flag)  deviation = deviation + midpoint[row].column - MT9V03X_W/2;
        }
        deviation=deviation/num_mid;

    return deviation;
}

二、 推荐IO分配

摄像头:8个数据口,一个串口,两eru中断
        数据口:00_0 00_1 00_2 00_3 00_4 00_5 00_6 00_7
        配置串口:摄像头RX:02_2     摄像头TX:02_3
        VSY:02_0
        HREF:程序不需要,所以不接
        PCLK:02_1

        四路运放 A0 A1 A2 A3 等

四个编码器:
    LSB:33_7   DIR:33_6
    LSB:02_8   DIR:00_9
    LSB:10_3   DIR:10_1
    LSB:20_3   DIR:20_0

8路pwm:

        21_2 21_3 21_4 21_5 02_4 02_5 02_6 02_7

ICM20602:
    CLK:    P20_11  
    MOSI:    P20_14 
    MISO:    P20_12 
    CS:    P20_13
    SPI0

TFT屏幕:
    CLK     15_3 
    MOSI     15_5 
    MISO     15_4    //实际上TFT没有这个引脚 这里仅仅占位而已  
    CS0     15_2 
    BL         15_4     //复用控制背光
    REST     15_1 
    DC         15_0  
    SPI2

舵机:

P33_9


        尽量不要使用以下引脚,以下引脚属于boot引脚,不合理的使用容易导致单片机无法启动等问题,因此建议大家尽量不要使用:
        P14_2 
        P14_3
        P14_4
        P14_5
        P14_6 
        P10_5 
        P10_6 

 

有关全国大学生智能汽车大赛(一):摄像头识别赛道代码的更多相关文章

  1. ruby - 如何在 buildr 项目中使用 Ruby 代码? - 2

    如何在buildr项目中使用Ruby?我在很多不同的项目中使用过Ruby、JRuby、Java和Clojure。我目前正在使用我的标准Ruby开发一个模拟应用程序,我想尝试使用Clojure后端(我确实喜欢功能代码)以及JRubygui和测试套件。我还可以看到在未来的不同项目中使用Scala作为后端。我想我要为我的项目尝试一下buildr(http://buildr.apache.org/),但我注意到buildr似乎没有设置为在项目中使用JRuby代码本身!这看起来有点傻,因为该工具旨在统一通用的JVM语言并且是在ruby中构建的。除了将输出的jar包含在一个独特的、仅限ruby​​

  2. ruby-on-rails - Rails 源代码 : initialize hash in a weird way? - 2

    在rails源中:https://github.com/rails/rails/blob/master/activesupport/lib/active_support/lazy_load_hooks.rb可以看到以下内容@load_hooks=Hash.new{|h,k|h[k]=[]}在IRB中,它只是初始化一个空哈希。和做有什么区别@load_hooks=Hash.new 最佳答案 查看rubydocumentationforHashnew→new_hashclicktotogglesourcenew(obj)→new_has

  3. ruby-on-rails - 浏览 Ruby 源代码 - 2

    我的主要目标是能够完全理解我正在使用的库/gem。我尝试在Github上从头到尾阅读源代码,但这真的很难。我认为更有趣、更温和的踏脚石就是在使用时阅读每个库/gem方法的源代码。例如,我想知道RubyonRails中的redirect_to方法是如何工作的:如何查找redirect_to方法的源代码?我知道在pry中我可以执行类似show-methodmethod的操作,但我如何才能对Rails框架中的方法执行此操作?您对我如何更好地理解Gem及其API有什么建议吗?仅仅阅读源代码似乎真的很难,尤其是对于框架。谢谢! 最佳答案 Ru

  4. ruby - 模块嵌套代码风格偏好 - 2

    我的假设是moduleAmoduleBendend和moduleA::Bend是一样的。我能够从thisblog找到解决方案,thisSOthread和andthisSOthread.为什么以及什么时候应该更喜欢紧凑语法A::B而不是另一个,因为它显然有一个缺点?我有一种直觉,它可能与性能有关,因为在更多命名空间中查找常量需要更多计算。但是我无法通过对普通类进行基准测试来验证这一点。 最佳答案 这两种写作方法经常被混淆。首先要说的是,据我所知,没有可衡量的性能差异。(在下面的书面示例中不断查找)最明显的区别,可能也是最著名的,是你的

  5. ruby - 寻找通过阅读代码确定编程语言的ruby gem? - 2

    几个月前,我读了一篇关于ruby​​gem的博客文章,它可以通过阅读代码本身来确定编程语言。对于我的生活,我不记得博客或gem的名称。谷歌搜索“ruby编程语言猜测”及其变体也无济于事。有人碰巧知道相关gem的名称吗? 最佳答案 是这个吗:http://github.com/chrislo/sourceclassifier/tree/master 关于ruby-寻找通过阅读代码确定编程语言的rubygem?,我们在StackOverflow上找到一个类似的问题:

  6. ruby - Net::HTTP 获取源代码和状态 - 2

    我目前正在使用以下方法获取页面的源代码:Net::HTTP.get(URI.parse(page.url))我还想获取HTTP状态,而无需发出第二个请求。有没有办法用另一种方法做到这一点?我一直在查看文档,但似乎找不到我要找的东西。 最佳答案 在我看来,除非您需要一些真正的低级访问或控制,否则最好使用Ruby的内置Open::URI模块:require'open-uri'io=open('http://www.example.org/')#=>#body=io.read[0,50]#=>"["200","OK"]io.base_ur

  7. 程序员如何提高代码能力? - 2

    前言作为一名程序员,自己的本质工作就是做程序开发,那么程序开发的时候最直接的体现就是代码,检验一个程序员技术水平的一个核心环节就是开发时候的代码能力。众所周知,程序开发的水平提升是一个循序渐进的过程,每一位程序员都是从“菜鸟”变成“大神”的,所以程序员在程序开发过程中的代码能力也是根据平时开发中的业务实践来积累和提升的。提高代码能力核心要素程序员要想提高自身代码能力,尤其是新晋程序员的代码能力有很大的提升空间的时候,需要针对性的去提高自己的代码能力。提高代码能力其实有几个比较关键的点,只要把握住这些方面,就能很好的、快速的提高自己的一部分代码能力。1、多去阅读开源项目,如有机会可以亲自参与开源

  8. 7个大一C语言必学的程序 / C语言经典代码大全 - 2

    嗨~大家好,这里是可莉!今天给大家带来的是7个C语言的经典基础代码~那一起往下看下去把【程序一】打印100到200之间的素数#includeintmain(){ inti; for(i=100;i 【程序二】输出乘法口诀表#includeintmain(){inti;for(i=1;i 【程序三】判断1000年---2000年之间的闰年#includeintmain(){intyear;for(year=1000;year 【程序四】给定两个整形变量的值,将两个值的内容进行交换。这里提供两种方法来进行交换,第一种为创建临时变量来进行交换,第二种是不创建临时变量而直接进行交换。1.创建临时变量来

  9. git使用常见问题(提交代码,合并冲突) - 2

    文章目录git常用命令(简介,详细参数往下看)Git提交代码步骤gitpullgitstatusgitaddgitcommitgitpushgit代码冲突合并问题方法一:放弃本地代码方法二:合并代码常用命令以及详细参数gitadd将文件添加到仓库:gitdiff比较文件异同gitlog查看历史记录gitreset代码回滚版本库相关操作远程仓库相关操作分支相关操作创建分支查看分支:gitbranch合并分支:gitmerge删除分支:gitbranch-ddev查看分支合并图:gitlog–graph–pretty=oneline–abbrev-commit撤消某次提交git用户名密码相关配置g

  10. ruby - 这两段代码有什么区别? - 2

    打印1:defsum(i)i=i+[2]end$x=[1]sum($x)print$x打印12:defsum(i)i.push(2)end$x=[1]sum($x)print$x后者是修改全局变量$x。为什么它在第二个例子中被修改而不是在第一个例子中?类Array的任何方法(不仅是push)都会发生这种情况吗? 最佳答案 变量范围在这里无关紧要。在第一段代码中,您仅使用赋值运算符=为变量i赋值,而在第二段代码中,您正在修改$x(也称为i)使用破坏性方法push。赋值从不修改任何对象。它只是提供一个名称来引用一个对象。方法要么是破坏性

随机推荐