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;
}