当前位置:   article > 正文

树莓派ROS stm32 slam Freertos VFH+A*避障路径规划-智能平衡计划(六)_可搭载slam的stm32

可搭载slam的stm32


前言

本章讲的是寻线功能,采用线性CCD摄像头实现


一、巡线功能

在这里插入图片描述

TSL1401 线性CCD介绍

线性CCD
我们常说的摄像头 CCD 模块通常使用的是面阵 CCD 芯片,一般以 OV 系列面阵 CCD 最为常用。而 TSL1401 属于线性 CCD,也可以叫做线阵 CCD。与面阵 CCD 相比,线性 CCD 最明显的特点就是其只能采集一行的可视像素。

在这里插入图片描述
在这里插入图片描述

TSL1401 管脚功能

在这里插入图片描述
在这里插入图片描述

TSL1401 功能描述

TSL1401 线性 CCD 传感器包含 128 个线性排列的光电二极管。每个光电二极管都有各自的积分电路,以下我们将此电路统称为像素。每个像素所采集的图像灰度值与它所感知的光强和积分时间成正比。以上名词所对应的物理量纲为:
灰度值:输出电压 V out ;
光强:器件对给定光波长在电压上的反应 V e ;(V e =V/(μJ/cm2)* μW/cm2)
积分时间:即曝光时间,t int 。

在这里插入图片描述

TSL1401 像素输出

128 个像素是怎么进行采集并输出的呢,这就用到了 SI 和 CLK 信号。根据下图可以简单的了解它们的功能。在 128 个像素之外,还有一个开关逻辑控制和移位寄存器电路。SI 通过该电路,控制每一个像素的积分和复位操作;CLK 通过该电路控制每一个像素电压的依次输出。

在这里插入图片描述

TSL1401 像素采集

在这里插入图片描述
首先利用一个 SI 周期来曝光CCD,紧接着在下一个 SI 周期对 128 个像素进行依次采集(CLK的下降沿采集数据)。在前一个 SI 周期内,必须输出129 个 CLK 以丢弃没用的 AO 输出,曝光结束后的第二个 SI 周期内采集的 AO 输出才是正确的。
该方法需要在两个 SI 信号之间添加延时函数来控制曝光时间。缺点是占用 CPU 运行时间来进行曝光,即在曝光期间内 CPU 只能处于等待状态。

TSL1401 线性CCD 电气原理图

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

二、Start代码实现

1.线性CCD代码

代码如下(示例):

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 */

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188

串口三中断打开配置DMA,不用cpu参与
在这里插入图片描述

2.中断接收代码

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	if(huart->Instance == USART3)
	{
		HAL_UART_Receive_DMA(&huart3, (uint8_t *)&CCD, sizeof(CCD));
	}

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

控制代码

**************************************************************************************************************
*函数名: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;
}



  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

3.task.c

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);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43

在这里插入图片描述

总结

此种方法可以实现转弯寻找轨迹

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/秋刀鱼在做梦/article/detail/929433
推荐阅读
相关标签
  

闽ICP备14008679号