This commit is contained in:
@@ -8,7 +8,9 @@ using OxyPlot.Axes;
|
||||
using OxyPlot.Series;
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
@@ -87,7 +89,8 @@ public partial class D7896ViewModel : ObservableObject
|
||||
|
||||
|
||||
private const double EulerGamma = 0.5772156649; // 欧拉常数
|
||||
private const double WireRadius = 0.00003; // 铂丝半径 (0.03 mm)
|
||||
//private const double WireRadius = 0.00003; // 铂丝半径 (0.03 mm)
|
||||
private const double WireRadius = 0.00010; // 铂丝半径 (0.03 mm)
|
||||
[ObservableProperty] private double _sampleDensity = 1000.0; // 新增,密度默认值1000 kg/m³(水)
|
||||
int samples = 1000; // 1秒 * 1000点/秒
|
||||
double heatingDuration = 1; // 加热时间 0.8 秒(需与您的加热脉冲宽度一致)
|
||||
@@ -273,46 +276,88 @@ public partial class D7896ViewModel : ObservableObject
|
||||
CurrentMeasurementIndex = i;
|
||||
StatusMessage = $"正在执行第 {i} 次测量...";
|
||||
|
||||
// === 正式加热与采集 ===
|
||||
|
||||
|
||||
// --- 步骤1:基线采集(加热前)---
|
||||
await _th1963Ustd.PrepareBatchAsync(50);
|
||||
await _th1953Ustd.PrepareBatchAsync(50);
|
||||
await Task.WhenAll(_th1963Ustd.TriggerAsync(), _th1953Ustd.TriggerAsync());
|
||||
await Task.Delay(100);
|
||||
double[] ustdBase = await _th1963Ustd.FetchBatchAsync();
|
||||
double[] uptBase = await _th1953Ustd.FetchBatchAsync();
|
||||
|
||||
double dynamicR0 = 2.45; // 默认值
|
||||
if (ustdBase != null && ustdBase.Length > 0 && uptBase != null && uptBase.Length > 0)
|
||||
{
|
||||
double sumR0 = 0; int cnt = 0;
|
||||
for (int j = 0; j < ustdBase.Length; j++)
|
||||
{
|
||||
if (ustdBase[j] > 0.01 && uptBase[j] > 0.01)
|
||||
{
|
||||
sumR0 += uptBase[j] / ustdBase[j];
|
||||
cnt++;
|
||||
}
|
||||
}
|
||||
if (cnt > 0)
|
||||
{
|
||||
dynamicR0 = sumR0 / cnt;
|
||||
Logger.Log($"基线采集 R0 = {dynamicR0:F6} Ω (有效点数: {cnt})");
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Log("基线采集数据无效(电压过低),使用加热前瞬间点");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Log("基线采集未返回数据,将使用加热段早期点");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// --- 步骤2:正式加热采集 ---
|
||||
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());
|
||||
|
||||
// 等待加热结束
|
||||
try { await Task.Delay((int)(heatingDuration * 1000), _testCts.Token); } catch (OperationCanceledException) { break; }
|
||||
|
||||
// 停止加热
|
||||
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();
|
||||
|
||||
|
||||
// 动态计算初始电阻 R0(取第2~11点,跳过初始扰动)
|
||||
double sumR0 = 0;
|
||||
int r0Cnt = 0;
|
||||
for (int j = 2; j < Math.Min(12, ustd.Length); j++)
|
||||
if (dynamicR0 == 2.45) // 仍为默认值,说明基线无效
|
||||
{
|
||||
if (ustd[j] > 0.01 && upt[j] > 0.01)
|
||||
double sumR0 = 0; int cnt = 0;
|
||||
// 改用第 10 到 19 点(共10个点),避开前10个点的干扰
|
||||
for (int j = 10; j < Math.Min(20, ustd.Length); j++)
|
||||
{
|
||||
sumR0 += upt[j] / ustd[j];
|
||||
r0Cnt++;
|
||||
if (ustd[j] > 0.01 && upt[j] > 0.01)
|
||||
{
|
||||
sumR0 += upt[j] / ustd[j];
|
||||
cnt++;
|
||||
}
|
||||
}
|
||||
if (cnt > 0)
|
||||
{
|
||||
dynamicR0 = sumR0 / cnt;
|
||||
Logger.Log($"使用加热段第10~19点计算 R0 = {dynamicR0:F6} Ω");
|
||||
}
|
||||
}
|
||||
double dynamicR0 = r0Cnt > 0 ? sumR0 / r0Cnt : 2.34;
|
||||
Logger.Log($"动态计算 R0 = {dynamicR0:F6} Ω (有效点数: {r0Cnt})");
|
||||
|
||||
|
||||
|
||||
@@ -398,7 +443,6 @@ public partial class D7896ViewModel : ObservableObject
|
||||
|
||||
|
||||
|
||||
|
||||
private (double lambda, double alpha, double[] deltaT, List<DataPoint> coolingPoints) ComputeThermalProperties(
|
||||
double[] upt, double[] ustd, double[] time, double initialResistance, double bathTemp)
|
||||
{
|
||||
@@ -406,12 +450,14 @@ public partial class D7896ViewModel : ObservableObject
|
||||
double[] deltaT = new double[n];
|
||||
double[] ptResistance = new double[n];
|
||||
double[] current = new double[n];
|
||||
double tStart = _config.TestParameters.FitStartTime;
|
||||
double tEnd = _config.TestParameters.FitEndTime;
|
||||
|
||||
// 1. 瞬时计算
|
||||
// 1. 瞬时计算(先用传入的 initialResistance)
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
current[i] = ustd[i] / StandardResistor;
|
||||
if (current[i] > 0.001) // 降低阈值到 1mA
|
||||
if (current[i] > 0.001)
|
||||
{
|
||||
ptResistance[i] = upt[i] / current[i];
|
||||
deltaT[i] = (ptResistance[i] - initialResistance) / (AlphaPt * initialResistance);
|
||||
@@ -423,7 +469,7 @@ public partial class D7896ViewModel : ObservableObject
|
||||
}
|
||||
}
|
||||
|
||||
// 1.5 滑动平均平滑(窗口5)
|
||||
// 滑动平均平滑(窗口5)
|
||||
double[] smoothDeltaT = new double[n];
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
@@ -435,22 +481,16 @@ public partial class D7896ViewModel : ObservableObject
|
||||
smoothDeltaT[i] = cnt > 0 ? sum / cnt : double.NaN;
|
||||
}
|
||||
|
||||
// 2. 寻找温升峰值点(使用平滑后的数据)
|
||||
int peakIdx = 5;
|
||||
// 寻找温升峰值点
|
||||
double maxDeltaT = 0;
|
||||
for (int i = 5; i < n; i++)
|
||||
{
|
||||
if (!double.IsNaN(smoothDeltaT[i]) && smoothDeltaT[i] > maxDeltaT)
|
||||
{
|
||||
maxDeltaT = smoothDeltaT[i];
|
||||
peakIdx = i;
|
||||
}
|
||||
}
|
||||
Logger.Log($"最大温升 = {maxDeltaT:F4} ℃");
|
||||
|
||||
// 3. 固定时间窗口(0.1 ~ 0.8 秒),避开初始扰动和冷却段
|
||||
double tStart = 0.15;
|
||||
double tEnd = 0.4;
|
||||
// 拟合窗口
|
||||
int startIdx = FindIndex(time, tStart);
|
||||
int endIdx = FindIndex(time, tEnd);
|
||||
if (startIdx < 0) startIdx = 5;
|
||||
@@ -458,13 +498,12 @@ public partial class D7896ViewModel : ObservableObject
|
||||
if (endIdx <= startIdx) endIdx = Math.Min(startIdx + 50, n - 1);
|
||||
Logger.Log($"拟合窗口: startIdx={startIdx}, endIdx={endIdx}, 点数={endIdx - startIdx + 1}");
|
||||
|
||||
|
||||
|
||||
|
||||
// 4. 收集拟合点
|
||||
// 收集拟合点
|
||||
var points = new List<DataPoint>();
|
||||
for (int i = startIdx; i <= endIdx; i++)
|
||||
{
|
||||
if (i <= startIdx + 5) // 只打印前5个点
|
||||
Logger.Log($"i={i}, time={time[i]:F6}, smoothDeltaT={smoothDeltaT[i]:F6}, deltaT={deltaT[i]:F6}");
|
||||
if (!double.IsNaN(smoothDeltaT[i]) && smoothDeltaT[i] > 0 && time[i] > 0)
|
||||
points.Add(new DataPoint(Math.Log(time[i]), smoothDeltaT[i]));
|
||||
}
|
||||
@@ -482,35 +521,41 @@ public partial class D7896ViewModel : ObservableObject
|
||||
return (0, 0, deltaT, new List<DataPoint>());
|
||||
}
|
||||
|
||||
// 打印前10个拟合点
|
||||
foreach (var p in points.Take(10))
|
||||
Logger.Log($"ln(t)={p.X:F4}, ΔT={p.Y:F4}");
|
||||
|
||||
|
||||
|
||||
|
||||
// 5. 计算功率
|
||||
double sumPower = 0;
|
||||
int validCount = 0;
|
||||
// 计算功率密度
|
||||
double sumI = 0, sumR = 0;
|
||||
int cntI = 0, cntR = 0;
|
||||
for (int i = startIdx; i <= endIdx; i++)
|
||||
{
|
||||
if (!double.IsNaN(ptResistance[i]))
|
||||
{
|
||||
sumPower += current[i] * current[i] * ptResistance[i];
|
||||
validCount++;
|
||||
}
|
||||
if (!double.IsNaN(current[i]) && Math.Abs(current[i]) > 1e-12) { sumI += current[i]; cntI++; }
|
||||
if (!double.IsNaN(ptResistance[i]) && ptResistance[i] > 1e-12) { sumR += ptResistance[i]; cntR++; }
|
||||
}
|
||||
if (validCount == 0) return (0, 0, deltaT, new List<DataPoint>());
|
||||
double avgPower = sumPower / validCount;
|
||||
double wireLength = _config.TestParameters.PlatinumWireLength;
|
||||
double powerPerLength = avgPower / wireLength;
|
||||
if (cntI == 0 || cntR == 0) return (0, 0, deltaT, new List<DataPoint>());
|
||||
double avgCurrent = sumI / cntI;
|
||||
double avgResistance = sumR / cntR;
|
||||
double wireLength = Math.Max(1e-12, _config.TestParameters.PlatinumWireLength);
|
||||
double powerPerLength = (avgCurrent * avgCurrent * avgResistance) / wireLength;
|
||||
|
||||
double lambda = powerPerLength / (4 * Math.PI * slope);
|
||||
Logger.Log($"功率密度 = {powerPerLength:F3} W/m, 斜率 B = {slope:F5}");
|
||||
if (_config.TestParameters.UseFixedLambda)
|
||||
{
|
||||
lambda = _config.TestParameters.FixedLambda;
|
||||
Logger.Log($"使用固定 lambda={lambda:F6} W/(m·K)");
|
||||
}
|
||||
Logger.Log($"constantCurrent(avg)={avgCurrent:E6} A, avgResistance={avgResistance:F6} Ω, powerPerLength={powerPerLength:E6} W/m, 斜率 B = {slope:F5}");
|
||||
|
||||
// 6. α 计算
|
||||
// 计算热扩散率
|
||||
double exponent = intercept / slope + EulerGamma;
|
||||
if (exponent > 30) exponent = 30;
|
||||
double alpha = (WireRadius * WireRadius / 4.0) * Math.Exp(exponent);
|
||||
if (_config.TestParameters.UseFixedAlpha)
|
||||
{
|
||||
alpha = _config.TestParameters.FixedAlpha;
|
||||
Logger.Log($"使用固定 alpha={alpha:E6} m²/s");
|
||||
}
|
||||
if (alpha <= 0 || double.IsNaN(alpha) || alpha > 1e-5)
|
||||
{
|
||||
Logger.Log($"警告:α 计算异常 ({alpha:E}),数据可能不可靠");
|
||||
@@ -526,11 +571,27 @@ public partial class D7896ViewModel : ObservableObject
|
||||
if (!double.IsNaN(deltaT[i]) && deltaT[i] > 0.01)
|
||||
coolingPoints.Add(new DataPoint(time[i], deltaT[i]));
|
||||
|
||||
// 导出CSV
|
||||
try
|
||||
{
|
||||
string tmp = Path.GetTempPath();
|
||||
string baseName = $"measure_{SampleId}_{DateTime.Now:yyyyMMdd_HHmmss}_{CurrentMeasurementIndex}";
|
||||
string dataPath = Path.Combine(tmp, baseName + ".csv");
|
||||
ExportMeasurementCsv(dataPath, time, ustd, upt, deltaT, startIdx, endIdx);
|
||||
Logger.Log($"已导出测量数据 CSV: {dataPath}");
|
||||
|
||||
string winPath = Path.Combine(tmp, baseName + "_windows.csv");
|
||||
ExportCandidateWindowsCsv(winPath, time, deltaT, startIdx, endIdx);
|
||||
Logger.Log($"已导出候选拟合窗 CSV: {winPath}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Log($"导出CSV失败: {ex.Message}");
|
||||
}
|
||||
|
||||
return (lambda, alpha, deltaT, coolingPoints);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 最小二乘法线性回归,返回 (斜率, 截距)
|
||||
/// </summary>
|
||||
@@ -566,6 +627,34 @@ public partial class D7896ViewModel : ObservableObject
|
||||
return timeArray.Length - 1;
|
||||
}
|
||||
|
||||
private void ExportMeasurementCsv(string path, double[] time, double[] ustd, double[] upt, double[] deltaT, int fitStart, int fitEnd)
|
||||
{
|
||||
using var sw = new StreamWriter(path, false, Encoding.UTF8);
|
||||
sw.WriteLine("index,time,U_std,U_pt,deltaT,fitWindow");
|
||||
int n = Math.Min(Math.Min(time.Length, ustd.Length), deltaT.Length);
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
bool inFit = (i >= fitStart && i <= fitEnd);
|
||||
sw.WriteLine($"{i},{time[i]:F6},{ustd[i]:F6},{upt[i]:F6},{(double.IsNaN(deltaT[i])?0:deltaT[i]):F6},{(inFit?1:0)}");
|
||||
}
|
||||
}
|
||||
|
||||
private void ExportCandidateWindowsCsv(string path, double[] time, double[] deltaT, int startIdx, int endIdx)
|
||||
{
|
||||
using var sw = new StreamWriter(path, false, Encoding.UTF8);
|
||||
sw.WriteLine("s,e,t_start,t_end,meanDelta");
|
||||
int n = time.Length;
|
||||
int minPts = 10;
|
||||
for (int s = startIdx; s <= endIdx - minPts; s++)
|
||||
{
|
||||
for (int e = s + minPts - 1; e <= endIdx; e++)
|
||||
{
|
||||
double mean = deltaT.Skip(s).Take(e - s + 1).Where(x => !double.IsNaN(x)).DefaultIfEmpty(0).Average();
|
||||
sw.WriteLine($"{s},{e},{time[s]:F6},{time[e]:F6},{mean:F6}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///// <summary>
|
||||
///// 最小二乘法拟合斜率 (X轴为横坐标,Y轴为纵坐标) — 用于加热段 ln(t) vs ΔT
|
||||
///// </summary>
|
||||
|
||||
@@ -12,7 +12,7 @@ public partial class MeasurementResult : ObservableObject
|
||||
private double _thermalConductivity; // W/m·K
|
||||
|
||||
[ObservableProperty]
|
||||
private double _thermalDiffusivity; // ×10⁻⁶ m²/s
|
||||
private double _thermalDiffusivity; // m²/s
|
||||
|
||||
[ObservableProperty]
|
||||
private double _specificHeatCapacity; // 比热容 J/(kg·K)
|
||||
@@ -50,11 +50,12 @@ public partial class MeasurementResult : ObservableObject
|
||||
|
||||
public void CalculateVhc()
|
||||
{
|
||||
Debug.WriteLine($"[MeasurementResult] 计算体积热容 - 热导率: {ThermalConductivity} W/(m·K), 热扩散率: {ThermalDiffusivity} ×10⁻⁶ m²/s");
|
||||
Debug.WriteLine($"[MeasurementResult] 计算体积热容 - 热导率: {ThermalConductivity} W/(m·K), 热扩散率: {ThermalDiffusivity} m²/s");
|
||||
|
||||
if (ThermalDiffusivity > 0)
|
||||
{
|
||||
VolumetricHeatCapacity = ThermalConductivity / (ThermalDiffusivity * 1e-6) / 1000.0;
|
||||
// VHC (kJ/(m³·K)) = (λ / α) / 1000
|
||||
VolumetricHeatCapacity = ThermalConductivity / ThermalDiffusivity / 1000.0;
|
||||
Debug.WriteLine($"[MeasurementResult] 计算得到体积热容: {VolumetricHeatCapacity} kJ/(m³·K)");
|
||||
}
|
||||
else
|
||||
|
||||
Reference in New Issue
Block a user