温度嵌入式软件算法补偿方案及步骤

目录

前言

一、测试方法

测试结果

二、温度算法补偿方案

三、算法选型

一阶线性补偿

分段一阶线性补偿

四、模型验证

​编辑

嵌入式端落地

注意事项

总结

前言

最近在使用环境温度、体温温度检测的传感器,但是遇到了温度不准确的问题,查了下资料,误差主要是来源于传感器的一致性、传感器自身温度和芯片本身读取的误差,为了消除这些误差,于是便有了软件算法补偿,让其接近真实的温度值,以下便是我的补偿方案

一、测试方法

测试结果

我用到的温湿度传感器是sht45及gy906,这两款传感器的理论误差是在±0.1℃,打印出来的温湿度也和理论值差的不多,大概是在1℃以内

二、温度算法补偿方案

我们现在用的体温传感器利用红外线 向目标物体发射电磁辐射,然后快速测量被测目标的温度。电磁辐射通过一个双电荷制 电磁感应 元件(DPP)进入传感器,该元件检测红外能量,并产生一个电荷 ,该电荷随后被放大并转换为数字输出。除此之外,还集成了一些其他的芯片和组件,如温度补偿电路、电源调节器、 EEPROM等,能够提供更加准确和稳定的测量结果,其硬件精度的上限为±0.1℃。所以太复杂精确的算法无意义。

下面是我的温度补偿的算法流程图,主要用的是

三、算法选型

一阶线性补偿

核心公式

说明:

  • Ycomp:补偿后的物体温度;

  • Yraw:GY906 读取的原始物体温度;

  • Tenv:GY906 的环境温度(芯片自身温度,通过 MLX90614 的 TA 寄存器读取);

  • Tref:参考温度(推荐 25℃,即常温标定基准);

  • k:温度系数(拟合得到,代表每℃温度变化带来的误差);

  • b:固定偏移量(拟合得到,补偿常温下的系统误差)。

优点:代码量少、MCU 计算耗时 < 1ms,完全适配低算力平台;

分段一阶线性补偿

如果你的使用温区跨度极大(如 - 40~100℃),且精度要求 ±0.3℃以内,可把温区划分为 2~3 段,每段单独拟合一阶系数。

  • 分段建议(针对 GY906):

    • 第一段:-40~0℃(低温区);

    • 第二段:0~50℃(常温区);

    • 第三段:50~100℃(高温区);

  • 核心逻辑:根据当前Tenv判断所属区间,调用对应区间的k和b计算补偿值。

以上两种方案都可以使用

四、模型验证

我选择的是一阶线性补偿算法,用Pycharm建模进行曲线绘制,具体代码如下:

python 复制代码
import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression
from sklearn.preprocessing import PolynomialFeatures
from sklearn.metrics import r2_score

# 设置中文字体(避免中文乱码,适配UI展示)
plt.rcParams['font.sans-serif'] = ['SimHei']  # 黑体
plt.rcParams['axes.unicode_minus'] = False    # 解决负号显示问题
plt.rcParams['figure.figsize'] = (12, 8)      # UI画布大小(适配屏幕展示)
plt.rcParams['font.size'] = 10                # 全局字体大小

# ===================== 1. 数据准备 =====================
# 传感器原始读数 & 标准温度计实际温度(可替换为你的真实标定数据)
raw_temp = np.array([20.5, 23.4, 25.4, 27.6, 30.5, 35.5]).reshape(-1, 1)
real_temp = np.array([20.1, 23.1, 25.2, 27.5, 30.5, 35.2])

# ===================== 2. 线性回归拟合(一阶补偿) =====================
model = LinearRegression()
model.fit(raw_temp, real_temp)

# 提取补偿系数(对应公式 y = K*x + B)
K = model.coef_[0]       # 斜率(补偿系数a)
B = model.intercept_     # 截距(补偿系数b)
compensated_temp = model.predict(raw_temp)  # 补偿后温度
residuals = compensated_temp - real_temp    # 残差(补偿后与实际值的差值)
r2 = r2_score(real_temp, compensated_temp)  # 拟合优度(越接近1拟合效果越好)

# ===================== 3. 生成UI级可视化曲线 =====================
# 创建2个子图:上半部分是拟合曲线,下半部分是残差分析
fig, (ax1, ax2) = plt.subplots(2, 1, gridspec_kw={'height_ratios': [3, 1]})

# ---- 子图1:原始数据 + 线性补偿拟合线 ----
# 绘制原始数据散点(UI级样式:大圆点、带边框)
ax1.scatter(raw_temp, real_temp, color='#2E86AB', s=80, edgecolors='#1A5276',
            label='标定原始数据', alpha=0.8)
# 绘制线性补偿拟合线(UI级样式:红色实线、加粗)
ax1.plot(raw_temp, compensated_temp, color='#E74C3C', linewidth=2.5,
         label=f'一阶线性补偿线 (y = {K:.4f}x + {B:.4f})')
# 添加完美拟合参考线(y=x,灰色虚线)
ax1.plot(raw_temp, raw_temp, color='#95A5A6', linestyle='--', linewidth=1.5,
         label='理想拟合线 (y=x)')

# 子图1样式美化(UI级)
ax1.set_title('GY906温度补偿 - 一阶线性拟合', fontsize=14, fontweight='bold', pad=20)
ax1.set_xlabel('传感器原始读数 (℃)', fontsize=11)
ax1.set_ylabel('标准实际温度 (℃)', fontsize=11)
ax1.legend(loc='best', frameon=True, shadow=True)
ax1.grid(True, alpha=0.3, linestyle='-')
ax1.set_xlim(raw_temp.min()-1, raw_temp.max()+1)  # x轴范围适配数据
ax1.set_ylim(real_temp.min()-1, real_temp.max()+1)  # y轴范围适配数据

# 标注关键信息(补偿系数、拟合优度)
ax1.text(0.02, 0.95, f'补偿系数 K = {K:.4f}\n截距 B = {B:.4f}\n拟合优度 R² = {r2:.6f}',
         transform=ax1.transAxes, fontsize=10, verticalalignment='top',
         bbox=dict(boxstyle='round,pad=0.5', facecolor='#F8F9FA', edgecolor='#DDDDDD'))

# ---- 子图2:残差分析(评估补偿效果) ----
# 绘制残差点(UI级样式:橙色圆点)
ax2.scatter(raw_temp, residuals, color='#F1C40F', s=60, edgecolors='#D35400', alpha=0.8)
# 绘制残差零线(灰色虚线)
ax2.axhline(y=0, color='#95A5A6', linestyle='--', linewidth=1.5)
# 子图2样式美化
ax2.set_title('残差分析(补偿后温度 - 实际温度)', fontsize=11, pad=6)
ax2.set_xlabel('传感器原始读数 (℃)', fontsize=15)
ax2.set_ylabel('残差 (℃)', fontsize=11)
ax2.grid(True, alpha=0.3, linestyle='-')
ax2.set_xlim(raw_temp.min()-1, raw_temp.max()+1)

# 整体布局调整(避免子图重叠,适配UI展示)
plt.tight_layout()
plt.subplots_adjust(hspace=0.2)

# 保存UI曲线(可选,生成高清图片)
# plt.savefig('GY906温度补偿拟合曲线.png', dpi=300, bbox_inches='tight')

# 显示UI曲线
plt.show()

# ===================== 4. 输出关键拟合信息 =====================
print("="*50)
print("一阶线性补偿参数(对应公式:补偿后温度 = K×原始读数 + B)")
print(f"补偿系数 K (斜率): {K:.6f}")
print(f"补偿截距 B (截距): {B:.6f}")
print(f"拟合优度 R²: {r2:.6f}(越接近1拟合效果越好)")
print(f"残差(补偿后-实际值): {np.round(residuals, 6)}")
print("="*50)

通过调整原始读数和温度计的实际温度读数进行绘图

运行代码就会得到一幅图,根据一阶线性回归进行建模,从而得出K(补偿系数)和B(固定偏移量),三条曲线越接近重合,说明补偿的效果越好,也越接近真实值

嵌入式端落地

在得到接近1的拟合度后将这个线性补偿模型集成到 STM32

python 复制代码
// GY906一阶线性温度补偿(适用于20--36℃温区)
float compensate_temperature(float raw_temp) {
    const float K = 1.0111f;  // 补偿系数
    const float B = -0.5187f; // 补偿截距
    return K * raw_temp + B;
}

注意事项

  • 采集的数据一定要是梯度的采集,不能是很接近的数据,否则导致线性拟合失去意义,例如:自变量(原始读数)无梯度raw_temp 仅在 27.4℃~27.6℃ 之间波动,极差仅 0.2℃;因变量(实际温度)无变化real_temp 仅在 27.1℃~27.2℃ 之间波动,极差仅 0.1℃。

总结

本文针对红外温度传感器(GY906)的测量误差问题,提出了软件算法补偿方案。通过测试发现传感器误差在±0.1℃范围内,采用一阶线性补偿算法进行温度校正,核心公式为Ycomp=K×Yraw+B。使用Python建模验证显示拟合优度接近1,残差分析良好。最终将补偿系数K=1.0111和截距B=-0.5187集成到STM32嵌入式平台,实现快速(<1ms)且精确的温度补偿,有效提升测量准确性。该方案代码量少,适合低算力平台应用。

相关推荐
3壹1 小时前
蓝桥杯-STM32CubeMX快速上手教程
stm32·单片机·嵌入式硬件
菜鸡儿齐1 小时前
leetcode-有效的括号
linux·算法·leetcode
汽车软件工程师0012 小时前
TC366 SPI框架和使用讲解
单片机·嵌入式硬件
We་ct2 小时前
LeetCode 102. 二叉树的层序遍历:图文拆解+代码详解
前端·算法·leetcode·typescript
历程里程碑2 小时前
26信号处理一:从闹钟到进程控制的奥秘
linux·运维·服务器·开发语言·c++·算法·排序算法
Gofarlic_OMS2 小时前
LS-DYNA许可证全局状态及集群计算资源使用可视化监控大屏
运维·开发语言·算法·matlab·自动化
载数而行5202 小时前
算法系列4之插入排序
数据结构·c++·算法·排序算法
会员果汁2 小时前
二分搜索-C
c语言·算法
智者知已应修善业2 小时前
【查找指定字符串首位置与数量不区分大小写完整匹配】2025-5-3
c语言·c++·经验分享·笔记·算法