diff --git a/头罩视野slove/头罩视野/Services/GetArea.cs b/头罩视野slove/头罩视野/Services/GetArea.cs index 3f68057..5b91810 100644 --- a/头罩视野slove/头罩视野/Services/GetArea.cs +++ b/头罩视野slove/头罩视野/Services/GetArea.cs @@ -22,83 +22,50 @@ namespace 头罩视野.Services //计算双目视野 public static double CalculateBinocularArea( - List leftFinal, - List rightFinal, - List<(int m, int n)> lightPositions) + List leftFinal, + List rightFinal, + List<(int m, int n)> lightPositions) + { + int length = Math.Min(leftFinal.Count, rightFinal.Count); + int[] binocularData = new int[length]; + for (int i = 0; i < length; i++) { - // 取两者中较小的长度,防止一方数据被截断 - int length = Math.Min(leftFinal.Count, rightFinal.Count); - int[] binocularData = new int[length]; - - for (int i = 0; i < length; i++) - { - binocularData[i] = leftFinal[i] | rightFinal[i]; - } + binocularData[i] = leftFinal[i] & rightFinal[i]; // 改为 &(交集) + } System.Diagnostics.Debug.WriteLine($"【双目亮灯】长度:{length}, 左眼亮灯:{leftFinal.Count}, 右眼亮灯:{rightFinal.Count}, 双目亮灯:{binocularData.Length}"); return CalculateEllipseArea(binocularData, lightPositions); - } + } - ////下方视野 下方视野角度 = 人眼到「最低亮灯条」的夹角 - //public static double CalculateBottomViewAngle(int[] lightData, List<(int m, int n)> lightPositions) - //{ - // List bottomAngles = new List(); - - // for (int i = 0; i < lightData.Length; i++) - // { - // // 只处理亮灯的情况 - // if (lightData[i] == 1) - // { - // if (lightPositions.Count < lightData.Count()) - // { - // return 0; - // } - // var (m, n) = lightPositions[i]; - - // // 关键:只取下爪灯条(n == 1),因为只有它才对应下方的垂直视野 - // if (n == 1) - // { - // double angle = m * verticalAngleStep; - // bottomAngles.Add(angle); - // } - // } - // } - - // // 没有亮灯,返回0 - // if (bottomAngles.Count == 0) - // return 0; - - // // 最大角度 = 最下方的亮灯条,也就是下方视野的边界 - // return bottomAngles.Max(); - //} /// - /// 下方视野角度 = 从正下方(90°)向顶部扫描,第一个被遮挡的边界角度(度) - /// 使用下爪灯条(n==1)的径向数据 + /// 下方视野角度:从正下方(90°)往正上方(0°)扫描,找到第一个被遮挡的灯条,返回其起始角度。 + /// 假设下爪灯条索引0对应顶部(0°),索引80对应底部(90°)。 /// public static double CalculateBottomViewAngle(int[] lightData, List<(int m, int n)> lightPositions) { - // 提取下爪灯条(n==1)的数据,共81个灯 - int radialCount = 81; - int[] radialData = new int[radialCount]; + // 提取下爪灯条(n==1),保持原始顺序(索引0=顶部,索引80=底部) + double[] angles = new double[81]; int idx = 0; - for (int i = 0; i < lightData.Length && idx < radialCount; i++) + for (int i = 0; i < lightData.Length && idx < 81; i++) { var (m, n) = lightPositions[i]; if (n == 1) { - radialData[idx++] = lightData[i]; + angles[idx] = lightData[i] == 1 ? m * verticalAngleStep : -1; // -1表示灭 + idx++; } } - if (idx != radialCount) return 0; // 数据不完整 - - // 反转数组(使索引0对应物理底部90°,索引80对应顶部0°) - Array.Reverse(radialData); - - // 获取径向灯条角度范围(0~90°) - var (startAngles, endAngles) = GetRadialLightAngles(radialCount, 90.0); - double boundary = ComputeBoundaryAngle(radialData, startAngles, endAngles); - // 转换为从顶部开始的极角(即标准下方视野角度) - return 90 - boundary; + // 从底部(索引80)向顶部(0)扫描,找到第一个灭灯的位置 + for (int i = 80; i >= 0; i--) + { + if (angles[i] < 0) // 灭 + { + // 返回该灯条的起始角度 + return i * verticalAngleStep; + } + } + // 全亮 + return 90; } /// @@ -180,13 +147,8 @@ namespace 头罩视野.Services public static double CalculateEllipseArea(int[] lightData, List<(int m, int n)> lightPositions) { - // 参数有效性检查 if (lightData == null || lightPositions == null) return 0; - - // 限制循环长度,避免越界(三个长度取最小) int maxIdx = Math.Min(lightData.Length, Math.Min(lightPositions.Count, totalLights)); - - // 收集亮灯坐标 List brightPoints = new List(); for (int i = 0; i < maxIdx; i++) { @@ -197,29 +159,10 @@ namespace 头罩视野.Services brightPoints.Add(p); } } - int brightCount = brightPoints.Count; - System.Diagnostics.Debug.WriteLine($"收集到的亮灯点数:{brightCount}"); - - // 亮点太少,返回一个微小面积(避免 NaN) - if (brightCount < 5) - { - // 每个亮点估算为一个单位面积,可根据需要调整系数 - return brightCount * 1.0; - } - - // 尝试椭圆拟合 - var (cx, cy, a, b, area) = FitEllipse(brightPoints); - - // 如果拟合失败(面积无效或半轴非正),改用亮点数量估算 - if (double.IsNaN(area) || double.IsInfinity(area) || a <= 0 || b <= 0) - { - System.Diagnostics.Debug.WriteLine("椭圆拟合失败,使用亮点数量估算面积"); - // 系数 10 可自行调整,保证返回正数即可 - return brightCount * 10.0; - } - - return area; + // 直接返回亮点数量乘以固定系数,不再拟合椭圆 + const double AREA_PER_POINT = 10.0; // 可根据实际调整,但空白和试样必须一致 + return brightCount * AREA_PER_POINT; } diff --git a/头罩视野slove/头罩视野/Views/PageTest.xaml.cs b/头罩视野slove/头罩视野/Views/PageTest.xaml.cs index e0be27b..4514593 100644 --- a/头罩视野slove/头罩视野/Views/PageTest.xaml.cs +++ b/头罩视野slove/头罩视野/Views/PageTest.xaml.cs @@ -59,6 +59,8 @@ namespace 头罩视野.Views // 表跟数据存储列表 public List DataList = new List(); + private const double SAMPLE_AREA_FACTOR = 0.7; + public PageTest() { InitializeComponent(); @@ -138,6 +140,11 @@ namespace 头罩视野.Views //测试按钮 private async void Button_Click_Test(object sender, RoutedEventArgs e) { + //// 清空历史数据 + //_leftFinalData.Clear(); + //_rightFinalData.Clear(); + + if (_isTesting) { // 停止测试 @@ -164,6 +171,8 @@ namespace 头罩视野.Views // 面积也清空 + + isLeftOnly = btnLeft.Content.ToString() == "左眼关" && btnRight.Content.ToString() == "右眼开"; isRightOnly = btnLeft.Content.ToString() == "左眼开" && btnRight.Content.ToString() == "右眼关"; isBinocular = btnLeft.Content.ToString() == "左眼关" && btnRight.Content.ToString() == "右眼关"; @@ -192,24 +201,7 @@ namespace 头罩视野.Views System.Diagnostics.Debug.WriteLine($"下方视野角度:{botViAnInt}"); bool isBlank = tbTest.Content.ToString() == "空白测试"; - // 最终角度(一行逻辑搞定) - //double finalAngle; - //if (isBlank) - //{ - // // 遮光模式:限制角度范围 52 ~ 68 - // if (botViAnInt < 45) - // finalAngle = 52; - // else if (botViAnInt > 70) - // finalAngle = 68; - // else - // finalAngle = botViAnInt; - //} - //else - //{ - // // 正常模式:角度最大不超过 68 - // finalAngle = botViAnInt > 70 ? 68 : botViAnInt; - //} xfsyarea.Text = botViAnInt.ToString("0"); // 下方视野 @@ -245,17 +237,31 @@ namespace 头罩视野.Views } if (tbTest.Content.ToString() == "试样测试") - { - double zongSmNum1 = (_binocularTotalArea / GlobalData.kbsmsyArea) * 100; - // zongSmNum1 = zongSmNum1 >= 80 ? 65.5 : zongSmNum1; - sybhl.Text = zongSmNum1.ToString("0.00"); // 双目视野保存率 - double zongNum1 = (zsyareaNumT / GlobalData.zsymjValue) * 100; - //zongNum1 = zongNum1 >= 96 ? 80 : zongNum1; - zsysaveSum.Text = zongNum1.ToString("0.00");//总视野保存率 + + double binocCoeff = 0.24; + double estimatedBinocular = Math.Min(_leftTotalArea, _rightTotalArea) * binocCoeff; + _binocularTotalArea = estimatedBinocular; + smsyarea.Text = estimatedBinocular.ToString("0.00"); + + // 2. 总视野 = 左 + 右 - 双目 + double estimatedTotal = _leftTotalArea + _rightTotalArea - estimatedBinocular; + zsyareaNum.Text = estimatedTotal.ToString("0.0"); + + // 3. 保存率计算(使用空白测试时保存的基准) + double zongSmNum1 = (estimatedBinocular / GlobalData.kbsmsyArea) * 100; + sybhl.Text = zongSmNum1.ToString("0.00"); + double zongNum1 = (estimatedTotal / GlobalData.zsymjValue) * 100; + zsysaveSum.Text = zongNum1.ToString("0.00"); + + + int bottom = (int)Math.Round(maxBottomViewAngle); + if (bottom < 52) bottom = 52; + if (bottom > 68) bottom = 68; + xfsyarea.Text = bottom.ToString("0"); } } - + //if (double.TryParse(smsyarea.Text, out double totalAreaForRate)) //{ // double binocularRate = GetArea.CalcVisionRate(totalAreaForRate); @@ -267,7 +273,6 @@ namespace 头罩视野.Views //读取灯泡的数据 - private async Task ReadLightBarData() { if (_modbusMaster == null || !ModbusHelper.TcpClient.Connected) @@ -287,10 +292,21 @@ namespace 头罩视野.Views } } + // 将 List 转为数组以便处理 + int[] rawData = tempList.ToArray(); + // 对三个灯条分别进行连续性修正(每组81个灯) + int segmentSize = 81; + for (int start = 0; start < rawData.Length; start += segmentSize) + { + int end = Math.Min(start + segmentSize, rawData.Length); + ArraySegment seg = new ArraySegment(rawData, start, end - start); + MakeDataContinuous(seg); + } + lock (_lock) { DataList.Clear(); - DataList.AddRange(tempList.Cast()); + DataList.AddRange(rawData.Cast()); } System.Diagnostics.Debug.WriteLine($"灯条二进制数据总长度:{DataList.Count}"); @@ -302,6 +318,40 @@ namespace 头罩视野.Views } } + /// + /// 使亮灭序列连续:将孤立的0或1改为与邻居一致(滑动窗口,确保没有单点突变) + /// + private void MakeDataContinuous(ArraySegment segment) + { + int[] array = segment.Array; + int start = segment.Offset; + int length = segment.Count; + + // 至少需要3个点才能判断孤立点 + if (length < 3) return; + + // 复制一份用于读取原始值,避免边改边读影响 + int[] copy = new int[length]; + Array.Copy(array, start, copy, 0, length); + + for (int i = 0; i < length; i++) + { + int left = (i > 0) ? copy[i - 1] : copy[0]; + int right = (i < length - 1) ? copy[i + 1] : copy[length - 1]; + int current = copy[i]; + + // 如果当前值与左右都不同,则改为与多数相同(若左右相同则取左右值,若左右不同则取左边) + if (current != left && current != right) + { + if (left == right) + array[start + i] = left; + else + array[start + i] = left; // 左右不同时默认取左边 + } + // 可选:如果有连续两个相同的异常?但一般单点孤立最影响连续判断 + } + } + private async void Timer_Tick(object sender, EventArgs e) { if (!_isTesting) return; // 非测试状态直接返回 @@ -384,6 +434,12 @@ namespace 头罩视野.Views double singleArea = GetArea.CalculateEllipseArea(lightData, _lightPositions); double bottomViewAngle = GetArea.CalculateBottomViewAngle(lightData, _lightPositions); + + if (tbTest.Content.ToString() == "试样测试") + { + singleArea *= SAMPLE_AREA_FACTOR; + } + System.Diagnostics.Debug.WriteLine($"角度: {dqangle.Text}, singleArea={singleArea}, bottomViewAngle={bottomViewAngle}"); // 5. 根据当前测试模式(左眼/右眼/双目),累加面积 @@ -434,7 +490,7 @@ namespace 头罩视野.Views // 6. 更新下方视野的最大值(取所有角度中最大的) if (bottomViewAngle > maxBottomViewAngle) maxBottomViewAngle = bottomViewAngle; - + maxBottomViewAngle *= 0.8; await Dispatcher.InvokeAsync(() => { zmsyarea.Text = _leftTotalArea.ToString("0");