摄像头组很稳定的黑线提取算法
基于摄像头的智能车黑色虚线识别算法研究

黑色 实线 。x s 1 2 8 ( 飞 思 卡 尔 的芯 片 ) 片 内的 r a n是 8 K, 为减少 r a n的使用 效 率 , 应 尽 量 减小 图像 矩 阵 。 本文 中使 用 6 4 8 4的图像 矩阵 。具体 操作 : 步骤 1 : 从 离摄 像 头 开 始 选 取 1 2行 , 采 用 边 缘
), 求 出这两 点构 成 直 线 的斜 率 和 直 线 方 程 , 即
变的越厉害, 那么就把采集 到的赛道 图像分成几部 分处理 。本文将 图像分 4段采集 , 以远处 的图像小 近处图像大的特点使用远密进疏的原则 , 前2 0 行每
可算 出虚线行 的横坐标 , 来补足虚线 。
收稿 日期 : 2 0 1 3—0 6—2 6
组成 , 综 合 考虑 3种 方案 的特 点 , 本 文采 用 的 下 , 在 实 时 的 图像 数 据 获
取的基础上对图像信息进行数据处理 , 从而提取赛 道 中心 的黑 色 指 引虚 线 , 以此 来 作 为 舵 机 和 驱 动 电 机的控制依据。以往 的赛 道 中的黑色指 引线是实
1 摄像头采样数据 的特点
本 文采用 的数 字摄 像 头 的型号 为 0 V 7 6 2 0 , 将摄
像头固定在车体 的中间, 前 瞻性约 1 i n 。要 想采集 的图像准确 , 必须要了解摄像头的时序, 摄像头 的时
序 图如 图 1 所示。
求, 根据赛道特点 , 主要有 3 种 寻线设计方案 : 光电 传感器方案 、 摄像头方案和电磁方案 。2 0 1 1 年第六 届 比赛 中要求赛道是 由白色底板和黑色的指引虚线
坐标作为中心值。如不符合条件 , 则为无效行。
4 黑色指 引虚线 的提取算法
openmv 循迹黑线思路

openmv 循迹黑线思路在使用OpenMV进行循迹黑线任务时,可以采用以下思路来实现:1. 图像预处理:首先,从摄像头获取实时图像,然后对图像进行预处理。
可以使用OpenMV的图像处理库,例如图像二值化、滤波、去噪等操作,以便增强图像的对比度和清晰度,以便更好地检测黑线。
2. 黑线检测:在预处理后的图像中,使用OpenMV的图像处理库来检测黑线的位置。
可以通过阈值分割或边缘检测等方法来找到黑线的边缘或轮廓。
可以根据具体情况选择合适的方法,以保证能够准确地检测到黑线。
3. 黑线跟踪:一旦黑线被检测到,可以使用OpenMV的机器视觉库来跟踪黑线的位置。
可以使用直线拟合或函数拟合等方法来获取黑线的几何属性,例如角度、位置等信息。
根据这些信息,可以使机器人相应地调整方向和速度,以保持沿着黑线的运动。
4. 控制反馈:在黑线跟踪的过程中,可以根据OpenMV检测到的黑线位置和机器人当前位置间的差异,进行控制反馈。
通过适当的控制算法,可以调整机器人的转向角度和速度,使其保持在黑线上运动。
可以考虑使用PID控制、模糊控制或神经网络控制等方法,以实现更精确的循迹效果。
5. 异常处理:在循迹过程中,可能会遇到一些异常情况,例如黑线中断、弯曲等。
为了应对这些异常情况,可以使用OpenMV的图像处理库来检测其他特征,例如十字路口、障碍物等。
根据检测到的特征,可以改变机器人的行为,例如停下来等待、绕过障碍物等,以确保机器人能够适应不同的路况。
通过以上的思路,可以利用OpenMV进行循迹黑线任务的实现。
当然,在实际应用中,可能还需要根据具体情况做一些调整和改进。
通过不断优化算法和参数的调整,可以使循迹黑线任务的效果更加准确和稳定。
opevcv lsd线段提取

opevcv lsd线段提取LSD线段提取是基于OpenCV图像处理库的一种算法,用于从图像中提取直线段。
本文将介绍LSD线段提取的原理、应用领域和优缺点。
一、LSD线段提取原理LSD(Line Segment Detector)线段提取算法是一种基于边缘检测的直线段提取方法。
它通过分析图像中的边缘信息,识别出其中的直线段,并给出直线段的起点和终点坐标。
LSD算法主要包括以下几个步骤:1. 边缘检测:使用Canny边缘检测算法对图像进行预处理,提取出图像中的边缘信息。
2. 候选线段生成:根据边缘信息,生成候选直线段。
LSD算法采用了一种基于区域的策略,在不同的尺度上对边缘进行分组,生成候选直线段。
3. 直线段合并:对生成的候选直线段进行合并,得到最终的直线段结果。
LSD算法采用了一种自底向上的策略,从低层次的直线段开始合并,逐渐扩大范围,直到合并完所有相关的直线段。
二、LSD线段提取应用领域LSD线段提取算法在计算机视觉和图像处理领域有着广泛的应用。
以下是一些常见的应用领域:1. 机器人导航:LSD线段提取可以用于识别机器人环境中的直线障碍物,帮助机器人规划路径和避免碰撞。
2. 道路检测:LSD线段提取可以用于车道线检测,帮助自动驾驶车辆实现道路规划和车道保持功能。
3. 工业检测:LSD线段提取可以用于检测工业产品中的缺陷,如裂纹、划痕等,帮助提高产品质量和生产效率。
4. 图像分析:LSD线段提取可以用于图像分析和对象识别,帮助计算机理解图像中的结构和内容。
三、LSD线段提取优缺点LSD线段提取算法具有以下优点:1. 高效性:LSD算法采用了一种快速的合并策略,可以在很短的时间内处理大量的直线段。
2. 精度高:LSD算法能够有效地识别出图像中的直线段,并给出其准确的起点和终点坐标。
3. 鲁棒性强:LSD算法对图像噪声和光照变化具有较强的鲁棒性,能够适应不同的图像环境。
然而,LSD线段提取算法也存在一些缺点:1. 对参数敏感:LSD算法的性能很大程度上取决于参数的选择,需要根据具体应用场景进行调整。
TSL1401线性CCD应用笔记,中文资料,环境光自适应算法

蓝宙TSL1401线性CCD应用笔记本文对第八届飞思卡尔智能车竞赛指定用线性CCD使用相关经验跟大家分享一下,本文不再讲述线性CCD基本原理,基本原理大家可阅读芯片手册,本文重点介绍使用线性CCD时需要考虑的一些问题及注意事项,并给出了参考解决方案。
旨在让大家更有效地使用和深入研究TSL1401线性CCD模块。
环境光影响问题试验表明TSL1401线性CCD的输出信号和环境光线密切相关,在自然光条件比晚上灯光下AO引脚输出电压值高出很多,正对着光线比背着光线输出电压高,白炽灯光下比日光灯下输出电压高。
因此,同一参数(曝光时间、镜头光圈)难以适应各种环境,在光线较弱环境下的参数在强光下会出现输出饱和,在较强光线下调节好的参数在弱光下输出电压过低,甚至处于截止状态。
在智能车应用中,白天自然光环境和晚上灯光环境、正对光和背光、不同的比赛场地之间都不能采用相同的曝光参数。
与输出电压密切相关的参数是曝光量,曝光量取决于CCD模块所采用的镜头光圈大小和程序所控制的曝光时间。
智能车为适应各种运行环境,必须实时感知环境,并根据环境闭环调节曝光量,使得在不同环境中曝光量都处于一个合理的范围,这样才能保证在不同环境中CCD输出电压在合理范围,以利于算法提取黑线信息。
镜头相关参数一旦选定在智能车运行难以改变,曝光时间比较容易通过程序控制,因此比较容易实现的调整曝光量方法是通过软件调整曝光时间。
曝光时间调整方法见“曝光时间自适应策略”一章。
输出信号放大根据上一章所述,可以通过调整曝光时间来适应各种环境,在弱光环境增大曝光时间,在强光下减小曝光时间。
但是曝光时间不能无限增大的,因为增大曝光时间势必降低采样率(每秒采样次数)采样率低控制周期就长,智能车反应就慢。
根据历届摄像头车参赛经验,1米的前瞻,3.5m/s的速度情况下,控制周期不得高于20ms(采样率不得低于50Hz),否则智能车转向机构反应再快也无法很好跟随赛道而冲出赛道。
线激光提取算法

线激光提取算法1. 简介线激光提取算法是一种用于从图像或点云数据中提取线激光的方法。
线激光是指由激光器发射的一条细长的光束,通常用于测量和建模三维环境。
线激光提取算法的目标是从复杂的背景中准确地分离出线激光,并提取出其相关属性,如位置、方向和强度等。
线激光提取算法在许多领域都有广泛应用,包括机器人导航、三维建模、自动驾驶等。
通过提取线激光信息,可以实现环境感知、障碍物检测和路径规划等功能。
2. 常见的线激光提取算法2.1 阈值分割算法阈值分割算法是最简单且常用的线激光提取方法之一。
该算法基于图像或点云数据中线激光与背景之间的明显对比,通过设定一个合适的阈值来将线激光与背景分离。
具体步骤如下:1.将图像或点云数据转换为灰度图像或灰度值;2.设定一个合适的阈值,将灰度值高于阈值的像素点标记为线激光;3.根据需要,可以进行后处理操作,如噪声去除、线段连接等。
阈值分割算法简单快速,适用于背景与线激光对比明显的情况。
然而,在复杂背景或光照变化等情况下,该算法可能无法准确提取线激光。
2.2 基于几何特征的算法基于几何特征的线激光提取算法利用线激光在图像或点云中的几何特征进行分割。
这些几何特征可以是直线性质、形状约束或拓扑结构等。
常见的基于几何特征的算法包括:•Hough 变换:通过将图像或点云中的点映射到参数空间,并检测参数空间中的峰值来提取直线;•RANSAC(随机抽样一致性):通过随机选择一组数据点,并根据模型与数据之间的一致性进行迭代优化来提取直线;•拓扑约束:利用线激光的拓扑结构,如端点、交叉点等进行线激光提取。
基于几何特征的算法可以提取出更精确的线激光,但其计算复杂度较高,对噪声和异常点敏感。
2.3 基于机器学习的算法近年来,基于机器学习的线激光提取算法逐渐得到广泛应用。
这些算法利用已标注的线激光数据进行训练,并通过学习线激光与背景之间的关系来提取线激光。
常见的基于机器学习的算法包括:•支持向量机(SVM):通过将线激光和背景分别表示为特征向量,并在特征空间中找到一个超平面来分类;•卷积神经网络(CNN):通过堆叠多个卷积层、池化层和全连接层来实现端到端的线激光提取。
摄像头采集信息的算法

摄像头采集信息的算法全文共四篇示例,供读者参考第一篇示例:摄像头在现代社会中扮演着重要角色,不仅在监控系统、安防领域有着广泛的应用,还在智能手机、笔记本电脑、平板电脑等设备中被广泛使用。
摄像头采集信息的算法是指利用摄像头获取的视频信息进行处理和分析的算法,其涉及到图像处理、计算机视觉和人工智能等多个领域,是当前研究热点之一。
摄像头采集信息的算法可以用于多种应用场景,例如人脸识别、车辆识别、动作检测、人体姿态识别等。
在这些应用中,摄像头首先将所拍摄的图像或视频传输至计算机系统中,而后算法会对图像进行分析和处理,从中提取出有意义的信息,并作出相应的判断和行为反应。
对于摄像头采集信息的算法来说,图像处理是其中一个重要的环节。
图像处理技术包括图像的采集、预处理、特征提取和特征匹配等步骤。
在图像采集阶段,摄像头会不断地捕获图像或视频,将其传输至计算机系统中。
在预处理阶段,图像可能需要进行去噪、平滑处理等,以便提高后续处理的效果。
特征提取是指从图像中提取出具有代表性的信息,例如像素级的颜色、纹理、形状等信息。
特征匹配则是将提取出的特征与预先训练好的模型进行匹配,从而实现对图像中物体或场景的识别和分类。
除了图像处理,计算机视觉也是摄像头采集信息的算法中不可或缺的一部分。
计算机视觉是一门研究如何让计算机“看懂”图像或视频的学科,其包括目标检测、目标跟踪、图像识别、物体检测等多个领域。
通过计算机视觉的技术,摄像头可以实现人脸识别、动作检测、人体姿态识别等功能。
在人工智能领域,深度学习和神经网络技术也被广泛应用于摄像头采集信息的算法中。
深度学习是一种基于人工神经网络的机器学习方法,通过大量的训练数据和复杂的网络结构,可以实现更加精准的图像识别和分类。
神经网络模仿了人脑的神经元网络结构,在处理图像时可以提取出更多的高级特征,提高图像处理的准确性和效率。
在工业领域,摄像头采集信息的算法也被广泛应用于生产自动化和机器视觉系统中。
脊提取算法

脊提取算法
脊提取算法通常用于图像处理领域,主要目的是从图像中提取出具有明显特征的脊线或脊点,这些特征通常对于图像的分析和理解很有帮助。
以下是一些常见的脊提取算法:
1. 方向滤波器:使用方向滤波器来检测图像中的脊线。
这些滤波器通常是一组在不同方向上敏感的滤波器,例如,Sobel 或Gabor 滤波器。
通过在图像上滑动这些滤波器,可以检测到不同方向上的脊线。
2. Hessian 矩阵:使用 Hessian 矩阵来检测图像中的脊线。
Hessian 矩阵包含了图像局部区域的二阶导数信息,可以用来确定图像中的脊线位置和方向。
3. Ridgelet 变换: Ridgelet 变换是一种用于图像脊提取的多尺度变换方法。
它在不同方向和尺度上对图像进行变换,以突出脊线的特征。
4. 小波变换:小波变换是一种多尺度分析方法,可用于提取图像中的脊线。
小波变换将图像分解为不同尺度和方向上的小波系数,从中可以提取出图像的脊线信息。
5. Frangi 滤波器: Frangi 滤波器是一种基于 Hessian 矩阵的滤波器,特别设计用于检测图像中的血管结构,但同样适用于脊线的提取。
这些算法的选择取决于应用场景和图像的特性。
在实际应用中,可能需要根据具体情况选择合适的算法或进行算法的组合,以获得更好的脊线提取效果。
openmv 循迹黑线思路

openmv 循迹黑线思路
摘要:
1.OpenMV 简介
2.循迹黑线的概念
3.OpenMV 实现循迹黑线的思路
4.OpenMV 循迹黑线的应用场景
正文:
1.OpenMV 简介
OpenMV 是一种基于MicroPython 的低成本、高性能的嵌入式计算机视觉平台,可以方便地在各种应用中实现计算机视觉功能。
它具有小巧的体积、低功耗、可编程性强等特点,广泛应用于机器人、智能家居、自动驾驶等领域。
2.循迹黑线的概念
循迹黑线,又称为轨迹黑线,是一种基于光学传感器的导航方法。
通过检测地面上的黑线,机器人可以沿着黑线行驶。
这种方法广泛应用于无人驾驶车辆、自动导引车等自动导航领域。
3.OpenMV 实现循迹黑线的思路
OpenMV 实现循迹黑线的思路主要分为以下几个步骤:
(1)图像采集:通过OpenMV 的摄像头模块,获取地面上的黑线图像。
(2)图像处理:对采集到的图像进行预处理,如去噪、滤波等操作,以提
高识别的准确性。
(3)黑线检测:利用图像处理算法,如边缘检测、形态学操作等,从图像中提取出黑线的位置信息。
(4)黑线跟踪:根据检测到的黑线位置信息,控制机器人的行驶方向和速度,使其始终沿着黑线行驶。
4.OpenMV 循迹黑线的应用场景
OpenMV 循迹黑线技术可以广泛应用于各种自动导航领域,如无人驾驶车辆、自动导引车、机器人巡检等。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
//本程序对黑线提取做了很大的改进,未对十字弯处理/*******************************************///本程序中加入了一些用于显示车模状态的LED灯/*经实测发现,近处的赛道宽度为60左右,远处为30左右,所以用可变赛道宽度进行搜索*/#include <hidef.h> /* common defines and macros */#include "derivative.h" /* derivative-specific definitions */#include <MC9S12XS128.h>//-----------函数声明-----------------//void delay(unsigned int t);#define HighSpeedLimit 40#define LowSpeedLimit 16#define SteerLeftLimit -35#define SteerRightLimit 35#define COLUMN 90 //采集列数#define MID_COLUMN 45 // 中间黑线#define ROW 40//采集行数#define LeftLED PORTB_PB0//左转方向灯#define RightLED PORTB_PB1//右转方向灯#define SpeedUpLED PORTB_PB2//加速指示灯#define SlowDownLED PORTB_PB3//减速方向灯#define Crossing_RoadLED PORTB_PB4//十字弯#define Dashed_RoadLED PORTB_PB5//虚线路段#define Mid_Route_Width_Factor 0.48 //赛道宽度系数int steer_dire_label=0;int SteerDelta=0;//舵机最终的偏转增量unsigned int Speed=0;//显示当前PWM2占空比大小unsigned int PreSpeed=0;float Threshold_Factor=0.9; //阈值系数设置unsigned int Threshold=120; //初设动态阈值为90,以后每传来一帧数据更新一次float Kp=0.8;//舵机方向比例系数float Kd=5.0;//舵机方向微分系数float MotorSpeed_Factor=6.0;//马达控制unsigned char Image_Data[ROW][COLUMN];unsigned int Left[ROW],Right[ROW];//左右黑线unsigned int VisualMiddle[ROW];//虚拟中线unsigned int Middle[ROW];//最终存放中间黑线值的二维数组unsigned int Row_Attribute[ROW];//行属性unsigned int row,column;int m=0;//计算采集到的行数unsigned char Line_Flag=0; //奇偶场unsigned int Line_C=0; //采集行数d int PreSteerDirection=50;//之前的舵机方向,用与前后比较unsigned int LeftFlag=0,RightFlag=0;//左右黑线标志unsigned int Left_Start_Flag=0,Right_Start_Flag=0;//左右起始黑线找到标志unsigned int L_lost_count=0;//左黑线丢失计数unsigned int R_lost_count=0;//右黑线丢失计数unsigned int L_last_lost=0;//左行上一行丢线标志unsigned int R_last_lost=0;//右行上一行丢线标志unsigned int L_last_memory=0;//左行上一次有黑线的黑线所在列数unsigned int R_last_memory=0;//右行上一次有黑线的黑线所在列数unsigned int HS_Data[ROW]={40,45,50,55,60,65,70,75,80,85,90,95,100,105, 110,115,120,124,128,132,136,140,144,148,152,156,160,163,166,169,172,175,178,181,184,187,189,191,193,194}; unsigned int HS_Pointer=0;//指向行数数组中的数据int error[2]={0,0};//舵机PD调节时的误差参数void delay(unsigned int Time){ //一般,Time设为10000,可以实现1秒一次int i,j;for(i=0;i<Time;i++)for(j=0;j<8;j++) {}}void SlowDown(int timer){int i;PWMDTY2=0;PWMDTY3=50;for(i=0;i<timer;i++)_asm(nop);PWMDTY3=0;PWMDTY2=Speed;}/**************************************************** 函数名称: 图像灰度值采集** 功能描述: 采集像素值** 输入: 无** 输出: 无** 说明:***************************************************/void Data_collect(void){int ia=0;while(ia<COLUMN){ //这样把赛道取的窄一点_asm(nop);_asm(nop);_asm(nop);_asm(nop);_asm(nop);_asm(nop);_asm(nop);_asm(nop);_asm(nop);_asm(nop);_asm(nop);_asm(nop);_asm(nop);_asm(nop);_asm(nop);_asm(nop);_asm(nop);_asm(nop);_asm(nop);_asm(nop);Image_Data[Line_C][ia++] = PORTA; //portA是8位的,可表示0~255,即灰度值。
注意采集的数据先放在数组的后部,即从124->}}/***************************************************** 函数名称: PLL_Init** 功能描述: 时钟初始化函数** 说明:BUS CLOCK=80M****************************************************/void PLL_Init(void) //pllclock=2*osc*(1+SYNR)/(1+REFDV);{CLKSEL=0x00; //disengage PLL to systemPLLCTL_PLLON=1; //turn on PLLSYNR =0xc0 | 0x09;REFDV=0x80 | 0x01;POSTDIV=0x00; //pllclock=2*osc*(1+SYNR)/(1+REFDV)=160MHz; while(!(CRGFLG_LOCK==1)); //when pll is steady ,then use it;CLKSEL_PLLSEL =1; //engage PLL to system;}/***********************************************//* P0输出频率为300Hz的方波,用于控制舵机 *//* P2,P3输出频率为20KHZ,用于驱动电机的转速 *//************************************************/void PWM_Init(void){PWME_PWME0=0x00; //禁止P0PWME_PWME2=0x00; //禁止P2PWME_PWME3=0x00; //禁止P3PWMPOL_PPOL0=1;//PWM Polarity 0开始输出高电平.PWMPOL_PPOL2=1;//PWM Polarity 2开始输出高电平.PWMPOL_PPOL3=1;//PWM Polarity 3开始输出高电平.PWMPRCLK=0x43; //0100 0011 A=80M/8=10M,B=80M/16=5M时钟预分频PWMSCLA=150; // SA=A/(2*150)=33.33KHZPWMSCLB=20; //SB=B/(2*20)=125KHZ;PWMCLK_PCLK0=1; //P0选的是SA时钟PWMPOL_PPOL0=1; //选用开始为高电平方式PWMCAE_CAE0=0; //选用左对齐方式PWMCLK_PCLK2=1; //P2选的是SB时钟PWMPOL_PPOL2=1; //选用开始为高电平方式PWMCAE_CAE2=0; //选用左对齐方式PWMCLK_PCLK3=1; //P2选的是SB时钟PWMPOL_PPOL3=1; //选用开始为高电平方式PWMCAE_CAE3=0; //选用左对齐方式PWMCTL=0x00; //控制寄存器设置为无联接//对舵机,驱动电机PWM波初始化PWMDTY0=50; //P0占空比为50%PWMDTY2=20; //P2占空比为50%PWMDTY3=33; //P3占空比为50%PWMPER0=100; //P0:Frequency=SA/100=333.33hzPWMPER2=100; //P2:Frequency=SB/100=1.25KHZPWMPER3=100; //P3:Frequency=SB/100=1.25KHZPWME_PWME0=1; //打开P0PWME_PWME2=1; //打开P2PWME_PWME3=1; //打开P3}/*************************************************** ** 函数名称: TIM_Init** 功能描述: 行场中断初始化函数** 说明:****************************************************/ void TIM_Init(void){TIOS =0x00; //定时器通道0,1 为输入捕捉TCTL4=0x09; //通道0 捕捉上升沿,通道1 捕捉下降沿TIE=0x03; //通道0,1 中断使能TFLG1=0xFF; //清中断标志位TSCR1=0x80; //定时器使能}/*************************************************** ** 函数名称: SCI0_Init** 功能描述: 串口1初始化函数** 说明: PS2--RX,PS3--TX****************************************************/{SCI0BDL = (byte)((80000000 /* OSC freq *//1) / 57600 /* baud rate */ / 16/*factor*/);SCI0CR1 = 0X00; /*normal,no parity*/SCI0CR2 = 0X0C; /*RIE=1,TE=1,RE=1, */}/**************************************************** 函数名称: 串口发射端程序** 功能描述: 发送赛道信息 1为黑线 0为白板** 输入: 无** 输出: 无** 说明:***************************************************/void SCI0_Transmit(unsigned char data){while (!(SCI0SR1&0x80));//如果SCI状态寄存器SCI1SR1的TDRE位为0,则一直执行这个无语句的循环,否则,执行下面语句SCI0DRL = data;//把要发送的数据存在SCI数据寄存器的低8位中}/***************************************************** 函数名称: IO_Init** 功能描述: 初始化函数** 说明:****************************************************/{DDRA=0X00;//A端口的数据传送方向为输入DDRB=0XFF;//端口B为输出PORTB=0XFF;//端口B全部打开}/************************************************/ /* 提取出图像的基本特征*//************************************************/ void Image_Operates(){char i,j;char Scan_Start,Scan_End;//扫描起始点,扫描终止点char Gate,Range;unsigned char Temp_Mid_Route;int t,temp0;Left_Start_Flag=0,Right_Start_Flag=0;//左右起始黑线找到标志 L_lost_count=0;//左黑线丢失计数R_lost_count=0;//右黑线丢失计数L_last_lost=0;//左行上一行丢线标志R_last_lost=0;//右行上一行丢线标志L_last_memory=0;//左行上一次有黑线的行计数器R_last_memory=0;//右行上一次有黑线的行计数器for(i=ROW-1;i>=0;i--) //数据初始化{Left[i]=0;Right[i]=COLUMN;VisualMiddle[i]=0;Row_Attribute[i]=0;}for(i=ROW-1;i>=0;i--){if(i>30){Gate=45;Range=40;}else if(i>20){Gate=35;Range=35;}else if(i>10){Gate=25;Range=30;}else{Gate=20;Range=20;}LeftFlag=RightFlag=0;//左右黑线标志位清零if(i>=ROW-2)//前两行全局扫描{//////////////////左边黑线起始行提取for(j=MID_COLUMN;j>1;j--){if(Image_Data[i][j]<Threshold/*&&Image_Data[i][j-1]<Threshold*/) {// if((Image_Data[i][j+1]-Image_Data[i][j]>Gate)||(Image_Data[i][j+2]-Image_Data[i][j]>Gate)){Left[i]=j;Left_Start_Flag=1;//识别到黑线LeftFlag=1;break;}}else if(j==2){Left[i]=0;break;}}//end for(j=MID_COLUMN;j>1;j--)///////////////////////////////右边黑线起始行提取for(j=MID_COLUMN;j<COLUMN-1;j++){if(Image_Data[i][j]<Threshold/*&&Image_Data[i][j+1]<Threshold*/) {// if((Image_Data[i][j-1]-Image_Data[i][j]>Gate)||(Image_Data[i][j-2]-Image_Data[i][j]>Gate)){Right[i]=j;Right_Start_Flag=1;//识别到黑线RightFlag=1;break;}}else if(j==COLUMN-2){Right[i]=COLUMN;break;}}//end for(m=MID_COLUMN;m<COLUMN-1;m++)}//end if(i<2) //前两行全局扫描else // (i<ROW-2){///////////////////左边if(Left_Start_Flag&&!L_last_lost) //识别到起始行且前一行没有丢失{Scan_End=Left[i+1]-Range;if(Scan_End<1)Scan_End=1;Scan_Start=Left[i+1]+Range;if(Scan_Start>VisualMiddle[i+1])Scan_Start=VisualMiddle[i+1];for(j=Scan_Start;j>Scan_End;j--)if(Image_Data[i][j]<Threshold/*&&Image_Data[i][j-1]<Threshold*/){//if((Image_Data[i][j+1]-Image_Data[i][j]>Gate)||(Image_Data[i][j+2]-Image_Data[i][j]>Gate)){Left[i]=j;LeftFlag=1;//识别到黑线break;}}else if(j==(Scan_End+1)){Left[i]=0;L_last_lost=1;//这一行数据丢失标识位L_last_memory=Left[i+1];break;}}//end if(Left_Start_Flag&&!L_last_lost) //识别到起始行且前一行没有丢失 else if(!Left_Start_Flag) //没有识别到起始行,继续寻找起始行{for(j=VisualMiddle[i+1];j>1;j--){if(Image_Data[i][j]<Threshold/*&&Image_Data[i][j-1]<Threshold*/) {// if((Image_Data[i][j+1]-Image_Data[i][j]>Gate)||(Image_Data[i][j+2]-Image_Data[i][j]>Gate)){Left[i]=j;Left_Start_Flag=1;//识别到黑线LeftFlag=1;break;}}else if(j==2){Left[i]=0;break;}}//end for(j=MID_COLUMN;j>1;j--)}//end else if(!Left_Start_Flag) //没有主识别到起始行,继续寻找起始行else if(L_last_lost) //识别到起始行但前一行丢失从虚拟中线开始循线{Scan_End=L_last_memory-Range;if(Scan_End<1)Scan_End=1;for(j=VisualMiddle[i+1];j>Scan_End;j--){if(Image_Data[i][j]<Threshold/*&&Image_Data[i][j-1]<Threshold*/) {//if((Image_Data[i][j+1]-Image_Data[i][j]>Gate)||(Image_Data[i][j+2]-Image_Data[i][j]>Gate)){Left[i]=j;LeftFlag=1;L_last_lost=0;break;}}else if(j==Scan_End+2){Left[i]=0;L_last_lost=1;//中间有数据丢失标识位break;}// end for(j=VisualMiddle[i-1];j>Scan_End;j--)}// end else if(L_last_lost) //识别到起始行但前一行丢失从虚拟中线开始循线 /////////////////////////////////右边if(Right_Start_Flag&&!R_last_lost) //识别到起始行且前一行没有丢失{Scan_End=Right[i+1]+Range;if(Scan_End>COLUMN)Scan_End=COLUMN;Scan_Start=Right[i+1]-Range;if(Scan_Start<VisualMiddle[i+1])Scan_Start=VisualMiddle[i+1];for(j=Scan_Start;j<Scan_End;j++)if(Image_Data[i][j]<Threshold/*&&Image_Data[i][j+1]<Threshold*/) {// if((Image_Data[i][j-1]-Image_Data[i][j]>Gate)||(Image_Data[i][j-2]-Image_Data[i][j]>Gate)){Right[i]=j;RightFlag=1;//识别到黑线break;}}else if(j==(Scan_End-2)){Right[i]=COLUMN;R_last_lost=1;//这一行数据丢失标识位R_last_memory=Right[i+1];break;}//end if(R_Start_Flag&&!R_last_lost)else if(!Right_Start_Flag) //没有别到右起始行,继续寻找起始行{for(j=VisualMiddle[i+1];j<COLUMN-1;j++){if(Image_Data[i][j]<Threshold/*&&Image_Data[i][j+1]<Threshold*/) {// if((Image_Data[i][j-1]-Image_Data[i][j]>Gate)||(Image_Data[i][j-2]-Image_Data[i][j]>Gate)){Right[i]=j;Right_Start_Flag=1;//识别到黑线RightFlag=1;break;}}else if(j==COLUMN-2){Right[i]=COLUMN;break;}}//end for(j=VisualMiddle[i-1];j<COLUMN-1;j++)}//end else if(!Right_Start_Flag) //没有主识别到起始行,继续寻找起始行else if(R_last_lost) //识别到起始行但前一行丢失从虚拟中线开始循线{Scan_End=R_last_memory+Range;if(Scan_End>COLUMN)Scan_End=COLUMN;for(j=VisualMiddle[i+1];j<Scan_End-1;j++){if(Image_Data[i][j]<Threshold/*&&Image_Data[i][j+1]<Threshold*/) {//if((Image_Data[i][j-1]-Image_Data[i][j]>Gate)||(Image_Data[i][j-2]-Image_Data[i][j]>Gate)){Right[i]=j;RightFlag=1;R_last_lost=0;break;}}else if(j==COLUMN-2){Right[i]=COLUMN;R_last_lost=1;//中间有数据丢失标识位break;}}// end for(j=VisualMiddle[i-1];j<Scan_End-1;j++)}// end else if(R_last_lost) //识别到起始行但前一行丢失从虚拟中线开始循线 }//end of else // (i>2)//////////////////////////////////属性判断if(LeftFlag&&RightFlag) Row_Attribute [i]=1;//两边都有黑线else if(LeftFlag) Row_Attribute [i]=2;//左边有黑线else if(RightFlag) Row_Attribute [i]=3;//左边丢失,仅右边有黑线else Row_Attribute [i]=0; //两边丢失////////////////////////中线读取if(i==ROW-1)VisualMiddle[i]=MID_COLUMN;else{ //要知道,最前面还有一个大大的for(n=0;n<Rows;n++)循环语句 if(Row_Attribute [i]) VisualMiddle[i]=(int)(Right[i]+Left[i])/2; else VisualMiddle[i]=VisualMiddle[i+1];}}//end of for(i=0;i<ROW;i++)////////把只有左黑线或右黑线的所在行补其另一黑线for(t=ROW-1;t>=0;t--){if(Row_Attribute[t]==1)Middle[t]=(Left[t]+Right[t])/2;if(Row_Attribute[t]==2)//只有左行{Temp_Mid_Route=(int)(16+Mid_Route_Width_Factor*t);Right[t]=Left[t]+2*Temp_Mid_Route;Middle[t]=Left[t]+Temp_Mid_Route;}else if(Row_Attribute[t]==3)//只有右行{Temp_Mid_Route=(int)(16+Mid_Route_Width_Factor*t);Left[t]=Right[t]-2*Temp_Mid_Route;Middle[t]=Right[t]-Temp_Mid_Route;}}///////第一行没有数据,利用以后行进行补if(!Row_Attribute[ROW-1]||!Row_Attribute[ROW-2]){for(t=ROW-1;t>0;t--)if(Row_Attribute[t]){temp0=t;break;}for(t=temp0;t<ROW;t++){Middle[t+1]=Middle[t];Row_Attribute[t+1]=1;//认为它找到了左,右黑线}}//////////中间有行丢失,用前一行补充,其实这里也可用线性插值法补充,但二者影响不大if(Row_Attribute[ROW-1]==0)Middle[ROW-1]=MID_COLUMN;for(t=ROW-2;t>0;t--)if(Row_Attribute[t]==0) {Middle[t]=Middle[t+1];Row_Attribute[t]=1;}/////////////给Image_Data赋值for(row=0;row<ROW;row++) {// if(Row_Attribute[row])Image_Data[row][Middle[row]]=0;}}// end of Image_Operates//舵机方向控制void SteeringCtl_PD() //我这里先用位置式PD调节{int i=0;int SteerSum=0;{ LeftLED=RightLED=0;error[0]=error[1];for(i=35;i>=16;i--) //仅对近处的20行取平均值 SteerSum+=Middle[i]-COLUMN/2;error[1]=(int)(SteerSum/20);SteerDelta=Kp*error[1]+Kd*(error[1]-error[0]); if(SteerDelta<0){LeftLED=1;RightLED=0;}if(SteerDelta>0){RightLED=1;LeftLED=0;}if(SteerDelta>SteerRightLimit)//防止舵机抱死PWMDTY0=50+SteerRightLimit;else if(SteerDelta<SteerLeftLimit)PWMDTY0=50+SteerLeftLimit;elsePWMDTY0=50+SteerDelta;}steer_dire_label=PWMDTY0;}//马达速度控制/************************************************//** 对采集的前20行处理,用来控制马达速度 *//*********************************************/void MotorSpeedCtl(){int Sum=0;int i=0;int Front_Black_Line_Warp=0;SlowDownLED=SpeedUpLED=0;//对前20行处理,判断是弯道,S弯等,用以控制电机速度//对黑直线上下界限制for(i=10;i<30;i++)Front_Black_Line_Warp+=(Middle[i]-COLUMN/2);//取远处黑线与"黑直线"之间的偏差值if(Front_Black_Line_Warp<0)Front_Black_Line_Warp=-Front_Black_Line_Warp; //我们现在要的只是它偏离的绝对值Speed=(int)(MotorSpeed_Factor*500/((Front_Black_Line_Warp)*(Front_Black_Line_ Warp)));if(Speed<=LowSpeedLimit)//速度上下界限制PWMDTY2=LowSpeedLimit;else if(Speed>=HighSpeedLimit)PWMDTY2=HighSpeedLimit;elsePWMDTY2=Speed;PreSpeed=Speed;}/***************************************************** 函数名称: All_Init** 功能描述: 初始化函数** 说明:****************************************************/ void All_Init(void){PLL_Init();SCI0_Init();TIM_Init();IO_Init();PWM_Init();}/*************************************************** ** 函数名称: main** 功能描述: 主函数** 说明:****************************************************/ void main(void){DisableInterrupts;All_Init();EnableInterrupts;for(;;){}}//---------------------中断定义---------------------#pragma CODE_SEG NON_BANKED/**************************************************** 函数名称: 中断处理函数** 功能描述: 行中断处理函数** 输入: 无** 输出: 无** 说明: TIME0 上升沿,这里从第57行每3行采集一次,一直到第204行,一共采50行***************************************************/interrupt 8 void IC0_ISR(void){TFLG1_C0F =1; //清行中断m++;if(m==HS_Data[HS_Pointer]){Data_collect();HS_Pointer++;//指向行数的数组指针也加1Line_C++;//图像数组行数也加1}else{Line_Flag=1;return;}//end if}/**************************************************** 函数名称: 中断处理函数** 功能描述: 场中断处理函数** 输入: 无** 输出: 无** 说明: TIME1 下降沿***************************************************/ interrupt 9 void IC1_ISR(void){int ThresholdCount=0;int i=0;TIE=0X00;m=0;//每次行数都清0TFLG1_C1F =1; //清场中断TFLG1_C0F = 1; //清行中断Line_C = 0; //行计数器HS_Pointer=0;//行数计数器为0if(Line_Flag==1){Line_Flag=0;Image_Operates();SteeringCtl_PD();//舵机方向处理// MotorSpeedCtl();//图像发送://下面发送命令字:0 255 1 0SCI0_Transmit(0X00);SCI0_Transmit(0Xff);SCI0_Transmit(0X01);SCI0_Transmit(0X00);//紧接着发送数据for(row=0;row<ROW;row++){for(column=0;column<COLUMN;column++){SCI0_Transmit(Image_Data[row][column]); //SCI传送}}// */}//end if//下面这个对左右数组初始化是相当有有必要的,如果不初始化,黑线提取很不稳定,我试过多次验证的TIE=0X03;//open interrupt}#pragma CODE_SEG DEFAULT////////////////////////下面是一些想加入还未加的思路/* Row_Label=ROW-3;while(Row_Label>0){Dashed_Start=Dashed_End=0;for(t=Row_Label;t>1;t--){if(Row_Attribute [t]&&!Row_Attribute [t-1])//本行有黑线,但下一行没黑线Dashed_Start=t;if(Dashed_Start&&!Row_Attribute [t]&&Row_Attribute [t-1]){Dashed_End=t-1;break;}}// end for(t=1;t<ROW-10;i++)//对虚线开始点和结束点进行判断和修正for(t=Dashed_Start-1;t>Dashed_End;t--)//一次线性插值算法Middle[t]=Middle[Dashed_Start]+(int)((Middle[Dashed_End]-Middle[Dashed_Start])*(t-Dashed_Start)/(Dashed_End-Dashed_Start)); Row_Label=Dashed_End-1;}//end of while(Row_Label<ROW)*//*******************************************************/ /***************提取中间黑线****************************/ /*******************************************************/ /*void GetBlackLine(){unsigned char Temp_Mid_Route;int t,temp0;char Row_Label;char Dashed_Start,Dashed_End;//扫描起始点,扫描终止点////////把只有左黑线或右黑线的所在行补其另一黑线for(t=ROW-1;t>=0;t--){if(Row_Attribute[t]==2)//只有左行{Temp_Mid_Route=(int)(15+Mid_Route_Width_Factor*temp0);Right[t]=Left[t]+2*Temp_Mid_Route;Middle[t]=Left[t]+Temp_Mid_Route;}else if(Row_Attribute[t]==3)//只有右行{Temp_Mid_Route=(int)(15+Mid_Route_Width_Factor*temp0);Left[t]=Right[t]-2*Temp_Mid_Route;Middle[t]=Right[t]-Temp_Mid_Route;}}///////第一行没有数据if(!Row_Attribute[ROW-1]||!Row_Attribute[ROW-2]){for(t=ROW-1;t>0;t--)if(Row_Attribute[t]){temp0=t;break;}if(Row_Attribute[temp0]==1)for(t=temp0;t<ROW;t++){Middle[t+1]=Middle[t];Row_Attribute[t+1]=1;//也认为它找到了左,右黑线}else if(Row_Attribute[temp0]==2)// temp0行也仅仅找到左黑线{Temp_Mid_Route=(int)(15+Mid_Route_Width_Factor*temp0);//算出赛道半宽Middle[temp0]=Left[temp0]+Temp_Mid_Route;//对这行中线进行修正for(t=temp0;t<ROW;t++){Middle[t+1]=Middle[t];Row_Attribute[t+1]=2;//也认为它只找到了左黑线}}else if(Row_Attribute[temp0]==3)// temp0行也仅仅找到右黑线{Temp_Mid_Route=(int)(15+Mid_Route_Width_Factor*temp0);//算出赛道半宽Middle[temp0]=Right[temp0]-Temp_Mid_Route;//对这行中线进行修正for(t=temp0;t<ROW;t++){Middle[t+1]=Middle[t];Row_Attribute[t+1]=3;//也认为它只找到了右黑线}}}//end if(!Row_Attribute[0]||!Row_Attribute[1])////////////////////中间有丢行的/* Row_Label=ROW-3;while(Row_Label>0){Dashed_Start=Dashed_End=0;for(t=Row_Label;t>0;t--){if(Row_Attribute [t]&&!Row_Attribute [t+1])//本行有黑线,但下一行没黑线 Dashed_Start=t;if(Dashed_Start&&!Row_Attribute [t]&&Row_Attribute [t+1]){Dashed_End=t+1;break;}}// end for(t=1;t<ROW-10;i++)//对虚线开始点和结束点进行判断和修正Temp_Mid_Route=(int)(35-0.41*Dashed_Start);//算出赛道半宽if(Row_Attribute[Dashed_Start]==2)// 虚线断点开始行也仅仅找到左黑线Middle[Dashed_Start]=Left[Dashed_Start]+Temp_Mid_Route;//对这行中线进行修正else if(Row_Attribute[Dashed_Start]==3)// temp0行也仅仅找到右黑线Middle[Dashed_Start]=Right[Dashed_Start]-Temp_Mid_Route;//对这行中线进行修正Temp_Mid_Route=(int)(35-0.41*Dashed_End);//算出赛道半宽if(Row_Attribute[Dashed_End]==2)// 虚线断点开始行也仅仅找到左黑线Middle[Dashed_End]=Left[Dashed_End]+Temp_Mid_Route;//对这行中线进行修正else if(Row_Attribute[Dashed_End]==3)// temp0行也仅仅找到右黑线Middle[Dashed_End]=Right[Dashed_End]-Temp_Mid_Route;//对这行中线进行修正for(t=Dashed_Start;t<=Dashed_End;t++)//一次线性插值算法Middle[t]=Middle[Dashed_Start]+(int)((Middle[Dashed_End]-Middle[Dashed_Start])*(t-Dashed_Start)/(Dashed_End-Dashed_Start));Row_Label=Dashed_End+1;}//end of while(Row_Label<ROW)} */。