Clarify ground tracked-point semantics

This commit is contained in:
tian 2026-04-06 08:55:53 +08:00
parent 348b3a75bf
commit 8a7563a4fe
5 changed files with 94 additions and 158 deletions

View File

@ -296,6 +296,16 @@ var rotation = new Rotation3D(qw, qx, qy, qz); // 错误
- 走平面姿态链
- 已禁止偷偷退回旧 `yaw` 方案
- 起点、逐帧、终点、通行空间,必须共享同一套尺寸语义
- `Ground + 真实物体` 的业务跟踪点固定为**原始包围盒中心**
- 旋转后的实时 `BoundingBox.Center` 只允许用于:
- 诊断旋转后漂移
- 计算当帧补偿
- 校验补偿结果
- 不允许把实时 `BoundingBox.Center` 直接当成:
- 起点目标跟踪点
- 逐帧业务跟踪点
- 碰撞记录主语义跟踪点
- 如果 `Ground` 出现“起点正确但越走越偏”或“起点偏了但拐弯后偏差反而减小”,优先检查是否把实时包围盒中心误当成了业务跟踪点
#### Rail

View File

@ -149,6 +149,23 @@ namespace NavisworksTransport.UnitTests.CoordinateSystem
Assert.IsFalse(PathAnimationManager.ShouldUseRealObjectOverrideRotation(false));
}
[TestMethod]
public void ShouldUseOriginalBoundingBoxCenterForBusinessTrackedPosition_ShouldEnableOnlyForGroundRealObjects()
{
Assert.IsTrue(PathAnimationManager.ShouldUseOriginalBoundingBoxCenterForBusinessTrackedPosition(
PathType.Ground,
isRealObjectMode: true));
Assert.IsFalse(PathAnimationManager.ShouldUseOriginalBoundingBoxCenterForBusinessTrackedPosition(
PathType.Ground,
isRealObjectMode: false));
Assert.IsFalse(PathAnimationManager.ShouldUseOriginalBoundingBoxCenterForBusinessTrackedPosition(
PathType.Hoisting,
isRealObjectMode: true));
Assert.IsFalse(PathAnimationManager.ShouldUseOriginalBoundingBoxCenterForBusinessTrackedPosition(
PathType.Rail,
isRealObjectMode: true));
}
[TestMethod]
public void ResolveHoistingPoseSourceLabel_ShouldDescribeCachedBasePose()
{

View File

@ -111,8 +111,19 @@
- `AnimatedObjectTrackedPosition` 的唯一语义:
- 当前动画主链路使用的跟踪点位置。
- 当前主链路已经切到:
- 几何中心跟踪。
- `Ground + 真实物体` 当前必须明确区分两类“中心”:
- 业务跟踪点应固定为物体**原始包围盒中心**
- 不是旋转后的实时包围盒中心
- 实时包围盒中心的用途仅限于:
- 诊断旋转后漂移
- 计算当帧补偿
- 验证补偿结果
- 不允许把实时包围盒中心直接回写成 `Ground` 路径的业务跟踪点定义。
- 这条规则的直接影响范围包括:
- 起点落位
- 逐帧补偿
- 动画日志
- 碰撞与结果记录语义
- 如果后续动画跟踪点再变,数据库和碰撞结果语义必须同步更新。
### 4.4 虚拟物体资源

View File

@ -186,7 +186,7 @@ namespace NavisworksTransport.Core.Animation
private Transform3D _originalTransform;
private Point3D _originalCenter; // 存储部件的原始中心位置
private Point3D _trackedPosition; // 动画管理器内部跟踪的位置状态,不等于宿主即时读回位置
private Point3D _trackedPosition; // 动画管理器内部业务跟踪点,不等于宿主即时读回的实时包围盒中心
private AnimationState _currentState = AnimationState.Idle;
private double _pausedProgress = 0.0; // 暂停时的进度0-1之间
private double _currentYaw = 0.0; // 当前偏航角(弧度)
@ -484,7 +484,7 @@ namespace NavisworksTransport.Core.Animation
}
var originalBoundingBox = objectToRestore.BoundingBox();
_trackedPosition = GetTrackedObjectPosition(objectToRestore);
_trackedPosition = ResolveInitialBusinessTrackedPosition(objectToRestore);
string objectName = isVirtual ? "虚拟物体" : objectToRestore.DisplayName;
_groundRealObjectStartCompensation = new Vector3D(0, 0, 0);
@ -525,7 +525,7 @@ namespace NavisworksTransport.Core.Animation
{
_originalTransform = animatedObject.Transform;
_originalCenter = animatedObject.BoundingBox().Center;
_trackedPosition = GetTrackedObjectPosition(animatedObject);
_trackedPosition = ResolveInitialBusinessTrackedPosition(animatedObject);
SyncTrackedRotationToObjectReference(animatedObject, isVirtualObject: false);
}
}
@ -689,7 +689,7 @@ namespace NavisworksTransport.Core.Animation
ResetRealObjectReferenceRotation();
_originalTransform = animatedObject.Transform;
_originalCenter = animatedObject.BoundingBox().Center;
_trackedPosition = GetTrackedObjectPosition(animatedObject);
_trackedPosition = ResolveInitialBusinessTrackedPosition(animatedObject);
if (isVirtualObject)
{
_currentYaw = ModelItemTransformHelper.GetYawFromTransform(_originalTransform);
@ -808,12 +808,12 @@ namespace NavisworksTransport.Core.Animation
UpdateObjectPosition(startPosition, planarRotation);
LogManager.Info("[移动到起点] 地面/吊装路径已应用完整三维姿态");
var startAppliedPoint = GetTrackedObjectPosition(CurrentControlledObject ?? _animatedObject);
var startAppliedPoint = GetLiveBoundingBoxCenter(CurrentControlledObject ?? _animatedObject);
LogManager.Info(
$"[路径起点诊断] 起点应用后实际trackedPoint=({startAppliedPoint.X:F3},{startAppliedPoint.Y:F3},{startAppliedPoint.Z:F3}), " +
$"[路径起点诊断] 起点应用后实际包围盒中心=({startAppliedPoint.X:F3},{startAppliedPoint.Y:F3},{startAppliedPoint.Z:F3}), " +
$"相对目标偏差=({startAppliedPoint.X - startPosition.X:F3},{startAppliedPoint.Y - startPosition.Y:F3},{startAppliedPoint.Z - startPosition.Z:F3})");
// 🔥 起点不再进行二次补偿,旋转偏移由 MoveItemIncrementallyToTrackedPositionAndRotation 内部处理
// Ground 起点不再进行二次补偿;业务跟踪点语义由 _trackedPosition 主链负责
if (IsRealObjectMode && _route.PathType == PathType.Ground)
{
var startOffset = new Vector3D(
@ -875,9 +875,9 @@ namespace NavisworksTransport.Core.Animation
// 使用 UpdateObjectPosition 统一处理移动和旋转
UpdateObjectPosition(startPosition, yaw);
var startAppliedPoint = GetTrackedObjectPosition(CurrentControlledObject ?? _animatedObject);
var startAppliedPoint = GetLiveBoundingBoxCenter(CurrentControlledObject ?? _animatedObject);
LogManager.Info(
$"[路径起点诊断] 起点应用后实际trackedPoint=({startAppliedPoint.X:F3},{startAppliedPoint.Y:F3},{startAppliedPoint.Z:F3}), " +
$"[路径起点诊断] 起点应用后实际包围盒中心=({startAppliedPoint.X:F3},{startAppliedPoint.Y:F3},{startAppliedPoint.Z:F3}), " +
$"相对目标偏差=({startAppliedPoint.X - startPosition.X:F3},{startAppliedPoint.Y - startPosition.Y:F3},{startAppliedPoint.Z - startPosition.Z:F3})");
}
@ -922,7 +922,7 @@ namespace NavisworksTransport.Core.Animation
ResetRealObjectReferenceRotation();
_originalTransform = animatedObject.Transform;
_originalCenter = animatedObject.BoundingBox().Center;
_trackedPosition = GetTrackedObjectPosition(animatedObject);
_trackedPosition = ResolveInitialBusinessTrackedPosition(animatedObject);
if (isVirtualObject)
{
_currentYaw = ModelItemTransformHelper.GetYawFromTransform(_originalTransform);
@ -989,11 +989,11 @@ namespace NavisworksTransport.Core.Animation
_groundRealObjectStartCompensation = new Vector3D(0, 0, 0);
_hasGroundRealObjectStartCompensation = false;
var startAppliedPoint = GetTrackedObjectPosition(CurrentControlledObject ?? _animatedObject);
var startAppliedPoint = GetLiveBoundingBoxCenter(CurrentControlledObject ?? _animatedObject);
LogManager.Info(
$"[平移到起点] 已按终点原始位姿残差搬运到起点: 路径point0=({pathStartPoint.X:F3},{pathStartPoint.Y:F3},{pathStartPoint.Z:F3}), " +
$"目标trackedPoint=({startPosition.X:F3},{startPosition.Y:F3},{startPosition.Z:F3}), " +
$"实际trackedPoint=({startAppliedPoint.X:F3},{startAppliedPoint.Y:F3},{startAppliedPoint.Z:F3}), 路径类型={_route.PathType.GetDisplayName()}");
$"实际包围盒中心=({startAppliedPoint.X:F3},{startAppliedPoint.Y:F3},{startAppliedPoint.Z:F3}), 路径类型={_route.PathType.GetDisplayName()}");
return true;
}
catch (Exception ex)
@ -2786,8 +2786,11 @@ namespace NavisworksTransport.Core.Animation
try
{
var actualHostPosition = GetTrackedObjectPosition(controlledObject);
currentPositionForTransform = actualHostPosition;
var actualHostPosition = GetLiveBoundingBoxCenter(controlledObject);
if (!(IsRealObjectMode && _route?.PathType == PathType.Ground))
{
currentPositionForTransform = actualHostPosition;
}
LogManager.Info(
$"[动画姿态入口] {controlledObject.DisplayName} 宿主即时读回点=({actualHostPosition.X:F3},{actualHostPosition.Y:F3},{actualHostPosition.Z:F3})");
@ -2830,26 +2833,12 @@ namespace NavisworksTransport.Core.Animation
$"Y=({targetLinear.Get(0, 1):F4},{targetLinear.Get(1, 1):F4},{targetLinear.Get(2, 1):F4}), " +
$"Z=({targetLinear.Get(0, 2):F4},{targetLinear.Get(1, 2):F4},{targetLinear.Get(2, 2):F4})");
// 🔥 Ground 路径使用带旋转偏移补偿的新方法
// Rail/Hoisting 保持原行为
if (_route?.PathType == PathType.Ground && IsRealObjectMode)
{
ModelItemTransformHelper.MoveItemIncrementallyToTrackedPositionAndRotation(
controlledObject,
currentPositionForTransform,
currentRotation,
appliedTargetPosition,
appliedTargetRotation);
}
else
{
ModelItemTransformHelper.MoveItemIncrementallyToPositionAndRotation(
controlledObject,
currentPositionForTransform,
currentRotation,
appliedTargetPosition,
appliedTargetRotation);
}
ModelItemTransformHelper.MoveItemIncrementallyToPositionAndRotation(
controlledObject,
currentPositionForTransform,
currentRotation,
appliedTargetPosition,
appliedTargetRotation);
_trackedPosition = appliedTargetPosition;
_trackedRotation = appliedTargetRotation;
@ -2858,7 +2847,7 @@ namespace NavisworksTransport.Core.Animation
if (isRailRealObject)
{
Point3D hostActualAfter = GetTrackedObjectPosition(controlledObject);
Point3D hostActualAfter = GetLiveBoundingBoxCenter(controlledObject);
LogManager.Debug(
$"[Rail姿态应用] 宿主最终跟踪中心=({hostActualAfter.X:F3},{hostActualAfter.Y:F3},{hostActualAfter.Z:F3}), " +
$"目标跟踪中心=({newPosition.X:F3},{newPosition.Y:F3},{newPosition.Z:F3}), " +
@ -3797,7 +3786,7 @@ namespace NavisworksTransport.Core.Animation
{
_originalTransform = animatedObject.Transform;
_originalCenter = animatedObject.BoundingBox().Center;
_trackedPosition = GetTrackedObjectPosition(animatedObject);
_trackedPosition = ResolveInitialBusinessTrackedPosition(animatedObject);
// 保持当前的 _currentYaw因为物体可能已经被 MoveObjectToPathStart 旋转)
// 不要从 Transform 中提取,因为 Transform 返回的是原始值,不是当前值
@ -3814,7 +3803,7 @@ namespace NavisworksTransport.Core.Animation
LogManager.Info($"[CreateAnimation] 动画已创建状态设置为Ready");
}
private Point3D GetTrackedObjectPosition(ModelItem item)
private Point3D GetLiveBoundingBoxCenter(ModelItem item)
{
if (item == null)
{
@ -3825,6 +3814,24 @@ namespace NavisworksTransport.Core.Animation
return bounds?.Center ?? new Point3D(0, 0, 0);
}
private Point3D ResolveInitialBusinessTrackedPosition(ModelItem item)
{
if (item == null)
{
return new Point3D(0, 0, 0);
}
if (ShouldUseOriginalBoundingBoxCenterForBusinessTrackedPosition(
_route?.PathType ?? PathType.Ground,
IsRealObjectMode) &&
_originalCenter != null)
{
return _originalCenter;
}
return GetLiveBoundingBoxCenter(item);
}
private Point3D ResolveGroundTrackedCenter(Point3D groundContactPoint, double objectHeight)
{
var adapter = CoordinateSystemManager.Instance.CreateHostAdapter();
@ -3892,7 +3899,7 @@ namespace NavisworksTransport.Core.Animation
return;
}
Point3D currentTrackedCenter = GetTrackedObjectPosition(CurrentControlledObject ?? _animatedObject);
Point3D currentTrackedCenter = GetLiveBoundingBoxCenter(CurrentControlledObject ?? _animatedObject);
Point3D semanticTrackedCenter = RailPathPoseHelper.ResolveObjectSpaceCenterPosition(
_route,
referencePoint,
@ -3979,6 +3986,13 @@ namespace NavisworksTransport.Core.Animation
return isRealObjectMode;
}
internal static bool ShouldUseOriginalBoundingBoxCenterForBusinessTrackedPosition(
PathType pathType,
bool isRealObjectMode)
{
return isRealObjectMode && pathType == PathType.Ground;
}
private void SyncTrackedRotationToDisplayedPose(ModelItem sourceObject)
{
if (sourceObject == null)
@ -5584,14 +5598,14 @@ namespace NavisworksTransport.Core.Animation
}
/// <summary>
/// 专门用于Ground路径动画帧播放。使用 _trackedPosition 作为当前位置,不重新读取包围盒。
/// 专门用于Ground路径动画帧播放。使用业务跟踪点 _trackedPosition 作为当前位置,不读取实时包围盒中心回写主链
/// </summary>
private void ApplyGroundAnimationFrame(Point3D targetPosition, Rotation3D targetRotation)
{
var controlledObject = CurrentControlledObject;
if (controlledObject == null) return;
// 使用 _trackedPosition 作为当前位置(几何中心),不重新读取包围盒
// 使用业务跟踪点 _trackedPosition 作为当前位置,不把实时包围盒中心混入主链
Point3D currentPosition = _trackedPosition;
Rotation3D currentRotation = _hasTrackedRotation ? _trackedRotation : Rotation3D.Identity;

View File

@ -591,122 +591,6 @@ namespace NavisworksTransport.Utils
LogIncrementalTransformActual(item, targetPosition, targetRotation);
}
public static void MoveItemIncrementallyToTrackedPositionAndRotation(
ModelItem item,
Point3D currentTrackedPosition,
Rotation3D currentRotation,
Point3D targetTrackedPosition,
Rotation3D targetRotation)
{
if (item == null)
{
throw new ArgumentNullException(nameof(item));
}
var doc = Application.ActiveDocument;
var modelItems = new ModelItemCollection { item };
Rotation3D deltaRotation = BuildDeltaRotation(currentRotation, targetRotation);
var currentLinear = new Transform3D(currentRotation).Linear;
var targetLinear = new Transform3D(targetRotation).Linear;
LogManager.Info(
$"[模型增量补偿] {item.DisplayName} 当前trackedPoint=({currentTrackedPosition.X:F3},{currentTrackedPosition.Y:F3},{currentTrackedPosition.Z:F3}), " +
$"目标trackedPoint=({targetTrackedPosition.X:F3},{targetTrackedPosition.Y:F3},{targetTrackedPosition.Z:F3})");
// LogManager.Debug(
// $"[模型增量补偿] {item.DisplayName} 当前旋转: " +
// $"X=({currentLinear.Get(0, 0):F4},{currentLinear.Get(1, 0):F4},{currentLinear.Get(2, 0):F4}), " +
// $"Y=({currentLinear.Get(0, 1):F4},{currentLinear.Get(1, 1):F4},{currentLinear.Get(2, 1):F4}), " +
// $"Z=({currentLinear.Get(0, 2):F4},{currentLinear.Get(1, 2):F4},{currentLinear.Get(2, 2):F4})");
// LogManager.Debug(
// $"[模型增量补偿] {item.DisplayName} 目标旋转: " +
// $"X=({targetLinear.Get(0, 0):F4},{targetLinear.Get(1, 0):F4},{targetLinear.Get(2, 0):F4}), " +
// $"Y=({targetLinear.Get(0, 1):F4},{targetLinear.Get(1, 1):F4},{targetLinear.Get(2, 1):F4}), " +
// $"Z=({targetLinear.Get(0, 2):F4},{targetLinear.Get(1, 2):F4},{targetLinear.Get(2, 2):F4})");
// LogGeometryLevelTransforms(item, "[模型增量补偿应用前][GeometryAPI]");
// 第1步将物体中心移到原点此时几何中心在原点
var toOrigin = Transform3D.CreateTranslation(new Vector3D(
-currentTrackedPosition.X,
-currentTrackedPosition.Y,
-currentTrackedPosition.Z));
doc.Models.OverridePermanentTransform(modelItems, toOrigin, false);
// 第2步在原点旋转
var rotationOnlyComponents = Transform3D.CreateTranslation(new Vector3D(0, 0, 0)).Factor();
rotationOnlyComponents.Rotation = deltaRotation;
var rotationOnly = rotationOnlyComponents.Combine();
doc.Models.OverridePermanentTransform(modelItems, rotationOnly, false);
// 🔥 关键:读取旋转后、平移前的包围盒中心(相对于几何中心的偏移)
Point3D centerAfterRotation = item.BoundingBox()?.Center ?? new Point3D(0, 0, 0);
// 这就是目标旋转下BoundingBox中心相对于几何中心的偏移
var rotationOffset = new Vector3D(
centerAfterRotation.X,
centerAfterRotation.Y,
centerAfterRotation.Z);
LogManager.Info(
$"[模型增量补偿] {item.DisplayName} 旋转后包围盒中心=({centerAfterRotation.X:F3},{centerAfterRotation.Y:F3},{centerAfterRotation.Z:F3}), " +
$"旋转偏移量=({rotationOffset.X:F3},{rotationOffset.Y:F3},{rotationOffset.Z:F3})");
// 🔥 关键修正:我们要几何中心对齐目标,而不是 BoundingBox 中心对齐目标
// rotationOffset = 旋转后 BoundingBox 中心相对于原点的位置
// 几何中心在原点,相对于 BoundingBox 中心的偏移 = -rotationOffset
// 所以 BoundingBox 中心应该位于 target + rotationOffset才能让几何中心对齐 target
var adjustedTargetPosition = new Point3D(
targetTrackedPosition.X + rotationOffset.X,
targetTrackedPosition.Y + rotationOffset.Y,
targetTrackedPosition.Z + rotationOffset.Z);
LogManager.Info(
$"[模型增量补偿] {item.DisplayName} 调整后目标位置=({adjustedTargetPosition.X:F3},{adjustedTargetPosition.Y:F3},{adjustedTargetPosition.Z:F3}), " +
$"补偿量=(+{rotationOffset.X:F3},+{rotationOffset.Y:F3},+{rotationOffset.Z:F3})");
// 第3步预落位到调整后的目标位置
var provisionalToTarget = Transform3D.CreateTranslation(new Vector3D(
adjustedTargetPosition.X,
adjustedTargetPosition.Y,
adjustedTargetPosition.Z));
doc.Models.OverridePermanentTransform(modelItems, provisionalToTarget, false);
Point3D actualTrackedPosition = item.BoundingBox()?.Center ?? new Point3D(0, 0, 0);
var correctionTranslation = new Vector3D(
adjustedTargetPosition.X - actualTrackedPosition.X,
adjustedTargetPosition.Y - actualTrackedPosition.Y,
adjustedTargetPosition.Z - actualTrackedPosition.Z);
LogManager.Info(
$"[模型增量补偿] {item.DisplayName} 预落位后实际trackedPoint=({actualTrackedPosition.X:F3},{actualTrackedPosition.Y:F3},{actualTrackedPosition.Z:F3}), " +
$"纠偏平移=({correctionTranslation.X:F3},{correctionTranslation.Y:F3},{correctionTranslation.Z:F3})");
if (Math.Abs(correctionTranslation.X) > 1e-6 ||
Math.Abs(correctionTranslation.Y) > 1e-6 ||
Math.Abs(correctionTranslation.Z) > 1e-6)
{
var correction = Transform3D.CreateTranslation(correctionTranslation);
doc.Models.OverridePermanentTransform(modelItems, correction, false);
LogManager.Info($"[模型增量补偿] {item.DisplayName} 已应用纠偏平移");
}
else
{
LogManager.Info($"[模型增量补偿] {item.DisplayName} 纠偏平移太小,跳过应用");
}
// 🔥 验证纠偏后的实际位置(包围盒中心应该偏离目标,但几何中心对齐目标)
Point3D finalTrackedPosition = item.BoundingBox()?.Center ?? new Point3D(0, 0, 0);
// 几何中心 = BoundingBox中心 - rotationOffset因为rotationOffset是BoundingBox中心相对于几何中心的偏移
var geometricCenterDeviation = new Vector3D(
targetTrackedPosition.X - (finalTrackedPosition.X - rotationOffset.X),
targetTrackedPosition.Y - (finalTrackedPosition.Y - rotationOffset.Y),
targetTrackedPosition.Z - (finalTrackedPosition.Z - rotationOffset.Z));
LogManager.Info(
$"[模型增量补偿] {item.DisplayName} 最终包围盒中心=({finalTrackedPosition.X:F3},{finalTrackedPosition.Y:F3},{finalTrackedPosition.Z:F3}), " +
$"几何中心偏差=({geometricCenterDeviation.X:F3},{geometricCenterDeviation.Y:F3},{geometricCenterDeviation.Z:F3})");
// LogIncrementalTransformActual(item, targetTrackedPosition, targetRotation);
}
/// <summary>
/// 读取物体当前实际几何姿态。
/// 优先使用 ModelGeometry.ActiveTransform因为 ModelItem.Transform 只反映原始设计变换。