[ 物联网 ]拟合模型解决传感器数据获取中数据与实际值的误差的补偿方法

目录

一、前言

二、拟合函数

三、程序实现

四、结果分析


一、前言

        在物联网、嵌入式或其他电子类项目开发中,我们常常会遇到误差。这些误差无法避免的出现,且呈非线性,其在没个真实值下的误差大小可能都不一样。

        下面我举个例子:我采集到了一组电压值数据,该数据经实际测量比较与实际值有很大差距,且该误差随着实际值的增大而增大,但非线性误差。

        数据如下:

测量值实际值误差值
133926
18022545
36341552
61368572
31603310150

二、拟合函数

        面对上述示例中的传感器值,是不是头大?那么类似这样的传感器还有很多,比如电容式水位传感器等等。那么面对这种传感器,我们常规的解决办法是给他分出来区间,分别对每个区间加上其该区间的误差值,这样就可以实现由测量值到真实值的补偿效果。

        但这种方法也有弊端,比如过于麻烦和浪费事件,需要测量记录并计算,选出认为应该划分的区间,然后再在程序内实现分段补偿。

        其实面对这种情况可以用数学方法实现,如果我们能得到测量值X与实际值Y的函数模型,是不是就能对曲线上的任意一点求出其实际值。这就是数据拟合。什么是拟合?拟合并非是要完全符合我们测量值和实际值的变化曲线,而是尽可能的缩小各个点测量值和实际值的误差,因此我们使用特征点拟合出的拟合函数并非一定经过特征点。

        数据拟合的方法有很多,可以使用MATLAB等进行,同样的,我们也可以使用在线拟合工具:曲线拟合工具

        我们将特征点输入进入,可以帮助我们生成拟合图像和拟合函数。

        由示例生成的拟合函数如下(经简化后):

        Y = ((124.716155*x^2)/10000000)+(1.07897504*x)+24.97543

三、程序实现

        根据该函数,我们编程单片机程序,编程中一点要考虑芯片的计算能力,对数据做一定简化变形处理。

        我使用的基本工程是伦茨ST17H66 ADC中断采集文章中的资源工程。

        下载地址为:simpleBlePeripheral-NewTask-ADC.zip资源-CSDN文库

#include "MyTask.h"

uint8 Mytask_id;

//拟合函数
//F(x) = -0.000012471615527228155*x*x+1.078975041723953*x+24.975432160612712
#include "math.h"
__ATTR_SECTION_SRAM__ double calc_val(int mv)
{
	if(mv == 0)
		return 0;
	double part1 = -124.71615527 * (mv /100.0) * (mv / 100.0);
	double part2 = 107.89750417 * mv;
	part1 = part1 / 1000.0;
	part2 = part2 / 100.0;
	return (part1 + part2 + 24.975432);
}

adc_Cfg_t adc_cfg =
{
	.channel = ADC_BIT(ADC_CH1N_P11),
	.is_continue_mode = FALSE,// 连续模式选择,采集完一次,是否继续再次采集
	.is_differential_mode = 0x00,//全0单端、否则差分
	.is_high_resolution = 0x00,//0代表attenuation模式,1代表bypass模式
};

static void adc_evt(adc_Evt_t* pev)
{
	float val = hal_adc_value_cal(adc_cfg.channel,pev->data,pev->size,0,0);
	int mv = (int)(val *1000);
	mv = calc_val(mv);
	uint8 data[30]={'\0'};
	sprintf((char *)data,"Val= %dmv",mv);
	LOG("\n%s\n",data);
	osal_start_timerEx(Mytask_id,MyTask_StartADC_EVT,1000);//再次采集
}


void MyTask_Init( uint8 task_id ){
	//保存任务id
	Mytask_id = task_id;
	//初始化完成事件
	osal_set_event(task_id,MyTask_INIT_EVT);
	
}
static void adcMeasureTask( void )
{
	int ret;
	bool batt_mode = FALSE;
	uint8_t batt_ch = ADC_CH3P_P20;
	GPIO_Pin_e pin;
	if(FALSE == batt_mode)// 正常采样
	{
		ret = hal_adc_config_channel(adc_cfg, adc_evt);
	}
	else
	{
		if((((1 << batt_ch) & adc_cfg.channel) == 0) || (adc_cfg.is_differential_mode != 0x00))
		return;
		pin = s_pinmap[batt_ch];
		hal_gpio_cfg_analog_io(pin,Bit_DISABLE);
		hal_gpio_write(pin, 1);
		ret = hal_adc_config_channel(adc_cfg, adc_evt);
		hal_gpio_cfg_analog_io(pin,Bit_DISABLE);
	}
	if(ret)
	{
		LOG("ret = %d\n",ret);
		return;
	}
	hal_adc_start(INTERRUPT_MODE);/// 启动采样
}

uint16 MyTask_ProcessEvent( uint8 task_id, uint16 events ){
	if(events & MyTask_INIT_EVT)
	{
		adcMeasureTask();
		return events ^ MyTask_INIT_EVT;
	}
	else if(events & MyTask_StartADC_EVT)
	{
		adcMeasureTask();
		//LOG("Satrt ADC...\n");
		return events ^ MyTask_StartADC_EVT;
	}
	else if(events & MyTask_StopADC_EVT)
	{
		//LOG("Stop ADC...\n");
		return events ^ MyTask_StopADC_EVT;
	}
	return 0;
}


四、结果分析

        通过上述程序,在单片机上运行后,误差大幅缩小,经测试数据误差小于5个单位。可以说是非常准确了。

  • 9
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

西西菜鸟

打赏支持获得问题解答机会

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值