赞
踩
本章讲的是寻线功能,采用线性CCD摄像头实现
线性CCD
我们常说的摄像头 CCD 模块通常使用的是面阵 CCD 芯片,一般以 OV 系列面阵 CCD 最为常用。而 TSL1401 属于线性 CCD,也可以叫做线阵 CCD。与面阵 CCD 相比,线性 CCD 最明显的特点就是其只能采集一行的可视像素。
TSL1401 线性 CCD 传感器包含 128 个线性排列的光电二极管。每个光电二极管都有各自的积分电路,以下我们将此电路统称为像素。每个像素所采集的图像灰度值与它所感知的光强和积分时间成正比。以上名词所对应的物理量纲为:
灰度值:输出电压 V out ;
光强:器件对给定光波长在电压上的反应 V e ;(V e =V/(μJ/cm2)* μW/cm2)
积分时间:即曝光时间,t int 。
128 个像素是怎么进行采集并输出的呢,这就用到了 SI 和 CLK 信号。根据下图可以简单的了解它们的功能。在 128 个像素之外,还有一个开关逻辑控制和移位寄存器电路。SI 通过该电路,控制每一个像素的积分和复位操作;CLK 通过该电路控制每一个像素电压的依次输出。
首先利用一个 SI 周期来曝光CCD,紧接着在下一个 SI 周期对 128 个像素进行依次采集(CLK的下降沿采集数据)。在前一个 SI 周期内,必须输出129 个 CLK 以丢弃没用的 AO 输出,曝光结束后的第二个 SI 周期内采集的 AO 输出才是正确的。
该方法需要在两个 SI 信号之间添加延时函数来控制曝光时间。缺点是占用 CPU 运行时间来进行曝光,即在曝光期间内 CPU 只能处于等待状态。
代码如下(示例):
uint16_t ADV[128]={0}; uint8_t i; struct tCCD { uint16_t middle; //中间位置值 uint16_t threshold; //像素ad阈值 uint16_t left; //左跳变的位置 uint16_t right; //右跳变的位置 }; struct tCCD CCD; /************************************************************************************************** *函数名:abs() *功能:将数进行绝对值处理 *形参:number 需要进行绝对值处理的数 *返回值:经过处理后绝对值 **************************************************************************************************/ int abs (int number) { return( number>=0 ? number : -number ); } /************************************************************************************************** *函数名:Dly_us() *功能:延时函数,用来调整CCD曝光 *形参:无 *返回值:无 ***************************************************************************************************/ void Dly_us(void) { int ii; for(ii=0;ii<220;ii++); } /************************************************************************************************** *函数名:Get_Adc() *功能:得到CCD数据 *形参:无 *返回值:读取到的电压值 ***************************************************************************************************/ uint16_t Get_Adc(void) { HAL_ADC_Start(&hadc); HAL_ADC_PollForConversion(&hadc, 50); if(HAL_IS_BIT_SET(HAL_ADC_GetState(&hadc), HAL_ADC_STATE_REG_EOC)) return HAL_ADC_GetValue(&hadc); else while(1); } /*************************************************************************************************** *函数名:RD_TSL() *功能:读取CCD原始数据 *形参:无 *返回值:无 ****************************************************************************************************/ void RD_TSL(void) { uint8_t i=0,tslp=0; TSL_CLK_H; TSL_SI_L; Dly_us(); TSL_SI_H; TSL_CLK_L; Dly_us(); TSL_CLK_H; TSL_SI_L; Dly_us(); for(i=0;i<128;i++){ TSL_CLK_L; Dly_us(); ADV[tslp]=(Get_Adc())>>4; ++tslp; TSL_CLK_H; Dly_us(); } } /************************************************************************************************************************ *函数名:Find_Middle_CCD() *功能:读取CCD中值 *形参:无 *返回值:CCD中值位置 *************************************************************************************************************************/ uint8_t Find_CCD_DATA(void) { static uint8_t i,j; //static uint8_t Last_Middle_CCD; uint8_t Middle_CCD_Value; static uint16_t value1_max,value1_min; value1_max=ADV[0]; //读取最大值 for(i=5;i<123;i++){ if(value1_max<=ADV[i]) value1_max=ADV[i]; } value1_min=ADV[0]; //得到最小值 for(i=5;i<123;i++){ if(value1_min>=ADV[i]) value1_min=ADV[i]; } //计算阈值 CCD.threshold=(value1_max+value1_min)/2; //计算左跳变值 for(i = 5;i<118; i++){ if(ADV[i]>CCD.threshold&&ADV[i+1]>CCD.threshold&&ADV[i+2]>CCD.threshold&&ADV[i+3]<CCD.threshold&&ADV[i+4]<CCD.threshold&&ADV[i+5]<CCD.threshold){ CCD.left=i; break; } } //计算右跳变值 for(j = 118;j>5; j--){ if(ADV[j]<CCD.threshold&&ADV[j+1]<CCD.threshold&&ADV[j+2]<CCD.threshold&&ADV[j+3]>CCD.threshold&&ADV[j+4]>CCD.threshold&&ADV[j+5]>CCD.threshold){ CCD.right=j; break; } } //计算中值 CCD.middle =(CCD.right+CCD.left)/2; // if(abs(Middle_CCD_Value-Last_Middle_CCD)>70){ // Middle_CCD_Value=Last_Middle_CCD; // Last_Middle_CCD=Middle_CCD_Value; // } return Middle_CCD_Value; } /***************************************************************************************************************************/ void ANO_Send_Data(void) { HAL_UART_Transmit(&huart1,(uint8_t *)&CCD,sizeof(CCD),0xFFFF); } /* USER CODE END 0 */ int main(void) { /* USER CODE BEGIN 1 */ /* USER CODE END 1 */ /* MCU Configuration----------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Configure the system clock */ SystemClock_Config(); /* USER CODE BEGIN SysInit */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_ADC_Init(); MX_USART1_UART_Init(); /* USER CODE BEGIN 2 */ /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { /* USER CODE END WHILE */ //每5ms发送一帧数据 HAL_Delay(5); //读取CCD原始数据 RD_TSL(); //对CCD数据进行处理(求阈值、中值、左右跳变值) Find_CCD_DATA(); //通过串口将封装好的CCD数据向外发送 ANO_Send_Data(); /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */ }
串口三中断打开配置DMA,不用cpu参与
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart->Instance == USART3)
{
HAL_UART_Receive_DMA(&huart3, (uint8_t *)&CCD, sizeof(CCD));
}
}
控制代码
************************************************************************************************************** *函数名:Vertical_turn_PD() *功能:转向环PD *形参:无 CCD小于64左转、CCD大于64右转。 yaw = z轴陀螺仪数值 *返回值:无 ***************************************************************************************************************/ int Vertical_turn_PD(u8 CCD,short yaw) { float Turn; float Bias; Bias=CCD-64; Turn=-Bias*PID.Turn_Kp-yaw*PID.Turn_Kd; return Turn; }
void Car_Task_100HZ(void) { //启动超声波模块检测 HC_SRC04_Start(); Encoder_left = Read_Encoder(1); Encoder_right = -Read_Encoder(2); //1、确定直立环PWM Balance_Pwm = Vertical_Ring_PD(OutMpu.pitch, OutMpu.gyro_x); //2、确定速度环PWM Velocity_Pwm = Vertical_speed_PI(Encoder_left,Encoder_right,OutMpu.pitch, Movement ); //3、确定转向环PWM if(FS_MODE == 0) //遥控模式 Turn_Pwm = Vertical_turn_PD(Contrl_Turn, OutMpu.gyro_z); else if(FS_MODE == 1) //蔽障模式 { if(Distence < 20) Turn_Pwm = Vertical_turn_PD(20, OutMpu.gyro_z); else Turn_Pwm = 0; } else if(FS_MODE == 2) //巡线模式 { Turn_Pwm = Vertical_turn_PD(CCD.middle, OutMpu.gyro_z); } //4、确定最终左右电机的PWM Motor1 = Balance_Pwm + Velocity_Pwm + Turn_Pwm; Motor2 = Balance_Pwm + Velocity_Pwm - Turn_Pwm; PWM_Limiting(&Motor1,&Motor2); //5、设置电机 Set_PWM(Motor1,Motor2); }
此种方法可以实现转弯寻找轨迹
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。