AutoDIffManifold

class AutoDiffManifold

创建一个流形,同时通过自动微分来计算导数。要获得自动微分流形,必须定义一个带有模板化加减函数的 Functor:

x_plus_delta = Plus(x, delta);
y_minus_x    = Minus(y, x);

其中,xy x_plus_delta 是局部空间上的向量(因此它们是 kAmbientSize 大小的向量),deltay_minus_x 是切线空间中的向量(因此它们是 kTangentSize 大小向量)。Functor 的定义如下:

struct Functor {
 template <typename T>
 bool Plus(const T* x, const T* delta, T* x_plus_delta) const;

 template <typename T>
 bool Minus(const T* y, const T* x, T* y_minus_x) const;
};

请注意,PlusMinus 操作是以参数 T 为模板的。自动微分框架会用适当的 Jet 对象代替 T,以便在必要时计算导数。这与使用 AutoDiffCostFunction 时计算导数的机制相同。如果计算成功,PlusMinus 应返回 true,否则返回 false,在这种情况下,计算结果将不会被使用。给定这个 Functor,相应的 Manifold 可以这样构造:

AutoDiffManifold<Functor, kAmbientSize, kTangentSize> manifold;

The following is only used for illustration purposes. Ceres Solver ships with an optimized, production grade QuaternionManifold implementation.

作为一个具体的例子,我们来看看 Quaternions 要如何实现。四元数是嵌入到 R4\mathbb{R}^4 的三维流形,即它们的局部维数为 4,切空间维数为 3。下面的函数定义了四元数流形上的 PlusMinus 运算。它假定四元数在内存中的布局为 [w,x,y,z],即实数或标量部分是第一坐标。

struct QuaternionFunctor {
  template <typename T>
  bool Plus(const T* x, const T* delta, T* x_plus_delta) const {
    const T squared_norm_delta =
        delta[0] * delta[0] + delta[1] * delta[1] + delta[2] * delta[2];

    T q_delta[4];
    if (squared_norm_delta > T(0.0)) {
      T norm_delta = sqrt(squared_norm_delta);
      const T sin_delta_by_delta = sin(norm_delta) / norm_delta;
      q_delta[0] = cos(norm_delta);
      q_delta[1] = sin_delta_by_delta * delta[0];
      q_delta[2] = sin_delta_by_delta * delta[1];
      q_delta[3] = sin_delta_by_delta * delta[2];
    } else {
      // We do not just use q_delta = [1,0,0,0] here because that is a
      // constant and when used for automatic differentiation will
      // lead to a zero derivative. Instead we take a first order
      // approximation and evaluate it at zero.
      q_delta[0] = T(1.0);
      q_delta[1] = delta[0];
      q_delta[2] = delta[1];
      q_delta[3] = delta[2];
    }

    QuaternionProduct(q_delta, x, x_plus_delta);
    return true;
  }

  template <typename T>
  bool Minus(const T* y, const T* x, T* y_minus_x) const {
    T minus_x[4] = {x[0], -x[1], -x[2], -x[3]};
    T ambient_y_minus_x[4];
    QuaternionProduct(y, minus_x, ambient_y_minus_x);
    T u_norm = sqrt(ambient_y_minus_x[1] * ambient_y_minus_x[1] +
                    ambient_y_minus_x[2] * ambient_y_minus_x[2] +
                    ambient_y_minus_x[3] * ambient_y_minus_x[3]);
    if (u_norm > 0.0) {
      T theta = atan2(u_norm, ambient_y_minus_x[0]);
      y_minus_x[0] = theta * ambient_y_minus_x[1] / u_norm;
      y_minus_x[1] = theta * ambient_y_minus_x[2] / u_norm;
      y_minus_x[2] = theta * ambient_y_minus_x[3] / u_norm;
    } else {
      We do not use [0,0,0] here because even though the value part is
      a constant, the derivative part is not.
      y_minus_x[0] = ambient_y_minus_x[1];
      y_minus_x[1] = ambient_y_minus_x[2];
      y_minus_x[2] = ambient_y_minus_x[3];
    }
    return true;
  }
};

根据这一结构,使用自动微分的四元数流形可以构造为

Manifold* manifold = new AutoDiffManifold<QuaternionFunctor, 4, 3>;

Last updated