diff --git a/Services/LanScpiSocket.cs b/Services/LanScpiSocket.cs index 778333b..a24132e 100644 --- a/Services/LanScpiSocket.cs +++ b/Services/LanScpiSocket.cs @@ -94,7 +94,7 @@ namespace ASTM_D7896_Tester.Services await SendCommandAsync($"VOLT:DC:RANG {DefaultVoltageRange}"); // 3. 设置积分时间 0.02PLC(最快速度) - await SendCommandAsync("VOLT:DC:NPLC 0.02"); + await SendCommandAsync("VOLT:DC:NPLC 10"); // 4. 关闭自动归零(提高速度) await SendCommandAsync("VOLT:DC:ZERO:AUTO OFF"); diff --git a/ViewModels/D7896ViewModel.cs b/ViewModels/D7896ViewModel.cs index c7c4286..ca055e2 100644 --- a/ViewModels/D7896ViewModel.cs +++ b/ViewModels/D7896ViewModel.cs @@ -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 } } - ///// - ///// 根据采集到的电压序列计算热导率 λ、热扩散率 α、温升数组以及冷却曲线数据点 - ///// - //private (double lambda, double alpha, double[] deltaT, List 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(); - // 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(); - // 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(); - // 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); - //} - /// - /// 根据采集到的电压序列计算热导率 λ 和热扩散率 α(标准截距法) - /// + private (double lambda, double alpha, double[] deltaT, List 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] = upt[i] / current[i]; + // 使用滤波后的 smoothedUpt 计算电阻,彻底消除毛刺! + ptResistance[i] = smoothedUpt[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 tStart = 0.1; - double tEndHeating = 0.8; + // 时间零点补偿 (t0_shift) + double t0_shift = 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(); 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(); - 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); } + + /// /// 最小二乘法线性回归,返回 (斜率, 截距) /// diff --git a/ViewModels/MeasurementResult.cs b/ViewModels/MeasurementResult.cs index 60016ef..cc2c75c 100644 --- a/ViewModels/MeasurementResult.cs +++ b/ViewModels/MeasurementResult.cs @@ -19,21 +19,30 @@ public partial class MeasurementResult : ObservableObject public void CalculateVhcAndCp(double density) { - CalculateVhc(); // 先计算 VHC - - // 日志:记录计算前的参数 - Debug.WriteLine($"[MeasurementResult] 计算比热容 - 密度: {density} kg/m³, 体积热容: {VolumetricHeatCapacity} kJ/(m³·K)"); + // 1. 计算体积热容 VHC = λ / α + // 注意:ThermalDiffusivity 已经是标准单位 m²/s,绝对不能再乘 1e-6! + if (ThermalDiffusivity > 0) + { + double vhc_J = ThermalConductivity / ThermalDiffusivity; // 单位: J/(m³·K) + VolumetricHeatCapacity = vhc_J / 1000.0; // 转换为 kJ/(m³·K) + } + else + { + VolumetricHeatCapacity = 0; + } + // 2. 计算比热容 Cp = VHC / ρ if (density > 0) { - SpecificHeatCapacity = VolumetricHeatCapacity * 1000 / density; - Debug.WriteLine($"[MeasurementResult] 计算得到比热容: {SpecificHeatCapacity} J/(kg·K)"); + // VHC 转回 J/(m³·K) 除以密度 + SpecificHeatCapacity = (VolumetricHeatCapacity * 1000.0) / density; } else { SpecificHeatCapacity = 0; - Debug.WriteLine($"[MeasurementResult] 警告: 密度无效 (density={density}),比热容设为0"); } + + Debug.WriteLine($"[MeasurementResult] 计算完成: VHC={VolumetricHeatCapacity:F2} kJ/(m³·K), Cp={SpecificHeatCapacity:F2} J/(kg·K)"); } [ObservableProperty] diff --git a/appsettings.json b/appsettings.json index afa7402..8f0f064 100644 --- a/appsettings.json +++ b/appsettings.json @@ -25,7 +25,7 @@ "TestParameters": { "MeasurementCount": 10, "IntervalSeconds": 30, - "PlatinumWireLength": 0.06, //铂丝长度(单位:米) + "PlatinumWireLength": 0.056, //铂丝长度(单位:米) "PlatinumWireDiameter": 0.00006, "ReportOutputPath": "Reports\\", "DefaultSampleVolume": 40.0,