AutoDIffManifold
class AutoDiffManifold
创建一个流形,同时通过自动微分来计算导数。要获得自动微分流形,必须定义一个带有模板化加减函数的 Functor:
x_plus_delta = Plus(x, delta);
y_minus_x = Minus(y, x);
其中,x
、y
和 x_plus_delta
是局部空间上的向量(因此它们是 kAmbientSize
大小的向量),delta
、y_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;
};
请注意,Plus
和 Minus
操作是以参数 T
为模板的。自动微分框架会用适当的 Jet
对象代替 T
,以便在必要时计算导数。这与使用 AutoDiffCostFunction
时计算导数的机制相同。如果计算成功,Plus
和 Minus
应返回 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 要如何实现。四元数是嵌入到 的三维流形,即它们的局部维数为 4,切空间维数为 3。下面的函数定义了四元数流形上的 Plus
和 Minus
运算。它假定四元数在内存中的布局为 [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