κ轨迹点生成代码说明
这个模块化的 C++ 代码,用于在给定 $\kappa \in [0,1]$ 的条件下,在航路点之间生成平滑的 $\kappa$-轨迹点。
该代码将专注于根据论文中的几何定义(Definition 3 和 Lemma 6 的证明图 Fig. 9 )计算构成转弯的三个圆弧段以及与这些圆弧相切的入段点和出段点。
核心思路:
- 确定几何参数:根据输入的三个航点 $w_{i-1}, w_i, w_{i+1}$,计算转弯角 $\beta$(即论文中的 $\beta_{corner}$,角 $w_{i-1}w_iw_{i+1}$),以及相关的方向向量。
- 计算 $p(\kappa)$ 点:这是由参数 $\kappa$ 控制的、位于角平分线上的一个关键点 。
- 确定中间圆弧 $\mathcal{C}_{p(\kappa)}$:该圆弧的圆心为 $c_d$,半径为 $R$,并通过 $p(\kappa)$ 点。其起始点 $P_A$ 和终止点 $P_B$ 由角度 $\theta = \cos^{-1}(\Xi(\kappa,\beta))$ 决定 。
- 确定过渡圆弧 $\mathcal{C}_i$ 和 $\mathcal{C}_{i+1}$:
- $\mathcal{C}_i$ 连接入航线段和 $\mathcal{C}_{p(\kappa)}$,圆心为 $c_1$,在 $P_A$ 点与 $\mathcal{C}_{p(\kappa)}$ 相切,并与入航线段在 $T_1$ 点相切。
- $\mathcal{C}_{i+1}$ 连接 $\mathcal{C}_{p(\kappa)}$ 和出航线段,圆心为 $c_2$,在 $P_B$ 点与 $\mathcal{C}_{p(\kappa)}$ 相切,并与出航线段在 $T_2$ 点相切。
- 生成轨迹点:沿直线段 $w_{i-1} \to T_1$,圆弧 $T_1 \to P_A$,圆弧 $P_A \to P_B$,圆弧 $P_B \to T_2$,以及直线段 $T_2 \to w_{i+1}$ 生成离散点。本代码将主要生成三个圆弧段的点以及切点 $T_1, T_2$。
代码说明:
Point2D结构体:用于表示二维坐标点和向量,并提供基本的向量运算(加减、数乘、点积、模长、单位化、旋转)。TurnGeometry结构体:用于存储生成结果,包括入航线段切点T1、出航线段切点T2以及构成三个平滑圆弧的路径点序列。KappaTrajectoryGenerator类:generateTurnTrajectory方法:- 接收三个连续航点
w_im1,w_i,w_ip1,期望速度v_hat,最大转向速率turn_rate_c,kappa参数以及每个圆弧段的点数。 - 计算转弯半径 $R = \hat{v}/c$。
- 计算角 $w_{i-1}w_iw_{i+1}$ (代码中为
beta_corner)。 - 处理直线和U型转弯等退化情况。
- 计算角平分线向量
u_bisector。 - 根据公式 (19) 计算 $p(\kappa)$ 点 (
pk) 。 - 计算中间圆弧 $\mathcal{C}_{p(\kappa)}$ 的圆心
c_d。 - 根据公式 (32) 和 (39) 计算 $\Xi(\kappa, \beta)$ (
xi_val) 和 $\theta$ (theta_xi) 。 - 确定转弯方向 (
turn_direction_sign)。 - 计算中间圆弧的起止点
P_A和P_B。 - 计算第一个过渡圆弧 $\mathcal{C}_i$ 的圆心
c_1和第二个过渡圆弧 $\mathcal{C}_{i+1}$ 的圆心c_2。这两个圆弧分别与中间圆弧在P_A和P_B点相切。 - 计算入航线段与 $\mathcal{C}_i$ 的切点
T1(tangent_point_on_incoming_segment) 和出航线段与 $\mathcal{C}_{i+1}$ 的切点T2(tangent_point_on_outgoing_segment)。 - 调用
addArcPoints辅助函数,依次生成三个圆弧段($T_1 \to P_A$, $P_A \to P_B$, $P_B \to T_2$)上的离散点,并存入result.path_points。
- 接收三个连续航点
addArcPoints辅助方法:- 根据圆心、起点、终点、半径和转弯方向,通过插值计算圆弧上的多个点。
- 它会处理角度的周期性,并尝试选择最短的弧线路径。
如何在无人机仿真系统中使用:
- 集成代码:将
Point2D、TurnGeometry和KappaTrajectoryGenerator的代码集成到您的项目中。 - 实例化生成器:
KappaTrajectoryGenerator generator; - 路径规划流程:
- 对于一条由多个航点 $W_0, W_1, ..., W_N$ 组成的路径。
- 第一段直线:$W_0$ 到 $W_1$ (如果只有一个航段)。
- 对于每个转弯点 $W_k$ (由 $W_{k-1}, W_k, W_{k+1}$ 定义,其中 $k=1, ..., N-1$):
- 调用
turn_geom = generator.generateTurnTrajectory(W_{k-1}, W_k, W_{k+1}, v_hat, c_rate, kappa_val_for_this_turn, ...)。 kappa_val_for_this_turn可以是固定的,也可以是通过前一个回复中的KappaTrajectorySolver(等长$\kappa$-轨迹算法) 计算得出的 $\kappa^*$。- 如果
turn_geom.is_valid为真:- 前一个航段的终点(或 $W_0$)连接到
turn_geom.tangent_point_on_incoming_segment(直线)。 - 然后是
turn_geom.path_points中的平滑圆弧点。 - 然后从
turn_geom.tangent_point_on_outgoing_segment连接到下一个转弯的入切点(或最终航点 $W_N$)(直线)。
- 前一个航段的终点(或 $W_0$)连接到
- 调用
- 将这些点序列组合起来,形成完整的平滑路径。
重要提示:
- 坐标系:确保所有输入的航点坐标都使用一致的二维坐标系。
- 单位:确保速度(米/秒)、转向速率(弧度/秒)的单位正确。
- 退化情况:代码中对直线和接近U型转弯的情况进行了初步处理。在这些情况下,三圆弧模型会退化,可能需要根据实际需求采用更特定的平滑策略(例如,对于U型转弯,直接使用一个半径为R的半圆)。
addArcPoints的鲁棒性:addArcPoints中的角度处理逻辑是为了确保圆弧按正确的方向(顺时针/逆时针)和最短路径生成。在某些极端情况下,可能仍需微调。- 点密度:
num_points_per_arc参数控制每个圆弧的平滑程度。值越大,轨迹越平滑,但点也越多。
此代码提供了一个基于论文几何定义的 $\kappa$-轨迹生成框架。您可以根据仿真系统的具体需求进行调整和扩展。
代码优化内容
对于 $\kappa=1$,根据论文中的描述和图示 (如图8 ),路径会简化为从入航线段直接过渡到一个单一的转弯圆弧,然后再连接到出航线段。这个单一圆弧的圆心就是论文图3中描述的内切圆 $\overline{\mathcal{C}}$ 的圆心,并且该圆弧会通过点 $\overline{p}$ (即 $p(1)$)。三圆弧模型在这种情况下会自然地退化:中间的圆弧长度变为零(因为 $\theta_{Xi}=0$),而两边的过渡圆弧会合并。
对于 $\kappa=0$,路径应直接经过航点 $w_i$ 。三圆弧几何结构依然适用,其中 $p(0) = w_i$,意味着中间的圆弧会通过 $w_i$。
以下是优化后的代码。主要改动包括:
- 针对 $\kappa=1$ 的显式处理:当
kappa接近1时,采用简化的单圆弧逻辑生成路径。 - 针对 $\kappa=0$ 的处理:虽然通用逻辑已能处理,但代码中明确了此时 $p(\kappa)$ 即为 $w_i$。
- 代码结构和注释:调整了部分代码结构,增加了更多中文注释,提高了可读性。
- 数值稳定性:对一些关键计算(如
sin_beta_half接近零)增加了检查。 addArcPoints优化:改进了addArcPoints中处理角度的逻辑,使其更鲁棒,并确保不会重复添加起点。
主要优化和改动点:
kappa = 1的特殊处理:- 当
std::abs(kappa - 1.0) < epsilon时,代码会进入一个专门的分支。 - 在此分支中,它计算点 $p(1)$ (论文中的 $\overline{p}$ ) 和以此点及半径 $R$ 定义的单个转弯圆弧的圆心 (论文图3中的 $\overline{\mathcal{C}}$ 的圆心 )。
- 然后计算入航线段和出航线段与这个单一圆弧的切点 $T_1$ 和 $T_2$。
- 最后,只调用一次
addArcPoints来生成 $T_1$ 和 $T_2$ 之间的单一圆弧段。这符合论文中对 $\kappa=1$ 时轨迹的描述,即一个更简单的、最短时间的转弯 。
- 当
kappa = 0的处理:- 当
std::abs(kappa) < epsilon时,明确将pk(即 $p(\kappa)$) 设置为w_i。 - 这确保了当 $\kappa=0$ 时,轨迹的中间部分会经过(或非常接近)原始转弯点 $w_i$,符合论文描述。
- 后续的计算(
c_d,theta_xi等)会基于pk = w_i进行,三圆弧模型依然适用。
- 当
addArcPoints函数改进:- 在添加圆弧点之前,会检查
points_vec是否为空,或者新圆弧的起点start_point是否与points_vec的最后一个点足够接近。如果是,则不重复添加start_point,避免路径点序列中出现不必要的重复点。 - 对
angle_diff的调整逻辑进行了优化,以更准确地处理跨越 $\pm\pi$ 的情况,并确保转弯方向正确。 - 如果起点和终点非常接近,会直接添加终点并返回,避免不必要的插值。
- 在添加圆弧点之前,会检查
数值稳定性与错误处理:
- 对除零操作(如向量单位化、除以
scalar)增加了更严格的检查(比较小的epsilon)。 - 对
sin(beta_corner / 2.0)过小的情况增加了检查,因为它是多个公式的分母。 - 对
acos的输入dot_product和xi_val进行了钳位,确保它们在[-1, 1]范围内。 TurnGeometry结构体中增加了info字段,用于返回生成过程中的一些提示信息或错误信息。
- 对除零操作(如向量单位化、除以
退化情况处理:
- 代码对
beta_corner接近0(直线)或 $\pi$(U型转弯)的情况进行了处理。在这些情况下,$\kappa$-轨迹模型可能会退化,代码会返回一个简化的结果(例如,对于直线,返回 $w_i$ 作为路径点)。
- 代码对
这些改动使得代码在处理特定的 $\kappa$ 值时更加精确和高效,同时也提高了其在各种几何情况下的鲁棒性。
代码输出内容
该C++代码(KappaTrajectoryGenerator 类中的 generateTurnTrajectory 方法)最终给用户(调用该方法的代码模块)提供的输出内容是一个名为 TurnGeometry 的结构体对象。这个结构体包含以下几个部分:
tangent_point_on_incoming_segment(类型:Point2D):- 这是计算出的平滑转弯路径与入航线段(由
w_im1和w_i定义)相切的切点 $T_1$ 的二维坐标。无人机将沿直线飞向此点,然后开始转弯。
- 这是计算出的平滑转弯路径与入航线段(由
tangent_point_on_outgoing_segment(类型:Point2D):- 这是计算出的平滑转弯路径与出航线段(由
w_i和w_ip1定义)相切的切点 $T_2$ 的二维坐标。无人机完成转弯后,将从此点开始沿直线飞向下一个目标。
- 这是计算出的平滑转弯路径与出航线段(由
path_points(类型:std::vector<Point2D>):- 这是一个包含多个
Point2D对象的动态数组(向量)。这些点共同构成了从切点 $T_1$ 开始,经过一个或三个平滑圆弧,到达切点 $T_2$ 的实际平滑转弯路径上的离散点序列。 - 这些点的顺序是沿着飞行方向的。
- 点的数量取决于输入参数
num_points_per_arc以及转弯的具体几何形状(例如,对于 $\kappa=1$ 的情况,总点数可能大约是num_points_per_arc * 3,因为代码中为单圆弧分配了更多点;对于三圆弧情况,总点数大约是num_points_per_arc * 3,如果中间圆弧长度不为零)。
- 这是一个包含多个
is_valid(类型:bool):- 一个布尔标志,指示本次平滑转弯几何路径的计算是否成功且有效。如果输入参数不合理(例如,航点重合、转向速率为零、$\kappa$ 值超出范围)或计算中遇到无法处理的退化情况,此标志会设为
false。
- 一个布尔标志,指示本次平滑转弯几何路径的计算是否成功且有效。如果输入参数不合理(例如,航点重合、转向速率为零、$\kappa$ 值超出范围)或计算中遇到无法处理的退化情况,此标志会设为
info(类型:std::string):- 一个字符串,用于存储关于本次路径生成的附加信息。这可能包括:
- 成功生成时的提示信息(例如,“Kappa=1: 单圆弧转弯模式。” 或 “Kappa=0: 路径将通过航点 w_i。")。
- 发生错误或遇到退化情况时的具体描述信息。
- 一个字符串,用于存储关于本次路径生成的附加信息。这可能包括:
总结来说,用户调用该函数后,会得到构成一个完整平滑转弯所需的关键几何信息:
- 转弯的起点 ($T_1$) 和终点 ($T_2$) 在原直线路径上的精确位置。
- 连接 $T_1$ 和 $T_2$ 的一系列平滑曲线上的离散坐标点。
- 一个状态标志表明计算结果是否可用。
- 相关的状态或错误文本信息。
仿真系统中的路径规划模块可以使用这些输出数据来构建最终的飞行路径:它会将前一段的直线路径连接到 $T_1$,然后使用 path_points 中的点序列作为转弯部分,最后从 $T_2$ 连接到后一段的直线路径。
输出曲线的离散化
连接 $T_1$ 和 $T_2$ 的一系列平滑曲线上的离散坐标点,是按照以下原则进行离散化的:
该平滑曲线是由一个或三个连续的圆弧段组成的。代码中的 addArcPoints 函数负责对每一个单独的圆弧段进行离散化。其原则是:
等角度间隔 (Equiangular Discretization):
- 对于构成平滑转弯的每一个圆弧段(例如,从 $T_1$ 到 $P_A$,或 $P_A$ 到 $P_B$,或 $P_B$ 到 $T_2$),函数首先计算出该圆弧段的起始角度和终止角度(相对于圆心)。
- 然后,根据这两个角度和转弯方向,确定圆弧的总张角(
angle_diff)。 - 这个总张角会被分割成
num_points个(num_points是传递给addArcPoints函数的参数,对应于您在generateTurnTrajectory中设置的num_points_per_arc)相等的小角度。 - 通过在起始角度上累加这些小角度间隔,得到一系列中间角度。
圆上点计算:
- 对于每一个通过等角度间隔计算出来的中间角度,利用圆的参数方程($x = center_x + R \cdot \cos(\text{angle})$, $y = center_y + R \cdot \sin(\text{angle})$)计算出该角度在圆弧上的对应点的二维坐标。
点序列的生成:
addArcPoints函数会将当前处理的圆弧段的起点加入到总路径点列表path_points中(除非它与列表中的最后一个点重复)。- 然后,它会通过上述的等角度间隔方法,依次生成
num_points个点,并将这些点加入到path_points列表中。最后一个生成的点将是当前圆弧段的终点。
总之:
- 整个平滑曲线($T_1 \to T_2$)是由一个或三个圆弧段拼接而成。
- 每个圆弧段都是通过在其总张角内进行均匀的角度分割来离散化的。
- 离散点的数量由
num_points_per_arc参数控制,它决定了每个圆弧段(除了起点之外)将被近似为多少个小直线段。这个值越大,生成的曲线点越多,路径看起来越平滑,但计算量和数据量也相应增加。
因此,离散化的核心原则是在每个构成转弯的圆弧上,以相等的角度增量来采样点。