第三章 数据处理:TGA原始数据导出、数据清洗与预处理、基线校正方法

做TGA动力学分析,最磨人的其实不是后面的拟合计算。

而是拿到原始数据后,那一堆「脏活累活」。

我刚开始带团队的时候,有个小伙子拿着热重曲线直接跑动力学模型,结果出来的活化能忽高忽低,完全没法看。后来一查,基线都没校正,数据里还混着仪器启动时的噪声。嗯,从那以后,我定了个规矩:数据处理不过关,不准进动力学计算环节。

3.1 TGA原始数据导出——别小看这一步

不同厂家的热重仪器,导出格式五花八门。

TA Instruments 给的是 .txt 或 .xls,Netzsch 喜欢用 .csv 或 .ngb-dta,PerkinElmer 则是 .txt 带表头。我个人习惯是:统一转成纯文本 CSV,编码用 UTF-8。为什么?因为后面用 Python 读数据时,最不容易出乱码。

我的小技巧: 导出时把「温度、时间、重量、重量百分比」四列都勾上。有些软件默认只导出重量百分比,但做动力学计算时,绝对重量有时候更直观。

举个例子,我常用的导出设置是这样的:

# 导出格式示例(TA Instruments Q50)
温度单位: °C
时间单位: min
重量单位: mg
数据间隔: 0.5 s/point
导出列: Time, Temp, Weight, Weight%

你想想看,如果间隔设得太密(比如0.1秒一个点),一次实验下来几万个点,后面处理起来电脑都卡。我建议等间隔采样,0.5~1秒一个点就够用了。

3.2 数据清洗与预处理——把「垃圾」筛出去

原始数据里总有些「不干净」的东西。

比如仪器刚启动时温度还没稳定,那段数据就是废的。再比如样品突然蹦了一下(热膨胀或气泡破裂),曲线上会多个毛刺。这些都得处理掉。

我一般分三步走:

  1. 截取有效区间:去掉升温开始前的恒温段,也去掉反应结束后的平台段。只保留从室温到最终分解完成的那一段。
  2. 剔除明显异常点:用滑动窗口法,计算每个点前后5个点的均值,如果偏差超过3倍标准差,就标记为异常点并剔除。
  3. 平滑处理:我偏爱 Savitzky-Golay 滤波器,窗口选 7~11 点,多项式阶数设 2 或 3。既能去噪声,又不损失峰的形状。
核心原则: 清洗的目的是「去伪存真」,不是「削足适履」。别为了曲线好看而过度平滑,否则动力学参数会失真。

下面是我常用的 Python 清洗代码片段:

import pandas as pd
import numpy as np
from scipy.signal import savgol_filter

# 读取原始数据
df = pd.read_csv('tga_raw.csv')

# 1. 截取有效区间(温度从 50°C 到 700°C)
df = df[(df['Temp'] >= 50) & (df['Temp'] <= 700)]

# 2. 剔除异常点(基于重量变化率)
weight_diff = np.abs(np.diff(df['Weight']))
outliers = np.where(weight_diff > 3 * weight_diff.std())[0]
df = df.drop(df.index[outliers])

# 3. Savitzky-Golay 平滑
df['Weight_smooth'] = savgol_filter(df['Weight'], window_length=9, polyorder=2)

这段代码我用了好几年,基本没出过问题。但要注意——窗口长度必须是奇数,不然会报错。我曾经因为这个卡了半小时,后来才发现是 window_length 设成了 8。

3.3 基线校正方法——把「背景」去掉

基线漂移是TGA里最烦人的问题之一。

说白了,就是仪器本身的热效应、浮力效应、坩埚重量变化等,叠加在样品信号上。如果不校正,你算出来的失重台阶可能偏大或偏小,活化能自然不准。

我常用的基线校正方法有三种:

方法 适用场景 优点 缺点
空白基线扣除法 有空白实验数据时 最准确,物理意义清晰 需要额外做一次空白实验
多项式拟合法 基线缓慢漂移 简单快速,无需额外实验 对剧烈漂移效果差
分段线性校正 基线有多个拐点 灵活,适应复杂基线 需要手动选点

我个人最推荐空白基线扣除法。 在同样条件下(相同升温速率、相同气氛、相同坩埚)做一次空跑,然后把样品曲线减去空白曲线。这样仪器自身的漂移就被干净地剔除了。

注意: 空白实验的坩埚要和样品实验用同一个批次。不同批次的坩埚,热历史不同,基线会有差异。我曾经吃过这个亏,换了坩埚批次后基线偏了 0.3%,导致活化能差了 5 kJ/mol。

如果实在没有空白数据,那就用多项式拟合。我一般选 3~5 阶多项式,拟合反应开始前和结束后的平台段,然后外插到整个温度区间。代码实现如下:

from numpy.polynomial import polynomial as P

# 选取反应前(50-150°C)和反应后(600-700°C)的平台段
mask_before = (df['Temp'] >= 50) & (df['Temp'] <= 150)
mask_after = (df['Temp'] >= 600) & (df['Temp'] <= 700)
mask_baseline = mask_before | mask_after

# 多项式拟合基线
coeffs = P.polyfit(df.loc[mask_baseline, 'Temp'], 
                   df.loc[mask_baseline, 'Weight'], 
                   deg=4)
baseline = P.polyval(df['Temp'], coeffs)

# 校正后的重量
df['Weight_corrected'] = df['Weight'] - baseline + baseline.iloc[0]

这里有个坑:多项式阶数别太高。阶数一高,基线会在中间区域乱抖,反而引入假信号。我一般不超过 5 阶。

3.4 本章知识体系一览

下面这张图,是我自己梳理的数据处理流程。每次带新人,我都先让他们看这张图,心里有个全局概念再动手。

TGA数据处理流程 原始数据导出 统一CSV格式 数据清洗与预处理 截取·剔除·平滑 基线校正 扣除背景漂移 干净数据输出 清洗三步骤 ① 截取有效温度区间 ② 3σ法则剔除异常点 ③ Savitzky-Golay平滑 基线校正方法 ▸ 空白基线扣除法 ▸ 多项式拟合法 ▸ 分段线性校正 推荐:空白基线法 数据处理是动力学分析的基石,这一步做扎实了,后面拟合才有意义

你看,整个流程其实就三步:导出→清洗→校正。但每一步都有细节,细节里藏着坑。我见过太多人急着跑动力学模型,结果数据没处理好,算出来的活化能忽大忽小,还以为是模型选错了。其实问题就出在数据预处理上。

我的习惯: 每次处理完数据,我都会把原始曲线和校正后的曲线叠在一起对比一下。如果校正后的曲线在反应前后平台段是水平的,说明基线扣得干净。如果还有斜率,那就得重新调整基线拟合区间。

好了,数据处理这部分就聊到这儿。记住一句话:垃圾进,垃圾出。数据预处理花的时间,会在后面的动力学分析里加倍还给你。


公众号:蓝海资料掘金营,微信deep3321