解决车辆漏检和多检的问题

This commit is contained in:
tian 2026-01-07 12:29:19 +08:00
parent 792d6d249c
commit cbc63809f0
4 changed files with 119 additions and 93 deletions

View File

@ -2,6 +2,11 @@
## 功能点
### [2026/1/6]
1. [ ] BUG虚拟车辆模型每次点击都重建
2. [ ] BUG碰撞结果高亮应该显示精确检测的结果
### [2025/12/25]
1. [x] (功能)对路径上的各点进行坐标编辑

View File

@ -498,14 +498,12 @@ namespace NavisworksTransport.Core.Animation
LogManager.Info(spatialIndexManager.GetStatistics());
double objectDiagonal = Math.Sqrt(
boundingBoxSize.X * boundingBoxSize.X +
boundingBoxSize.Y * boundingBoxSize.Y +
boundingBoxSize.Z * boundingBoxSize.Z
);
// 🔥 修改:使用车辆的最大水平尺寸而不是对角线
// 原因:车辆在通道上水平移动,主要检测侧面碰撞
double maxHorizontalDimension = Math.Max(boundingBoxSize.X, boundingBoxSize.Y);
searchRadiusInModelUnits = objectDiagonal + _detectionGap;
LogManager.Info($"空间查询半径: {searchRadiusInModelUnits:F2} 模型单位");
searchRadiusInModelUnits = maxHorizontalDimension + _detectionGap;
LogManager.Info($"空间查询半径: {searchRadiusInModelUnits:F2} 模型单位 (基于最大水平尺寸: {maxHorizontalDimension:F2})");
}
// 3. 生成每一帧
@ -541,6 +539,9 @@ namespace NavisworksTransport.Core.Animation
double yawRadians = ComputeYawOnPath(i, allSampledPoints, edgeIndex, edgeProgress);
// 🔥 调试日志输出framePosition
LogManager.Debug($"[调试] 帧{i}: framePosition=({framePosition.X:F2},{framePosition.Y:F2},{framePosition.Z:F2})");
// 创建帧并检测碰撞
var frame = new AnimationFrame
{
@ -552,7 +553,33 @@ namespace NavisworksTransport.Core.Animation
};
// 虚拟碰撞检测
var virtualBoundingBox = CreateVirtualBoundingBox(framePosition, boundingBoxSize, yawRadians);
// 计算车辆包围盒相对于framePosition的偏移
var currentVehicleBoundingBox = _animatedObject.BoundingBox();
var originalCenter = new Point3D(
(currentVehicleBoundingBox.Min.X + currentVehicleBoundingBox.Max.X) / 2,
(currentVehicleBoundingBox.Min.Y + currentVehicleBoundingBox.Max.Y) / 2,
(currentVehicleBoundingBox.Min.Z + currentVehicleBoundingBox.Max.Z) / 2
);
// 计算偏移量当前包围盒中心到framePosition的偏移
var offsetX = framePosition.X - originalCenter.X;
var offsetY = framePosition.Y - originalCenter.Y;
var offsetZ = framePosition.Z - currentVehicleBoundingBox.Min.Z; // Z方向framePosition是底面originalCenter是中心
// 创建新的包围盒将当前包围盒移动到framePosition
var virtualBoundingBox = new BoundingBox3D(
new Point3D(
currentVehicleBoundingBox.Min.X + offsetX,
currentVehicleBoundingBox.Min.Y + offsetY,
framePosition.Z // 底面Z坐标
),
new Point3D(
currentVehicleBoundingBox.Max.X + offsetX,
currentVehicleBoundingBox.Max.Y + offsetY,
framePosition.Z + (currentVehicleBoundingBox.Max.Z - currentVehicleBoundingBox.Min.Z) // 保持高度
)
);
IEnumerable<ModelItem> nearbyObjects;
if (manualOverrideActive)
@ -572,7 +599,19 @@ namespace NavisworksTransport.Core.Animation
{
var colliderBox = collider.BoundingBox();
if (BoundingBoxGeometryUtils.BoundingBoxesIntersectWithTolerance(virtualBoundingBox, colliderBox, _detectionGap))
// 🔥 调试日志:输出包围盒信息
bool intersects = BoundingBoxGeometryUtils.BoundingBoxesIntersectWithTolerance(virtualBoundingBox, colliderBox, _detectionGap);
if (intersects)
{
double distanceX = Math.Max(0, Math.Max(virtualBoundingBox.Min.X - colliderBox.Max.X, colliderBox.Min.X - virtualBoundingBox.Max.X));
double distanceY = Math.Max(0, Math.Max(virtualBoundingBox.Min.Y - colliderBox.Max.Y, colliderBox.Min.Y - virtualBoundingBox.Max.Y));
double distanceZ = Math.Max(0, Math.Max(virtualBoundingBox.Min.Z - colliderBox.Max.Z, colliderBox.Min.Z - virtualBoundingBox.Max.Z));
double minDistance = Math.Max(distanceX, Math.Max(distanceY, distanceZ));
LogManager.Debug($"[碰撞检测] 帧{i}: 车辆包围盒=[({virtualBoundingBox.Min.X:F2},{virtualBoundingBox.Min.Y:F2},{virtualBoundingBox.Min.Z:F2})-({virtualBoundingBox.Max.X:F2},{virtualBoundingBox.Max.Y:F2},{virtualBoundingBox.Max.Z:F2})], 物体={collider.DisplayName}, 包围盒=[({colliderBox.Min.X:F2},{colliderBox.Min.Y:F2},{colliderBox.Min.Z:F2})-({colliderBox.Max.X:F2},{colliderBox.Max.Y:F2},{colliderBox.Max.Z:F2})], 最小距离={minDistance:F3}m, 检测间隙={_detectionGap:F3}m");
}
if (intersects)
{
var collisionResult = new CollisionResult
{
@ -801,81 +840,6 @@ namespace NavisworksTransport.Core.Animation
return new Vector3D(cross.X, cross.Y, cross.Z).Normalize();
}
/// <summary>
/// 创建虚拟包围盒(用于碰撞检测),考虑旋转
/// </summary>
private BoundingBox3D CreateVirtualBoundingBox(Point3D position, Vector3D size, double yawRadians = 0.0)
{
// 如果没有旋转,返回简单的轴对齐包围盒
if (Math.Abs(yawRadians) < 1e-6)
{
return new BoundingBox3D(
new Point3D(
position.X - size.X / 2,
position.Y - size.Y / 2,
position.Z
),
new Point3D(
position.X + size.X / 2,
position.Y + size.Y / 2,
position.Z + size.Z
)
);
}
// 有旋转:计算旋转后的轴对齐包围盒
// 1. 定义物体包围盒的4个底面角点相对于中心
double halfX = size.X / 2;
double halfY = size.Y / 2;
var corners = new[]
{
new { x = -halfX, y = -halfY },
new { x = halfX, y = -halfY },
new { x = halfX, y = halfY },
new { x = -halfX, y = halfY }
};
// 2. 旋转这些角点
double cos = Math.Cos(yawRadians);
double sin = Math.Sin(yawRadians);
double minX = double.MaxValue;
double maxX = double.MinValue;
double minY = double.MaxValue;
double maxY = double.MinValue;
foreach (var corner in corners)
{
// 旋转公式x' = x*cos - y*sin, y' = x*sin + y*cos
double rotatedX = corner.x * cos - corner.y * sin;
double rotatedY = corner.x * sin + corner.y * cos;
minX = Math.Min(minX, rotatedX);
maxX = Math.Max(maxX, rotatedX);
minY = Math.Min(minY, rotatedY);
maxY = Math.Max(maxY, rotatedY);
}
// 3. 创建包含所有旋转后角点的轴对齐包围盒
return new BoundingBox3D(
new Point3D(
position.X + minX,
position.Y + minY,
position.Z
),
new Point3D(
position.X + maxX,
position.Y + maxY,
position.Z + size.Z
)
);
}
/// <summary>
/// 开始播放动画
/// </summary>
@ -888,7 +852,6 @@ namespace NavisworksTransport.Core.Animation
throw new InvalidOperationException("请先调用SetupAnimation设置动画参数");
}
// 创建 TimeLiner 任务(如果可用且未创建过)
if (_timeLinerManager != null && _timeLinerManager.IsTimeLinerAvailable)
{

View File

@ -214,11 +214,9 @@ namespace NavisworksTransport.Core.Spatial
if (getPositionFunc == null)
throw new ArgumentNullException(nameof(getPositionFunc));
return FindInRadius(center, radius, obj =>
{
var objPos = getPositionFunc(obj);
return center.Distance(objPos);
});
// 🔥 修改:不做距离过滤,只返回格子范围内的所有对象
// 原因:使用中心点距离过滤会漏检包围盒很大的对象(如墙)
return FindInRadius(center, radius, distanceFunc: null);
}
/// <summary>

View File

@ -99,9 +99,69 @@ namespace NavisworksTransport.Utils
/// <returns>如果包围盒在容差范围内相交则返回true</returns>
public static bool BoundingBoxesIntersectWithTolerance(BoundingBox3D box1, BoundingBox3D box2, double tolerance)
{
return box1.Min.X <= box2.Max.X + tolerance && box1.Max.X >= box2.Min.X - tolerance &&
box1.Min.Y <= box2.Max.Y + tolerance && box1.Max.Y >= box2.Min.Y - tolerance &&
box1.Min.Z <= box2.Max.Z + tolerance && box1.Max.Z >= box2.Min.Z - tolerance;
// 🔥 修改:计算每个方向的最小距离
// 只有当所有方向的最小距离都小于检测间隙时,才判定为碰撞
// 这样可以避免"一个方向相交,另一个方向很远"的情况
// 计算X方向的最小距离
double distanceX;
if (box1.Max.X < box2.Min.X)
{
// box1在box2左侧
distanceX = box2.Min.X - box1.Max.X;
}
else if (box2.Max.X < box1.Min.X)
{
// box2在box1左侧
distanceX = box1.Min.X - box2.Max.X;
}
else
{
// X方向相交
distanceX = 0;
}
// 计算Y方向的最小距离
double distanceY;
if (box1.Max.Y < box2.Min.Y)
{
// box1在box2下方
distanceY = box2.Min.Y - box1.Max.Y;
}
else if (box2.Max.Y < box1.Min.Y)
{
// box2在box1下方
distanceY = box1.Min.Y - box2.Max.Y;
}
else
{
// Y方向相交
distanceY = 0;
}
// 计算Z方向的最小距离
double distanceZ;
if (box1.Max.Z < box2.Min.Z)
{
// box1在box2下方
distanceZ = box2.Min.Z - box1.Max.Z;
}
else if (box2.Max.Z < box1.Min.Z)
{
// box2在box1下方
distanceZ = box1.Min.Z - box2.Max.Z;
}
else
{
// Z方向相交
distanceZ = 0;
}
// 计算最大距离(最接近的方向)
double maxDistance = Math.Max(distanceX, Math.Max(distanceY, distanceZ));
// 只有当最大距离小于检测间隙时,才判定为碰撞
return maxDistance <= tolerance;
}
/// <summary>