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.引腳說明
 通過查看數據手冊模塊的管腳介紹如上
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); //串口初始化為115200 Adc_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线性摄像头寻线寻迹小车的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: Proxy SwitchyOmega
- 下一篇: EC-PCA: 利润中心段(Segmen
