CostThetaSmoother
是Apollo
中离散点参考线平滑方法的一种。
参考线平滑的首要目标当然是平滑性,使用向量 P 1 P 2 ⃗ \vec{P_1 P_2} P1P2和 P 2 P 3 ⃗ \vec{P_2 P_3} P2P3之间夹角的余弦值来表示,显然 cos θ \cos{\theta} cosθ越小,三个点 P 1 , P 2 , P 3 P_1,P_2,P_3 P1,P2,P3越接近一条直线,越平滑。因为 θ \theta θ越接近 0 0 0, cos θ \cos{\theta} cosθ越大,所以应该使 − cos θ -\cos{\theta} −cosθ越小。
J s m o o t h = − ∑ i = 2 N − 1 cos θ i = − ∑ i = 2 N − 1 P i − 1 P i ⃗ ⋅ P i P i + 1 ⃗ ∣ P i − 1 P i ⃗ ∣ ∣ P i P i + 1 ⃗ ∣ = − ∑ i = 2 N − 1 ( x i − x i − 1 ) ( x i + 1 − x i ) + ( y i − y i − 1 ) ( y i + 1 − y i ) ( x i − 1 − x i ) 2 + ( y i − 1 − y i ) 2 ( x i + 1 − x i ) 2 + ( y i + 1 − y i ) 2 (1-1) J_{smooth} = - \sum^{N-1}_{i=2} \cos{\theta_i} = - \sum^{N-1}_{i=2} \frac{\vec{P_{i-1} P_i} \cdot \vec{P_i P_{i+1}}} {|\vec{P_{i-1} P_i}| |\vec{P_i P_{i+1}}|} = - \sum^{N-1}_{i=2} \frac{(x_i - x_{i-1}) (x_{i+1} - x_i) + (y_i - y_{i-1}) (y_{i+1} - y_i)} {\sqrt{(x_{i-1} - x_i)^2 + (y_{i-1} - y_i)^2} \sqrt{(x_{i+1} - x_i)^2 + (y_{i+1} - y_i)^2}} \tag{1-1} Jsmooth=−i=2∑N−1cosθi=−i=2∑N−1∣Pi−1Pi∣∣PiPi+1∣Pi−1Pi⋅PiPi+1=−i=2∑N−1(xi−1−xi)2+(yi−1−yi)2(xi+1−xi)2+(yi+1−yi)2(xi−xi−1)(xi+1−xi)+(yi−yi−1)(yi+1−yi)(1-1)
平滑后的参考线,希望能够保留原始道路的几何信息,不会把弯道的处的参考线平滑成一条直线。使用平滑后点与原始点的距离来表示。
J d e v i a t i o n = ∑ i = 1 N ∣ P r , i P i ⃗ ∣ 2 = ∑ i = 1 N ( ( x i − x i , r ) 2 + ( y i − y i , r ) 2 ) (1-2) J_{deviation} = \sum^N_{i=1} | \vec{P_{r,i} P_i}|^ 2 = \sum^N_{i=1} ( (x_i - x_{i,r})^ 2 + (y_i - y_{i,r})^ 2 ) \tag{1-2} Jdeviation=i=1∑N∣Pr,iPi∣2=i=1∑N((xi−xi,r)2+(yi−yi,r)2)(1-2)
平滑后的参考线的每两个相邻点之间的长度尽量均匀一直。
J l e n g t h = ∑ i = 1 N − 1 ∣ P i P i + 1 ⃗ ∣ 2 = ∑ i = 1 N − 1 ( ( x i + 1 − x i ) 2 + ( y i + 1 − y i ) 2 ) (1-3) J_{length} = \sum^{N-1}_{i=1} | \vec{P_i P_{i+1}}|^2 = \sum^{N-1}_{i=1} ( (x_{i+1} - x_i)^2 + (y_{i+1} - y_i)^2 ) \tag{1-3} Jlength=i=1∑N−1∣PiPi+1∣2=i=1∑N−1((xi+1−xi)2+(yi+1−yi)2)(1-3)
因此,参考线平滑的优化目标可以定义为:
J = w s m o o t h ∗ J s m o o t h + w d e v i a t i o n ∗ J d e v i a t i o n + w l e n g t h ∗ J l e n g t h (1-4) J = w_{smooth} * J_{smooth} + w_{deviation} * J_{deviation} + w_{length} * J_{length} \tag{1-4} J=wsmooth∗Jsmooth+wdeviation∗Jdeviation+wlength∗Jlength(1-4)
只考虑边界约束,即:
x i , l o w e r ≤ x i ≤ x i , u p p e r y i , l o w e r ≤ y i ≤ y i , u p p e r (2-1) x_{i,lower} \leq x_i \leq x_{i,upper} \\ y_{i,lower} \leq y_i \leq y_{i,upper} \tag{2-1} xi,lower≤xi≤xi,upperyi,lower≤yi≤yi,upper(2-1)
可以转化为:
x i , r − b o u n d ≤ x i ≤ x i , r + b o u n d y i , r − b o u n d ≤ y i ≤ y i , r + b o u n d (2-2) x_{i,r} - bound \leq x_i \leq x_{i,r} + bound \\ y_{i,r} - bound \leq y_i \leq y_{i,r} + bound \tag{2-2} xi,r−bound≤xi≤xi,r+boundyi,r−bound≤yi≤yi,r+bound(2-2)
对参考线的起点和终点进行约束,令其等于原始参考线上的点:
x 1 , r ≤ x 1 ≤ x 1 , r y 1 , r ≤ y 1 ≤ y 1 , r (2-3) x_{1,r} \leq x_1 \leq x_{1,r} \\ y_{1,r} \leq y_1 \leq y_{1,r} \tag{2-3} x1,r≤x1≤x1,ry1,r≤y1≤y1,r(2-3)
根据Ipopt
求解器使用要求,需要求解梯度向量函数、雅可比矩阵和黑森矩阵。需要注意的是,在Apollo
中通过配置可以使用ADOL-C
自动求梯度向量函数、雅可比矩阵和黑森矩阵,也可以自己求解。但是Apollo
这里的代码实现有问题,在不使用ADOL-C
时,cost function
代码中没有 J l e n g t h J_{length} Jlength优化项,并且 J d e v i a t i o n J_{deviation} Jdeviation项少了权重系数。
bool CosThetaIpoptInterface::eval_f(int n, const double* x, bool new_x,
double& obj_value) {
CHECK_EQ(static_cast(n), num_of_variables_);
// 使用ADOL-C自动求解优化目标函数
if (use_automatic_differentiation_) {
eval_obj(n, x, &obj_value);
return true;
}
obj_value = 0.0;
for (size_t i = 0; i < num_of_points_; ++i) {
size_t index = i << 1;
// 缺少了权重系数
obj_value +=
(x[index] - ref_points_[i].first) * (x[index] - ref_points_[i].first) +
(x[index + 1] - ref_points_[i].second) *
(x[index + 1] - ref_points_[i].second);
}
for (size_t i = 0; i < num_of_points_ - 2; i++) {
size_t findex = i << 1;
size_t mindex = findex + 2;
size_t lindex = mindex + 2;
obj_value -=
weight_cos_included_angle_ *
(((x[mindex] - x[findex]) * (x[lindex] - x[mindex])) +
((x[mindex + 1] - x[findex + 1]) * (x[lindex + 1] - x[mindex + 1]))) /
std::sqrt((x[mindex] - x[findex]) * (x[mindex] - x[findex]) +
(x[mindex + 1] - x[findex + 1]) *
(x[mindex + 1] - x[findex + 1])) /
std::sqrt((x[lindex] - x[mindex]) * (x[lindex] - x[mindex]) +
(x[lindex + 1] - x[mindex + 1]) *
(x[lindex + 1] - x[mindex + 1]));
}
// 缺少了点之间线段长度均匀紧凑的优化项
return true;
}
下面的代码是通过ADOL-C
求解的,可见是完全按照第一部分叙述的优化目标计算的。
/** Template to return the objective value */
template
bool CosThetaIpoptInterface::eval_obj(int n, const T* x, T* obj_value) {
*obj_value = 0.0;
for (size_t i = 0; i < num_of_points_; ++i) {
size_t index = i << 1;
*obj_value +=
weight_anchor_points_ *
((x[index] - ref_points_[i].first) * (x[index] - ref_points_[i].first) +
(x[index + 1] - ref_points_[i].second) *
(x[index + 1] - ref_points_[i].second));
}
for (size_t i = 0; i < num_of_points_ - 2; ++i) {
size_t findex = i << 1;
size_t mindex = findex + 2;
size_t lindex = mindex + 2;
*obj_value -=
weight_cos_included_angle_ *
(((x[mindex] - x[findex]) * (x[lindex] - x[mindex])) +
((x[mindex + 1] - x[findex + 1]) * (x[lindex + 1] - x[mindex + 1]))) /
(sqrt((x[mindex] - x[findex]) * (x[mindex] - x[findex]) +
(x[mindex + 1] - x[findex + 1]) *
(x[mindex + 1] - x[findex + 1])) *
sqrt((x[lindex] - x[mindex]) * (x[lindex] - x[mindex]) +
(x[lindex + 1] - x[mindex + 1]) *
(x[lindex + 1] - x[mindex + 1])));
}
// Total length
for (size_t i = 0; i < num_of_points_ - 1; ++i) {
size_t findex = i << 1;
size_t nindex = findex + 2;
*obj_value +=
weight_length_ *
((x[findex] - x[nindex]) * (x[findex] - x[nindex]) +
(x[findex + 1] - x[nindex + 1]) * (x[findex + 1] - x[nindex + 1]));
}
return true;
}