【C++】sophus : spline.hpp 在李群(Lie Group)上进行样条插值 (十二)

这段代码实现了在李群(Lie Group)上进行样条插值的数学工具,基于 Lovegrove 等人在 2013 年的论文提出的算法。以下是代码的关键点总结:


1. 核心概念

  • 样条函数

    :利用 B 样条基函数(B-Spline Basis Functions)构建插值函数,适用于连续变换的平滑插值。

  • 李群和李代数

    :基于李群(如旋转、位移等连续变换的集合)和其对应的李代数,完成插值和微分操作。


2. 主要功能

(1)B 样条基函数实现

类 SplineBasisFunction

  • 提供了三次 B 样条的基函数 B(u) 和其一阶、二阶导数 Dt_B(u, delta_t) 与 Dt2_B(u, delta_t) 的实现。

  • 使用矩阵 C 定义 B 样条的系数矩阵。

(2)样条插值函数

类 BasisSplineFn

  • 在李群中通过控制点计算样条插值值(parent_T_spline)、一阶导数(Dt_parent_T_spline)和二阶导数(Dt2_parent_T_spline)。

  • 核心方法:

    • A(...):利用基函数值计算控制点的李群变换。
    • Dt_A(...) 和 Dt2_A(...):分别计算插值的一阶和二阶导数。
    • 使用李群的指数映射 exp 和对数映射 log 处理插值。

(3)样条段的分段计算

类 BasisSplineSegment

  • 处理样条插值的单个段,根据分段类型(SegmentCase)计算插值值及导数。

  • 分段类型:

    • first:样条段起始。
    • normal:普通样条段。
    • last:样条段末尾。
  • 使用控制点的相对位姿(通过李代数对数计算)定义控制矢量。

(4)完整样条实现

类 BasisSplineImpl

  • 提供多段样条的整体实现。

  • 核心功能:

    • parent_T_spline:计算第 i 段样条的插值值。
    • Dt_parent_T_spline 和 Dt2_parent_T_spline:计算插值值的一阶和二阶导数。
    • 管理控制点并提供分段数量、时间步长等信息。


3. 应用场景

  • 运动插值

    :用于机器人学、计算机视觉等领域中平滑表示连续的位姿(如旋转和位移)。

  • 时间微分计算

    :支持计算位姿的速度和加速度。

  • 关键帧控制

    :利用离散控制点生成连续轨迹。


4. 总结

该代码结合了数学理论(B 样条)与几何表示(李群与李代数),提供了高效且精确的插值工具,适用于对连续变换(如旋转和平移)的光滑插值和导数计算。

/// @file// Basis spline implementation on Lie Group following:// S. Lovegrove, A. Patron-Perez, G. Sibley, BMVC 2013// http://www.bmva.org/bmvc/2013/Papers/paper0093/paper0093.pdf
#pragma once  // 确保该文件只被编译一次
#include "types.hpp"  // 包含头文件 types.hpp
namespace Sophus {  // 定义命名空间 Sophus
template <class Scalar>class SplineBasisFunction {  // 定义模板类 SplineBasisFunction public:  static SOPHUS_FUNC Eigen::Matrix<Scalar, 3, 4> C() {  // 定义静态成员函数 C 返回一个 Eigen 矩阵    Eigen::Matrix<Scalar, 3, 4> C;  // 定义 3x4 大小的矩阵    Scalar const o(0);  // 定义常量 o 值为 0
    // clang-format off    C << Scalar(5./6),  Scalar(3./6), -Scalar(3./6),  Scalar(1./6),  // 填充矩阵 C 的值         Scalar(1./6),  Scalar(3./6),  Scalar(3./6), -Scalar(2./6),                    o,             o,             o,  Scalar(1./6);    // clang-format on    return C;  // 返回填充好的矩阵 C  }
  static SOPHUS_FUNC Vector3<Scalar> B(Scalar const& u) {  // 定义静态成员函数 B 返回一个 3 维向量    // SOPHUS_ENSURE(u >= Scalar(0), "but %", u);  // 确保 u >= 0    // SOPHUS_ENSURE(u < Scalar(1), "but %", u);  // 确保 u < 1    Scalar u_square(u * u);  // 计算 u 的平方    return C() * Vector4<Scalar>(Scalar(1), u, u_square, u * u_square);  // 计算并返回向量 B  }
  static SOPHUS_FUNC Vector3<Scalar> Dt_B(Scalar const& u, Scalar const& delta_t) {  // 定义静态成员函数 Dt_B 返回一个 3 维向量    // SOPHUS_ENSURE(u >= Scalar(0), "but %", u);  // 确保 u >= 0    // SOPHUS_ENSURE(u < Scalar(1), "but %", u);  // 确保 u < 1    return (Scalar(1) / delta_t) * C() *           Vector4<Scalar>(Scalar(0), Scalar(1), Scalar(2) * u, Scalar(3) * u * u);  // 计算并返回向量 Dt_B  }
  static SOPHUS_FUNC Vector3<Scalar> Dt2_B(Scalar const& u, Scalar const& delta_t) {  // 定义静态成员函数 Dt2_B 返回一个 3 维向量    // SOPHUS_ENSURE(u >= Scalar(0), "but %", u);  // 确保 u >= 0    // SOPHUS_ENSURE(u < Scalar(1), "but %", u);  // 确保 u < 1    return (Scalar(1) / (delta_t * delta_t)) * C() *           Vector4<Scalar>(Scalar(0), Scalar(0), Scalar(2), Scalar(6) * u);  // 计算并返回向量 Dt2_B  }};
template <class LieGroup_>class BasisSplineFn {  // 定义模板类 BasisSplineFn public:  using LieGroup = LieGroup_;  // 定义类型别名 LieGroup  using Scalar = typename LieGroup::Scalar;  // 定义标量类型  using Transformation = typename LieGroup::Transformation;  // 定义变换矩阵类型  using Tangent = typename LieGroup::Tangent;  // 定义切向量类型
  static LieGroup parent_T_spline(      const LieGroup& parent_Ts_control_point,      std::tuple<Tangent, Tangent, Tangent> const& control_tagent_vectors,      double u) {  // 定义静态成员函数 parent_T_spline    auto AA = A(control_tagent_vectors, u);  // 计算 A 矩阵    return parent_Ts_control_point * std::get<0>(AA) * std::get<1>(AA) * std::get<2>(AA);  // 计算并返回 spline 变换矩阵  }
  static Transformation Dt_parent_T_spline(      const LieGroup& parent_Ts_control_point,      std::tuple<Tangent, Tangent, Tangent> const& control_tagent_vectors,      double u, double delta_t) {  // 定义静态成员函数 Dt_parent_T_spline    auto AA = A(control_tagent_vectors, u);  // 计算 A 矩阵    auto Dt_AA = Dt_A(AA, control_tagent_vectors, u, delta_t);  // 计算 Dt_A 矩阵    return parent_Ts_control_point.matrix() *           ((std::get<0>(Dt_AA) * std::get<1>(AA).matrix() * std::get<2>(AA).matrix()) +            (std::get<0>(AA).matrix() * std::get<1>(Dt_AA) * std::get<2>(AA).matrix()) +            (std::get<0>(AA).matrix() * std::get<1>(AA).matrix() * std::get<2>(Dt_AA)));  // 计算并返回 spline 的导数  }
  static Transformation Dt2_parent_T_spline(      const LieGroup& parent_Ts_control_point,      std::tuple<Tangent, Tangent, Tangent> const& control_tagent_vectors,      double u, double delta_t) {  // 定义静态成员函数 Dt2_parent_T_spline    using Scalar = typename LieGroup::Scalar;  // 定义标量类型    auto AA = A(control_tagent_vectors, u);  // 计算 A 矩阵    auto Dt_AA = Dt_A(AA, control_tagent_vectors, u, delta_t);  // 计算 Dt_A 矩阵    auto Dt2_AA = Dt2_A(AA, Dt_AA, control_tagent_vectors, u, delta_t);  // 计算 Dt2_A 矩阵
    return parent_Ts_control_point.matrix() *           ((std::get<0>(Dt2_AA) * std::get<1>(AA).matrix() * std::get<2>(AA).matrix()) +            (std::get<0>(AA).matrix() * std::get<1>(Dt2_AA) * std::get<2>(AA).matrix()) +            (std::get<0>(AA).matrix() * std::get<1>(AA).matrix() * std::get<2>(Dt2_AA)) +            Scalar(2) * ((std::get<0>(Dt_AA) * std::get<1>(Dt_AA) * std::get<2>(AA).matrix()) +                         (std::get<0>(Dt_AA) * std::get<1>(AA).matrix() * std::get<2>(Dt_AA)) +                         (std::get<0>(AA).matrix() * std::get<1>(Dt_AA) * std::get<2>(Dt_AA))));  // 计算并返回 spline 的二阶导数  }
 private:  static std::tuple<LieGroup, LieGroup, LieGroup> A(      std::tuple<Tangent, Tangent, Tangent> const& control_tagent_vectors,      double u) {  // 定义静态成员函数 A    Eigen::Vector3d B = SplineBasisFunction<double>::B(u);  // 计算基函数 B    return std::make_tuple(        LieGroup::exp(B[0] * std::get<0>(control_tagent_vectors)),        LieGroup::exp(B[1] * std::get<1>(control_tagent_vectors)),        LieGroup::exp(B[2] * std::get<2>(control_tagent_vectors)));  // 计算并返回 A 矩阵  }
  static std::tuple<Transformation, Transformation, Transformation> Dt_A(      std::tuple<LieGroup, LieGroup, LieGroup> const& AA,      const std::tuple<Tangent, Tangent, Tangent>& control_tagent_vectors,      double u, double delta_t) {  // 定义静态成员函数 Dt_A    Eigen::Vector3d Dt_B = SplineBasisFunction<double>::Dt_B(u, delta_t);  // 计算 Dt_B    return std::make_tuple(        Dt_B[0] * std::get<0>(AA).matrix() * LieGroup::hat(std::get<0>(control_tagent_vectors)),        Dt_B[1] * std::get<1>(AA).matrix() * LieGroup::hat(std::get<1>(control_tagent_vectors)),        Dt_B[2] * std::get<2>(AA).matrix() * LieGroup::hat(std::get<2>(control_tagent_vectors)));  // 计算并返回 Dt_A 矩阵  }
  static std::tuple<Transformation, Transformation, Transformation> Dt2_A(      std::tuple<LieGroup, LieGroup, LieGroup> const& AA,      std::tuple<Transformation, Transformation, Transformation> const& Dt_AA,      std::tuple<Tangent, Tangent, Tangent> const& control_tagent_vectors,      double u, double delta_t) {  // 定义静态成员函数 Dt2_A    Eigen::Vector3d Dt_B = SplineBasisFunction<double>::Dt_B(u, delta_t);  // 计算 Dt_B    Eigen::Vector3d Dt2_B = SplineBasisFunction<double>::Dt2_B(u, delta_t);  // 计算 Dt2_B
    return std::make_tuple(        (Dt_B[0] * std::get<0>(Dt_AA).matrix() +         Dt2_B[0] * std::get<0>(AA).matrix()) * LieGroup::hat(std::get<0>(control_tagent_vectors)),        (Dt_B[1] * std::get<1>(Dt_AA).matrix() +         Dt2_B[1] * std::get<1>(AA).matrix()) * LieGroup::hat(std::get<1>(control_tagent_vectors)),        (Dt_B[2] * std::get<2>(Dt_AA).matrix() +         Dt2_B[2] * std::get<2>(AA).matrix()) * LieGroup::hat(std::get<2>(control_tagent_vectors)));  // 计算并返回 Dt2_A 矩阵  }    enum class SegmentCase { first, normal, last };  // 枚举类型 SegmentCase 定义了三种状态:first(第一段),normal(正常段),last(最后一段)
template <class LieGroup>struct BasisSplineSegment {  // 模板结构体 BasisSplineSegment public:  using T = typename LieGroup::Scalar;  // 定义标量类型别名 T  using Transformation = typename LieGroup::Transformation;  // 定义变换矩阵类型别名
  BasisSplineSegment(SegmentCase segment_case, T const* const raw_ptr0,                     T const* const raw_ptr1, T const* const raw_ptr2,                     T const* const raw_ptr3)  // 构造函数,接受段类型和四个指针参数      : segment_case_(segment_case),  // 初始化段类型        raw_params0_(raw_ptr0),  // 初始化指针参数        raw_params1_(raw_ptr1),        raw_params2_(raw_ptr2),        raw_params3_(raw_ptr3) {}
  Eigen::Map<LieGroup const> const world_pose_foo_prev() const {  // 返回第一个指针指向的 LieGroup 对象    return Eigen::Map<LieGroup const>(raw_params0_);  }  Eigen::Map<LieGroup const> const world_pose_foo_0() const {  // 返回第二个指针指向的 LieGroup 对象    return Eigen::Map<LieGroup const>(raw_params1_);  }
  Eigen::Map<LieGroup const> const world_pose_foo_1() const {  // 返回第三个指针指向的 LieGroup 对象    return Eigen::Map<LieGroup const>(raw_params2_);  }
  Eigen::Map<LieGroup const> const world_pose_foo_2() const {  // 返回第四个指针指向的 LieGroup 对象    return Eigen::Map<LieGroup const>(raw_params3_);  }  LieGroup parent_T_spline(double u) {  // 计算父节点的样条曲线    switch (segment_case_) {      case SegmentCase::first:  // 如果是第一段        return BasisSplineFn<LieGroup>::parent_T_spline(            world_pose_foo_0(),            std::make_tuple(                (world_pose_foo_0().inverse() * world_pose_foo_0()).log(),                (world_pose_foo_0().inverse() * world_pose_foo_1()).log(),                (world_pose_foo_1().inverse() * world_pose_foo_2()).log()),            u);      case SegmentCase::normal:  // 如果是正常段        return BasisSplineFn<LieGroup>::parent_T_spline(            world_pose_foo_prev(),            std::make_tuple(                (world_pose_foo_prev().inverse() * world_pose_foo_0()).log(),                (world_pose_foo_0().inverse() * world_pose_foo_1()).log(),                (world_pose_foo_1().inverse() * world_pose_foo_2()).log()),            u);      case SegmentCase::last:  // 如果是最后一段        return BasisSplineFn<LieGroup>::parent_T_spline(            world_pose_foo_prev(),            std::make_tuple(                (world_pose_foo_prev().inverse() * world_pose_foo_0()).log(),                (world_pose_foo_0().inverse() * world_pose_foo_1()).log(),                (world_pose_foo_1().inverse() * world_pose_foo_1()).log()),            u);    }    SOPHUS_ENSURE(false, "logic error");  // 逻辑错误处理  }
  Transformation Dt_parent_T_spline(double u, double delta_t) {  // 计算样条曲线的一阶导数    switch (segment_case_) {      case SegmentCase::first:  // 如果是第一段        return BasisSplineFn<LieGroup>::Dt_parent_T_spline(            world_pose_foo_0(),            std::make_tuple(                (world_pose_foo_0().inverse() * world_pose_foo_0()).log(),                (world_pose_foo_0().inverse() * world_pose_foo_1()).log(),                (world_pose_foo_1().inverse() * world_pose_foo_2()).log()),            u, delta_t);      case SegmentCase::normal:  // 如果是正常段        return BasisSplineFn<LieGroup>::Dt_parent_T_spline(            world_pose_foo_prev(),            std::make_tuple(                (world_pose_foo_prev().inverse() * world_pose_foo_0()).log(),                (world_pose_foo_0().inverse() * world_pose_foo_1()).log(),                (world_pose_foo_1().inverse() * world_pose_foo_2()).log()),            u, delta_t);      case SegmentCase::last:  // 如果是最后一段        return BasisSplineFn<LieGroup>::Dt_parent_T_spline(            world_pose_foo_prev(),            std::make_tuple(                (world_pose_foo_prev().inverse() * world_pose_foo_0()).log(),                (world_pose_foo_0().inverse() * world_pose_foo_1()).log(),                (world_pose_foo_1().inverse() * world_pose_foo_1()).log()),            u, delta_t);    }    SOPHUS_ENSURE(false, "logic error");  // 逻辑错误处理  }
  Transformation Dt2_parent_T_spline(double u, double delta_t) {  // 计算样条曲线的二阶导数    switch (segment_case_) {      case SegmentCase::first:  // 如果是第一段        return BasisSplineFn<LieGroup>::Dt2_parent_T_spline(            world_pose_foo_0(),            std::make_tuple(                (world_pose_foo_0().inverse() * world_pose_foo_0()).log(),                (world_pose_foo_0().inverse() * world_pose_foo_1()).log(),                (world_pose_foo_1().inverse() * world_pose_foo_2()).log()),            u, delta_t);      case SegmentCase::normal:  // 如果是正常段        return BasisSplineFn<LieGroup>::Dt2_parent_T_spline(            world_pose_foo_prev(),            std::make_tuple(                (world_pose_foo_prev().inverse() * world_pose_foo_0()).log(),                (world_pose_foo_0().inverse() * world_pose_foo_1()).log(),                (world_pose_foo_1().inverse() * world_pose_foo_2()).log()),            u, delta_t);      case SegmentCase::last:  // 如果是最后一段        return BasisSplineFn<LieGroup>::Dt2_parent_T_spline(            world_pose_foo_prev(),            std::make_tuple(                (world_pose_foo_prev().inverse() * world_pose_foo_0()).log(),                (world_pose_foo_0().inverse() * world_pose_foo_1()).log(),                (world_pose_foo_1().inverse() * world_pose_foo_1()).log()),            u, delta_t);    }    SOPHUS_ENSURE(false, "logic error");  // 逻辑错误处理  }
 private:  SegmentCase segment_case_;  // 段的类型  T const* raw_params0_;  // 原始参数指针 0  T const* raw_params1_;  // 原始参数指针 1  T const* raw_params2_;  // 原始参数指针 2  T const* raw_params3_;  // 原始参数指针 3};
template <class LieGroup_>class BasisSplineImpl {  // 定义模板类 BasisSplineImpl public:  using LieGroup = LieGroup_;  // 定义 LieGroup 类型别名  using Scalar = typename LieGroup::Scalar;  // 定义标量类型别名  using Transformation = typename LieGroup::Transformation;  // 定义变换矩阵类型别名  using Tangent = typename LieGroup::Tangent;  // 定义切向量类型别名
  BasisSplineImpl(const std::vector<LieGroup>& parent_Ts_control_point,                  double delta_t)  // 构造函数,接受控制点和时间间隔      : parent_Ts_control_point_(parent_Ts_control_point), delta_t_(delta_t) {    SOPHUS_ENSURE(parent_Ts_control_point_.size() >= 2u, ", but {}",                  parent_Ts_control_point_.size());  // 确保控制点数量不少于2个  }
  LieGroup parent_T_spline(int i, double u) const {  // 计算样条曲线    SOPHUS_ENSURE(i >= 0, "i = {}", i);  // 确保 i 大于等于0    SOPHUS_ENSURE(i < this->getNumSegments(),                  "i = {};  this->getNumSegments() = {};  "                  "parent_Ts_control_point_.size() = {}",                  i, this->getNumSegments(), parent_Ts_control_point_.size());  // 确保 i 小于段数
    SegmentCase segment_case =        i == 0 ? SegmentCase::first               : (i == this->getNumSegments() - 1 ? SegmentCase::last                                                  : SegmentCase::normal);  // 确定段的类型
    int idx_prev = std::max(0, i - 1);  // 确定前一个索引    int idx_0 = i;  // 当前索引    int idx_1 = i + 1;  // 下一个索引    int idx_2 = std::min(i + 2, int(this->parent_Ts_control_point_.size()) - 1);  // 再下一个索引
    return BasisSplineSegment<LieGroup>(               segment_case, parent_Ts_control_point_[idx_prev].data(),               parent_Ts_control_point_[idx_0].data(),               parent_Ts_control_point_[idx_1].data(),               parent_Ts_control_point_[idx_2].data())        .parent_T_spline(u);  // 返回样条曲线  }
  Transformation Dt_parent_T_spline(int i, double u) const {  // 计算样条曲线的一阶导数    SOPHUS_ENSURE(i >= 0, "i = {}", i);  // 确保 i 大于等于0    SOPHUS_ENSURE(i < this->getNumSegments(),                  "i = {};  this->getNumSegments() = {};  "                  "parent_Ts_control_point_.size() = {}",                  i, this->getNumSegments(), parent_Ts_control_point_.size());  // 确保 i 小于段数
    SegmentCase segment_case =        i == 0 ? SegmentCase::first               : (i == this->getNumSegments() - 1 ? SegmentCase::last                                                  : SegmentCase::normal);  // 确定段的类型
    int idx_prev = std::max(0, i - 1);  // 确定前一个索引    int idx_0 = i;  // 当前索引    int idx_1 = i + 1;  // 下一个索引    int idx_2 = std::min(i + 2, int(this->parent_Ts_control_point_.size()) - 1);  // 再下一个索引
    return BasisSplineSegment<LieGroup>(               segment_case, parent_Ts_control_point_[idx_prev].data(),               parent_Ts_control_point_[idx_0].data(),               parent_Ts_control_point_[idx_1].data(),               parent_Ts_control_point_[idx_2].data())        .Dt_parent_T_spline(u, delta_t_);  // 返回样条曲线的一阶导数  }
  Transformation Dt2_parent_T_spline(int i, double u) const {  // 计算样条曲线的二阶导数    SOPHUS_ENSURE(i >= 0, "i = {}", i);  // 确保 i 大于等于0    SOPHUS_ENSURE(i < this->getNumSegments(),                  "i = {};  this->getNumSegments() = {};  "                  "parent_Ts_control_point_.size() = {}",                  i, this->getNumSegments(), parent_Ts_control_point_.size());  // 确保 i 小于段数
    SegmentCase segment_case =        i == 0 ? SegmentCase::first               : (i == this->getNumSegments() - 1 ? SegmentCase::last                                                  : SegmentCase::normal);  // 确定段的类型
    int idx_prev = std::max(0, i - 1);  // 确定前一个索引    int idx_0 = i;  // 当前索引    int idx_1 = i + 1;  // 下一个索引    int idx_2 = std::min(i + 2, int(this->parent_Ts_control_point_.size()) - 1);  // 再下一个索引
    return BasisSplineSegment<LieGroup>(               segment_case, parent_Ts_control_point_[idx_prev].data(),               parent_Ts_control_point_[idx_0].data(),               parent_Ts_control_point_[idx_1].data(),               parent_Ts_control_point_[idx_2].data())        .Dt2_parent_T_spline(u, delta_t_);  // 返回样条曲线的二阶导数  }
  const std::vector<LieGroup>& parent_Ts_control_point() const {  // 获取控制点的常量引用    return parent_Ts_control_point_;  }
  std::vector<LieGroup>& parent_Ts_control_point() {  // 获取控制点的引用    return parent_Ts_control_point_;  }
  int getNumSegments() const {  // 获取段数    return int(parent_Ts_control_point_.size()) - 1;  }
  double delta_t() const { return delta_t_; }  // 获取时间间隔
 private:  std::vector<LieGroup> parent_Ts_control_point_;  // 控制点向量  double delta_t_;  // 时间间隔};
struct IndexAndU {  // 定义结构体 IndexAndU  int i;  // 段索引  double u;  // 参数 u};
template <class LieGroup_>class BasisSpline {  // 定义模板类 BasisSpline public:  using LieGroup = LieGroup_;  // 定义 LieGroup 类型别名  using Scalar = typename LieGroup::Scalar;  // 定义标量类型别名  using Transformation = typename LieGroup::Transformation;  // 定义变换矩阵类型别名  using Tangent = typename LieGroup::Tangent;  // 定义切向量类型别名
  BasisSpline(std::vector<LieGroup> parent_Ts_control_point, double t0,              double delta_t)  // 构造函数,接受控制点、起始时间 t0 和时间间隔 delta_t      : impl_(std::move(parent_Ts_control_point), delta_t), t0_(t0) {}
  LieGroup parent_T_spline(double t) const {  // 计算样条曲线    IndexAndU index_and_u = this->index_and_u(t);  // 获取索引和参数 u
    return impl_.parent_T_spline(index_and_u.i, index_and_u.u);  // 返回样条曲线  }
  Transformation Dt_parent_T_spline(double t) const {  // 计算样条曲线的一阶导数    IndexAndU index_and_u = this->index_and_u(t);  // 获取索引和参数 u    return impl_.Dt_parent_T_spline(index_and_u.i, index_and_u.u);  // 返回样条曲线的一阶导数  }
  Transformation Dt2_parent_T_spline(double t) const {  // 计算样条曲线的二阶导数    IndexAndU index_and_u = this->index_and_u(t);  // 获取索引和参数 u    return impl_.Dt2_parent_T_spline(index_and_u.i, index_and_u.u);  // 返回样条曲线的二阶导数  }
  double t0() const { return t0_; }  // 返回起始时间 t0
  double tmax() const { return t0_ + impl_.delta_t() * getNumSegments(); }  // 返回最大时间 tmax
  const std::vector<LieGroup>& parent_Ts_control_point() const {  // 获取控制点的常量引用    return impl_.parent_Ts_control_point();  }
  std::vector<LieGroup>& parent_Ts_control_point() {  // 获取控制点的引用    return impl_.parent_Ts_control_point();  }
  int getNumSegments() const { return impl_.getNumSegments(); }  // 获取段数
  double s(double t) const { return (t - t0_) / impl_.delta_t(); }  // 计算归一化时间 s
  double delta_t() const { return impl_.delta_t(); }  // 返回时间间隔
  IndexAndU index_and_u(double t) const {  // 计算索引和参数 u    SOPHUS_ENSURE(t >= t0_, "{} vs. {}", t, t0_);  // 确保 t 大于等于 t0    SOPHUS_ENSURE(t <= this->tmax(), "{} vs. {}", t, this->tmax());  // 确保 t 小于等于 tmax
    double s = this->s(t);  // 计算归一化时间 s    double i;    IndexAndU index_and_u;    index_and_u.u = std::modf(s, &i);  // 分离整数部分和小数部分    index_and_u.i = int(i);    if (index_and_u.u > Sophus::Constants<double>::epsilon()) {  // 判断小数部分是否大于精度阈值      return index_and_u;    }
    // u ~=~ 0.0    if (t < 0.5 * this->tmax()) {      // 样条曲线的前半段,保持为 (i, 0.0)      return index_and_u;    }    // 样条曲线的后半段,使用 (i-1, 1.0) 来表示 t == tmax(而不是 t < tmax)    index_and_u.u += 1.0;    --index_and_u.i;
    return index_and_u;  }
 private:  BasisSplineImpl<LieGroup> impl_;  // 样条曲线的实现
  double t0_;  // 起始时间};
}  // namespace Sophus
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值