在初始的多项式轨迹生成后,是要经过一个关键点采样,使用关键点来进行后续的 B 样条曲线拟合的。即:

初始多项式拟合->关键点采样->B样条拟合

关键点采样的思路

关键点采样使用时间步长 ts 来在初始轨迹方程中取点。

在上一步的初始轨迹生成后,轨迹方程保存在 gl_traj 中,ego 的关键点采样就是选取合适的 ts,在轨迹方程中代入 ts ,选取一定量的关键点,用于后续的 B 样条拟合。

ts的选取

初始步长的确定

采样步长的初始值使用参数步长 (pp_.ctrl_pt_dist) 以及参数最大速度 (pp_.max_vel_) 来确定:

// ts 初始数值先使用参数控制点距离与最大速度参数来设定
double ts = (start_pt - local_target_pt).norm() > 0.1 ? pp_.ctrl_pt_dist / pp_.max_vel_ * 1.2 : pp_.ctrl_pt_dist / pp_.max_vel_ * 5;

动态确定最终合适的步长

初始步长不一定是最合适的,可能会有采样点比较密集或者稀疏的问题,因此需要再进行一次动态确定最终步长的过程,在 ego 中是使用了一个 do,while 循环,在进行循环之前,先把初始的 ts 进行放大:

ts *= 1.5; // ts will be divided by 1.5 in the next  将最初的时间间隔放大,避免因为初始 ts 过小导致采样点很密集

之后在每次循环中,逐步减小 ts ,然后判断根据当前 ts 采样到的点是否满足要求,不满足就继续降低 ts:

// 以下循环用于找到合适的时间步 ts,保证分割点合适,不会太过密集,同时也控制数量需要超大于等于7个,用于后续的B样条拟合
do
{ts /= 1.5;  // 每次循环时将 ts 除以 1.5,逐渐找到合适的时间步point_set.clear();    // 每次修改时间步后,清除点flag_too_far = false;Eigen::Vector3d last_pt = gl_traj.evaluate(0);  // 获取轨迹起点// 根据当前的时间步 来循环采点,判断是否需要进一步缩小 ts 来采点for (t = 0; t < time; t += ts){Eigen::Vector3d pt = gl_traj.evaluate(t); // 计算 t 时刻的轨迹位置// 检查相邻的采样点距离是否超限if ((last_pt - pt).norm() > pp_.ctrl_pt_dist * 1.5){flag_too_far = true;  // 距离过远时,置位标志,进一步缩小 ts break;}last_pt = pt;point_set.push_back(pt);}
} while (flag_too_far || point_set.size() < 7); // To make sure the initial path has enough points.

根据最终的出循环条件,找到一个距离足够远,且数量大于等于 7 个的采样点的 ts。

之后将此 ts 对应的起点,终点的速度,加速度记录下来:

t -= ts;  // 回退到最后一个采样点的时刻(因为上面循环中有最后一个 t += ts)
// 记录轨迹起点,终点的速度,加速度信息
start_end_derivatives.push_back(gl_traj.evaluateVel(0));
start_end_derivatives.push_back(local_target_vel);
start_end_derivatives.push_back(gl_traj.evaluateAcc(0));
start_end_derivatives.push_back(gl_traj.evaluateAcc(t));

python代码验证

编写代码来验证效果的话,只需要在上一篇的基础上,添加一下总时间,以及动态计算即可,然后把点给画出来看看效果:

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
class PolynomialTraj:"""实现EGO-Planner中描述的五次多项式轨迹生成"""def __init__(self, coeffs, total_time):self.coeffs = coeffs  # 多项式系数 (3x6矩阵)self.total_time = total_time  # 轨迹总时间@staticmethoddef one_segment_traj_gen(start_pt, start_vel, start_acc,end_pt, end_vel, end_acc, total_time):"""生成单段五次多项式轨迹,满足6个运动学约束条件参数:start_pt: 起点位置 [x, y, z]start_vel: 起点速度 [vx, vy, vz]start_acc: 起点加速度 [ax, ay, az]end_pt: 终点位置 [x, y, z]end_vel: 终点速度 [vx, vy, vz]end_acc: 终点加速度 [ax, ay, az]total_time: 轨迹总时间返回:PolynomialTraj对象"""# 构造约束矩阵Ct = total_timeC = np.zeros((6, 6))# t=0时刻的约束C[0, 5] = 1  # 位置: x(0) = c0C[1, 4] = 1  # 速度: v(0) = c1C[2, 3] = 2  # 加速度: a(0) = 2*c2# t=T时刻的约束C[3] = [t **5, t** 4, t ** 3, t ** 2, t, 1]  # 位置C[4] = [5 * t **4, 4 * t** 3, 3 * t ** 2, 2 * t, 1, 0]  # 速度C[5] = [20 * t **3, 12 * t** 2, 6 * t, 2, 0, 0]  # 加速度# 求解三个方向的多项式系数coeffs = np.zeros((3, 6))for i in range(3):# 构造约束向量BB = np.array([start_pt[i],  # 起点位置start_vel[i],  # 起点速度start_acc[i],  # 起点加速度end_pt[i],  # 终点位置end_vel[i],  # 终点速度end_acc[i]  # 终点加速度])# 求解线性方程组 C·coeff = B,得到系数coeffs[i] = np.linalg.solve(C, B)return PolynomialTraj(coeffs, total_time)def evaluate(self, time):"""计算指定时刻的位置"""t = np.clip(time, 0, self.total_time)# 计算位置 (x(t) = c5*t^5 + c4*t^4 + c3*t^3 + c2*t^2 + c1*t + c0)pos = np.zeros(3)pos[0] = np.dot(self.coeffs[0], [t **5, t** 4, t ** 3, t ** 2, t, 1])pos[1] = np.dot(self.coeffs[1], [t **5, t** 4, t ** 3, t ** 2, t, 1])pos[2] = np.dot(self.coeffs[2], [t **5, t** 4, t ** 3, t ** 2, t, 1])return posdef evaluate_full(self, time):"""计算指定时刻的位置、速度和加速度"""t = np.clip(time, 0, self.total_time)pos = np.zeros(3)vel = np.zeros(3)acc = np.zeros(3)# 计算位置pos[0] = np.dot(self.coeffs[0], [t **5, t** 4, t ** 3, t ** 2, t, 1])pos[1] = np.dot(self.coeffs[1], [t **5, t** 4, t ** 3, t ** 2, t, 1])pos[2] = np.dot(self.coeffs[2], [t **5, t** 4, t ** 3, t ** 2, t, 1])# 计算速度vel[0] = np.dot(self.coeffs[0], [5 * t **4, 4 * t** 3, 3 * t ** 2, 2 * t, 1, 0])vel[1] = np.dot(self.coeffs[1], [5 * t **4, 4 * t** 3, 3 * t ** 2, 2 * t, 1, 0])vel[2] = np.dot(self.coeffs[2], [5 * t **4, 4 * t** 3, 3 * t ** 2, 2 * t, 1, 0])# 计算加速度acc[0] = np.dot(self.coeffs[0], [20 * t **3, 12 * t** 2, 6 * t, 2, 0, 0])acc[1] = np.dot(self.coeffs[1], [20 * t **3, 12 * t** 2, 6 * t, 2, 0, 0])acc[2] = np.dot(self.coeffs[2], [20 * t **3, 12 * t** 2, 6 * t, 2, 0, 0])return pos, vel, acc
def calculate_trajectory_time(distance, max_vel, max_acc):"""根据论文中的方法计算轨迹总时间短距离: 仅加速阶段长距离: 加速→匀速→减速阶段"""# 计算临界距离:达到最大速度所需的距离critical_distance = (max_vel ** 2) / max_accif distance < critical_distance:# 短距离:仅加速即可到达目标return np.sqrt(distance / max_acc) * 2  # 修正系数使结果更合理else:# 长距离:加速→匀速→减速time_acc_dec = 2 * max_vel / max_acc  # 加速和减速时间之和time_constant = (distance - critical_distance) / max_vel  # 匀速时间return time_acc_dec + time_constant
def find_optimal_ts_and_sample(traj, ctrl_pt_dist, min_point_num=7):"""动态调整采样时间步ts,生成满足条件的采样点集参数:traj: PolynomialTraj对象ctrl_pt_dist: B样条控制点基准间距(m)min_point_num: 最小采样点数量返回:optimal_ts: 最优采样时间步(s)point_set: 采样点集(Nx3)sample_times: 采样时刻(s)"""# 初始ts猜测(先放大1.5倍,后续逐步缩小)initial_ts_guess = traj.total_time / 5  # 初始预估5个点optimal_ts = initial_ts_guess * 1.5  # 预放大flag_too_far = Truepoint_set = []sample_times = []# 循环调整ts直到满足条件while flag_too_far or len(point_set) < min_point_num:optimal_ts /= 1.5  # 每次缩小1.5倍point_set.clear()sample_times.clear()flag_too_far = False# 起点初始化last_pt = traj.evaluate(0.0)point_set.append(last_pt)sample_times.append(0.0)# 按当前ts采样t = optimal_tswhile t < traj.total_time + 1e-6:  # 允许微小误差,避免漏采终点current_pt = traj.evaluate(t)# 检查相邻点距离是否超限dist_between = np.linalg.norm(current_pt - last_pt)if dist_between > ctrl_pt_dist * 1.5:flag_too_far = Truebreak  # 超限,退出当前采样point_set.append(current_pt)sample_times.append(t)last_pt = current_ptt += optimal_ts# 确保最后一个点是轨迹终点final_pt = traj.evaluate(traj.total_time)if np.linalg.norm(final_pt - point_set[-1]) > 1e-3:point_set.append(final_pt)sample_times.append(traj.total_time)# 转换为numpy数组point_set = np.array(point_set)sample_times = np.array(sample_times)return optimal_ts, point_set, sample_times# *********************************** 动态寻找 ts 并生成采样点 ************************************
def visualize_trajectory(traj, point_set, sample_times, optimal_ts, ctrl_pt_dist):"""可视化轨迹和采样点"""# 生成高密度轨迹点用于绘制连续曲线dense_times = np.linspace(0, traj.total_time, 1000)dense_points = np.array([traj.evaluate(t) for t in dense_times])# 创建3D图形fig = plt.figure(figsize=(15, 10))# 3D轨迹图ax3d = fig.add_subplot(111, projection='3d')ax3d.plot(dense_points[:, 0], dense_points[:, 1], dense_points[:, 2],'gray', linewidth=1, label='traj')ax3d.scatter(point_set[:, 0], point_set[:, 1], point_set[:, 2],c='red', s=50, zorder=5, label='sample')ax3d.scatter(point_set[0, 0], point_set[0, 1], point_set[0, 2],c='green', s=100, marker='o', label='start')ax3d.scatter(point_set[-1, 0], point_set[-1, 1], point_set[-1, 2],c='blue', s=100, marker='*', label='end')ax3d.set_xlabel('X (m)')ax3d.set_ylabel('Y (m)')ax3d.set_zlabel('Z (m)')ax3d.set_title('3D traj')ax3d.legend()plt.tight_layout()plt.show()
def ego_ts_main():# 轨迹参数设置start_pt = np.array([0.0, 0.0, 0.0])  # 起点位置start_vel = np.array([0.1, 0.1, 0.0])  # 起点速度start_acc = np.array([0.0, 0.0, 0.0])  # 起点加速度end_pt = np.array([8.0, 4.0, 2.0])  # 终点位置end_vel = np.array([0.1, 0.1, 0.0])  # 终点速度end_acc = np.array([0.0, 0.0, 0.0])  # 终点加速度max_vel = 2.0  # 最大速度max_acc = 1.0  # 最大加速度ctrl_pt_dist = 0.8  # 控制点基准间距# 计算轨迹距离和时间distance = np.linalg.norm(end_pt - start_pt)total_time = calculate_trajectory_time(distance, max_vel, max_acc)print(f"轨迹距离: {distance:.2f}m, 计算得到总时间: {total_time:.2f}s")# 生成多项式轨迹traj = PolynomialTraj.one_segment_traj_gen(start_pt, start_vel, start_acc,end_pt, end_vel, end_acc,total_time)# 动态确定最优采样时间步并采样optimal_ts, point_set, sample_times = find_optimal_ts_and_sample(traj, ctrl_pt_dist)print(f"最优采样时间步: {optimal_ts:.4f}s, 采样点数量: {len(point_set)}")# 可视化结果visualize_trajectory(traj, point_set, sample_times, optimal_ts, ctrl_pt_dist)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/95462.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/95462.shtml
英文地址,请注明出处:http://en.pswp.cn/web/95462.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

专项智能练习(信息安全防护措施)

3.以下属于网络安全威胁的是&#xff08;A &#xff09;。 A.非授权访问、病毒感染、信息泄露、拒绝网络服务 B.信息泄露、非授权访问、病毒感染、硬盘损坏 C.信息篡改、非授权访问、病毒感染、硬盘损坏 D.网络异常、非授权访问、信息篡改、病毒感染 解析本题考查网络安全威胁。…

ubuntu编译webrtc库

一. 前言 本文介绍在 ubuntu 下如何通过 webrtc 源码编译出指定版本 webrtc.lib 库&#xff08;以 m94 版本为例&#xff09;。 二. 编译步骤 1. 下载depot_tools工具 depot_tools 是 Google 用来管理大型项目代码&#xff08;例如 WebRTC&#xff09;的工具集&#xff0c;它…

基于ZooKeeper实现分布式锁(Spring Boot接入)及与Kafka实现的对比分析

在分布式系统中,多节点对共享资源的并发访问往往会引发数据一致性问题,分布式锁正是解决这一问题的核心组件。本文将从原理出发,详细讲解基于ZooKeeper实现分布式锁的完整流程,提供Spring Boot接入的可运行代码,并深入对比其与Kafka实现分布式锁的异同点及优缺点,帮助开发…

Shell 三剑客之 awk 命令详解(理论+实战)

目录 一、前言 二、工作流程总览 三、最常用内置变量 四、命令格式 五、20 个高频实战案例 5.1 基础打印 awk {print "hello"} < /etc/passwd 所有行打印成hello awk {print} test6.txt 打印test6.txt文件 awk {print $1} test6.txt 默认以空格为分割&am…

一个真正跨平台可用的免费PDF解决方案

在整理资料时&#xff0c;常需将不同格式的文件统一转为PDF格式&#xff0c;确保排版不乱、便于长期保存和打印 它的功能全面&#xff0c;支持批量操作&#xff0c;使用非常方便&#xff1a;只需把PDF文件拖进界面&#xff0c;选择目标格式&#xff0c;无论PDF转换成Word、PDF…

强化微调:以Swift框架进行GRPO多模态模型强化微调为例

一、TL&#xff1b;DR 整体介绍&#xff1a;强化微调RFT的原因、步骤、作用以及常见的rft方式dmeo举例&#xff1a;以Swift给的Qwen2.5-Math-7B-Instruct为例介绍了整个RFT的流程和代码细节实际强化微调&#xff1a;以qwen/internVL为例完成一次指令微调并且使用强化学习进一步…

时序数据:使用关系数据库 vs 时序数据库存储的核心区别是什么?

一、时序数据使用关系数据库 vs 时序数据库存储的核心区别 时序数据&#xff08;Time Series Data&#xff09;是指随时间连续产生的数据&#xff08;如传感器读数、服务器指标、交易记录等&#xff09;&#xff0c;其核心特点是高频写入、时间有序、量大且查询模式集中于时间范…

ansible判断

ansible判断 一、判断运算符 “” “!” “>” “<” “>” “<” “and” “or” “not” is in 每次执行完一个任务&#xff0c;不管成功与失败&#xff0c;都会将执行的结果进行注册&#xff0c;可以使用这个注册的变量来判断 when&#…

接口设计标准化流程,结合RESTful最佳实践和实际开发经验,涵盖从需求分析到部署的全过程

目录一、接口设计流程二、需求分析阶段1. 功能需求2. 非功能性需求三、接口设计规范四、详细实现步骤1. 选择Web框架2. 接口路由设计3. 请求参数定义4. 请求参数验证5. 业务逻辑分层6. 错误处理机制7. 异步任务处理8. 安全策略9. 接口文档10. 测试策略11. 服务部署11.1 生产环境…

LeetCode 1023.驼峰式匹配

给你一个字符串数组 queries&#xff0c;和一个表示模式的字符串 pattern&#xff0c;请你返回一个布尔数组 answer 。只有在待查项 queries[i] 与模式串 pattern 匹配时&#xff0c; answer[i] 才为 true&#xff0c;否则为 false。 如果可以将 小写字母 插入模式串 pattern 得…

【IQA技术专题】 无参考自然图像IQA:NIQE

无参考自然图像IQA&#xff1a;NIQE&#xff1a;Making a “Completely Blind” Image Quality Analyzer&#xff08;2012 IEEE&#xff09;专题介绍一、研究背景二、NIQE方法2.1 NSS model2.2 Patch Selection2.3 Characterizing Image Patches2.4 Multivariate Gaussian Mode…

变位齿轮:分度圆、节圆与中心距的 “特殊关联”

接着上回的话题&#xff0c;在标准齿轮中&#xff0c;我们追求的是“节圆与分度圆重合”的理想状态。但当实际工程提出更苛刻的要求时&#xff0c;比如&#xff1a;需要避免齿轮根切&#xff08;齿数过少时&#xff09;。要配凑一个非标准的中心距。需要大幅提高小齿轮的强度和…

Spring Boot集成Kafka常见业务场景最佳实践实战指南

一、基础集成与核心组件解析 &#xff08;一&#xff09;环境搭建与依赖配置 在 Spring Boot 项目中集成 Kafka&#xff0c;首先需通过 Maven 添加核心依赖&#xff1a; <dependency> <groupId>org.springframework.kafka</groupId> <artifactId>…

黑芝麻智能与云深处科技达成战略合作,共推具身智能平台全球市场应用

8月28日&#xff0c;智能汽车计算芯片引领者黑芝麻智能与具身智能创新技术与行业应用引领者云深处科技达成战略合作。双方将围绕具身智能控制平台开发、行业智能解决方案共建与国际市场拓展三大方向展开深度合作&#xff0c;携手推进高性能机器人在多行业场景的规模化落地与应用…

AI零售创业公司:零眸智能

零眸智能公司分析 引言 “这次融资与合作&#xff0c;让我们的全球化节奏更坚实也更有确定性。秉持‘让热爱与科技成就无限可能’&#xff0c;我们坚持真诚合作、长期主义与价值共享&#xff0c;把行业垂直AI能力按里程碑推进并沉淀为可复制的标准。” —— 零眸智能CEO樊凌云①…

学习插入排序+希尔排序并使用java写代码

目录 插入排序 例子时间复杂度java代码 希尔排序&#xff08;缩小增量排序&#xff09; 例子时间复杂度java代码 相关文章 学习数据结构理论算法时间复杂度学习有序二叉树平衡二叉树红黑树学习冒泡排序选择排序并使用java写代码学习插入排序希尔排序并使用java写代码学习堆…

win10虚拟机报错打不开和ubuntu空间不足

ubuntu主机安装的win10虚拟机报错如下&#xff0c;导致虚拟机无法打开解决办法 如上图&#xff0c;找到ubuntu主机home目录中win10的路径&#xff0c;将红色框的文件删除&#xff0c;然后将绿色框中的文件.prev后缀去掉&#xff0c;如下图所示。重新打开虚拟机就可以了 ubuntu空…

指纹手机技术:破解亚马逊多账号运营痛点的底层逻辑与实践

在亚马逊平台运营中&#xff0c;账号关联、行为异常、网络不合规是卖家绕不开的三大核心风险。随着亚马逊反作弊系统&#xff08;如 A9 算法&#xff09;对设备指纹、操作轨迹、网络特征的识别精度持续提升&#xff0c;传统 “普通手机 VPN” 的多账号运营模式已频繁触发风控&…

《UE5_C++多人TPS完整教程》学习笔记46 ——《P47 蹲伏行走(Crouching Walking)》

本文为B站系列教学视频 《UE5_C多人TPS完整教程》 —— 《P47 蹲伏行走&#xff08;Crouching Walking&#xff09;》 的学习笔记&#xff0c;该系列教学视频为计算机工程师、程序员、游戏开发者、作家&#xff08;Engineer, Programmer, Game Developer, Author&#xff09; S…

TiDB v8.5.3 单机集群部署指南

前言 最近在做 TiDB 的恢复演练&#xff0c;需要在单台 Linux 服务器上部署一套 TiDB 最小的完整拓扑的集群&#xff0c;本文记录一下安装过程。 环境准备 开始部署 TiDB 集群前&#xff0c;准备一台部署主机&#xff0c;确保其软件满足需求&#xff1a; 推荐安装 CentOS 7…