GP2D12红外测距传感器使用介绍
一、“看到”障碍物的眼睛——红外测距传感器
机器视觉中最接近人眼的莫过于摄像头了,可图像处理小车的“大脑”对付不了,至少目前的“大脑”能力不够,等进化后也许能够应付。
为了能“看到”障碍物,小车目前能用的主要是各类测距传感器,典型的有超声波和红外两种,此外还有利用光线的反射强弱来判断的,这种方式不具备“测距”功能,但可以判断有无!因为不同物体表面及颜色反射的能力不同(看后面的数据)。
本文主要讨论的是机器人中最常用的红外测距传感器 —— GP2D12。
首先认识一下:
模块共三个接口:红线---VCC_5V;黑线---GND;黄线—PC5(ADC数据采样)
将模块通电,黄线接到ADC通道输入端即可工作;将采样得到的电压值通过填入表格,进行线性化处理,得到线性化公式。通过公式,可将ADC采样值转化为距离值。实测,在10cm—30cm范围内,较为准确,最大误差在1cm。模块对被测角度的灵敏度很高,同一位置,不同的角度,误差可以超过5cm,最好的测量角度是90度。
之所以选择 GP2D12 红外测距传感器,理由如下:
首先是因为在机器人活动中(不包含工业机器人)这个传感器最常用,几乎每家国外的机器人配件供应商都提供。使用英文版搜索一下 “MiniSumo”,你将会发现 GP2D12使用是多么普遍。
其二是因为它的测距范围和小车的“个头”及运动速度匹配,对于 10cm 见方、运动速度10 – 30cm/s 的小个头,能“看到”几米开外的东西意义不大,而 10 – 80cm 正是它所要关注的范围。
GP2D12的工作原理我理解为(仅供参考,欢迎指正):
它是由一个红外发射管和一个 PSD(Position Sensing Device位置敏感检测装置)以及相应的计算电路构成,Sharp 公司的 PSD 很有特色,它可以检测到光点落在它上面的微小位移, 分辨率达微米,GP2D12 正是利用这个特性实现了几何方式测距。
红外发射管发出的光束,遇到障碍物反射回来,落在 PSD 上,构成了一个等腰三角形, 借助于 PSD 可以测得三角形的底,而两个底角是固定的,由发射管确定,此时便可通过底边 推算出高,也就是我们所要的距离。如下图所示:
测量原理示意图
从图中可以看出,这是一个顶角极锐的等腰三角形,底边只有2cm ,高却要有10 – 80cm,所以 PSD 的分辨率必须极高,否则微小的偏差都会带来距离的巨大误差。从这一点也可以得出,它的测距结果很难稳定、精确,毕竟比值太大。
因为 PSD 的尺寸有限,从图中就很容易理解为何它的测量距离超出范围后就不可能是有效数据,连趋势都得不到。
从上述原理描述还可以知道,它不是连续测量,得到底边长度后,必须经过计算才能得到距离值,然后转换为模拟信号输出。
这两个推论在那篇“Sharp GP2D12 applicationNote”(应用指南)有所印证,具体表现为它测距的强指向性和输出的不确定性(噪音高达 200mV,相对于 2.4V 的满量程输出而言达5%)。 这篇文章好像是国外一个爱好者写的,他做了大量的测试,对使用者掌握GP2D12 的性能及合理的使用它极有帮助。
总有人问 GP2D12 是否能用于某些场合?如果能仔细吃透上述指标,自然会有答案。 还有人问它与超声波传感器那个好,我想这些指标也会告诉你! 至于更详细的内容,读者可进一步阅读GP2D12的数据手册以及上面介绍的应用指南, 在此我就不再赘述。
在硬件上,没有太多的难度,但是要用好GP2D12,软件上似乎要做些努力,必须解决的有两个问题:
一是信号的线性化 ,因为输出与距离的关系是非线性的,为便于程序中使用距离信息,必须将模拟信号转换为相应得距离值。
二是滤波 ,因为按照上述应用指南的测量分析,GP2D12的输出噪声很大;此外,还由于测量的非连续性,导致连续的距离变化对应的输出为阶跃信号,也需要通过滤波将其平滑。
2.1 线性化
关于线性化,开始时我也一筹莫展,曾想用折线近似实现,但尝试后觉得代码量太大,而且需要做大量数据采集。
后来在acroname网站上(二年前),发现了一个极好的“东东” —个用 Excel 制作的电子表,表格的格式如下:
gp2d12-使用表格(本文附件可下载)
里面有作者根据 GP2D12 特性建立的数学模型(线性化公式),并预留的使用者输入参数的地方,只需按其要求填入:
AD 的位数、AD 供电电压(满量程),并采集 8 点(10cm 间隔)GP2D12 的输出电压, 填入表中,它就可自动生成线性化公式的参数 ,提供了整形和浮点两种格式,还附有由此产生的结果与实际的偏差表,并用生动的图形表示,十分直观、实用。
此表可在embedream的相关资料中下载,本该提供它的原始链接,无奈现在没有了,只找到了一个类似的文档 — Sharp IR Range Finder Voltage-to-Range Conversion Article 内容也是讨论线性化的,读者不妨一读。配合此文也许更容易理解使用那张Excel 表格。
根据上述公式及程序得到的结果如下:
GP2D12 不同颜色测距结果对比
第一列为实际距离,第二列障碍物表面为白纸,第三列障碍物为褐色木盒,读者可比照Excel 表中的数据,可以看出基本吻合。同时还可以从上面数据中看出,GP2D12 确实如其手册中所说,基本不受障碍物的颜色影响。
2.2 滤波
滤波主要解决两个问题,一是在GP2D12恒定输出阶段,按应用指南的分析,有不小的噪声,需要通过滤波消除。
二是由于其非连续测量的特性,导致其测量连续变化的距离时,输出是阶跃形式的,这对程序判断极为不利,为了弱化这个影响,也期望通过滤波实现。
根据 GP2D12 的手册,其测量周期为 40ms 左右(38ms),综合小车单片机的内存及处理需求,采用 5ms 采样一次,取最近 8 次的结果平均值的滤波方式,也就是说,一个测量周期采 8 个数据平均。
这样处理可以降低噪声的影响,这点容易理解。至于弱化阶跃信号,不知读者是否认同?
我是这样考虑:在出现阶跃信号时,8 个数据中随着时间推移,新的信号所占的权重不断加大,使得信号逐渐从前一个信号平缓的过渡到新的信号上。但是这样处理,导致了距离信号反映滞后,要到下一个信号快到时,本次的输出才接近本次的信号。就这一点而言,似 乎有些不尽合理,有待读者深入探讨。
红外测试数据: 10cm 11.55--11.66
15cm 16.15--16.38
16.5cm 17.46--17.92
18cm 20.01--20.13
19cm 19.44--19.81
20cm 20.01--20.15
22cm 21.56--21.82
23cm 23.20--23.66
24.5cm 23.96--24.23
28cm 27.39--27.93
stm32单片机红外测距源程序如下:
- /******************** (C) COPYRIGHT 2007 STMicroelectronics ********************
- * File Name : main.c
- * Author : MCD Application Team
- * Version : V1.0
- * Date : 10/08
- * Description : Main program body
- ********************************************************************************
- * THE PRESENT SOFTWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
- * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE TIME.
- * AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY DIRECT,
- * INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING FROM THE
- * CONTENT OF SUCH SOFTWARE AND/OR THE USE MADE BY CUSTOMERS OF THE CODING
- * INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
- *******************************************************************************/
- /* Includes ------------------------------------------------------------------*/
- #include "stm32f10x_lib.h"
- #include "stdio.h"
- /* Private macro -------------------------------------------------------------*/
- #define countof(a) (sizeof(a) / sizeof(*(a)))
- /* Private typedef -----------------------------------------------------------*/
- #define TxBufferSize (countof(TxBuffer) - 1)
- /* Private define ------------------------------------------------------------*/
- u8 TxBuffer[] = "\n\rADC Example1: ADC TO DMA TO UART1\n\r";
- u8 TxCounter = 0;
- /* Private define ------------------------------------------------------------*/
- #define ADC1_DR_Address ((u32)0x4001244C)
- /* Private variables ---------------------------------------------------------*/
- USART_InitTypeDef USART_InitStructure;
- ADC_InitTypeDef ADC_InitStructure;
- DMA_InitTypeDef DMA_InitStructure;
- vu16 ADC_ConvertedValue;
- ErrorStatus HSEStartUpStatus;
-
- /* Private function prototypes -----------------------------------------------*/
- void RCC_Configuration(void);
- void GPIO_Configuration(void);
- void NVIC_Configuration(void);
-
- /* Private functions ---------------------------------------------------------*/
- void Delay_us(unsigned short us)
- {
- unsigned short i;
- while(us--)
- {
- for(i=0;i<10;i++);
- }
- }
- void Delay_ms(unsigned short ms)
- {
- unsigned short i;
- while(ms--)
- {
- for(i=0;i<10000;i++);
- }
- }
- //发送数据
- int fputc(int ch, FILE *f){
- USART_SendData(USART1,(unsigned char)ch);//USART1可以换成USART2等
- while(!(USART1->SR&USART_FLAG_TXE));
- return(ch);
- }
- //接收数据
- int GetKey(void){
- while(!(USART1->SR&USART_FLAG_RXNE));
- return((int)(USART1->DR&0x1FF));
- }
- /*******************************************************************************
- * Function Name : main
- * Description : Main program
- * Input : None
- * Output : None
- * Return : None
- *******************************************************************************/
- int main(void)
- {
- //unsigned char i=1;
- unsigned long Tmp_Dat,i=1;
- float distance,sum=0;
- #ifdef DEBUG
- debug();
- #endif
- /* System clocks configuration ---------------------------------------------*/
- RCC_Configuration();
- /* NVIC configuration ------------------------------------------------------*/
- NVIC_Configuration();
- /* GPIO configuration ------------------------------------------------------*/
- GPIO_Configuration();
- /* USART1 configuration ------------------------------------------------------*/
- /* USART1 configured as follow:
- - BaudRate = 9600 baud
- - Word Length = 8 Bits
- - Two Stop Bit
- - Odd parity
- - Hardware flow control disabled (RTS and CTS signals)
- - Receive and transmit enabled
- - USART Clock disabled
- - USART CPOL: Clock is active low
- - USART CPHA: Data is captured on the second edge
- - USART LastBit: The clock pulse of the last data bit is not output to
- the SCLK pin
- */
- USART_InitStructure.USART_BaudRate = 9600;
- USART_InitStructure.USART_WordLength = USART_WordLength_8b;
- USART_InitStructure.USART_StopBits = USART_StopBits_1;
- USART_InitStructure.USART_Parity = USART_Parity_No;
- USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
- USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
- USART_InitStructure.USART_Clock = USART_Clock_Disable;
- USART_InitStructure.USART_CPOL = USART_CPOL_Low;
- USART_InitStructure.USART_CPHA = USART_CPHA_2Edge;
- USART_InitStructure.USART_LastBit = USART_LastBit_Disable;
- /* Configure the USART1 */
- USART_Init(USART1, &USART_InitStructure);
- /* Enable the USART Transmoit interrupt: this interrupt is generated when the
- USART1 transmit data register is empty */
- USART_ITConfig(USART1, USART_IT_TXE, ENABLE);
- /* Enable the USART Receive interrupt: this interrupt is generated when the
- USART1 receive data register is not empty */
- USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
- /* Enable USART1 */
- USART_Cmd(USART1, ENABLE);
- /* DMA channel1 configuration ----------------------------------------------*/
- DMA_DeInit(DMA_Channel1);
- DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address;
- DMA_InitStructure.DMA_MemoryBaseAddr = (u32)&ADC_ConvertedValue;
- DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
- DMA_InitStructure.DMA_BufferSize = 1;
- DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
- DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable;
- DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
- DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
- DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
- DMA_InitStructure.DMA_Priority = DMA_Priority_High;
- DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
- DMA_Init(DMA_Channel1, &DMA_InitStructure);
-
- /* Enable DMA channel1 */
- DMA_Cmd(DMA_Channel1, ENABLE);
-
- /* ADC1 configuration ------------------------------------------------------*/
- ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
- ADC_InitStructure.ADC_ScanConvMode = ENABLE;
- ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
- ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
- ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
- ADC_InitStructure.ADC_NbrOfChannel = 1;
- ADC_Init(ADC1, &ADC_InitStructure);
- /* ADC1 regular channel1 configuration */
- ADC_RegularChannelConfig(ADC1, ADC_Channel_15, 1, ADC_SampleTime_55Cycles5);
- /* Enable ADC1 DMA */
- ADC_DMACmd(ADC1, ENABLE);
-
- /* Enable ADC1 */
- ADC_Cmd(ADC1, ENABLE);
- /* Enable ADC1 reset calibaration register */
- ADC_ResetCalibration(ADC1);
- /* Check the end of ADC1 reset calibration register */
- while(ADC_GetResetCalibrationStatus(ADC1));
- /* Start ADC1 calibaration */
- ADC_StartCalibration(ADC1);
- /* Check the end of ADC1 calibration */
- while(ADC_GetCalibrationStatus(ADC1));
-
- /* Start ADC1 Software Conversion */
- ADC_SoftwareStartConvCmd(ADC1, ENABLE);
- for(i=0;i<TxBufferSize;i++)
- {
- /* Write one byte to the transmit data register */
- USART_SendData(USART1, TxBuffer[TxCounter++]);
- while (!(USART1->SR & USART_FLAG_TXE)); //等待缓冲区空
- while (!(USART1->SR & USART_FLAG_TC)); //等待发送完成
- }
- while (1)
- {
- Delay_ms(1000);
-
- Tmp_Dat = ADC_ConvertedValue;
- distance = (1/(Tmp_Dat*0.0000228324+0.00140335))-4.0;
- printf("\n%ld\n",Tmp_Dat);
- printf("%.2f",distance);
-
-
- Tmp_Dat = Tmp_Dat*3300/0x0fff;
- TxBuffer[0] = Tmp_Dat/1000+'0';
- TxBuffer[1] = '.';
- TxBuffer[2] = (Tmp_Dat%1000)/100+'0';
- TxBuffer[3] = (Tmp_Dat%100)/10+'0';
- TxBuffer[4] = Tmp_Dat%10+'0';
- TxBuffer[5] = 'V';
- USART_SendData(USART1, '[');
- while (!(USART1->SR & USART_FLAG_TXE)); //等待缓冲区空
- while (!(USART1->SR & USART_FLAG_TC)); //等待发送完成
- USART_SendData(USART1, TxBuffer[0]);
- while (!(USART1->SR & USART_FLAG_TXE)); //等待缓冲区空
- while (!(USART1->SR & USART_FLAG_TC)); //等待发送完成
- USART_SendData(USART1, TxBuffer[1]);
- while (!(USART1->SR & USART_FLAG_TXE)); //等待缓冲区空
- while (!(USART1->SR & USART_FLAG_TC)); //等待发送完成
- USART_SendData(USART1, TxBuffer[2]);
- while (!(USART1->SR & USART_FLAG_TXE)); //等待缓冲区空
- while (!(USART1->SR & USART_FLAG_TC)); //等待发送完成
- USART_SendData(USART1, TxBuffer[3]);
- while (!(USART1->SR & USART_FLAG_TXE)); //等待缓冲区空
- while (!(USART1->SR & USART_FLAG_TC)); //等待发送完成
- USART_SendData(USART1, TxBuffer[4]);
- while (!(USART1->SR & USART_FLAG_TXE)); //等待缓冲区空
- while (!(USART1->SR & USART_FLAG_TC)); //等待发送完成
- USART_SendData(USART1, TxBuffer[5]);
- while (!(USART1->SR & USART_FLAG_TXE)); //等待缓冲区空
- while (!(USART1->SR & USART_FLAG_TC)); //等待发送完成
- USART_SendData(USART1, ']');
- while (!(USART1->SR & USART_FLAG_TXE)); //等待缓冲区空
- while (!(USART1->SR & USART_FLAG_TC)); //等待发送完成
- printf("\n");
- }
- }
- /*******************************************************************************
- * Function Name : RCC_Configuration
- * Description : Configures the different system clocks.
- * Input : None
- * Output : None
- * Return : None
- *******************************************************************************/
- void RCC_Configuration(void)
- {
- /* RCC system reset(for debug purpose) */
- RCC_DeInit();
- /* Enable HSE */
- RCC_HSEConfig(RCC_HSE_ON);
- /* Wait till HSE is ready */
- HSEStartUpStatus = RCC_WaitForHSEStartUp();
- if(HSEStartUpStatus == SUCCESS)
- {
- /* Enable Prefetch Buffer */
- FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
- /* Flash 2 wait state */
- FLASH_SetLatency(FLASH_Latency_2);
-
- /* HCLK = SYSCLK */
- RCC_HCLKConfig(RCC_SYSCLK_Div1);
-
- /* PCLK2 = HCLK */
- RCC_PCLK2Config(RCC_HCLK_Div1);
- /* PCLK1 = HCLK/2 */
- RCC_PCLK1Config(RCC_HCLK_Div2);
- /* ADCCLK = PCLK2/4 */
- RCC_ADCCLKConfig(RCC_PCLK2_Div4);
-
- /* PLLCLK = 8MHz * 7 = 56 MHz */
- RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_7);
- /* Enable PLL */
- RCC_PLLCmd(ENABLE);
- /* Wait till PLL is ready */
- while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET)
- {
- }
- /* Select PLL as system clock source */
- RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
- /* Wait till PLL is used as system clock source */
- while(RCC_GetSYSCLKSource() != 0x08)
- {
- }
- }
- /* Enable peripheral clocks --------------------------------------------------*/
- /* Enable DMA clock */
- RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA, ENABLE);
- /* Enable ADC1 and GPIOC clock */
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_GPIOC, ENABLE);
- /* Enable GPIOA and USART1 clocks */
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1, ENABLE);
- }
- /*******************************************************************************
- * Function Name : GPIO_Configuration
- * Description : Configures the different GPIO ports.
- * Input : None
- * Output : None
- * Return : None
- *******************************************************************************/
- void GPIO_Configuration(void)
- {
- GPIO_InitTypeDef GPIO_InitStructure;
- /* Configure PC.05 (ADC Channel15) as analog input -------------------------*/
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
- GPIO_Init(GPIOC, &GPIO_InitStructure);
- /* Configure USART1 Tx (PA.09) as alternate function push-pull */
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
- GPIO_Init(GPIOA, &GPIO_InitStructure);
- /* Configure USART1 Rx (PA.10) as input floating */
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
- GPIO_Init(GPIOA, &GPIO_InitStructure);
- }
- /*******************************************************************************
- * Function Name : NVIC_Configuration
- * Description : Configures Vector Table base location.
- * Input : None
- * Output : None
- * Return : None
- *******************************************************************************/
- void NVIC_Configuration(void)
- ……………………
- …………限于本文篇幅 余下代码请从51黑下载附件…………
复制代码
所有资料51hei提供下载:
红外模块.zip
(2.25 MB, 下载次数: 605)
|