目录
一 传感器介绍
自制7路灰度传感器,由嘉立创打板,不同于普通的红外循迹模块,这个7路灰度可以用来循迹白底黑线,也可以用来寻蓝底黑线等其他环境。并且灰度传感器,相对于红外传感器干扰小,探测距离高。且不通过电位器进行电压比较,而是通过MCU用ADC采样取得接收管电压,将此电压与之前按键保存的电压相比较(前面为大概介绍,具体使用方法在后面)。
二. 传感器参数
2.1 原理介绍
7路灰度,每一路由一个发光二极管和一个光敏二极管组成。灰度传感器利用不同颜色背景对光的反射程度不同来进行颜色深浅的检测。发光二极管发射的光,照射在检测面上,检测面反射部分光线,光敏二极管检测光线的强度,光强越大,光敏二极管电阻越小,分压越大,传感器上的MCU通过检测此电压值,对其进行二值化处理。当电压大于一个值给高电平,小于一个值给低电平,用led灯来显示。
此7路灰度传感器可以 直接传7路的高低电平,或者通过串口发送相应数据--数据口/偏移量 两种。
2.2 接口介绍
UART接口和数字接口两个任意接一个就可以进行数据读取。如果用串口就不用接数字口的线。如果用数字口,就不用接UART口线。
IIC 接口是接显示屏的接口。使用过程中可以不接,显示屏只是为了观察数据。
三 传感器工作模式
两种工作模式 | |
循迹模式 | 调试设置模式 |
循迹模式下可以传输三种数据: 第一种是通过数字接口读取数字口的高 低电平; 第二种是串口通信,传输的是和数字口 一样的高低电平数据; 第三种也是串口通信,传输的数据是偏 移值数据。 (指示灯,一边接vcc,一边接单片机引脚,指示灯亮,则单片机引脚为低电平) | 调试模式有5个选项: 第一个(指示灯 1 亮起):采集场地的背 景色,把传感器的所有灯全部放在背景场地 上; 第二个(指示灯 2 亮起):采集的是线的 颜色,把传感器的所有灯全部放在线上; 第三个(指示灯 3 亮起):高低电平输出 选项,如果是指示灯 6 亮起,说明是颜色浅 (比如白色)的场地返回高电平,如果指示 灯 6 熄灭,说明颜色浅(比如白色)的场地 返回低电平,指示灯亮; 第四个(指示灯 4 亮起):串口线数据输 出类型选择,如果是指示灯 7 亮起,说明串 口线传输的是偏移值的数据,如果是指示灯 7熄灭,说明串口线传输的是数字口的高低电 平数据; 第五个(指示灯 5 亮起):(只有选择串 口线输出偏移值的数据时,这个选项才起作 用)采集线居中的值。 |
四 通信协议
4.1 串口通信格式
波特率:115200
4.2 串口通信输出格式
表一:数字量数据意义对照表
顺序 | 主机发送 (TXD) | 主机接收 (RXD) | Bit7、Bit5、Bit4、Bit3、Bit2、Bit1、Bit0 |
1 | 0x57 | idle | (Bit7 恒定为0(以定),因为7路传感器只用传7路数据) |
2 | idle | Data | 7路 |
Idle:数据线空闲,该数据线无数据传送。
Date:8 位的数据,7路传感器,只需接收一个 8 位数据即可。
(传感器发送给MCU)一个通讯周期有 1个字节(8位)
表二:偏移量数字意义对照表(偏移量为-80->80)
顺序 | 主机发送 (TXD) | 主机接收 (RXD) | Bit7,Bit6、Bit5、Bit4、Bit3、Bit2、Bit1、Bit0 |
1 | 0x57 | idle | |
2 | idle | date | 0x00:负值,0x01:正值 |
3 | idle | date | 高 8 位(16 位的数据) |
4 | idle | date | 低 8 位(16 位的数据) |
(MCU发送传感器)一个通讯周期有 3个字节(8 位),这些数据依次按位传送。
4.3 数字口输出值
数字口可以直接连接在我们单片机的 IO 口上,设置 IO 口的模式为输入模式(和按键的配置一样),读取每路传感器的输出。数字口有高电平和低电平两种电平信号。是 5V 左右的电压。低电平都是 0V。
五 传感器调试方式
5.1 调试方式
短按 K1(单击)----> 进入调试模式
指示灯 1 亮起,进入背景颜色采集项,这
时按下 K2 可以采集当前场地背景的颜色。(传
感器的所有灯放在场地的背景色上)
短按 K1(单击)
指示灯 2 亮起,进入循线颜色采集项,这
时按下 K2 可以采集当前循线的颜色。(传感器
的所有灯放在循线的颜色上)
短按 K1(单击)
指示灯 3 亮起,高低电平输出选项,这时
按下 K2 可以选择线上的电平。如果是指示灯 6
亮起,说明是颜色浅(比如白色)的场地返回
低电平,如果指示灯 6 熄灭,说明颜色浅(比
如白色)的场地返回高电平。
短按 K1(单击)
指示灯 4 亮起,串口输出数据类型选项,
这时按下 K2 可以选择串口输出数据类型。如果
是指示灯 7 亮起,说明串口线传输的是偏移值
的数据,如果是指示灯 7 熄灭,说明串口线传
输的是数字口的高低电平数据。
短按 K1(单击)
指示灯 5 亮起,偏移值校准选项(只有选
择串口线输出偏移值的数据时,这个选项才起
作用),这时按下 K2 可以采集线居中的位置的
值。(红色指示灯闪烁说明采集有效
短按 K1(单击)----> 进入循迹模式
(具体调试细节可以接 OLED 查看具体数据)
5.2 断电后怎么保存数据
当调试模式完整结束一次(也就是K1按键 按下,OLED重新显示循迹数据的时候),传感器上的单片机会将 传感器取到的背景色上7路传感器的电压模拟值,循迹颜色上的电压模拟值,串口输出数据的选择模式(偏移量/数字),偏移量模式下测得的值保存在内部flash。
当我们按下复位键或者重新断电重启,传感器单片机上数据按理是会复位,而我们在循迹模式下 按下K2按键,这时来读取之前提前保存在flash的数据,这样就可以重新调用上次调试完后的结果值。
六 制作过程中遇到的问题
- (我写传感器程序时)单片机将要数组中保存的数据写入到FLASH中,必须保证你要赋值的值已经全赋值到数组中,不然就会导致flash中数据不是你想要的,说白了就是赋值和保存操作要注意顺序
- (我们在使用传感器的时候要注意)在调试的时候,必须按下6次K1, 保证从调试模式到完全退出调试模式到循迹模式.
- 串口通信的波特率为115200,并且自己的MCU要发0x38给传感器,传感器才会往自己的MCU传数据
- 再用洞洞板测试的时候,因为将两个按键的引脚接线焊到一起了,导致按键怎么都不好使
- 传感器最后循迹使用的数据是 将ADC采样的原始数据,通过一级低通滤波再通过平均中值滤波处理数据,在将此数据/4095*3.3 转换为电压值。(抛弃卡尔曼滤波是因为卡尔曼滤波滞后比较严重,并且7路使用卡尔曼滤波最后导致数据还是比较飘)
- 开始pcb打板到了后,最左边的灰度对管在OLED上显示的数据一直都是4095(一直分压很大),后面发现是GND没有焊好,导致断路了
- 相关算法介绍
7.1 卡尔曼滤波
算法的核心思想就是:根据当前的仪器”测量值”和”误差”。计算当前的最优解
接下来了解一个卡尔曼滤波中几个参数的含义:概率,随即变量,高斯或正态分配还有State-space Model 等等
R: 测量噪声协方差,由于V_{K}(测量噪声)不可测,所以假设其符合正态分布
Q:过程噪声协方差。
R值固定,Q值越大,代表越信任测量值,Q值无穷大,代表只用测量值
Q值越小,代表越信任模型预测值,Q值为0,代表只用模型预测值
Q控制误差,R控制响应速度
Q:过程噪声,Q增大,动态响应变快,收敛稳定性变坏
R:测量噪声,R增大,动态变慢,收敛稳定性变好
Float KalmanFiter(float inData) {
Static float prevData = 0;//上一个数据
//q控制误差,r控制响应速度
Static float p = 10,q = 0.001,r = 0.001,kGain = 0;
p= p + q;
kGain = p / (p+r)//计算卡尔曼增益
inData = prevData + (kGain * (inData - prevData));//计算本次滤波估计值
P = (1-kGain)*p;//更新测量方差
prevData = inData;
Return inData;
}
7.2 卡尔曼滤波加一阶低通滤波
//第一. ADC取样速率和单片机,晶振。
//第二 算法问题 参数
uint16_t kalman_filter(uint16_t ADC_Value_hh) {
float x_k1_k1,x_k_k1;
//static float ADC_OLD_Value;
float Z_k;
static float P_k1_k1;
// static float Q = 0.0001;//测量均方差
// static floa估计均方差
// static float Kg = 0;
// static float P_k_k1 = t R = 5;//1;
static float Q = 0.0001;//测量均方差
static float R = 5;//估计均方差
static float Kg = 0;
static float P_k_k1 = 1;
//uint32_t kalman_adc;
static float kalman_adc_old = 0;
float kalman_adc;
uint16_t kal;
Z_k = ADC_Value_hh;
//采样值和优化值相差大于10,一阶滤波
//小于该值采用卡尔曼滤波算法
if(abs(kalman_adc_old - ADC_Value_hh)>=10) {
x_k1_k1 = ADC_Value_hh*0.382 + kalman_adc_old * 0.618;
}else {
x_k1_k1 = kalman_adc_old;
}
//新加
//x_k1_k1 = kalman_adc_old;
x_k_k1= x_k1_k1;
P_k_k1 = P_k1_k1 + Q;
Kg = P_k_k1/(P_k_k1 + R);//计算卡尔曼增益
//计算本次滤波估计值
kalman_adc = x_k_k1 + Kg * (Z_k - kalman_adc_old);
//更新测量方差
P_k1_k1 = (1 - Kg)*P_k_k1;
P_k_k1 = P_k1_k1;
//ADC_OLD_Value有啥用
//ADC_OLD_Value = ADC_Value_hh;
kalman_adc_old = kalman_adc;
kal = (uint16_t)(kalman_adc*10)%10;
if(kal>=5)
kalman_adc++;
return (uint16_t)kalman_adc;
// return get_int_num(kalman_adc); 原代码
}
7.3 中值滤波+一阶滞后滤波
//一阶滞后滤波
#define a 0.05
uint16_t value;
float value_yijie;
uint16_t filter_yijie(int num){
uint16_t new_value;
new_value = AD_Value[num];
value_yijie = ((1-a)*value + a*new_value);
value = new_value;
return (uint16_t)value_yijie;
}
//中值滤波
//采样若干次,舍弃较大数据和较小数据,然后将中间值平均
#define N 300//采样次数
#define BAN 100 //舍弃数据
uint16_t GetADC1Average(int num)
{
uint16_t AdcValueBuf[N] = {0},AdcValuetemp=0;
unsigned int count=0,i=0,j=0;
uint32_t sum = 0;
for(count=0;count<N;count++)
AdcValueBuf[count]=AD_Value[num];
//冒泡排序
for(j=0;j<N-1;j++){
for(i=0;i<N-j;i++){
if(AdcValueBuf[i] > AdcValueBuf[i+1]){
AdcValuetemp = AdcValueBuf[i];
AdcValueBuf[i] = AdcValueBuf[i+1];
AdcValueBuf[i+1] = AdcValuetemp;
}
}
}
for(count=BAN;count<N-BAN;count++)
sum+=AdcValueBuf[count];
return ((unsigned int)((float)sum/(N-BAN*2)));
}
七.总结
7路智能灰度传感器这个全过程程序都是自制的,大家需要代码可以去下面分享的gitee下面下载原程序。文章或者开源资料中出现问题,大家可以多给给建议,我这边传感器是已经测试可用,大家要是要自己制作,可以使用gitee上分享的文件打开立创,直接打印即可。
八. 链接
1. gitee链接
2. 淘宝购买对管链接
https://item.taobao.com/item.htm?_u=r20cn5s87i333a&id=569390946281&spm=a1z09.2.0.0.31912e8deJi1VW