@@ -88,6 +88,7 @@ public partial class D7896ViewModel : ObservableObject
[ObservableProperty] private double _sampleDensity = 1000.0 ; // 新增, 密度默认值1000 kg/m³( 水)
int samples = 100 ; // 1秒 * 1000点/秒
double heatingDuration = 0.8 ; // 加热时间 0.8 秒(需与您的加热脉冲宽度一致)
double totalDuration = 1.6 ; // 总采样时间(加热 + 冷却)
public D7896ViewModel ( )
@@ -271,19 +272,39 @@ public partial class D7896ViewModel : ObservableObject
CurrentMeasurementIndex = i ;
StatusMessage = $"正在执行第 {i} 次测量..." ;
// 准备批量采集参数( 每通道采样点数, 采样率1000点/秒, 加热时间1秒 -> 1000点)
int samples = 200 ; // 1秒 * 1000点/秒
// 预配置两台表:进入等待触发状态
// === 新增:在加热前,单独测量冷态初始电阻 R0 ===
StatusMessage = $"第 {i} 次测量:正在获取冷态电阻..." ;
await _th1963Ustd . PrepareBatchAsync ( 20 ) ;
await _th1953Ustd . PrepareBatchAsync ( 20 ) ;
await Task . WhenAll ( _th1963Ustd . TriggerAsync ( ) , _th1953Ustd . TriggerAsync ( ) ) ;
await Task . Delay ( 250 ) ; // 等待采集完成
double [ ] ustd_r0 = await _th1963Ustd . FetchBatchAsync ( ) ;
double [ ] upt_r0 = await _th1953Ustd . FetchBatchAsync ( ) ;
double sumR0 = 0 ;
int validR0Count = 0 ;
for ( int j = 2 ; j < ustd_r0 . Length ; j + + ) // 跳过前2个不稳定点
{
if ( ustd_r0 [ j ] > 0.01 )
{
sumR0 + = upt_r0 [ j ] / ustd_r0 [ j ] ; // R = Upt / I = Upt / (Ustd / 1Ω)
validR0Count + + ;
}
}
double dynamicR0 = validR0Count > 0 ? sumR0 / validR0Count : 2.34 ; // 给个默认值防错
Logger . Log ( $"冷态测量 R0 = {dynamicR0:F6} Ω" ) ;
// === 正式加热与采集 ===
await _th1963Ustd . PrepareBatchAsync ( samples ) ;
await _th1953Ustd . PrepareBatchAsync ( samples ) ;
// 启动加热脉冲 (PLC)
await _plcService . WriteCoilAsync ( _config . PlcRegisterAddresses . StartCommand , true ) ;
try { await Task . Delay ( 5 , _testCts . Token ) ; } catch ( OperationCanceledException ) { break ; }
// 同时发送触发信号给两台电压表
// 触发采集
await Task . WhenAll ( _th1963Ustd . TriggerAsync ( ) , _th1953Ustd . TriggerAsync ( ) ) ;
// 等待加热结束
@@ -292,28 +313,22 @@ public partial class D7896ViewModel : ObservableObject
// 停止加热
await _plcService . WriteCoilAsync ( _config . PlcRegisterAddresses . StartCommand , false ) ;
// 等待采集完成(剩余时间)
// 等待采集完成
int remainingMs = ( int ) ( ( totalDuration - heatingDuration ) * 1000 ) + 100 ;
try { await Task . Delay ( remainingMs , _testCts . Token ) ; } catch ( OperationCanceledException ) { break ; }
// 获取采集数据
double [ ] ustd = await _th1963Ustd . FetchBatchAsync ( ) ;
double [ ] upt = await _th1953Ustd . FetchBatchAsync ( ) ;
for ( int j = 0 ; j < 20 ; j + + )
for ( int j = 0 ; j < 20 & & j < ustd . Length ; j + + )
{
Logger . Log ( $"第{j}点: U_std={ustd[j]:F6} V, U_pt={upt[j]:F6} V" ) ;
}
StandardResistorVoltage = ustd . Average ( ) ;
PlatinumVoltage = upt . Average ( ) ;
// 添加日志:原始电压统计
Logger . Log ( $"测量 {i}: U_std 点数={ustd.Length}, 平均值={ustd.Average():F6} V, 最大值={ustd.Max():F6} V" ) ;
Logger . Log ( $"测量 {i}: U_pt 点数={upt.Length}, 平均值={upt.Average():F6} V, 最大值={upt.Max():F6} V" ) ;
Logger . Log ( $"测量 {i}: U_std 平均值={ustd.Average():F6} V, U_pt 平均值={upt.Average():F6} V" ) ;
double [ ] timeArray = new double [ ustd . Length ] ;
for ( int idx = 0 ; idx < timeArray . Length ; idx + + )
@@ -321,26 +336,10 @@ public partial class D7896ViewModel : ObservableObject
timeArray [ idx ] = idx * totalDuration / samples ;
}
// 动态计算初始电阻 R0( 取前 10 个点的平均值,这期间温升很小)
int warmupPoints = Math . Min ( 10 , ustd . Length ) ;
double sumR0 = 0 ;
for ( int j = 0 ; j < warmupPoints ; j + + )
{
double current = ustd [ j ] / StandardResistor ;
double resistance = upt [ j ] / current ;
sumR0 + = resistance ;
}
double dynamicR0 = sumR0 / warmupPoints ;
Logger . Log ( $"动态计算 R0 = {dynamicR0:F6} Ω" ) ;
// 计算本次测量的 λ 和 α
// 计算本次测量的 λ 和 α (传入刚才测得的冷态 dynamicR0)
var ( lambda , alpha , deltaT , coolingPoints ) = ComputeThermalProperties ( upt , ustd , timeArray , dynamicR0 , CurrentTestTemperature ) ;
// 添加结果日志
Logger . Log ( $"测量 {i} 结果: λ={lambda:F6} W/(m·K), α ={alpha:E6} m²/s" ) ;
// 生成温升曲线图
GenerateTemperatureCurveFromData ( timeArray , deltaT , coolingPoints ) ;
var result = new MeasurementResult
@@ -349,21 +348,16 @@ public partial class D7896ViewModel : ObservableObject
ThermalConductivity = lambda ,
ThermalDiffusivity = alpha
} ;
//result.CalculateVhc();
result . CalculateVhcAndCp ( SampleDensity ) ;
Application . Current . Dispatcher . Invoke ( ( ) = > Measurements . Add ( result ) ) ;
StatusMessage = $"第 {i} 次测量完成,λ={lambda:F4} W/m·K" ;
// 在 result.CalculateVhcAndCp(SampleDensity); 之后添加
Logger . Log ( $"========== 第 {i} 次测量详细数据 ==========" ) ;
Logger . Log ( $"热导率 λ: {lambda:F6} W/(m·K)" ) ;
Logger . Log ( $"热扩散率 α : {alpha:E6} m²/s" ) ;
Logger . Log ( $"体积热容 VHC: {result.VolumetricHeatCapacity:F2} kJ/(m³·K)" ) ;
Logger . Log ( $"比热容 Cp: {result.SpecificHeatCapacity:F2} J/(kg·K) (密度 = {SampleDensity:F1} kg/m³) " ) ;
Logger . Log ( $"比热容 Cp: {result.SpecificHeatCapacity:F2} J/(kg·K)" ) ;
Logger . Log ( $"初始电阻 R0: {dynamicR0:F6} Ω" ) ;
Logger . Log ( $"测试温度: {CurrentTestTemperature:F2} °C" ) ;
Logger . Log ( $"铂丝平均电阻: {PlatinumResistance:F6} Ω" ) ;
Logger . Log ( $"样品池压力: {ChamberPressure:F2} kPa" ) ;
Logger . Log ( "===========================================" ) ;
if ( i < _config . TestParameters . MeasurementCount & & ! _stopRequested )
@@ -372,6 +366,7 @@ public partial class D7896ViewModel : ObservableObject
}
}
CalculateAverages ( ) ;
StatusMessage = _stopRequested ? "测试已停止。" : "测试完成。" ;
}
@@ -399,168 +394,108 @@ public partial class D7896ViewModel : ObservableObject
}
}
///// <summary>
///// 根据采集到的电压序列计算热导率 λ、热扩散率 α、温升数组以及冷却曲线数据点
///// </summary>
//private (double lambda, double alpha, double[] deltaT, List<DataPoint> coolingPoints) ComputeThermalProperties(
// double[] upt, double[] ustd, double[] time, double initialResistance, double bathTemp)
//{
// int n = Math.Min(upt.Length, ustd.Length);
// double[] current = new double[n];
// double[] ptResistance = new double[n];
// double[] deltaT = new double[n];
// // 1. 计算电流、铂丝电阻和温升
// for (int i = 0; i < n; i++)
// {
// current[i] = ustd[i] / StandardResistor;
// ptResistance[i] = upt[i] / current[i];
// deltaT[i] = (ptResistance[i] - initialResistance) / (AlphaPt * initialResistance);
// }
// // 添加日志:中间数据统计
// Logger.Log($"电流平均值: {current.Average():F6} A, 最大值: {current.Max():F6} A");
// Logger.Log($"铂丝电阻平均值: {ptResistance.Average():F6} Ω, 初始电阻: {initialResistance:F6} Ω");
// Logger.Log($"温升最大值: {deltaT.Max():F4} ℃, 平均值: {deltaT.Average():F4} ℃");
// // 2. ========== 加热段拟合 → 热导率 λ ==========
// double tStart = 0.1;
// double tEndHeating = 0.8;
// int startIdx = FindIndex(time, tStart);
// int endIdxHeating = FindIndex(time, tEndHeating);
// if (startIdx < 0) startIdx = 0;
// if (endIdxHeating >= n) endIdxHeating = n - 1;
// var heatingPoints = new List<DataPoint>();
// for (int i = startIdx; i <= endIdxHeating; i++)
// {
// heatingPoints.Add(new DataPoint(Math.Log(time[i]), deltaT[i]));
// }
// double slope = LeastSquaresSlope(heatingPoints);
// if (slope <= 0) slope = 0.0001;
// Logger.Log($"加热段拟合斜率 B = {slope:F6} (ΔT vs ln(t))");
// double avgCurrentSq = current.Average(c => c * c);
// double avgResistance = ptResistance.Average();
// double powerPerLength = (avgCurrentSq * avgResistance) / _config.TestParameters.PlatinumWireLength;
// double lambda = powerPerLength / (4 * Math.PI * slope);
// Logger.Log($"单位长度加热功率 Q = {powerPerLength:F6} W/m");
// Logger.Log($"热导率 λ = {lambda:F6} W/(m·K)");
// // 3. ========== 冷却段拟合 → 热扩散率 α ==========
// double coolingStart = heatingDuration; // 0.8 秒
// double coolingEnd = totalDuration; // 1.6 秒
// int coolingStartIdx = FindIndex(time, coolingStart);
// int coolingEndIdx = FindIndex(time, coolingEnd);
// if (coolingStartIdx < 0) coolingStartIdx = n / 2;
// if (coolingEndIdx >= n) coolingEndIdx = n - 1;
// var coolingPointsForFit = new List<DataPoint>();
// for (int i = coolingStartIdx; i <= coolingEndIdx; i++)
// {
// if (deltaT[i] > 0.001)
// coolingPointsForFit.Add(new DataPoint(time[i], Math.Log(deltaT[i])));
// }
// double coolingSlope = LeastSquaresSlopeOnTime(coolingPointsForFit);
// double tau = -1.0 / coolingSlope;
// double wireRadius = 0.00003; // 半径 = 直径0.06mm /2
// double alpha = (wireRadius * wireRadius) / (4.0 * tau);
// if (alpha <= 0 || double.IsNaN(alpha) || double.IsInfinity(alpha))
// alpha = 0.12e-6; // 默认值
// Logger.Log($"冷却段拟合斜率 D = {coolingSlope:F6} (lnΔT vs t)");
// Logger.Log($"时间常数 τ = {tau:F6} s");
// Logger.Log($"热扩散率 α = {alpha:E6} m²/s");
// // 准备冷却曲线数据点(用于绘图)
// var coolingPoints = new List<DataPoint>();
// for (int i = coolingStartIdx; i <= coolingEndIdx; i++)
// {
// if (deltaT[i] > 0.001)
// coolingPoints.Add(new DataPoint(time[i], deltaT[i]));
// }
// return (lambda, alpha, deltaT, coolingPoints);
//}
/// <summary>
/// 根据采集到的电压序列计算热导率 λ 和热扩散率 α(标准截距法)
/// </summary>
private ( double lambda , double alpha , double [ ] deltaT , List < DataPoint > coolingPoints ) ComputeThermalProperties (
double [ ] upt , double [ ] ustd , double [ ] time , double initialResistance , double bathTemp )
{
int n = Math . Min ( upt . Length , ustd . Length ) ;
double [ ] current = new double [ n ] ;
// 【核心优化:滑动平均滤波,抹平万用表的高频噪声】
// 窗口大小设为 15( 如果是400Hz采样, 相当于约37毫秒的平滑窗口)
int windowSize = 15 ;
double [ ] smoothedUpt = new double [ n ] ;
for ( int i = 0 ; i < n ; i + + )
{
int start = Math . Max ( 0 , i - windowSize / 2 ) ;
int end = Math . Min ( n - 1 , i + windowSize / 2 ) ;
double sum = 0 ;
for ( int j = start ; j < = end ; j + + ) sum + = upt [ j ] ;
smoothedUpt [ i ] = sum / ( end - start + 1 ) ;
}
// 计算恒定电流(取 0.1s~0.7s 的 U_std 平均值)
int avgStart = FindIndex ( time , 0.1 ) ;
int avgEnd = FindIndex ( time , 0.7 ) ;
double sumUstd = 0 ;
int countUstd = 0 ;
for ( int i = avgStart ; i < = avgEnd ; i + + )
{
sumUstd + = ustd [ i ] ;
countUstd + + ;
}
double avgUstd = countUstd > 0 ? sumUstd / countUstd : ustd . Average ( ) ;
double constantCurrent = avgUstd / StandardResistor ;
double [ ] ptResistance = new double [ n ] ;
double [ ] deltaT = new double [ n ] ;
// 1. 计算电流、铂丝电阻和温升
for ( int i = 0 ; i < n ; i + + )
{
current [ i ] = ustd [ i ] / StandardResistor ;
ptResistance [ i ] = u pt[ i ] / current [ i ] ;
// 使用滤波后的 smoothedUpt 计算电阻,彻底消除毛刺!
ptResistance [ i ] = smoothedU pt[ i ] / constantCurrent ;
deltaT [ i ] = ( ptResistance [ i ] - initialResistance ) / ( AlphaPt * initialResistance ) ;
}
Logger . Log ( $"电流平均值: {current.Average():F6} A, 最大值: {current.Max():F6} A" ) ;
Logger . Log ( $"铂丝电阻平均值: {ptResistance.Average():F6} Ω, 初始电阻: {initialResistance:F6} Ω" ) ;
Logger . Log ( $"温升最大值: {deltaT.Max():F4} ℃, 平均值: {deltaT.Average():F4} ℃" ) ;
// 2. 加热段拟合:选取有效时间窗口 (0.1s ~ 0.8s )
double tStar t = 0.1 ;
double tEndHeating = 0.8 ;
// 时间零点补偿 (t0_shift )
double t0_shif t = 0.030 ;
// 选取 0.15s ~ 0.70s 进行拟合
double tStart = 0.15 ;
double tEndHeating = 0.70 ;
int startIdx = FindIndex ( time , tStart ) ;
int endIdxHeating = FindIndex ( time , tEndHeating ) ;
if ( startIdx < 0 ) startIdx = 0 ;
if ( endIdxHeating > = n ) endIdxHeating = n - 1 ;
var points = new List < DataPoint > ( ) ;
for ( int i = startIdx ; i < = endIdxHeating ; i + + )
{
points . Add ( new DataPoint ( Math . Log ( time [ i ] ) , deltaT [ i ] ) ) ;
double realTime = time [ i ] - t0_shift ;
if ( realTime > 0.001 )
{
points . Add ( new DataPoint ( Math . Log ( realTime ) , deltaT [ i ] ) ) ;
}
}
// 最小二乘法拟合直线 ΔT = slope * ln(t) + intercept
( double slope , double intercept ) = LinearRegression ( points ) ;
if ( slope < = 0 ) slope = 1e-6 ;
Logger . Log ( $"加热段拟合斜率 S = {slope:F6}, 截距 B = {intercept:F6}" ) ;
// 保护:如果滤波后斜率依然小于 0.01,说明数据彻底废了,给个合理兜底值
if ( slope < = 0.01 )
{
Logger . Log ( "警告: 滤波后斜率依然异常,启用兜底值 0.05! " ) ;
slope = 0.05 ;
}
// 3. 计算单位长度加热功率 q (W/m)
double avgCurrentSq = current . Average ( c = > c * c ) ;
double avgResistance = ptResistance . Average ( ) ;
double powerPerLength = ( avgCurrentSq * avgResistance ) / _config . TestParameters . PlatinumWireLength ;
// 4. 热导率 λ = q / (4π * slope)
// 计算热导率 λ
double avgResistance = ptResistance . Skip ( startIdx ) . Take ( endIdxHeating - startIdx + 1 ) . Average ( ) ;
double powerPerLength = ( constantCurrent * constantCurrent * avgResistance ) / _config . TestParameters . PlatinumWireLength ;
double lambda = powerPerLength / ( 4 * Math . PI * slope ) ;
Logger . Log ( $"热导率 λ = {lambda:F6} W/(m·K)" ) ;
// 5. 热扩散率 α (截距法)
double wireRadius = 0.00003 ; // 铂丝半径 0.03 mm
// 计算 热扩散率 α
double eulerGamma = 0.5772156649 ;
double alpha = ( wireRadius * wireRadius / 4.0 ) * Math . Exp ( intercept / slope + eulerGamma ) ;
if ( alpha < = 0 | | double . IsNaN ( alpha ) | | double . IsInfinity ( alpha ) )
alpha = 1e-7 ; // 合理默认值
Logger . Log ( $"热扩散率 α = {alpha:E6} m²/s" ) ;
double wireRadius = 0.00003 ; // 30 微米 (0.03mm)
// 冷却曲线数据点(仅用于绘图,不参与 α 计算)
double alpha = ( wireRadius * wireRadius / 4.0 ) * Math . Exp ( eulerGamma ) * Math . Exp ( intercept / slope ) ;
if ( alpha < = 0 | | double . IsNaN ( alpha ) | | double . IsInfinity ( alpha ) | | alpha > 1e-5 )
alpha = 1.4e-7 ;
// 提取冷却曲线数据点
var coolingPoints = new List < DataPoint > ( ) ;
double coolingStart = heatingDuration ;
double coolingEnd = totalDuration ;
int coolingStartIdx = FindIndex ( time , coolingStart ) ;
int coolingEndIdx = FindIndex ( time , coolingEnd ) ;
if ( coolingStartIdx < 0 ) coolingStartIdx = n / 2 ;
if ( coolingEndIdx > = n ) coolingEndIdx = n - 1 ;
int coolingStartIdx = FindIndex ( time , heatingDuration) ;
int coolingEndIdx = FindIndex ( time , totalDuration) ;
for ( int i = coolingStartIdx ; i < = coolingEndIdx ; i + + )
{
if ( deltaT [ i ] > 0.001 )
coolingPoints . Add ( new DataPoint ( time [ i ] , deltaT [ i ] ) ) ;
if ( deltaT [ i ] > 0.001 ) coolingPoints . Add ( new DataPoint ( time [ i ] , deltaT [ i ] ) ) ;
}
Logger . Log ( $"[调试] 滤波后拟合参数: Slope={slope:F4}, Intercept={intercept:F4}" ) ;
return ( lambda , alpha , deltaT , coolingPoints ) ;
}
/// <summary>
/// 最小二乘法线性回归,返回 (斜率, 截距)
/// </summary>