STM32版CCD线性摄像头寻线寻迹小车
基于STM32F103C8T6的CCD线性摄像头寻线寻迹小车
目录
- 基于STM32F103C8T6的CCD线性摄像头寻线寻迹小车
- 前言
- 一、模块介绍
- 二、使用说明
- 1.引脚说明
- 2.其他
- 三、调试过程
- 四、寻线原理
- 五、部分代码
- 1.CCD摄像头相关代码
- 2.ADC采集相关代码
- 3.主函数代码
- 六、小车硬件
- 七、演示视频
- 总结
前言
目前大多数的小车寻线寻迹都是用红外对管寻线,这是比较简单也比较成熟的技术方案,且成本也低。本文将介绍使用CCD线性摄像头寻线寻迹。
一、模块介绍
TSL1401 线性传感器由一个 1x128 的光电二极管阵列、相关的电荷放大电路以及一个内部像素数据保功能组成。内部像素数据保功能可以为所有像素点提供同时积分的开始和停止时间。该阵列由 128 个像素组成,每个像素的感光面积为 3,524.3 平方微米。 像素之间的间隔为 8μm。内部控制逻辑简化了操作,该模块需要串行输入(SI)信号和时钟信号(CLK)。
二、使用说明
1.引脚说明
通过查看数据手册模块的管脚介绍如上
- AO: 信号输出(供单片机进行信号采集读取)
- CLK: 时钟信号
- GND: 地线
- SI: 信号输入(单片机向 CCD 发送指令)
- VDD: 电源(模块引脚标识为 VCC,接单片机逻辑电平,支持 3~5V)
2.其他
CCD 传感器是光学传感器,会受到环境光线的影响;程序中已经运用了动态阈值算法,尽量减小环境光线的影响,但是太暗或者太亮的环境下是不能正常工作的(一般室内正常光线可以运行)。
三、调试过程
在CCD调试助手中可以看出,当CCD摄像头扫描到黑线时,会出现一个凹槽,左右移动,凹槽也移动,脱离黑线凹槽消失。从这条线大概可以看出中值为多少。
通过串口助手打印出的中值结果如下:
四、寻线原理
小车寻线原理是通过 CCD 线性摄像头扫描黑线,摄像头扫描到 128 的像素点,中值为 64,扫描到黑线会得到一个二值化数据,用这个二值化数据减去中值64再除以 2,就得到小车偏离黑线的值(有正有负,如果为正,小车左转,如果为负,小车右转),舵机初始角度的值加上偏差的值就可以控小车沿着黑线跑了(数据是实时获取的,所以小车沿着黑线行驶不会卡方向)。
五、部分代码
1.CCD摄像头相关代码
首先对CCD摄像头初始化所用到的管脚初始化:
void Ccd_Init(void)
{ GPIO_InitTypeDef GPIO_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能PA端口时钟GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; //PA.2 端口配置GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHzGPIO_Init(GPIOA, &GPIO_InitStructure); //根据设定参数初始化GPIOA.2GPIO_SetBits(GPIOA,GPIO_Pin_2); //PA.2 输出高GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3; //PA.3 端口配置, 推挽输出GPIO_Init(GPIOA, &GPIO_InitStructure); //推挽输出 ,IO口速度为50MHzGPIO_SetBits(GPIOA,GPIO_Pin_3); //PA.7 输出高
}
获取中值:
void RD_TSL(void)
{u8 i=0,tslp=0;static u8 j,Left,Right,Last_CCD_Zhongzhi;static u16 value1_max,value1_min;TSL_CLK=1; //CLK引脚设为高电平TSL_SI=0; Dly_us(TIME_us);TSL_SI=1; TSL_CLK=0;Dly_us(TIME_us);TSL_CLK=1;TSL_SI=0;Dly_us(TIME_us); for(i=0;i<128;i++){ TSL_CLK=0; Dly_us(TIME_us); //调节曝光时间ccd_adc[tslp]=(u8)((float)Get_Adc(ADC_Channel_1)/4096*255); //将读取到的电压值存入数组中++tslp;TSL_CLK=1;Dly_us(TIME_us);} value1_max=ccd_adc[0]; //动态阈值算法,读取最大和最小值for(i=5;i<123;i++) //两边各去掉5个点{if(value1_max<=ccd_adc[i])value1_max=ccd_adc[i];}value1_min=ccd_adc[0]; //最小值for(i=5;i<123;i++) {if(value1_min>=ccd_adc[i]){value1_min=ccd_adc[i]; }}CCD_Yuzhi=(value1_max+value1_min)/2; //计算出本次中线提取的阈值for(i = 5;i<118; i++) //寻找左边跳变沿{if(ccd_adc[i]>CCD_Yuzhi&&ccd_adc[i+1]>CCD_Yuzhi&&ccd_adc[i+2]>CCD_Yuzhi&&ccd_adc[i+3]<CCD_Yuzhi&&ccd_adc[i+4]<CCD_Yuzhi&&ccd_adc[i+5]<CCD_Yuzhi){ Left=i;break; }}for(j = 118;j>5; j--)//寻找右边跳变沿{if(ccd_adc[j]<CCD_Yuzhi&&ccd_adc[j+1]<CCD_Yuzhi&&ccd_adc[j+2]<CCD_Yuzhi&&ccd_adc[j+3]>CCD_Yuzhi&&ccd_adc[j+4]>CCD_Yuzhi&&ccd_adc[j+5]>CCD_Yuzhi){ Right=j;break; }}CCD_Zhongzhi=(Right+Left)/2;//计算中线位置if(myabs(CCD_Zhongzhi-Last_CCD_Zhongzhi)>70) //计算中线的偏差,如果太大CCD_Zhongzhi=Last_CCD_Zhongzhi; //则取上一次的值Last_CCD_Zhongzhi=CCD_Zhongzhi; //保存上一次的偏差}
发送至上位机调试:
void sendToPc(void)
{ int i;slove_data();printf("*");printf("LD");for(i=2;i<134;i++){ printf("%c",binToHex_high(SciBuf[i])); //以字符形式发送高4位对应的16进制printf("%c",binToHex_low(SciBuf[i])); //以字符形式发送低?位对应的16进制}printf("00"); //通信协议要求printf("#"); //通信协议要求
}
2.ADC采集相关代码
ADC定义初始化:
void Adc_Init(void)
{ ADC_InitTypeDef ADC_InitStructure; GPIO_InitTypeDef GPIO_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |RCC_APB2Periph_ADC1 , ENABLE ); //使能ADC1通道时钟RCC_ADCCLKConfig(RCC_PCLK2_Div6); //设置ADC分频因子6 72M/6=12,ADC最大时间不能超过14M//PA1 作为模拟通道输入引脚GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模拟输入引脚GPIO_Init(GPIOA, &GPIO_InitStructure); ADC_DeInit(ADC1); //复位ADC1,将外设 ADC1 的全部寄存器重设为缺省值ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //ADC工作模式:ADC1和ADC2工作在独立模式ADC_InitStructure.ADC_ScanConvMode = DISABLE; //模数转换工作在单通道模式ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; //模数转换工作在单次转换模式ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //转换由软件而不是外部触发启动ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //ADC数据右对齐ADC_InitStructure.ADC_NbrOfChannel = 1; //顺序进行规则转换的ADC通道的数目ADC_Init(ADC1, &ADC_InitStructure); //根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器ADC_Cmd(ADC1, ENABLE); //使能指定的ADC1ADC_ResetCalibration(ADC1); //使能复位校准while(ADC_GetResetCalibrationStatus(ADC1)); //等待复位校准结束ADC_StartCalibration(ADC1); //开启AD校准while(ADC_GetCalibrationStatus(ADC1)); //等待校准结束// ADC_SoftwareStartConvCmd(ADC1, ENABLE); //使能指定的ADC1的软件转换启动功能}
获取ADC值:
//获得ADC值
//ch:通道值 0~3
u16 Get_Adc(u8 ch)
{//设置指定ADC的规则组通道,一个序列,采样时间ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_239Cycles5 ); //ADC1,ADC通道,采样时间为239.5周期ADC_SoftwareStartConvCmd(ADC1, ENABLE); //使能指定的ADC1的软件转换启动功能while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));//等待转换结束return ADC_GetConversionValue(ADC1); //返回最近一次ADC1规则组的转换结果
}
3.主函数代码
u8 CCD_Zhongzhi=64,CCD_Yuzhi; //线性CCD相关int main(void)
{ int piancha=0,jiaodu;delay_init(); //延时函数初始化NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);// 设置中断优先级分组2uart_init(115200); //串口初始化为115200Adc_Init(); //ADC初始化Ccd_Init(); //CCD初始化TIME_us=1; //设置曝光时间TIM1_PWM_Init(7199,0); //=====初始化PWM 10KHZ,用于驱动电机。Motor_Init(); //=====初始化与电机连接的硬件IO接口SERVO_Init(); //=====舵机PWM定时器3, 初始占空比7.5%while(1){
// sendToPc(); //发送信息至上位机RD_TSL(); //获取中值printf("%d\n",CCD_Zhongzhi);piancha = (CCD_Zhongzhi-64)/3;jiaodu = 75+piancha;SERVO_Angle_Control(jiaodu);go();}
}
六、小车硬件
主控:STM32F103C8T6
摄像头:线性CCD( TSL1401CL)
电机驱动:TB6612FNG
舵机一个
电机两个
LM2596S稳压模块
7.4V航模锂电池
七、演示视频
CCD摄像头寻线寻迹小车
总结
用CCD线性摄像头寻迹寻线小车跑起来更稳更丝滑,跑黑线的类型也多一点。参考:线性CCD基础学习
STM32版CCD线性摄像头寻线寻迹小车相关推荐
- 黑白线循迹小车利用STM32F407与三个红外对管实现
小车循迹模块编程思路 所用模块:LM399红外对管循迹模块 特征:可同时搭载4个红外对管,进行黑白线检测 检测:当它探寻到黑线时候,DX口输出TTL电平接近于VCC电压 当它探寻到白线时候,DX口输出 ...
- 具有避障和寻线功能的Arduino小车
标签: Arduino 乐高 机器人 创客对于成年人来说,多半是科技娱乐,或者是一种是一种向往科技的人生态度,总是希望自己不仅可以看到或者听到科技的资讯,还希望能够亲身制作科技玩意,从而更好地体 ...
- 智能车摄像头算法——寻线
寻线 1.灰度图像二值化 2.找边线 3.获得中线 1.灰度图像二值化 如果使用的是小钻风摄像(二值化摄像头),就不用再进行软件二值化. 使用灰度摄像头,就需要这步. 以下展示常用的大津法 (1)首先 ...
- STM32红外寻迹小车
STM32红外寻迹小车(寄存器版) 最近学习了STM32,想通过制作一辆小车来加深对STM32的理解,在平时学习时经常用正点原子提供的源代码稍加该装就行,但是正点原子没有提供关于红外寻迹模板的相关程序 ...
- 智能车寻线算法之北科寻线可能用的方法
智能车寻线算法之北科寻线可能用的方法 先上一张北科的寻线图片,从上图可以看出,北科的寻线方法能够寻找到方向向下的曲线,肯定和我们一般的左右寻线的方法有所不一样,我想了很久,查阅的了数字图像处理第三版, ...
- 智能车图像处理(二)基础寻线
在我们得到一张稳定的二值化图像后,就可以对图像进行一些寻线的处理,在这里简单提一下,那就是图像第一行所在的位置,作为初学者的我刚开始就曾搞错,摄像头第一行所在的位置取决于你摄像头是正装还是反装,如果是 ...
- 【ROS RIKIBOT 基础--使用系列 第八章节】超声波跟随、雷达跟随、手机APP建地图、视觉单线寻线
1.RIKIBOT 超声波跟随 1.1 把小车平放在地板上,用远程软件登录到小车系统上,打开一个终端启动 roslaunch rikibot bringup.launch. 1.2再打开一个终端,启动 ...
- OpenCV使用霍夫变换进行寻线的实例(附完整代码)
OpenCV使用霍夫变换进行寻线的实例 OpenCV使用霍夫变换进行寻线的实例 OpenCV使用霍夫变换进行寻线的实例 #include "opencv2/imgcodecs.hpp&quo ...
- 线性表的顺寻存储结构
数据结构: 数据结构也是分两个部分,一个是数据部分,一个是结构部分: 数据: 能够被计算机存储.识别和计算的东西都叫数据,但是这些数据都是以二进制存储的 硬盘中的:MP3.JPG.doc.AVI.EX ...
最新文章
- python xml etree_python xml.etree解析xml
- python元素元组抓7_Python7元组,字典,集合
- 从程序员到项目经理(七):程序员加油站 -- 完美主义也是一种错
- echarts时间散点图_ECharts 实现地图散点图(下)
- 第8周课堂测试3(课上未完成)
- 《OpenGL编程指南》一3.2 OpenGL缓存数据
- 基于主从博弈的电热综合能源系统动态定价与能量管理 主要做的是电热综合能源系统的动态定价问题,采用是主从博弈方法
- 微信支付商户号和企业付款到零钱开通方法
- 用scratch实现网上“超人训练”游戏
- Windows10 如何清理注册表,教大家清理注册表方法
- VMware14 kali linux安装教程
- n子棋,你能下赢电脑吗,来玩玩吧
- matlab八分之一中点画圆算法,中点八分画圆算法
- 阿里云云计算专业认证考试(ACP级)
- JMeter—录制脚本
- Android---简易Snackbar
- AD20设计规则检查设置(DRC检查设置)
- solr增量 dih deltaimport 入门
- 传统人工智能中的三大问题
- linux中apache与tomcat如何使用
热门文章
- 最小公倍数c语言调用该函数,C语言中如何调用函数求最大公约数和最小公倍数...
- 524. 愤怒的小鸟
- 研发管理学习笔记4-学习研发管理的51CTO视频课
- 最强特殊字符、表情符号、Unicdeo字符串处理
- inteli211网卡linux驱动,intel(R)I211网卡刷I210简易教程
- 粤嵌实验板 linux 环境,粤嵌实习报告
- django restful mysql_如何用Django和restful搭建api接口服务
- C---------------LessonCircle
- HAUTOJ 1262 魔法宝石
- android8.1 彩蛋,遥遥领先!OPPO R11s成为搭载安卓8.1的国内厂商,还有一个彩蛋!...