This commit is contained in:
xyy
2026-04-11 20:47:41 +08:00
parent 9da91d4ca5
commit ea8e76bff4
2 changed files with 164 additions and 21 deletions

View File

@@ -36,34 +36,71 @@ namespace MembranePoreTester.Helpers
return 0;
}
public static double CalculatePoreRangePercentage(
IEnumerable<DataPoint> points,
string unit,
TestLiquid liquid,
double lowerPore,
double upperPore)
IEnumerable<DataPoint> points,
string unit,
TestLiquid liquid,
double lowerPore,
double upperPore)
{
var sorted = points.OrderBy(p => p.Pressure).ToList();
if (sorted.Count < 2) return 0;
double[] pressures = sorted.Select(p => p.Pressure).ToArray();
double[] wetFlows = sorted.Select(p => p.WetFlow).ToArray();
double[] dryFlows = sorted.Select(p => p.DryFlow).ToArray();
// 提取有效点(流量 > 0
var wetValid = sorted.Where(p => p.WetFlow > 0).Select(p => (Pressure: p.Pressure, Flow: p.WetFlow)).ToList();
var dryValid = sorted.Where(p => p.DryFlow > 0).Select(p => (Pressure: p.Pressure, Flow: p.DryFlow)).ToList();
// 大孔径对应低压,小孔径对应高压
if (wetValid.Count < 2 || dryValid.Count < 2) return 0;
// 有效压力重叠区间
double minPressure = Math.Max(wetValid.First().Pressure, dryValid.First().Pressure);
double maxPressure = Math.Min(wetValid.Last().Pressure, dryValid.Last().Pressure);
if (minPressure >= maxPressure) return 0;
// 孔径转压力
double pLower = PoreCalculator.PoreToPressure(upperPore, unit, liquid);
double pUpper = PoreCalculator.PoreToPressure(lowerPore, unit, liquid);
// 插值
double qWetLower = Interpolation.Linear(pressures, wetFlows, pLower);
double qDryLower = Interpolation.Linear(pressures, dryFlows, pLower);
double qWetUpper = Interpolation.Linear(pressures, wetFlows, pUpper);
double qDryUpper = Interpolation.Linear(pressures, dryFlows, pUpper);
pLower = Math.Max(pLower, minPressure);
pUpper = Math.Min(pUpper, maxPressure);
if (pUpper <= pLower) return 0;
// 在有效点集上插值
double qWetLower = InterpolateOnValid(wetValid, pLower);
double qDryLower = InterpolateOnValid(dryValid, pLower);
double qWetUpper = InterpolateOnValid(wetValid, pUpper);
double qDryUpper = InterpolateOnValid(dryValid, pUpper);
if (qDryLower <= 0 || qDryUpper <= 0) return 0;
double ratioLow = qWetLower / qDryLower;
double ratioHigh = qWetUpper / qDryUpper;
double result = (ratioHigh - ratioLow) * 100;
return result < 0 ? 0 : result;
}
return (ratioHigh - ratioLow) * 100;
// 辅助插值函数:基于有效点列表(已按压力升序)
private static double InterpolateOnValid(List<(double Pressure, double Flow)> validPoints, double pressure)
{
if (validPoints.Count == 0) return 0;
if (pressure <= validPoints[0].Pressure) return validPoints[0].Flow;
if (pressure >= validPoints[^1].Pressure) return validPoints[^1].Flow;
for (int i = 0; i < validPoints.Count - 1; i++)
{
if (pressure >= validPoints[i].Pressure && pressure <= validPoints[i + 1].Pressure)
{
double p1 = validPoints[i].Pressure;
double f1 = validPoints[i].Flow;
double p2 = validPoints[i + 1].Pressure;
double f2 = validPoints[i + 1].Flow;
return f1 + (f2 - f1) * (pressure - p1) / (p2 - p1);
}
}
return 0;
}
}
}

View File

@@ -111,6 +111,22 @@ namespace MembranePoreTester.ViewModels
float rawPressure = await _plcService.ReadPressureAsync(StationId);
double pressure = Math.Round(rawPressure * _plcConfig.PressureFactor, 2);
// 2. 读取当前工位的加压上限(实时)
ushort upperLimitAddress = StationId == 1 ? _plcConfig.PressureUpperLimit
: StationId == 2 ? _plcConfig.PressureUpperLimit2
: _plcConfig.PressureUpperLimit3;
double pressureUpperLimit = await _plcService.ReadFloatAsync(upperLimitAddress);
// 3. 如果压力已达到或超过上限,停止采集
if (pressure >= pressureUpperLimit - 3)
{
StopCollecting();
return; // 不再添加当前数据点
}
// 2. 读取当前模式对应的流量
double flow = 0;
if (TestMode.Contains("湿膜"))
@@ -550,11 +566,18 @@ namespace MembranePoreTester.ViewModels
}
// 修改 Calculate 方法:
//修改 Calculate 方法:
private void Calculate()
{
// 先清洗数据并替换到绑定的集合中确保DataGrid和曲线显示清洗后的数据
var originalPoints = Record.DataPoints.ToList();
System.Diagnostics.Debug.WriteLine("=== 清洗前的数据点 ===");
foreach (var p in originalPoints)
{
System.Diagnostics.Debug.WriteLine($"P={p.Pressure}, Wet={p.WetFlow}, Dry={p.DryFlow}");
}
var cleanedPoints = CleanDataPoints(originalPoints);
if (cleanedPoints.Count < 2)
@@ -587,22 +610,105 @@ namespace MembranePoreTester.ViewModels
}
}
//private void Calculate()
//{
// // 直接使用原始数据,不清洗
// if (Record.DataPoints.Count < 2)
// {
// MessageBox.Show("有效数据点不足,至少需要 2 个数据点进行计算。");
// return;
// }
// // 注意:不清洗,也不改变 Record.DataPoints 的顺序(但计算函数内部会自己排序)
// // 直接计算
// AveragePoreSize = PoreDistributionAnalysis.CalculateAveragePore(
// Record.DataPoints, Record.PressureUnit, Record.Liquid);
// RangePercentage = PoreDistributionAnalysis.CalculatePoreRangePercentage(
// Record.DataPoints, Record.PressureUnit, Record.Liquid, LowerPore, UpperPore);
// // 可选:提示用户当前使用了原始数据(不清洗)
// // MessageBox.Show("已使用原始数据(未清洗)进行计算");
//}
//private List<Models.DataPoint> CleanDataPoints(IEnumerable<Models.DataPoint> points)
//{
// return points
// .Where(p => p.Pressure > 0.001) // 移除压力接近0的点
// .Where(p => !(Math.Abs(p.WetFlow) < 0.001 && Math.Abs(p.DryFlow) < 0.001)) // 移除双零流量点
// .GroupBy(p => Math.Round(p.Pressure, 2)) // 按压力四舍五入分组(避免重复压力)
// .Select(g => new Models.DataPoint
// {
// Pressure = g.Key,
// WetFlow = g.Max(x => x.WetFlow), // 取最大值避免0覆盖有效值
// DryFlow = g.Max(x => x.DryFlow)
// })
// .OrderBy(p => p.Pressure)
// .ToList();
//}
private List<Models.DataPoint> CleanDataPoints(IEnumerable<Models.DataPoint> points)
{
return points
.Where(p => p.Pressure > 0.001) // 移除压力接近0的点
.Where(p => !(Math.Abs(p.WetFlow) < 0.001 && Math.Abs(p.DryFlow) < 0.001)) // 移除双零流量点
.GroupBy(p => Math.Round(p.Pressure, 2)) // 按压力四舍五入分组(避免重复压力)
// 第一步基础清洗移除压力≤0、双零流量、合并相同压力取最大流量
var cleaned = points
.Where(p => p.Pressure > 0.001)
.Where(p => !(Math.Abs(p.WetFlow) < 0.001 && Math.Abs(p.DryFlow) < 0.001))
.GroupBy(p => Math.Round(p.Pressure, 2))
.Select(g => new Models.DataPoint
{
Pressure = g.Key,
WetFlow = g.Max(x => x.WetFlow), // 取最大值避免0覆盖有效值
WetFlow = g.Max(x => x.WetFlow),
DryFlow = g.Max(x => x.DryFlow)
})
.OrderBy(p => p.Pressure)
.ToList();
if (cleaned.Count < 4) return cleaned; // 点数太少,不进行高级剔除
// 第二步仅对湿膜流量进行异常孤立点剔除干膜保持原样因为干膜可能有很多0和少量非零
var result = new List<Models.DataPoint>();
const double threshold = 3.0; // 孤立高点阈值(倍),比之前放宽
for (int i = 0; i < cleaned.Count; i++)
{
var current = cleaned[i];
bool isAbnormal = false;
// 仅检查中间的点(非首尾),且只检查湿膜流量
if (i > 0 && i < cleaned.Count - 1)
{
double prevWet = cleaned[i - 1].WetFlow;
double nextWet = cleaned[i + 1].WetFlow;
double maxNeighbor = Math.Max(prevWet, nextWet);
double minNeighbor = Math.Min(prevWet, nextWet);
// 如果当前湿膜流量远大于前后邻居的最大值孤立高峰且前后邻居都不为0
if (maxNeighbor > 0 && current.WetFlow > maxNeighbor * threshold)
isAbnormal = true;
// 如果当前湿膜流量远小于前后邻居的最小值(孤立低谷),一般很少见,但保留
if (minNeighbor > 0 && current.WetFlow < minNeighbor / threshold)
isAbnormal = true;
}
// 不剔除干膜中的非零点(即使孤立也不删)
if (!isAbnormal)
result.Add(current);
}
// 如果全部被误判为异常,则回退到原始清洗结果
return result.Count == 0 ? cleaned : result;
}
//private void GenerateReport()
//{
// ReportGenerator.GeneratePoreDistributionReport(Record);