轨迹优化 | 基于Savitzky-Golay滤波的无约束路径平滑(附ROS C++/Python仿真)

目录

  • 0 专栏介绍
  • 1 什么是Savitzky-Golay滤波?
  • 2 Savitzky-Golay滤波推导
  • 3 算法仿真与验证
    • 3.1 ROS C++仿真
    • 3.2 Python仿真

0 专栏介绍

🔥课设、毕设、创新竞赛必备!🔥本专栏涉及更高阶的运动规划算法轨迹优化实战,包括:曲线生成、碰撞检测、安全走廊、优化建模(QP、SQP、NMPC、iLQR等)、轨迹优化(梯度法、曲线法等),每个算法都包含代码实现加深理解

🚀详情:运动规划实战进阶:轨迹优化篇


1 什么是Savitzky-Golay滤波?

Savitzky--Golay滤波器是一种数字滤波器,能够应用于一组数字数据点,用于平滑数据,即在不扭曲信号趋势的情况下提高数据的精度,如下图所示。Savitzky--Golay滤波器使用低阶多项式通过最小二乘法拟合相邻数据点的连续子集。当数据点是等间距时,可以找到最小二乘方程的解析解,形式为一组卷积系数,这些系数可以应用于所有数据子集,用于估算平滑信号在每个子集中心点的值。该方法可以扩展到2维和3维数据的处理。

2 Savitzky-Golay滤波推导

形式化地,针对由 K K K个点组成的离散路径 { x k } \left\{ x_k \right\} {xk},构造以 x k x_k xk为中心,其相邻的共 2 M + 1 2M+1 2M+1个数据点组成的连续子集窗口 W = { x ^ i } i = − M M W=\left\{ \hat{x}_i \right\} _{i=-M}^{M} W={x^i}i=−MM。用 N N N次多项式

p ( i ) = ∑ n = 0 N a n i n p\left( i \right) =\sum_{n=0}^N{a_ni^n} p(i)=n=0∑Nanin

拟合 W W W内的数据,拟合残差为

ε = ∑ i = − M M ( p ( i ) − x ^ i ) 2 = ∑ i = − M M ( ∑ j = 0 N a j i j − x ^ i ) 2 \varepsilon =\sum_{i=-M}^M{\left( p\left( i \right) -\hat{x}i \right) ^2}=\sum{i=-M}^M{\left( \sum_{j=0}^N{a_ji^j}-\hat{x}_i \right) ^2} ε=i=−M∑M(p(i)−x^i)2=i=−M∑M(j=0∑Najij−x^i)2

为了使残差最小,令

∂ ε ∂ a = [ 2 ∑ i = − M M i 0 ( ∑ j = 0 N a j i j − x ^ i ) ⋯ 2 ∑ i = − M M i N ( ∑ j = 0 N a j i j − x ^ i ) ] ( N + 1 ) × 1 T = 0 \frac{\partial \varepsilon}{\partial \boldsymbol{a}}=\left[ \begin{matrix} 2\sum_{i=-M}^M{i^0\left( \sum_{j=0}^N{a_ji^j}-\hat{x}i \right)}& \cdots& 2\sum{i=-M}^M{i^N\left( \sum_{j=0}^N{a_ji^j}-\hat{x}_i \right)}\\\end{matrix} \right] {{\left( N+1 \right) \times 1}}^{T}=\mathbf{0} ∂a∂ε=[2∑i=−MMi0(∑j=0Najij−x^i)⋯2∑i=−MMiN(∑j=0Najij−x^i)](N+1)×1T=0

其中 a = [ a 0 a 1 ⋯ a N ] T \boldsymbol{a}=\left[ \begin{matrix} a_0& a_1& \cdots& a_N\\\end{matrix} \right] ^T a=[a0a1⋯aN]T为多项式系数向量

设只关联于预设的窗口大小 M M M和多项式拟合次数 N N N的参数矩阵

A = [ ( − M ) 0 ( − M ) 1 ⋯ ( − M ) N ⋮ ⋮ ⋱ ⋮ ( M ) 0 ( M ) 1 ⋯ ( M ) N ] ( 2 M + 1 ) × ( N + 1 ) \boldsymbol{A}=\left[ \begin{matrix} \left( -M \right) ^0& \left( -M \right) ^1& \cdots& \left( -M \right) ^N\\ \vdots& \vdots& \ddots& \vdots\\ \left( M \right) ^0& \left( M \right) ^1& \cdots& \left( M \right) ^N\\\end{matrix} \right] _{\left( 2M+1 \right) \times \left( N+1 \right)} A=⎣⎢⎡(−M)0⋮(M)0(−M)1⋮(M)1⋯⋱⋯(−M)N⋮(M)N⎦⎥⎤(2M+1)×(N+1)

则最小二乘关系可表示为

A T A a = A T x ^ ⇒ a = H x ^ \boldsymbol{A}^T\boldsymbol{Aa}=\boldsymbol{A}^T\boldsymbol{\hat{x}}\Rightarrow \boldsymbol{a}=\boldsymbol{H\hat{x}} ATAa=ATx^⇒a=Hx^

其中 H = ( A T A ) − 1 A T \boldsymbol{H}=\left( \boldsymbol{A}^T\boldsymbol{A} \right) ^{-1}\boldsymbol{A}^T H=(ATA)−1AT

由于每次平滑只输出中心的平滑信号,因此只需考虑 H \boldsymbol{H} H的第一行元素,称为Savitzky-Golay滤波器的卷积核

3 算法仿真与验证

3.1 ROS C++仿真

核心算法如下所示

cpp 复制代码
bool SavitzkyGolayPathProcessor::_applyFilterOverPath(const Points3d& path_in, Points3d& path_out)
{
  int path_size = static_cast<int>(path_in.size());
  if (path_size < kernel.size())
  {
    return true;
  }

  path_out.clear();
  auto pt_m3 = path_in[0];
  auto pt_m2 = path_in[0];
  auto pt_m1 = path_in[0];
  auto pt = path_in[1];
  auto pt_p1 = path_in[2];
  auto pt_p2 = path_in[3];
  auto pt_p3 = path_in[4];

  // First ang last point is fixed
  path_out.emplace_back(path_in[0].x(), path_in[0].y());
  for (unsigned int idx = 1; idx < path_size; idx++)
  {
    Point3d smooth_pt;
    if (!_applyFilter({ pt_m3, pt_m2, pt_m1, pt, pt_p1, pt_p2, pt_p3 }, smooth_pt))
    {
      return false;
    }
    pt_m3 = pt_m2;
    pt_m2 = pt_m1;
    pt_m1 = pt;
    pt = pt_p1;
    pt_p1 = pt_p2;
    pt_p2 = pt_p3;
    pt_p3 = idx + 4 < path_size - 1 ? path_in[idx + 4] : path_in.back();
    path_out.emplace_back(smooth_pt.x(), smooth_pt.y());
  }
  return true;
}

算法效果如下所示


3.2 Python仿真

核心算法如下所示

python 复制代码
def applyFilterOverAxes(self, waypoints: list) -> list:
    M = int((len(self.kernel) - 1) / 2)
    filtered_waypoints = [waypoints[0]]
    window = [waypoints[0] for _ in range(M)]
    window.append(waypoints[1])
    for i in range(M):
        window.append(waypoints[2 + i])

    # First point is fixed
    path_len = len(waypoints)
    for i in range(path_len):
        smooth_pt = self.applyFilter(window)
        filtered_waypoints.append((smooth_pt.x, smooth_pt.y))
        for j in range(2 * M):
            window[j] = window[j + 1]

        if i + M + 1 < path_len - 1:
            window[-1] = waypoints[i + M + 1]
        else:
            # Return the last point
            window[-1] = waypoints[-1]
    return filtered_waypoints

算法效果如下所示

完整工程代码请联系下方博主名片获取


🔥 更多精彩专栏

👇源码获取 · 技术交流 · 抱团学习 · 咨询分享 请联系👇

相关推荐
标贝科技21 分钟前
标贝科技:AI基础数据服务,人工智能行业发展的底层支撑
人工智能·机器学习·语言模型·数据挖掘·自动驾驶·database·big data
Sui_Network1 小时前
World Wide Walrus:下一代数据存储协议
大数据·人工智能·web3·去中心化·区块链
一水鉴天1 小时前
智能工厂的设计软件 为了监管控一体化的全能Supervisor 的监督学习 之 序6 进化论及科学的信息技术创新:分布式账本/区块链/智能合约
开发语言·人工智能·学习·区块链·智能合约·分布式账本
SuhyOvO1 小时前
std::sort的底层原理(混合排序算法)
c++·算法·stl·排序算法
licy__1 小时前
python常用的排序算法
python·算法·排序算法
行码棋1 小时前
【机器学习】决策树算法原理详解
算法·决策树·机器学习
卷卷的小趴菜学编程1 小时前
类和对象(中)
java·c语言·开发语言·数据结构·c++·算法·链表
不打灰的小刘1 小时前
基于自动反馈的大语言模型纠错策略综述
人工智能·算法·语言模型·chatgpt
mslion1 小时前
整理:4篇专注于多模态大语言模型(MLLM)的瘦身变体论文
人工智能·语言模型·自然语言处理·大语言模型·多模态
唯创知音2 小时前
WTV芯片在智能电子锁语音留言上的应用方案解析
人工智能·单片机·物联网·智能家居·语音识别