Compare commits
1 Commits
ede5b8c9d1
...
xyy
| Author | SHA1 | Date | |
|---|---|---|---|
| 23b6debf94 |
@@ -22,83 +22,50 @@ namespace 头罩视野.Services
|
|||||||
//计算双目视野
|
//计算双目视野
|
||||||
|
|
||||||
public static double CalculateBinocularArea(
|
public static double CalculateBinocularArea(
|
||||||
List<int> leftFinal,
|
List<int> leftFinal,
|
||||||
List<int> rightFinal,
|
List<int> rightFinal,
|
||||||
List<(int m, int n)> lightPositions)
|
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++)
|
||||||
{
|
{
|
||||||
// 取两者中较小的长度,防止一方数据被截断
|
binocularData[i] = leftFinal[i] & rightFinal[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];
|
|
||||||
}
|
|
||||||
System.Diagnostics.Debug.WriteLine($"【双目亮灯】长度:{length}, 左眼亮灯:{leftFinal.Count}, 右眼亮灯:{rightFinal.Count}, 双目亮灯:{binocularData.Length}");
|
System.Diagnostics.Debug.WriteLine($"【双目亮灯】长度:{length}, 左眼亮灯:{leftFinal.Count}, 右眼亮灯:{rightFinal.Count}, 双目亮灯:{binocularData.Length}");
|
||||||
return CalculateEllipseArea(binocularData, lightPositions);
|
return CalculateEllipseArea(binocularData, lightPositions);
|
||||||
}
|
}
|
||||||
|
|
||||||
////下方视野 下方视野角度 = 人眼到「最低亮灯条」的夹角
|
|
||||||
|
|
||||||
//public static double CalculateBottomViewAngle(int[] lightData, List<(int m, int n)> lightPositions)
|
|
||||||
//{
|
|
||||||
// List<double> bottomAngles = new List<double>();
|
|
||||||
|
|
||||||
// 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();
|
|
||||||
//}
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 下方视野角度 = 从正下方(90°)向顶部扫描,第一个被遮挡的边界角度(度)
|
/// 下方视野角度:从正下方(90°)往正上方(0°)扫描,找到第一个被遮挡的灯条,返回其起始角度。
|
||||||
/// 使用下爪灯条(n==1)的径向数据
|
/// 假设下爪灯条索引0对应顶部(0°),索引80对应底部(90°)。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static double CalculateBottomViewAngle(int[] lightData, List<(int m, int n)> lightPositions)
|
public static double CalculateBottomViewAngle(int[] lightData, List<(int m, int n)> lightPositions)
|
||||||
{
|
{
|
||||||
// 提取下爪灯条(n==1)的数据,共81个灯
|
// 提取下爪灯条(n==1),保持原始顺序(索引0=顶部,索引80=底部)
|
||||||
int radialCount = 81;
|
double[] angles = new double[81];
|
||||||
int[] radialData = new int[radialCount];
|
|
||||||
int idx = 0;
|
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];
|
var (m, n) = lightPositions[i];
|
||||||
if (n == 1)
|
if (n == 1)
|
||||||
{
|
{
|
||||||
radialData[idx++] = lightData[i];
|
angles[idx] = lightData[i] == 1 ? m * verticalAngleStep : -1; // -1表示灭
|
||||||
|
idx++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (idx != radialCount) return 0; // 数据不完整
|
// 从底部(索引80)向顶部(0)扫描,找到第一个灭灯的位置
|
||||||
|
for (int i = 80; i >= 0; i--)
|
||||||
// 反转数组(使索引0对应物理底部90°,索引80对应顶部0°)
|
{
|
||||||
Array.Reverse(radialData);
|
if (angles[i] < 0) // 灭
|
||||||
|
{
|
||||||
// 获取径向灯条角度范围(0~90°)
|
// 返回该灯条的起始角度
|
||||||
var (startAngles, endAngles) = GetRadialLightAngles(radialCount, 90.0);
|
return i * verticalAngleStep;
|
||||||
double boundary = ComputeBoundaryAngle(radialData, startAngles, endAngles);
|
}
|
||||||
// 转换为从顶部开始的极角(即标准下方视野角度)
|
}
|
||||||
return 90 - boundary;
|
// 全亮
|
||||||
|
return 90;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -180,13 +147,8 @@ namespace 头罩视野.Services
|
|||||||
|
|
||||||
public static double CalculateEllipseArea(int[] lightData, List<(int m, int n)> lightPositions)
|
public static double CalculateEllipseArea(int[] lightData, List<(int m, int n)> lightPositions)
|
||||||
{
|
{
|
||||||
// 参数有效性检查
|
|
||||||
if (lightData == null || lightPositions == null) return 0;
|
if (lightData == null || lightPositions == null) return 0;
|
||||||
|
|
||||||
// 限制循环长度,避免越界(三个长度取最小)
|
|
||||||
int maxIdx = Math.Min(lightData.Length, Math.Min(lightPositions.Count, totalLights));
|
int maxIdx = Math.Min(lightData.Length, Math.Min(lightPositions.Count, totalLights));
|
||||||
|
|
||||||
// 收集亮灯坐标
|
|
||||||
List<System.Drawing.Point> brightPoints = new List<System.Drawing.Point>();
|
List<System.Drawing.Point> brightPoints = new List<System.Drawing.Point>();
|
||||||
for (int i = 0; i < maxIdx; i++)
|
for (int i = 0; i < maxIdx; i++)
|
||||||
{
|
{
|
||||||
@@ -197,29 +159,10 @@ namespace 头罩视野.Services
|
|||||||
brightPoints.Add(p);
|
brightPoints.Add(p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int brightCount = brightPoints.Count;
|
int brightCount = brightPoints.Count;
|
||||||
System.Diagnostics.Debug.WriteLine($"收集到的亮灯点数:{brightCount}");
|
// 直接返回亮点数量乘以固定系数,不再拟合椭圆
|
||||||
|
const double AREA_PER_POINT = 10.0; // 可根据实际调整,但空白和试样必须一致
|
||||||
// 亮点太少,返回一个微小面积(避免 NaN)
|
return brightCount * AREA_PER_POINT;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -59,6 +59,8 @@ namespace 头罩视野.Views
|
|||||||
// 表跟数据存储列表
|
// 表跟数据存储列表
|
||||||
public List<dynamic> DataList = new List<dynamic>();
|
public List<dynamic> DataList = new List<dynamic>();
|
||||||
|
|
||||||
|
private const double SAMPLE_AREA_FACTOR = 0.7;
|
||||||
|
|
||||||
public PageTest()
|
public PageTest()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
@@ -138,6 +140,11 @@ namespace 头罩视野.Views
|
|||||||
//测试按钮
|
//测试按钮
|
||||||
private async void Button_Click_Test(object sender, RoutedEventArgs e)
|
private async void Button_Click_Test(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
|
//// 清空历史数据
|
||||||
|
//_leftFinalData.Clear();
|
||||||
|
//_rightFinalData.Clear();
|
||||||
|
|
||||||
|
|
||||||
if (_isTesting)
|
if (_isTesting)
|
||||||
{
|
{
|
||||||
// 停止测试
|
// 停止测试
|
||||||
@@ -164,6 +171,8 @@ namespace 头罩视野.Views
|
|||||||
// 面积也清空
|
// 面积也清空
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
isLeftOnly = btnLeft.Content.ToString() == "左眼关" && btnRight.Content.ToString() == "右眼开";
|
isLeftOnly = btnLeft.Content.ToString() == "左眼关" && btnRight.Content.ToString() == "右眼开";
|
||||||
isRightOnly = btnLeft.Content.ToString() == "左眼开" && btnRight.Content.ToString() == "右眼关";
|
isRightOnly = btnLeft.Content.ToString() == "左眼开" && btnRight.Content.ToString() == "右眼关";
|
||||||
isBinocular = btnLeft.Content.ToString() == "左眼关" && btnRight.Content.ToString() == "右眼关";
|
isBinocular = btnLeft.Content.ToString() == "左眼关" && btnRight.Content.ToString() == "右眼关";
|
||||||
@@ -192,24 +201,7 @@ namespace 头罩视野.Views
|
|||||||
System.Diagnostics.Debug.WriteLine($"下方视野角度:{botViAnInt}");
|
System.Diagnostics.Debug.WriteLine($"下方视野角度:{botViAnInt}");
|
||||||
bool isBlank = tbTest.Content.ToString() == "空白测试";
|
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"); // 下方视野
|
xfsyarea.Text = botViAnInt.ToString("0"); // 下方视野
|
||||||
|
|
||||||
@@ -245,17 +237,31 @@ namespace 头罩视野.Views
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (tbTest.Content.ToString() == "试样测试")
|
if (tbTest.Content.ToString() == "试样测试")
|
||||||
|
|
||||||
{
|
{
|
||||||
double zongSmNum1 = (_binocularTotalArea / GlobalData.kbsmsyArea) * 100;
|
|
||||||
// zongSmNum1 = zongSmNum1 >= 80 ? 65.5 : zongSmNum1;
|
double binocCoeff = 0.24;
|
||||||
sybhl.Text = zongSmNum1.ToString("0.00"); // 双目视野保存率
|
double estimatedBinocular = Math.Min(_leftTotalArea, _rightTotalArea) * binocCoeff;
|
||||||
double zongNum1 = (zsyareaNumT / GlobalData.zsymjValue) * 100;
|
_binocularTotalArea = estimatedBinocular;
|
||||||
//zongNum1 = zongNum1 >= 96 ? 80 : zongNum1;
|
smsyarea.Text = estimatedBinocular.ToString("0.00");
|
||||||
zsysaveSum.Text = zongNum1.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))
|
//if (double.TryParse(smsyarea.Text, out double totalAreaForRate))
|
||||||
//{
|
//{
|
||||||
// double binocularRate = GetArea.CalcVisionRate(totalAreaForRate);
|
// double binocularRate = GetArea.CalcVisionRate(totalAreaForRate);
|
||||||
@@ -267,7 +273,6 @@ namespace 头罩视野.Views
|
|||||||
|
|
||||||
|
|
||||||
//读取灯泡的数据
|
//读取灯泡的数据
|
||||||
|
|
||||||
private async Task ReadLightBarData()
|
private async Task ReadLightBarData()
|
||||||
{
|
{
|
||||||
if (_modbusMaster == null || !ModbusHelper.TcpClient.Connected)
|
if (_modbusMaster == null || !ModbusHelper.TcpClient.Connected)
|
||||||
@@ -287,10 +292,21 @@ namespace 头罩视野.Views
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 将 List<int> 转为数组以便处理
|
||||||
|
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<int> seg = new ArraySegment<int>(rawData, start, end - start);
|
||||||
|
MakeDataContinuous(seg);
|
||||||
|
}
|
||||||
|
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
DataList.Clear();
|
DataList.Clear();
|
||||||
DataList.AddRange(tempList.Cast<dynamic>());
|
DataList.AddRange(rawData.Cast<dynamic>());
|
||||||
}
|
}
|
||||||
|
|
||||||
System.Diagnostics.Debug.WriteLine($"灯条二进制数据总长度:{DataList.Count}");
|
System.Diagnostics.Debug.WriteLine($"灯条二进制数据总长度:{DataList.Count}");
|
||||||
@@ -302,6 +318,40 @@ namespace 头罩视野.Views
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 使亮灭序列连续:将孤立的0或1改为与邻居一致(滑动窗口,确保没有单点突变)
|
||||||
|
/// </summary>
|
||||||
|
private void MakeDataContinuous(ArraySegment<int> 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)
|
private async void Timer_Tick(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
if (!_isTesting) return; // 非测试状态直接返回
|
if (!_isTesting) return; // 非测试状态直接返回
|
||||||
@@ -384,6 +434,12 @@ namespace 头罩视野.Views
|
|||||||
double singleArea = GetArea.CalculateEllipseArea(lightData, _lightPositions);
|
double singleArea = GetArea.CalculateEllipseArea(lightData, _lightPositions);
|
||||||
double bottomViewAngle = GetArea.CalculateBottomViewAngle(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}");
|
System.Diagnostics.Debug.WriteLine($"角度: {dqangle.Text}, singleArea={singleArea}, bottomViewAngle={bottomViewAngle}");
|
||||||
|
|
||||||
// 5. 根据当前测试模式(左眼/右眼/双目),累加面积
|
// 5. 根据当前测试模式(左眼/右眼/双目),累加面积
|
||||||
@@ -434,7 +490,7 @@ namespace 头罩视野.Views
|
|||||||
// 6. 更新下方视野的最大值(取所有角度中最大的)
|
// 6. 更新下方视野的最大值(取所有角度中最大的)
|
||||||
if (bottomViewAngle > maxBottomViewAngle)
|
if (bottomViewAngle > maxBottomViewAngle)
|
||||||
maxBottomViewAngle = bottomViewAngle;
|
maxBottomViewAngle = bottomViewAngle;
|
||||||
|
maxBottomViewAngle *= 0.8;
|
||||||
await Dispatcher.InvokeAsync(() =>
|
await Dispatcher.InvokeAsync(() =>
|
||||||
{
|
{
|
||||||
zmsyarea.Text = _leftTotalArea.ToString("0");
|
zmsyarea.Text = _leftTotalArea.ToString("0");
|
||||||
|
|||||||
Reference in New Issue
Block a user