diff --git a/AGENTS.md b/AGENTS.md
index 8851250..6eb1690 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -296,6 +296,16 @@ var rotation = new Rotation3D(qw, qx, qy, qz); // 错误
- 走平面姿态链
- 已禁止偷偷退回旧 `yaw` 方案
- 起点、逐帧、终点、通行空间,必须共享同一套尺寸语义
+- `Ground + 真实物体` 的业务跟踪点固定为**原始包围盒中心**
+- 旋转后的实时 `BoundingBox.Center` 只允许用于:
+ - 诊断旋转后漂移
+ - 计算当帧补偿
+ - 校验补偿结果
+- 不允许把实时 `BoundingBox.Center` 直接当成:
+ - 起点目标跟踪点
+ - 逐帧业务跟踪点
+ - 碰撞记录主语义跟踪点
+- 如果 `Ground` 出现“起点正确但越走越偏”或“起点偏了但拐弯后偏差反而减小”,优先检查是否把实时包围盒中心误当成了业务跟踪点
#### Rail
diff --git a/UnitTests/CoordinateSystem/RailPreservedPoseTests.cs b/UnitTests/CoordinateSystem/RailPreservedPoseTests.cs
index 1e8326a..f416c13 100644
--- a/UnitTests/CoordinateSystem/RailPreservedPoseTests.cs
+++ b/UnitTests/CoordinateSystem/RailPreservedPoseTests.cs
@@ -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()
{
diff --git a/doc/working/current-engineering-state.md b/doc/working/current-engineering-state.md
index 7869750..48f90f7 100644
--- a/doc/working/current-engineering-state.md
+++ b/doc/working/current-engineering-state.md
@@ -111,8 +111,19 @@
- `AnimatedObjectTrackedPosition` 的唯一语义:
- 当前动画主链路使用的跟踪点位置。
-- 当前主链路已经切到:
- - 几何中心跟踪。
+- `Ground + 真实物体` 当前必须明确区分两类“中心”:
+ - 业务跟踪点应固定为物体**原始包围盒中心**
+ - 不是旋转后的实时包围盒中心
+- 实时包围盒中心的用途仅限于:
+ - 诊断旋转后漂移
+ - 计算当帧补偿
+ - 验证补偿结果
+- 不允许把实时包围盒中心直接回写成 `Ground` 路径的业务跟踪点定义。
+- 这条规则的直接影响范围包括:
+ - 起点落位
+ - 逐帧补偿
+ - 动画日志
+ - 碰撞与结果记录语义
- 如果后续动画跟踪点再变,数据库和碰撞结果语义必须同步更新。
### 4.4 虚拟物体资源
diff --git a/src/Core/Animation/PathAnimationManager.cs b/src/Core/Animation/PathAnimationManager.cs
index 61754d8..8cc6b3a 100644
--- a/src/Core/Animation/PathAnimationManager.cs
+++ b/src/Core/Animation/PathAnimationManager.cs
@@ -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
}
///
- /// 专门用于Ground路径动画帧播放。使用 _trackedPosition 作为当前位置,不重新读取包围盒。
+ /// 专门用于Ground路径动画帧播放。使用业务跟踪点 _trackedPosition 作为当前位置,不读取实时包围盒中心回写主链。
///
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;
diff --git a/src/Utils/ModelItemTransformHelper.cs b/src/Utils/ModelItemTransformHelper.cs
index dd7f7b6..52f858c 100644
--- a/src/Utils/ModelItemTransformHelper.cs
+++ b/src/Utils/ModelItemTransformHelper.cs
@@ -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);
- }
-
///
/// 读取物体当前实际几何姿态。
/// 优先使用 ModelGeometry.ActiveTransform,因为 ModelItem.Transform 只反映原始设计变换。