feat: 添加注释

master
邱棚 2024-11-29 14:19:06 +08:00
parent 03d49805a4
commit 469d42f4ac
3 changed files with 66 additions and 14 deletions

View File

@ -16,14 +16,15 @@ class MPC:
ay_max): ay_max):
""" """
Constructor for the Model Predictive Controller. Constructor for the Model Predictive Controller.
:param model: bicycle model object to be controlled MPC的构造函数
:param N: time horizon | int :param model: bicycle model object to be controlled 自行车模型对象用于控制
:param Q: state cost matrix :param N: time horizon | int 时间范围
:param R: input cost matrix :param Q: state cost matrix 状态成本矩阵
:param QN: final state cost matrix :param R: input cost matrix 输入成本矩阵
:param StateConstraints: dictionary of state constraints :param QN: final state cost matrix 最终状态成本矩阵
:param InputConstraints: dictionary of input constraints :param StateConstraints: dictionary of state constraints 状态约束的字典
:param ay_max: maximum allowed lateral acceleration in curves :param InputConstraints: dictionary of input constraints 输入约束的字典
:param ay_max: maximum allowed lateral acceleration in curves 允许的最大横向加速度
""" """
# Parameters # Parameters
@ -36,7 +37,7 @@ class MPC:
self.model = model self.model = model
# Dimensions # Dimensions
self.nx = self.model.n_states self.nx = self.model.n_states # 状态变量的数量
self.nu = 2 self.nu = 2
# Constraints # Constraints
@ -44,52 +45,67 @@ class MPC:
self.input_constraints = InputConstraints self.input_constraints = InputConstraints
# Maximum lateral acceleration # Maximum lateral acceleration
# 最大横向加速度
self.ay_max = ay_max self.ay_max = ay_max
# Current control and prediction # Current control and prediction
# 当前控制和预测
self.current_prediction = None self.current_prediction = None
# Counter for old control signals in case of infeasible problem # Counter for old control signals in case of infeasible problem
# 如果问题不可行,旧控制信号的计数器
self.infeasibility_counter = 0 self.infeasibility_counter = 0
# Current control signals # Current control signals
# 当前控制信号
self.current_control = np.zeros((self.nu*self.N)) self.current_control = np.zeros((self.nu*self.N))
# Initialize Optimization Problem # Initialize Optimization Problem
# 初始化优化问题
self.optimizer = osqp.OSQP() self.optimizer = osqp.OSQP()
def _init_problem(self): def _init_problem(self):
""" """
Initialize optimization problem for current time step. Initialize optimization problem for current time step.
为当前时间步初始化优化问题
""" """
# Constraints # Constraints
# 加载约束
umin = self.input_constraints['umin'] umin = self.input_constraints['umin']
umax = self.input_constraints['umax'] umax = self.input_constraints['umax']
xmin = self.state_constraints['xmin'] xmin = self.state_constraints['xmin']
xmax = self.state_constraints['xmax'] xmax = self.state_constraints['xmax']
# LTV System Matrices # LTV System Matrices
# LTV系统矩阵
A = np.zeros((self.nx * (self.N + 1), self.nx * (self.N + 1))) A = np.zeros((self.nx * (self.N + 1), self.nx * (self.N + 1)))
B = np.zeros((self.nx * (self.N + 1), self.nu * (self.N))) B = np.zeros((self.nx * (self.N + 1), self.nu * (self.N)))
# Reference vector for state and input variables # Reference vector for state and input variables
# 状态和输入变量的参考向量
ur = np.zeros(self.nu*self.N) ur = np.zeros(self.nu*self.N)
xr = np.zeros(self.nx*(self.N+1)) xr = np.zeros(self.nx*(self.N+1))
# Offset for equality constraint (due to B * (u - ur)) # Offset for equality constraint (due to B * (u - ur))
# 等式约束的偏移由于B *u - ur
uq = np.zeros(self.N * self.nx) uq = np.zeros(self.N * self.nx)
# Dynamic state constraints # Dynamic state constraints
# 动态状态约束
xmin_dyn = np.kron(np.ones(self.N + 1), xmin) xmin_dyn = np.kron(np.ones(self.N + 1), xmin)
xmax_dyn = np.kron(np.ones(self.N + 1), xmax) xmax_dyn = np.kron(np.ones(self.N + 1), xmax)
# Dynamic input constraints # Dynamic input constraints
# 动态输入约束
umax_dyn = np.kron(np.ones(self.N), umax) umax_dyn = np.kron(np.ones(self.N), umax)
# Get curvature predictions from previous control signals # Get curvature predictions from previous control signals
# 从先前的控制信号中获取曲率预测
kappa_pred = np.tan(np.array(self.current_control[3::] + kappa_pred = np.tan(np.array(self.current_control[3::] +
self.current_control[-1:])) / self.model.length self.current_control[-1:])) / self.model.length
# Iterate over horizon # Iterate over horizon
# 遍历时间范围
for n in range(self.N): for n in range(self.N):
# Get information about current waypoint # Get information about current waypoint
# 获取当前路标的信息
current_waypoint = self.model.reference_path.get_waypoint(self.model.wp_id + n) current_waypoint = self.model.reference_path.get_waypoint(self.model.wp_id + n)
next_waypoint = self.model.reference_path.get_waypoint(self.model.wp_id + n + 1) next_waypoint = self.model.reference_path.get_waypoint(self.model.wp_id + n + 1)
delta_s = next_waypoint - current_waypoint delta_s = next_waypoint - current_waypoint
@ -97,22 +113,27 @@ class MPC:
v_ref = current_waypoint.v_ref v_ref = current_waypoint.v_ref
# Compute LTV matrices # Compute LTV matrices
# 计算LTV矩阵
f, A_lin, B_lin = self.model.linearize(v_ref, kappa_ref, delta_s) f, A_lin, B_lin = self.model.linearize(v_ref, kappa_ref, delta_s)
A[(n+1) * self.nx: (n+2)*self.nx, n * self.nx:(n+1)*self.nx] = A_lin A[(n+1) * self.nx: (n+2)*self.nx, n * self.nx:(n+1)*self.nx] = A_lin
B[(n+1) * self.nx: (n+2)*self.nx, n * self.nu:(n+1)*self.nu] = B_lin B[(n+1) * self.nx: (n+2)*self.nx, n * self.nu:(n+1)*self.nu] = B_lin
# Set reference for input signal # Set reference for input signal
# 设置输入信号的参考值
ur[n*self.nu:(n+1)*self.nu] = np.array([v_ref, kappa_ref]) ur[n*self.nu:(n+1)*self.nu] = np.array([v_ref, kappa_ref])
# Compute equality constraint offset (B*ur) # Compute equality constraint offset (B*ur)
# 计算等式约束偏移B * ur
uq[n * self.nx:(n+1)*self.nx] = B_lin.dot(np.array uq[n * self.nx:(n+1)*self.nx] = B_lin.dot(np.array
([v_ref, kappa_ref])) - f ([v_ref, kappa_ref])) - f
# Constrain maximum speed based on predicted car curvature # Constrain maximum speed based on predicted car curvature
# 根据预测的汽车曲率限制最大速度
vmax_dyn = np.sqrt(self.ay_max / (np.abs(kappa_pred[n]) + 1e-12)) vmax_dyn = np.sqrt(self.ay_max / (np.abs(kappa_pred[n]) + 1e-12))
if vmax_dyn < umax_dyn[self.nu*n]: if vmax_dyn < umax_dyn[self.nu*n]:
umax_dyn[self.nu*n] = vmax_dyn umax_dyn[self.nu*n] = vmax_dyn
# Compute dynamic constraints on e_y # Compute dynamic constraints on e_y
# 计算e_y的动态约束
ub, lb, _ = self.model.reference_path.update_path_constraints( ub, lb, _ = self.model.reference_path.update_path_constraints(
self.model.wp_id+1, self.N, 2*self.model.safety_margin, self.model.wp_id+1, self.N, 2*self.model.safety_margin,
self.model.safety_margin) self.model.safety_margin)
@ -122,31 +143,39 @@ class MPC:
xmax_dyn[self.nx::self.nx] = ub xmax_dyn[self.nx::self.nx] = ub
# Set reference for state as center-line of drivable area # Set reference for state as center-line of drivable area
# 将状态的参考值设置为可驾驶区域的中心线
xr[self.nx::self.nx] = (lb + ub) / 2 xr[self.nx::self.nx] = (lb + ub) / 2
# Get equality matrix # Get equality matrix
# 获取等式矩阵
Ax = sparse.kron(sparse.eye(self.N + 1), Ax = sparse.kron(sparse.eye(self.N + 1),
-sparse.eye(self.nx)) + sparse.csc_matrix(A) -sparse.eye(self.nx)) + sparse.csc_matrix(A)
Bu = sparse.csc_matrix(B) Bu = sparse.csc_matrix(B)
Aeq = sparse.hstack([Ax, Bu]) Aeq = sparse.hstack([Ax, Bu])
# Get inequality matrix # Get inequality matrix
# 获取不等式矩阵
Aineq = sparse.eye((self.N + 1) * self.nx + self.N * self.nu) Aineq = sparse.eye((self.N + 1) * self.nx + self.N * self.nu)
# Combine constraint matrices # Combine constraint matrices
# 组合约束矩阵
A = sparse.vstack([Aeq, Aineq], format='csc') A = sparse.vstack([Aeq, Aineq], format='csc')
# Get upper and lower bound vectors for equality constraints # Get upper and lower bound vectors for equality constraints
# 获取等式约束的上下限向量
lineq = np.hstack([xmin_dyn, lineq = np.hstack([xmin_dyn,
np.kron(np.ones(self.N), umin)]) np.kron(np.ones(self.N), umin)])
uineq = np.hstack([xmax_dyn, umax_dyn]) uineq = np.hstack([xmax_dyn, umax_dyn])
# Get upper and lower bound vectors for inequality constraints # Get upper and lower bound vectors for inequality constraints
# 获取不等式约束的上下限向量
x0 = np.array(self.model.spatial_state[:]) x0 = np.array(self.model.spatial_state[:])
leq = np.hstack([-x0, uq]) leq = np.hstack([-x0, uq])
ueq = leq ueq = leq
# Combine upper and lower bound vectors # Combine upper and lower bound vectors
# 组合上下限向量
l = np.hstack([leq, lineq]) l = np.hstack([leq, lineq])
u = np.hstack([ueq, uineq]) u = np.hstack([ueq, uineq])
# Set cost matrices # Set cost matrices
# 设置成本矩阵
P = sparse.block_diag([sparse.kron(sparse.eye(self.N), self.Q), self.QN, P = sparse.block_diag([sparse.kron(sparse.eye(self.N), self.Q), self.QN,
sparse.kron(sparse.eye(self.N), self.R)], format='csc') sparse.kron(sparse.eye(self.N), self.R)], format='csc')
q = np.hstack( q = np.hstack(
@ -155,6 +184,7 @@ class MPC:
-np.tile(self.R.diagonal(), self.N) * ur]) -np.tile(self.R.diagonal(), self.N) * ur])
# Initialize optimizer # Initialize optimizer
# 初始化优化器
self.optimizer = osqp.OSQP() self.optimizer = osqp.OSQP()
self.optimizer.setup(P=P, q=q, A=A, l=l, u=u, verbose=False) self.optimizer.setup(P=P, q=q, A=A, l=l, u=u, verbose=False)
@ -162,28 +192,35 @@ class MPC:
""" """
Get control signal given the current position of the car. Solves a Get control signal given the current position of the car. Solves a
finite time optimization problem based on the linearized car model. finite time optimization problem based on the linearized car model.
给定车辆的当前位置获取控制信号基于线性化的汽车模型解决有限时间优化问题
""" """
# Number of state variables # Number of state variables
# 状态变量的数量
nx = self.model.n_states nx = self.model.n_states
nu = 2 nu = 2
# Update current waypoint # Update current waypoint
# 更新当前路标
self.model.get_current_waypoint() self.model.get_current_waypoint()
# Update spatial state # Update spatial state
# 更新空间状态
self.model.spatial_state = self.model.t2s(reference_state= self.model.spatial_state = self.model.t2s(reference_state=
self.model.temporal_state, reference_waypoint= self.model.temporal_state, reference_waypoint=
self.model.current_waypoint) self.model.current_waypoint)
# Initialize optimization problem # Initialize optimization problem
# 初始化优化问题
self._init_problem() self._init_problem()
# Solve optimization problem # Solve optimization problem
# 解决优化问题
dec = self.optimizer.solve() dec = self.optimizer.solve()
try: try:
# Get control signals # Get control signals
# 获取控制信号
control_signals = np.array(dec.x[-self.N*nu:]) control_signals = np.array(dec.x[-self.N*nu:])
control_signals[1::2] = np.arctan(control_signals[1::2] * control_signals[1::2] = np.arctan(control_signals[1::2] *
self.model.length) self.model.length)
@ -191,18 +228,23 @@ class MPC:
delta = control_signals[1] delta = control_signals[1]
# Update control signals # Update control signals
# 更新控制信号
self.current_control = control_signals self.current_control = control_signals
# Get predicted spatial states # Get predicted spatial states
# 获取预测的空间状态
x = np.reshape(dec.x[:(self.N+1)*nx], (self.N+1, nx)) x = np.reshape(dec.x[:(self.N+1)*nx], (self.N+1, nx))
# Update predicted temporal states # Update predicted temporal states
# 更新预测的时间状态
self.current_prediction = self.update_prediction(x) self.current_prediction = self.update_prediction(x)
# Get current control signal # Get current control signal
# 获取当前控制信号
u = np.array([v, delta]) u = np.array([v, delta])
# if problem solved, reset infeasibility counter # if problem solved, reset infeasibility counter
# 如果问题解决了,重置不可行计数器
self.infeasibility_counter = 0 self.infeasibility_counter = 0
except: except:
@ -213,6 +255,7 @@ class MPC:
u = np.array(self.current_control[id:id+2]) u = np.array(self.current_control[id:id+2])
# increase infeasibility counter # increase infeasibility counter
# 增加不可行计数器
self.infeasibility_counter += 1 self.infeasibility_counter += 1
if self.infeasibility_counter == (self.N - 1): if self.infeasibility_counter == (self.N - 1):
@ -225,23 +268,29 @@ class MPC:
""" """
Transform the predicted states to predicted x and y coordinates. Transform the predicted states to predicted x and y coordinates.
Mainly for visualization purposes. Mainly for visualization purposes.
:param spatial_state_prediction: list of predicted state variables 将预测的状态转换为预测的x和y坐标主要用于可视化目的
:return: lists of predicted x and y coordinates :param spatial_state_prediction: list of predicted state variables 预测状态变量的列表
:return: lists of predicted x and y coordinates 预测的x和y坐标的列表
""" """
# Containers for x and y coordinates of predicted states # Containers for x and y coordinates of predicted states
# 预测状态的x和y坐标的容器
x_pred, y_pred = [], [] x_pred, y_pred = [], []
# Iterate over prediction horizon # Iterate over prediction horizon
# 遍历预测范围
for n in range(2, self.N): for n in range(2, self.N):
# Get associated waypoint # Get associated waypoint
# 获取关联的路标
associated_waypoint = self.model.reference_path.\ associated_waypoint = self.model.reference_path.\
get_waypoint(self.model.wp_id+n) get_waypoint(self.model.wp_id+n)
# Transform predicted spatial state to temporal state # Transform predicted spatial state to temporal state
# 将预测的空间状态转换为时间状态
predicted_temporal_state = self.model.s2t(associated_waypoint, predicted_temporal_state = self.model.s2t(associated_waypoint,
spatial_state_prediction[n, :]) spatial_state_prediction[n, :])
# Save predicted coordinates in world coordinate frame # Save predicted coordinates in world coordinate frame
# 保存世界坐标系中的预测坐标
x_pred.append(predicted_temporal_state.x) x_pred.append(predicted_temporal_state.x)
y_pred.append(predicted_temporal_state.y) y_pred.append(predicted_temporal_state.y)
@ -250,6 +299,7 @@ class MPC:
def show_prediction(self): def show_prediction(self):
""" """
Display predicted car trajectory in current axis. Display predicted car trajectory in current axis.
在当前轴中显示预测的汽车轨迹
""" """
if self.current_prediction is not None: if self.current_prediction is not None:

View File

@ -105,6 +105,7 @@ if __name__ == '__main__':
v_max = 1.0 # m/s v_max = 1.0 # m/s
delta_max = 0.66 # rad delta_max = 0.66 # rad
ay_max = 4.0 # m/s^2 ay_max = 4.0 # m/s^2
# 控制量的约束,速度和转弯半径
InputConstraints = {'umin': np.array([0.0, -np.tan(delta_max)/car.length]), InputConstraints = {'umin': np.array([0.0, -np.tan(delta_max)/car.length]),
'umax': np.array([v_max, np.tan(delta_max)/car.length])} 'umax': np.array([v_max, np.tan(delta_max)/car.length])}
StateConstraints = {'xmin': np.array([-np.inf, -np.inf, -np.inf]), StateConstraints = {'xmin': np.array([-np.inf, -np.inf, -np.inf]),

View File

@ -26,9 +26,10 @@ class TemporalState:
def __init__(self, x, y, psi): def __init__(self, x, y, psi):
""" """
Temporal State Vector containing car pose (x, y, psi) Temporal State Vector containing car pose (x, y, psi)
:param x: x position in global coordinate system | [m] 状态向量包含车辆姿态xypsi
:param y: y position in global coordinate system | [m] :param x: x position in global coordinate system | [m] 全局坐标系中的 x 位置
:param psi: yaw angle | [rad] :param y: y position in global coordinate system | [m] 全局坐标系中的 y 位置
:param psi: yaw angle | [rad] 偏航角
""" """
self.x = x self.x = x
self.y = y self.y = y