1. 重要函数
def is_convex_and_ordered(self, points: NDArray[np.float64]) -> tuple[bool, str]
这个函数的作用是判断按照顺序排列的顶点是否构成凸集,并判断点是顺时针排列"CW"还是逆时针排列"CCW"
实现思路:按顺序取三个点o,a,b,判断oa和ob的叉积,如果所有叉积同号则说明a点在ob的外侧,那么所有内角都为凸角,同时根据叉积的正负还可以判断点是顺时针排列(负)还是逆时针排列(正)
def gen_inequal_global(self, vertex: NDArray[np.float64])-> tuple[NDArray[np.float64], NDArray[np.float64]]
此函数的作用是根据顶点生成半空间不等式Ax≤b
实现思路:先判断顶点的凸性和排列顺序,统一为逆时针排列。随后按顺序遍历相邻的两个顶点,计算半空间表达式
每个超平面可以表达为a⊤(x−x0)=0,即与x0的连线与a垂直的点,因此a为法向量;x0为超平面上的点。变换后为a⊤x=b,变为不等号即得到半空间
计算半空间的过程
- 求直线向量v=p2−p1=(x2−x1,y2−y1),向量指向逆时针方向
- 令v与a的叉乘结果为负,可以求连线右侧的法向向量。注意,对于凸多边形,由于表达式是≤,因此法向量必须指向多边形外侧
- 用a⊤p1求解b
def cone_para_array(self, array: cp.Variable, cone_flag: cp.Variable)-> cp.constraints.nonpos.Inequality
此函数的作用是构建约束λt,m⪰Om∗0
此函数将非负象限锥和二阶锥的情况都放进一个约束中,因为优化问题是提前建立好的,在运动前无法确定第n个障碍物到底是哪种类型,因此都需要考虑进去
def pre_process(self, state, ref_path, cur_index, **kwargs):
- 利用上个时刻计算出的最优控制量序列,以当前状态为起始点,根据运动学方程预测一步,得到将旋转矩阵和运动学方程线性化需要的参考状态
- 利用参考速度和预测的时间间隔计算出距离间隔,对稀疏的轨迹插值得到稠密的参考轨迹
def control(self, state, ref_speed=5, obstacle_list=[], **kwargs):
- 调用
pre_process()函数得到参考状态和参考轨迹
- 将障碍物的表达方式转为RDA求解器的输入格式
- 调用
iterative_solve()函数求解最优控制量
def iterative_solve(self,nom_s,nom_u,ref_states,ref_speed,obstacle_list,**kwargs,):
- 根据
pre_process()得到的参考状态和上个时刻计算出的最优控制量序列为状态相关的优化参数赋初始值
- 根据障碍物数据为障碍物相关的优化参数赋初始值
- 反复调用
rda_solver()进行迭代求解,直至满足终止条件或迭代次数达到最大
def rda_solver():
利用ADMM求解优化问题
- 求解变量为s,u,d的优化问题
su_prob
- 将求解出的最优值传入作为参数的s,u,d中
- 为联合参数λ⊤D和λ⊤b赋值
- 对每一个障碍物求解以λ,μ为变量的优化问题
LamMuZ_prob
- 将求解出的最优值传入作为参数的λ,μ中
- 为联合参数Dp和DR赋值
- 更新ξ和ζ
2. 优化问题构建
加粗代表变量
2.1 construct_su_prob
将s,u,d视为变量
优化问题为
min s.t. (costnav+costsu)constraintsnav∩constraintssu
约束包含
constraintsnav:st+1=Atst+Btut+Ct,∀t∥vt+1−vt∥≤amax,∀t∥ut∥≤umax,∀ts0=s0
代价函数为
costnav:Qtt=0∑N∥st−st′∥22+Ptt=0∑N(vt−vt′)2
约束包含
constraintssu:Rt′−(J′ϕ′)t+Jt′θt−Rt=0,∀tdt∈[dmin,dmax],∀tIt,m=(λD)t,mst−(λb)t,m−μt,m⊤h−dt+ζt,m,∀t,mHt,m=μt,m⊤G+(λD)Rt+ξt,m,∀t,m
代价函数为
costsu:−ηt=0∑Ndt+2ρ1t=0∑Nm=0∑M∥min(It,m,0)∥22+2ρ2t=0∑Nm=0∑M∥Ht,m∥22
这里在计算It,m的代价函数时使用了min(It,m,0),这是为了惩罚大于零的情况,本质上是将约束从=0放松为≥0,因为安全距离适当变大是可接受的,这样可以加快计算速度
2.2 construct_LamMuZ_prob
将λ,μ视为变量
优化问题为
∀mmin s.t. costmconstraintsm
LamMuZ_cost_cons
约束包含
constraintsm:∥Dt,m⊤λt,m∥∗≤1,∀t,mλt,m⪰Om∗0,∀tμt,m⪰Kr∗0,∀tIt,m=λt,m⊤(Dp)t,m−λt,m⊤bt,m−μt,m⊤h−dt+ζt,m,∀t,mHt,m=μt,m⊤G+λt,m⊤(DR)t+ξt,m,∀t,m
代价函数为
costm:2ρ1t=0∑N∥min(It,m,0)∥22+2ρ2t=0∑N∥Ht,m∥22
2.3 更新ξ和ζ
ξt,mk+1=ξt,mk+(μt,mk)⊤G+(λt,mk)⊤DmRt(stk)ζt,mk+1=ζt,mk+(λt,mk)⊤Dmpt(stk)−(λt,mk)⊤bm−(μt,mk)⊤h−dtk
2.4迭代终止条件
需要
t∑m∑∥λt,mk+1−λt,mk∥22+∥μt,mk+1−μt,mk∥22≤ϵt∑m∑∥Ht,m∥22≤ϵ
实际中,变量值只会保存为最新值,上个时刻的值与对应参数的值一致。注意此刻Ht,m是参数,不是变量,因此终止条件写为
t∑m∑∥λt,mk+1−λt,mk∥22+∥μt,mk+1−μt,mk∥22≤ϵt∑m∑∥(μt,mk)⊤G+(λt,mk)⊤DmRt(stk)∥22≤ϵ
3. 存在的问题
- 插值方法过于简单,可以利用
scipy库提供的三次样条曲线插值CubicSpline或者三次埃尔米特样条曲线插值CubicHermiteSpline
- 当程序第一次启动时应该使用参考轨迹上的点作为状态参考