无人物流车转弯路径曲线化功能 - 实施方案

1. 目标概述
实现基于圆弧过渡（Arc Fillet）的路径曲线化功能，替代现有的直线连接方式，确保仿真系统能准确检测物体转弯时的扫掠路径（Swept Path）碰撞。

核心原理
控制点与物理点分离：用户操作的
PathPoint
 作为控制点，实际物理路径由 PathEdge 表示
圆弧过渡法：在相邻路径段转折处插入切圆弧，使用进入切点 Ts 和退出切点 Te 连接
安全截断：当计算的切线长度超过线段长度45%时，自动缩减半径，防止圆弧过大
2. 数据结构改动
2.1 新增数据模型
[NEW] PathSegmentType 枚举
/// <summary>
/// 路径段类型
/// </summary>
public enum PathSegmentType
{
    /// <summary>
    /// 直线段
    /// </summary>
    Straight,

    /// <summary>
    /// 圆弧段
    /// </summary>
    Arc
}
[NEW] ArcTrajectory 类
/// <summary>
/// 圆弧轨迹数据
/// </summary>
[Serializable]
public class ArcTrajectory
{
    /// <summary>
    /// 进入切点
    /// </summary>
    public Point3D Ts { get; set; }

    /// <summary>
    /// 退出切点
    /// </summary>
    public Point3D Te { get; set; }
    
    /// <summary>
    /// 圆心位置
    /// </summary>
    public Point3D ArcCenter { get; set; }
    
    /// <summary>
    /// 请求半径（配置的转向半径）
    /// </summary>
    public double RequestedRadius { get; set; }
    
    /// <summary>
    /// 实际半径（安全截断后）
    /// </summary>
    public double ActualRadius { get; set; }
    
    /// <summary>
    /// 偏转角（弧度）
    /// </summary>
    public double DeflectionAngle { get; set; }
    
    /// <summary>
    /// 圆弧长度（米）
    /// </summary>
    public double ArcLength { get; set; }
}
[NEW] PathEdge 类
/// <summary>
/// 路径边 - 连接两个连续控制点的物理路径段
/// </summary>
[Serializable]
public class PathEdge
{
    /// <summary>
    /// 边唯一标识符
    /// </summary>
    public string Id { get; set; }

    /// <summary>
    /// 起始控制点ID
    /// </summary>
    public string StartPointId { get; set; }
    
    /// <summary>
    /// 结束控制点ID
    /// </summary>
    public string EndPointId { get; set; }
    
    /// <summary>
    /// 路径段类型
    /// </summary>
    public PathSegmentType SegmentType { get; set; }
    
    /// <summary>
    /// 圆弧轨迹数据（仅当 SegmentType == Arc 时有效）
    /// </summary>
    public ArcTrajectory Trajectory { get; set; }
    
    /// <summary>
    /// 边的物理长度（米）
    /// 直线段：两点间距离；圆弧段：直线段长度 + 圆弧长度
    /// </summary>
    public double PhysicalLength { get; set; }
    
    /// <summary>
    /// 采样点序列（用于碰撞检测和动画）
    /// </summary>
    [XmlIgnore]
    public List<Point3D> SampledPoints { get; set; }
}
2.2 修改现有数据模型
[MODIFY]
PathPoint.cs
新增属性：

/// <summary>
/// 自定义转向半径（米），用于局部急转弯场景
/// 为 null 时使用全局默认值
/// </summary>
public double? CustomTurnRadius { get; set; }
[MODIFY]
PathRoute.cs
新增属性：

/// <summary>
/// 路径边集合 - 存储物理路径段
/// </summary>
public List<PathEdge> Edges { get; set; } = new List<PathEdge>();
/// <summary>
/// 默认转向半径（米）
/// </summary>
public double TurnRadius { get; set; } = 1.5;
/// <summary>
/// 是否已曲线化
/// </summary>
public bool IsCurved { get; set; } = false;
修改方法：

UpdateTotalLength()
: 综合计算所有 Edges 的物理长度
Clone()
: 需要深拷贝 Edges 集合
2.3 数据库表结构扩展
[MODIFY]
PathDatabase.cs
新增表 PathEdges：

CREATE TABLE IF NOT EXISTS PathEdges (
    Id TEXT PRIMARY KEY,
    RouteId TEXT NOT NULL,
    StartPointId TEXT NOT NULL,
    EndPointId TEXT NOT NULL,
    SegmentType INTEGER NOT NULL,  -- 0=Straight, 1=Arc
    PhysicalLength REAL NOT NULL,

    -- 圆弧轨迹数据 (仅 SegmentType=1 时有效)
    Ts_X REAL,
    Ts_Y REAL,
    Ts_Z REAL,
    Te_X REAL,
    Te_Y REAL,
    Te_Z REAL,
    ArcCenter_X REAL,
    ArcCenter_Y REAL,
    ArcCenter_Z REAL,
    RequestedRadius REAL,
    ActualRadius REAL,
    DeflectionAngle REAL,
    ArcLength REAL,
    
    FOREIGN KEY (RouteId) REFERENCES PathRoutes(Id) ON DELETE CASCADE
);
修改表
PathRoutes
：

ALTER TABLE PathRoutes ADD COLUMN TurnRadius REAL DEFAULT 1.5;
ALTER TABLE PathRoutes ADD COLUMN IsCurved INTEGER DEFAULT 0;
修改表
PathPoints
：

ALTER TABLE PathPoints ADD COLUMN CustomTurnRadius REAL;
3. 核心算法实现
3.1 PathCurveEngine 类
位置: src/Core/PathCurveEngine.cs (新建)

3.1.1 CalculateFillet 方法
/// <summary>
/// 计算圆弧切点和轨迹参数
/// </summary>
/// <param name="pPrev">前一个控制点</param>
/// <param name="pCurr">当前控制点</param>
/// <param name="pNext">下一个控制点</param>
/// <param name="turnRadius">转向半径（米）</param>
/// <returns>圆弧轨迹数据</returns>
public static ArcTrajectory CalculateFillet(
    Point3D pPrev,
    Point3D pCurr,
    Point3D pNext,
    double turnRadius)
{
    // 1. 计算单位向量
    Vector3D v1 = (pPrev - pCurr).Normalize();
    Vector3D v2 = (pNext - pCurr).Normalize();

    // 2. 计算夹角 α
    double cosAlpha = Vector3D.DotProduct(v1, v2);
    double angleRad = Math.Acos(Math.Clamp(cosAlpha, -1.0, 1.0));
    
    // 3. 计算切线长 Lt = R / tan(α/2)
    double Lt = turnRadius / Math.Tan(angleRad / 2.0);
    
    // 4. 安全截断检查
    double seg1Length = (pPrev - pCurr).Length;
    double seg2Length = (pNext - pCurr).Length;
    double maxAllowedLt = Math.Min(seg1Length, seg2Length) * 0.45;
    
    double actualRadius = turnRadius;
    if (Lt > maxAllowedLt)
    {
        Lt = maxAllowedLt;
        actualRadius = Lt * Math.Tan(angleRad / 2.0);
    }
    
    // 5. 计算切点
    Point3D ts = pCurr + v1 * Lt;
    Point3D te = pCurr + v2 * Lt;
    
    // 6. 计算圆心 (使用角平分线方向)
    Vector3D bisector = (v1 + v2).Normalize();
    double distToCenter = actualRadius / Math.Sin(angleRad / 2.0);
    Point3D arcCenter = pCurr + bisector * distToCenter;
    
    // 7. 计算圆弧长度
    double arcLength = actualRadius * angleRad;
    
    return new ArcTrajectory
    {
        Ts = ts,
        Te = te,
        ArcCenter = arcCenter,
        RequestedRadius = turnRadius,
        ActualRadius = actualRadius,
        DeflectionAngle = angleRad,
        ArcLength = arcLength
    };
}
3.1.2 SampleArc 方法
/// <summary>
/// 圆弧采样为离散点序列
/// </summary>
/// <param name="trajectory">圆弧轨迹</param>
/// <param name="samplingStep">采样步长（米）</param>
/// <returns>采样点列表</returns>
public static List<Point3D> SampleArc(
    ArcTrajectory trajectory,
    double samplingStep)
{
    var points = new List<Point3D>();

    // 根据采样步长计算采样点数量
    int sampleCount = Math.Max(2, (int)Math.Ceiling(trajectory.ArcLength / samplingStep));
    
    // 计算起始和结束向量
    Vector3D startVec = (trajectory.Ts - trajectory.ArcCenter).Normalize();
    Vector3D endVec = (trajectory.Te - trajectory.ArcCenter).Normalize();
    
    // 计算旋转轴（叉乘）
    Vector3D rotationAxis = Vector3D.CrossProduct(startVec, endVec).Normalize();
    
    // 等角度插值
    for (int i = 0; i <= sampleCount; i++)
    {
        double t = i / (double)sampleCount;
        double theta = t * trajectory.DeflectionAngle;
        
        // Rodrigues旋转公式
        Point3D sampledPoint = RotatePointAroundAxis(
            trajectory.Ts, 
            trajectory.ArcCenter, 
            rotationAxis, 
            theta
        );
        
        points.Add(sampledPoint);
    }
    
    return points;
}
3.1.3 ApplyCurvatureToRoute 方法
/// <summary>
/// 对路径应用曲线化处理
/// </summary>
/// <param name="route">待处理的路径</param>
/// <param name="samplingStep">采样步长（米）</param>
public static void ApplyCurvatureToRoute(PathRoute route, double samplingStep)
{
    route.Edges.Clear();
    var sortedPoints = route.GetSortedPoints();

    if (sortedPoints.Count < 2)
    {
        route.IsCurved = false;
        return;
    }
    
    for (int i = 0; i < sortedPoints.Count - 1; i++)
    {
        var p1 = sortedPoints[i];
        var p2 = sortedPoints[i + 1];
        
        // 判断是否为转折点（需要圆弧过渡）
        bool needsArc = (i > 0 && i < sortedPoints.Count - 2);
        
        if (needsArc)
        {
            var p0 = sortedPoints[i - 1];
            var p2Next = sortedPoints[i + 2];
            
            // 获取转向半径（优先使用自定义值）
            double radius = p1.CustomTurnRadius ?? route.TurnRadius;
            
            // 计算圆弧
            var arcTraj = CalculateFillet(p0, p1, p2, radius);
            
            // 创建Edge（包含直线段 + 圆弧段）
            var edge = BuildEdgeWithArc(p1, p2, arcTraj, samplingStep);
            route.Edges.Add(edge);
        }
        else
        {
            // 直线边
            var edge = BuildStraightEdge(p1, p2, samplingStep);
            route.Edges.Add(edge);
        }
    }
    
    route.IsCurved = true;
    route.RecalculateLength();
}
4. 配置文件扩展
[MODIFY]
SystemConfig.cs
在
PathEditingConfig
 类中新增：

/// <summary>
/// 默认转向半径（米）
/// 推荐值：1.0-2.0
/// </summary>
public double DefaultTurnRadius { get; set; } = 1.5;
/// <summary>
/// 圆弧采样步长（米）
/// 推荐值：0.02-0.1
/// </summary>
public double ArcSamplingStep { get; set; } = 0.05;

配置文件示例 (TOML)
[PathEditing]
CellSizeMeters = 0.5
MaxHeightDiffMeters = 0.35
ObjectLengthMeters = 1.0
ObjectWidthMeters = 1.0
ObjectHeightMeters = 2.0
SafetyMarginMeters = 0.05

# 路径曲线化配置

DefaultTurnRadius = 1.5
ArcSamplingStep = 0.05

1. 业务流程集成
5.1 路径编辑完成流程
[MODIFY]
PathPlanningManager.cs

-

FinishEditing
 方法
public bool FinishEditing()
{
    try
    {
        if (_currentRoute == null)
            return false;

        // **新增：应用曲线化**
        if (ConfigManager.Instance.Current.PathEditing.EnablePathCurving)
        {
            double samplingStep = ConfigManager.Instance.Current.PathEditing.ArcSamplingStep;
            _currentRoute.TurnRadius = ConfigManager.Instance.Current.PathEditing.DefaultTurnRadius;
            
            PathCurveEngine.ApplyCurvatureToRoute(_currentRoute, samplingStep);
            LogManager.Info($"路径曲线化完成: {_currentRoute.Name}, 边数: {_currentRoute.Edges.Count}");
        }
        
        // 保存到数据库（包含Edges）
        SaveRouteToDatabase(_currentRoute);
        
        // ... 现有逻辑 ...
    }
    catch (Exception ex)
    {
        LogManager.Error($"完成编辑失败: {ex.Message}");
        return false;
    }
}
5.2 数据持久化
[MODIFY]
PathDatabase.cs
新增方法：

SavePathEdges(string routeId, List<PathEdge> edges)
LoadPathEdges(string routeId) : List<PathEdge>
DeletePathEdges(string routeId)
修改方法：

SavePathRoute(PathRoute route)
: 同时保存 Edges
GetAllPathRoutes()
: 同时加载 Edges
DeletePathRoute(string routeId)
: 级联删除 Edges
5.3 导出格式扩展
[MODIFY]
PathDataManager.cs
DELMIA XML 格式：

<Path name="路径1">
  <Edges>
    <Edge type="arc">
      <Trajectory>
        <Ts x="10.5" y="5.2" z="0.0"/>
        <Te x="11.3" y="6.8" z="0.0"/>
        <ArcCenter x="10.9" y="6.0" z="0.0"/>
        <Radius>1.5</Radius>
      </Trajectory>
    </Edge>
  </Edges>
</Path>
CSV 格式（扁平化采样点）：

RouteId,EdgeIndex,SegmentType,PointIndex,X,Y,Z
route_001,0,Arc,0,10.5,5.2,0.0
route_001,0,Arc,1,10.52,5.25,0.0
...
6. 碰撞检测与动画集成
6.1 碰撞检测
[MODIFY] PathAnimationManager.cs - 碰撞检测逻辑
// 从 Edges 获取采样点而非直接使用 Points
foreach (var edge in route.Edges)
{
    if (edge.SampledPoints == null || edge.SampledPoints.Count == 0)
    {
        // 懒加载：若采样点未生成，现场生成
        edge.SampledPoints = GenerateSampledPoints(edge, arcSamplingStep);
    }

    foreach (var point in edge.SampledPoints)
    {
        // 执行OBB碰撞检测 ...
    }
}
6.2 动画生成
使用 edge.SampledPoints 代替原始控制点生成动画帧序列。

1. 验证计划
7.1 单元测试
新建测试文件: test/Core/PathCurveEngineTests.cs

测试用例：

基础几何计算

验证90度直角转弯的圆弧计算
验证锐角/钝角场景
验证3D空间旋转正确性
安全截断

验证半径过大时自动缩减
边界条件：最小夹角（~0°）和最大夹角（~180°）
采样精度

验证采样点数量符合步长要求
验证采样点均匀分布在圆弧上
运行命令（需确认实际测试框架）：

# 假设使用 xUnit

dotnet test NavisworksTransport.Tests --filter "FullyQualifiedName~PathCurveEngine"
7.2 集成测试
场景测试路径：

创建包含3个控制点的简单L型路径
设置转向半径 = 1.5m
完成编辑触发曲线化
验证：
Edges 数量= 2
第一个Edge为直线段，第二个Edge包含圆弧
TotalLength 正确（直线段 + 圆弧段）
重新加载路径验证持久化
7.3 手动验证
IMPORTANT

以下步骤需要用户在Navisworks中手动验证

测试步骤：

打开Navisworks，加载测试模型
进入路径规划模式，手工添加4个控制点形成Z字形路径
在配置文件中设置：
DefaultTurnRadius = 2.0
ArcSamplingStep = 0.05
EnablePathCurving = true
完成编辑，观察路径可视化是否显示平滑曲线（而非折线）
运行碰撞检测，检查转弯处的采样点密度
导出DELMIA XML，用文本编辑器验证圆弧轨迹参数存在
预期结果：

路径在转折点处显示圆弧过渡
碰撞检测能捕获内轮差区域的碰撞
XML包含完整的 <Trajectory> 节点
8. 风险与注意事项
8.1 性能影响
圆弧采样：每个转折点生成 ArcLength / SamplingStep 个采样点
缓解措施：默认步长0.05m平衡精度与性能，支持配置调整
8.2 数据兼容性
旧版路径数据库无 Edges 表
迁移策略：
检测表是否存在，不存在则创建
加载旧路径时，Edges为空视为"未曲线化"，用户完成编辑时自动曲线化
8.3 极端场景
非常小的转向半径 + 锐角：可能导致无法满足几何约束
处理：最小半径限制0.3m，警告日志记录
9. 实施顺序建议
✅ 数据结构 → 新增类和修改现有模型
✅ 配置管理 → 扩展SystemConfig和TOML
✅ 核心算法 → PathCurveEngine + 单元测试
✅ 数据库 → 表结构变更 + CRUD方法
✅ 业务集成 → FinishEditing + 数据持久化
✅ 导出扩展 → XML/CSV格式支持
✅ 碰撞检测 → 使用Edges采样点
✅ 验证测试 → 集成测试 + 手动验证
10. 后续优化方向
支持Clothoid曲线（回旋曲线）以实现更平滑的加速度变化
UI可视化：实时预览圆弧位置
性能优化：采样点缓存机制

物流车转弯路径曲线化功能实施任务规划
总体目标
实现基于圆弧过渡（Arc Fillet）的路径曲线化功能，替代现有的直线连接方式，满足仿真系统对物理真实性的要求。

实施阶段
[ ] 阶段1: 数据结构设计与扩展
 设计 Edge（边）数据结构
 PathEdge 类定义
 PathSegmentType 枚举（直线/圆弧）
 ArcTrajectory 圆弧轨迹数据
 扩展 PathPoint 支持局部半径覆盖
 新增 CustomTurnRadius 属性
 重构 PathRoute 类
 新增 Edges 集合
 新增 TurnRadius 全局转向半径
 更新长度计算逻辑
 更新数据库表结构
 设计 Edges 表结构
 设计数据迁移策略
[ ] 阶段2: 曲线化算法实现
 创建 PathCurveEngine 核心算法类
 CalculateFillet 方法（圆弧切点计算）
 安全截断逻辑实现
 SampleArc 方法（圆弧采样）
 实现路径曲线化主流程
 ApplyCurvatureToRoute 方法
 BuildEdgesFromPoints 方法
 单元测试
 基础几何计算测试
 边界条件测试
[ ] 阶段3: 配置管理
 添加曲线化相关配置项
 DefaultTurnRadius (默认转向半径)
 ArcSamplingStep (圆弧采样步长，默认0.05m)
 EnablePathCurving (启用开关)
 更新配置文件读取/保存逻辑
[ ] 阶段4: 集成到现有业务流程
 修改 PathRoute 长度计算
 直线段 + 圆弧段综合计算
 集成到路径编辑完成流程
 FinishEditing 触发曲线化
 更新数据持久化
 PathDatabase 保存/加载 Edges
 PathDataManager 导出格式扩展
[ ] 阶段5: 碰撞检测与动画集成
 碰撞检测使用曲线路径
 采样点序列生成
 集成到现有碰撞检测
 PathAnimationManager 支持曲线路径
 使用 Edges 生成动画帧
[ ] 阶段6: DELMIA 导出扩展
 XML Tag Group 格式支持圆弧
 导出 Ts, Te, ArcCenter
 CSV 格式导出采样点序列
[ ] 阶段7: 测试与验证
 功能测试
 性能测试
 边界场景测试
