四旋翼无人机动力学与控制(2)-飞行器控制


继上一篇博客搭建完无人机的动力学模型之后,进而可以开始设计控制算法,首先需要了解常见的飞控策略:

还是先附上仓库地址:四旋翼无人机仿真建模: 本仓库实现了四旋翼无人机的动力学仿真建模,并在此基础上完成了控制器的设计,包括串级PID、LQR、MPC等,实现了位置跟踪和路径规划。

总体框架如上图,本项目采用常见的分级控制,由内到外分为角速率环、姿态环、速度环和位置环。上一篇中笔者写过,我将无人机的运动浅显的理解为依靠四个轴上的电机带动螺旋桨提供升力,通过调节电机转速制造升力差引起角度的偏转进而带动其运动,因此无人机的角度偏转是无人机在xy平面内运动的前提。换句话说,控制好角度是控制好位置的前提,而采用角速率环和速度环是由于被控量(位置、姿态)和控制量(加速度、角加速度)之间差了两个阶次,只使用一个环无法控制中间变量,容易导致超调,因此加入一个中间环来达到更好的控制效果。

本项目中采用了PID、LQR和MPC三种控制算法,以下是控制部分的代码:

(1)PID:原理不做阐述

  1. # Author:gyx
  2. # Usage: pid controller
  3. # Last change: 2024-7-20
  4. import numpy as np
  5. import matplotlib.pyplot as plt
  6. class PIDController:
  7. def __init__(self, Kp, Ki, Kd, integrator_max=None, integrator_min=None,max=None,min=None):
  8. self.Kp = Kp
  9. self.Ki = Ki
  10. self.Kd = Kd
  11. self.integrator = 0
  12. self.prev_error = 0
  13. self.integrator_max = integrator_max
  14. self.integrator_min = integrator_min
  15. self.max = max
  16. self.min = min
  17. self.error = 0
  18. self.measurement_history = []
  19. self.setpoint_history = []
  20. def reset(self):
  21. self.integrator = 0
  22. self.prev_error = 0
  23. def update(self, setpoint, measurement, dt):
  24. self.measurement_history.append(measurement)
  25. self.setpoint_history.append(setpoint)
  26. error = setpoint - measurement
  27. self.error = error
  28. self.integrator += error * dt
  29. if self.integrator_max is not None and self.integrator > self.integrator_max:
  30. self.integrator = self.integrator_max
  31. if self.integrator_min is not None and self.integrator < self.integrator_min:
  32. self.integrator = self.integrator_min
  33. derivative = (error - self.prev_error) / dt
  34. output = self.Kp * error + self.Ki * self.integrator + self.Kd * derivative
  35. self.prev_error = error
  36. if self.max is not None and self.min is not None:
  37. output = np.clip(output,self.min,self.max)
  38. return output
  39. def plot_error(self,fig,i,title='none'):
  40. # plt.plot(self.setpoint_history, [i for i in self.setpoint_history], linewidth =2.0, label = r"setpoint",color='b', linestyle='dotted')
  41. # plt.plot(self.measurement_history, [i for i in self.measurement_history], linewidth =2.0, label = r"setpoint",color='r',linestyle='dashed')
  42. ax = fig.add_subplot(131 + i)
  43. ax.plot([i/100 for i in range(len(self.setpoint_history))], self.setpoint_history, label='setpoint')
  44. ax.plot([i/100 for i in range(len(self.measurement_history))], self.measurement_history,label='measurement')
  45. ax.set_xlabel('time')
  46. ax.set_ylabel('value')
  47. ax.set_title(title)
  48. ax.legend()
  49. class CascadePIDController:
  50. def __init__(self, outer_controller, inner_controller):
  51. self.outer_controller = outer_controller
  52. self.inner_controller = inner_controller
  53. def update(self, outer_setpoint, outer_measurement, inner_measurement, dt):
  54. outer_output = self.outer_controller.update(outer_setpoint, outer_measurement, dt)
  55. inner_output = self.inner_controller.update(outer_output, inner_measurement, dt)
  56. return inner_output
  57. class posion_pos_controller:
  58. def __init__(self,params):
  59. self.params = params
  60. def position_controller(self):
  61. x_position_controller = PIDController(self.params.Kpxp, 0, 0, integrator_max=1,
  62. integrator_min=-1, max=5, min=-5)
  63. x_velocity_controller = PIDController(self.params.Kvxp, self.params.Kvxi, self.params.Kvxd, integrator_max=1,
  64. integrator_min=-1)
  65. y_position_controller = PIDController(self.params.Kpyp, 0, 0, integrator_max=1,
  66. integrator_min=-1, max=5, min=-5, )
  67. y_velocity_controller = PIDController(self.params.Kvyp, self.params.Kvyi, self.params.Kvyd, integrator_max=1,
  68. integrator_min=-1)
  69. height_position_controller = PIDController(self.params.Kpzp, 0, 0, integrator_max=1,
  70. integrator_min=-1, min=-3, max=3)
  71. height_velocity_controller = PIDController(self.params.Kvzp, self.params.Kvzi, self.params.Kvzd, integrator_max=1,
  72. integrator_min=-1, min=-0.4, max=0.4)
  73. x_controller = CascadePIDController(x_position_controller, x_velocity_controller)
  74. y_controller = CascadePIDController(y_position_controller, y_velocity_controller)
  75. height_controller = CascadePIDController(height_position_controller, height_velocity_controller)
  76. return [x_controller,y_controller,height_controller]
  77. def pos_controller(self):
  78. DEG2RAD = self.params.DEG2RAD
  79. roll_angle_controller = PIDController(self.params.Kp_RP_ANGLE, 0, 0, integrator_max=1,
  80. integrator_min=-1, max=DEG2RAD * 35, min=-DEG2RAD * 35)
  81. roll_rate_controller = PIDController(self.params.Kp_RP_AngleRate, self.params.Ki_RP_AngleRate,
  82. self.params.Kd_RP_AngleRate, integrator_max=1,
  83. integrator_min=-1, max=DEG2RAD * 220, min=-DEG2RAD * 220)
  84. roll_controller = CascadePIDController(roll_angle_controller, roll_rate_controller)
  85. pitch_angle_controller = PIDController(self.params.Kp_RP_ANGLE, 0, 0, integrator_max=1,
  86. integrator_min=-1, max=DEG2RAD * 35, min=-DEG2RAD * 35)
  87. pitch_rate_controller = PIDController(self.params.Kp_RP_AngleRate, self.params.Ki_RP_AngleRate,
  88. self.params.Kd_RP_AngleRate, integrator_max=1,
  89. integrator_min=-1, max=DEG2RAD * 220, min=-DEG2RAD * 220)
  90. pitch_controller = CascadePIDController(pitch_angle_controller, pitch_rate_controller)
  91. yaw_angle_controller = PIDController(self.params.Kp_RP_ANGLE, 0, 0, integrator_max=1,
  92. integrator_min=-1, max=DEG2RAD * 35, min=-DEG2RAD * 35)
  93. yaw_rate_controller = PIDController(self.params.Kp_YAW_AngleRate, self.params.Ki_YAW_AngleRate,
  94. self.params.Kd_YAW_AngleRate, integrator_max=1,
  95. integrator_min=-1, max=DEG2RAD * 220, min=-DEG2RAD * 220)
  96. yaw_controller = CascadePIDController(yaw_angle_controller, yaw_rate_controller)
  97. return [roll_controller,pitch_controller,yaw_controller]

(2)LQR:

  1. # Author:gyx
  2. # Usage: LQR controller
  3. # Last change: 2024-7-20
  4. import numpy as np
  5. import matplotlib.pyplot as plt
  6. class LQRController:
  7. def __init__(self, A, B, Q, R):
  8. self.A = A
  9. self.B = B
  10. self.Q = Q
  11. self.R = R
  12. self.measurement_history = []
  13. self.setpoint_history = []
  14. def lqr_solve(self):
  15. n = self.A.shape[0]
  16. m = self.B.shape[1]
  17. # Riccati equation using a Lyapunov approach
  18. Z = np.vstack([
  19. np.hstack([self.A, -self.B @ np.linalg.inv(self.R) @ self.B.T]),
  20. np.hstack([-self.Q, -self.A.T])
  21. ])
  22. eigvals, eigvecs = np.linalg.eig(Z)
  23. # Select stable eigenvalues
  24. stable_indices = np.where(np.real(eigvals) < 0)[0]
  25. eigvecs_stable = eigvecs[:, stable_indices]
  26. X = eigvecs_stable[:n, :]
  27. Y = eigvecs_stable[n:, :]
  28. # Solve P
  29. P = np.real(Y @ np.linalg.inv(X))
  30. # Compute gain
  31. K = np.linalg.inv(self.R) @ self.B.T @ P
  32. eigVals, _ = np.linalg.eig(self.A - self.B @ K)
  33. return K, P, eigVals
  34. def update(self, setpoint, measurement):
  35. self.measurement_history.append(measurement)
  36. self.setpoint_history.append(setpoint)
  37. K, _, _ = self.lqr_solve()
  38. u = -K @ (measurement - setpoint)
  39. return u
  40. def plot_error(self, title='none'):
  41. self.setpoint_history = np.array(self.setpoint_history)
  42. self.measurement_history = np.array(self.measurement_history)
  43. fig = plt.figure(figsize=(14, 4))
  44. subtitle=['x','y','z']
  45. for i in range(len(self.setpoint_history[0])):
  46. ax = fig.add_subplot(131+i)
  47. ax.plot([i / 100 for i in range(len(self.setpoint_history[:,i]))], self.setpoint_history[:,i], label='setpoint')
  48. ax.plot([i / 100 for i in range(len(self.measurement_history[:,i]))], self.measurement_history[:,i], label='measurement')
  49. ax.set_xlabel('time')
  50. ax.set_ylabel('value')
  51. ax.set_title(subtitle[i]+title)
  52. ax.legend()
  53. plt.tight_layout()
  54. # fig.legend()
  55. if __name__ == '__main__':
  56. A = np.array([[0., 1.], [-1., -1.]])
  57. B = np.array([[0.], [1.]])
  58. Q = np.array([[1., 0.], [0., 1.]])
  59. R = np.array([[1.]])
  60. controller = LQRController(A, B, Q, R)
  61. K, P, eigVals = controller.lqr_solve()
  62. x = np.array([0, 0])
  63. r = np.array([1, 0])
  64. u = controller.update(np.array([1, 0]), np.array([0, 0]))
  65. print("LQR gain matrix K:")
  66. print(K)
  67. print("\nSolution to Riccati equation P:")
  68. print(P)
  69. print("\nClosed loop system eigenvalues:")
  70. print(eigVals)
  71. print("Control value:")
  72. print(u)

(3)MPC:

  1. # Author:gyx
  2. # Usage: MPC controller
  3. # Last change: 2024-7-20
  4. import numpy as np
  5. import matplotlib.pyplot as plt
  6. class MPCController:
  7. def __init__(self, A, B, C, N, Q, R, u_min, u_max, reg=1e-5):
  8. self.A = A
  9. self.B = B
  10. self.C = C
  11. self.N = N
  12. self.Q = Q
  13. self.R = R
  14. self.u_min = u_min
  15. self.u_max = u_max
  16. self.reg = reg # 正则化参数
  17. self.measurement_history = []
  18. self.setpoint_history = []
  19. def mpc_control(self, x, r):
  20. x = x.reshape(-1,1)
  21. r = r.reshape(-1,1)
  22. n_states = self.A.shape[0]
  23. n_controls = self.B.shape[1]
  24. # 构建优化问题
  25. H = np.zeros((self.N * n_controls, self.N * n_controls))
  26. F = np.zeros((self.N * n_controls, 1))
  27. for i in range(self.N):
  28. H[i * n_controls:(i + 1) * n_controls, i * n_controls:(i + 1) * n_controls] = self.R
  29. F[i * n_controls:(i + 1) * n_controls, 0] = 0
  30. G = np.zeros((self.N * n_states, self.N * n_controls))
  31. E = np.zeros((self.N * n_states, n_states))
  32. L = np.zeros((self.N * n_states, 1))
  33. for i in range(self.N):
  34. for j in range(i + 1):
  35. G[i * n_states:(i + 1) * n_states, j * n_controls:(j + 1) * n_controls] \
  36. = np.linalg.matrix_power(self.A,i - j) @ self.B
  37. E[i * n_states:(i + 1) * n_states, :] = np.linalg.matrix_power(self.A, i + 1)
  38. L[i * n_states:(i + 1) * n_states, :] = np.linalg.matrix_power(self.A, i + 1) @ x - r
  39. Q_bar = np.kron(np.eye(self.N), self.Q)
  40. H += G.T @ Q_bar @ G
  41. F += (G.T @ Q_bar @ L).reshape(-1, 1)
  42. # 计算最优控制输入
  43. u_opt = -np.linalg.inv(H) @ F
  44. # 取第一个控制输入
  45. u = u_opt[:n_controls]
  46. return u
  47. def update(self, setpoint, measurement):
  48. self.measurement_history.append(measurement)
  49. self.setpoint_history.append(setpoint)
  50. u = self.mpc_control(np.array(measurement), np.array(setpoint))
  51. # print(u)
  52. return u
  53. def plot_error(self, title='none'):
  54. self.setpoint_history = np.array(self.setpoint_history)
  55. self.measurement_history = np.array(self.measurement_history)
  56. subtitle = ['x', 'y', 'z']
  57. fig = plt.figure(figsize=(14, 4))
  58. for i in range(len(self.setpoint_history[0])):
  59. ax = fig.add_subplot(131+i)
  60. ax.plot([i / 100 for i in range(len(self.setpoint_history[:, i]))], self.setpoint_history[:, i],
  61. label='setpoint')
  62. ax.plot([i / 100 for i in range(len(self.measurement_history[:, i]))], self.measurement_history[:, i],
  63. label='measurement')
  64. ax.set_xlabel('time')
  65. ax.set_ylabel('value')
  66. ax.set_title(subtitle[i]+title)
  67. ax.legend()
  68. plt.tight_layout()
  69. if __name__ == '__main__':
  70. A = np.array([[1, 0,0], [0, 1,0],[0,0,1]])
  71. B = np.array([[0.5,0.6,0.2], [1,0.1,0.4],[0.5,0.5,0.8]])
  72. C = np.eye(3) # 输出矩阵
  73. Q = np.eye(3)*2
  74. R = np.eye(1)
  75. N = 10
  76. u_min = -1
  77. u_max = 1
  78. reg = 1e-5
  79. outer_controller = MPCController(A, B, C, N, Q, R, u_min, u_max, reg)
  80. inner_controller = MPCController(A, B, C, N, Q, R, u_min, u_max, reg)
  81. setpoint = np.array([[1], [1],[1]])
  82. # r = np.ones((1, N)) # 目标为1
  83. measurement = np.array([[0],[0],[0]])
  84. x = np.array([[0], [0],[0]])
  85. x_dot = np.array([[0], [0], [0]])
  86. for _ in range(10):
  87. u = outer_controller.update(setpoint, x)
  88. u_v = inner_controller.update(u,x_dot)
  89. x_dot = A @ x_dot + B @ u_v.reshape(-1, 1)
  90. x = A @ x + B @ x_dot.reshape(-1, 1)
  91. # x = A @ x + B @ u.reshape(-1, 1)
  92. y = C @ x
  93. print("控制输入 u:")
  94. print(u)
  95. print('状态 x:')
  96. print(x)
  97. inner_controller.plot_error()
  98. outer_controller.plot_error()
  99. plt.show()

仿真运行代码:

  1. import numpy as np
  2. import matplotlib.pyplot as plt
  3. from init_control import UAVParameters
  4. from uav_dynamics import UAVDynamics
  5. from common_function import rotation_matrix, angle_body2world
  6. from PIDController import posion_pos_controller
  7. from LQRController import LQRController
  8. from MPCController import MPCController
  9. from square_signal import generate_ramp_signal
  10. class UAVSimulation:
  11. def __init__(self, params, dynamics, position_controller, pose_controller, controller_type):
  12. self.params = params
  13. self.dynamics = dynamics
  14. self.controller_type = controller_type
  15. if controller_type == "PID":
  16. self.x_controller = position_controller[0]
  17. self.y_controller = position_controller[1]
  18. self.height_controller = position_controller[2]
  19. else:
  20. self.position_controller = position_controller[0]
  21. self.velocity_controller = position_controller[1]
  22. self.roll_controller = pose_controller[0]
  23. self.pitch_controller = pose_controller[1]
  24. self.yaw_controller = pose_controller[2]
  25. self.position_history = []
  26. self.setpoint_history = []
  27. def desired_euler_angles(self, eax, eay, psi):
  28. """
  29. Convert the desired horizontal acceleration to the desired Euler angle
  30. Input:
  31. eax, eay: desired horizontal acceleration
  32. Output:
  33. phi, theta: desired roll and pitch
  34. """
  35. g = 9.8 # 重力加速度 (m/s^2)
  36. phi = (-np.sin(psi) * eax + np.cos(psi) * eay) / g
  37. theta = (-np.cos(psi) * eax - np.sin(psi) * eay) / g
  38. return phi, theta
  39. def motor_mixer(self, roll, pitch, yaw, thrust):
  40. """
  41. Control allocation. The quadrotor type is X-configuration,
  42. and the airframe is as follows:
  43. 3↓ 1↑
  44. \ /
  45. / \
  46. 2↑ 4↓
  47. Input:
  48. roll, pitch, yaw: attitude controller output.
  49. thrust: total thrust.
  50. Output:
  51. M1, M2, M3, M4: motor speed commands.
  52. """
  53. idle_PWM = 1000
  54. scale = 1000
  55. M1 = (thrust - roll + pitch + yaw) * scale + idle_PWM
  56. M2 = (thrust + roll - pitch + yaw) * scale + idle_PWM
  57. M3 = (thrust + roll + pitch - yaw) * scale + idle_PWM
  58. M4 = (thrust - roll - pitch - yaw) * scale + idle_PWM
  59. return M1, M2, M3, M4
  60. def pwm_to_rpm(self, pwm):
  61. """
  62. Convert PWM signal to RPM. This is a simple linear approximation.
  63. Input:
  64. pwm: PWM signal value
  65. Output:
  66. rpm: motor speed in RPM
  67. """
  68. min_pwm = 1000
  69. max_pwm = 2000
  70. # 限制PWM范围
  71. pwm = np.clip(pwm, min_pwm, max_pwm)
  72. # 线性插值计算RPM
  73. rpm = np.clip((pwm - min_pwm) / (max_pwm - min_pwm), -1, 1)
  74. return rpm
  75. def run(self, square_corners, duration, dt):
  76. DEG2RAD = self.params.DEG2RAD
  77. posE = self.params.ModelInit_PosE.astype(np.float64)
  78. velB = self.params.ModelInit_VelB.astype(np.float64)
  79. velE = self.params.ModelInit_VelB.astype(np.float64)
  80. angEuler = self.params.ModelInit_AngEuler
  81. rateB = self.params.ModelInit_RateB
  82. rateE = self.params.ModelInit_RateB
  83. ModelParam_motorCr = self.params.ModelParam_motorCr
  84. ModelParam_motorWb = self.params.ModelParam_motorWb
  85. state = [velB, angEuler, rateB]
  86. current_corner_index = 0
  87. time_steps = int(duration / dt)
  88. num_steps = time_steps // (len(square_corners) - 1)
  89. # next_corner_time = time_steps // (len(square_corners)-1)
  90. input_signal = generate_ramp_signal(square_corners, num_steps)
  91. for step in range(time_steps):
  92. setpoint = input_signal[step]
  93. eax, eay, altitude_output = None, None, None
  94. print('setpoint:', setpoint)
  95. if self.controller_type == 'PID':
  96. eax = self.x_controller.update(outer_setpoint=setpoint[0], outer_measurement=posE[0],
  97. inner_measurement=velE[0], dt=dt)
  98. eay = self.y_controller.update(outer_setpoint=setpoint[1], outer_measurement=posE[1],
  99. inner_measurement=velE[1], dt=dt)
  100. altitude_output = self.height_controller.update(outer_setpoint=setpoint[2], outer_measurement=posE[2],
  101. inner_measurement=velE[2], dt=dt)
  102. else:
  103. vel_setpoint = self.position_controller.update(setpoint, posE)
  104. # 速度环LQR控制
  105. acc_setpoint = self.velocity_controller.update(vel_setpoint, velE)
  106. if self.controller_type == "MPC":
  107. eax, eay, altitude_output = acc_setpoint[0][0], acc_setpoint[1][0], acc_setpoint[2][0]
  108. elif self.controller_type == "LQR":
  109. eax, eay, altitude_output = acc_setpoint[0], acc_setpoint[1], acc_setpoint[2]
  110. else:
  111. print("unknown controller")
  112. phi, theta = self.desired_euler_angles(eax, eay, angEuler[2])
  113. phi = np.clip(phi, -35 * DEG2RAD, 35 * DEG2RAD)
  114. theta = np.clip(theta, -35 * DEG2RAD, 35 * DEG2RAD)
  115. print('eax,eay:', eax, eay)
  116. print('phi,theta:', phi, theta)
  117. roll_output = np.clip(self.roll_controller.update(outer_setpoint=phi, outer_measurement=angEuler[0],
  118. inner_measurement=rateE[0], dt=dt), -1, 1)
  119. pitch_output = np.clip(self.pitch_controller.update(outer_setpoint=theta, outer_measurement=angEuler[1],
  120. inner_measurement=rateE[1], dt=dt), -1, 1)
  121. yaw_output = np.clip(self.yaw_controller.update(outer_setpoint=(step // num_steps) * 0.5 * 0.0174533,
  122. outer_measurement=angEuler[2],
  123. inner_measurement=rateE[2], dt=dt), -1, 1)
  124. thrust = np.clip(-altitude_output + self.params.THR_HOVER, 0.05, 0.9)
  125. print('roll_output,pitch_output,yaw_output,altitude_output:', roll_output, pitch_output, yaw_output,
  126. altitude_output)
  127. M1, M2, M3, M4 = self.motor_mixer(roll_output, pitch_output, yaw_output, thrust)
  128. print('M1,M2,M3,M4:', M1, M2, M3, M4)
  129. w_target = (np.array(
  130. [self.pwm_to_rpm(M1), self.pwm_to_rpm(M2), self.pwm_to_rpm(M3),
  131. self.pwm_to_rpm(M4)]) * ModelParam_motorCr +
  132. np.array([ModelParam_motorWb, ModelParam_motorWb, ModelParam_motorWb, ModelParam_motorWb]))
  133. print('w_target:', w_target)
  134. state = self.dynamics.update_dynamics(state, w_target, dt)
  135. velB, angEuler, rateB = state
  136. # R = rotation_matrix(angEuler)
  137. R_angle = angle_body2world(angEuler)
  138. R = rotation_matrix(angEuler)
  139. rateE = R_angle @ rateB
  140. angEuler = angEuler + rateE * dt
  141. velE = R @ velB
  142. posE = posE + velE * dt
  143. state = velB, angEuler, rateB
  144. self.position_history.append(posE.copy())
  145. print(
  146. f"Time: {step * dt:.2f}s, Position: {posE}, Velocity_world: {velE}, Angle: {angEuler}, Rate: {rateE}")
  147. # if step % (next_corner_time-1) == 0 and step != 0:
  148. # current_corner_index = (current_corner_index + 1) % len(square_corners)
  149. self.setpoint_history.append(setpoint.copy())
  150. def plot_trajectory(self):
  151. position_history = np.array(self.position_history)
  152. setpoint_history = np.array(self.setpoint_history)
  153. fig = plt.figure()
  154. ax = fig.add_subplot(121, projection='3d')
  155. ax.plot(position_history[:, 0], position_history[:, 1], -position_history[:, 2], label='UAV Trajectory')
  156. ax.set_xlabel('X Position (m)')
  157. ax.set_ylabel('Y Position (m)')
  158. ax.set_zlabel('Z Position (m)')
  159. ax.set_title('Trajectory of UAV ')
  160. ax.legend()
  161. ax = fig.add_subplot(122, projection='3d')
  162. ax.plot(setpoint_history[:, 0], setpoint_history[:, 1], -setpoint_history[:, 2], label='setpoint Trajectory')
  163. ax.set_xlabel('X Position (m)')
  164. ax.set_ylabel('Y Position (m)')
  165. ax.set_zlabel('Z Position (m)')
  166. ax.set_title(' Desired Trajectory ')
  167. ax.legend()
  168. def plot_state_error(self):
  169. if self.controller_type == 'PID':
  170. fig_position = plt.figure(figsize=(14, 4))
  171. self.x_controller.outer_controller.plot_error(fig_position, 0, title='x_position')
  172. self.y_controller.outer_controller.plot_error(fig_position, 1, title='y_position')
  173. self.height_controller.outer_controller.plot_error(fig_position, 2, title='height_position')
  174. fig_velocity = plt.figure(figsize=(14, 4))
  175. self.x_controller.inner_controller.plot_error(fig_velocity, 0, title='x_velocity')
  176. self.y_controller.inner_controller.plot_error(fig_velocity, 1, title='y_velocity')
  177. self.height_controller.inner_controller.plot_error(fig_velocity, 2, title='height_velocity')
  178. else:
  179. self.position_controller.plot_error('_position')
  180. self.velocity_controller.plot_error('_velocity')
  181. fig_angle = plt.figure(figsize=(14, 4))
  182. self.roll_controller.outer_controller.plot_error(fig_angle, 0, title='roll_angle')
  183. self.pitch_controller.outer_controller.plot_error(fig_angle, 1, title='pitch_angle')
  184. self.yaw_controller.outer_controller.plot_error(fig_angle, 2, title='yaw_angle')
  185. plt.tight_layout()
  186. fig_rate = plt.figure(figsize=(14, 4))
  187. self.roll_controller.inner_controller.plot_error(fig_rate, 0, title='roll_rate')
  188. self.pitch_controller.inner_controller.plot_error(fig_rate, 1, title='pitch_rate')
  189. self.yaw_controller.inner_controller.plot_error(fig_rate, 2, title='yaw_rate')
  190. plt.tight_layout()
  191. # plt.show()
  192. if __name__ == '__main__':
  193. params = UAVParameters()
  194. dynamics = UAVDynamics(params)
  195. # controller = CascadePIDController(PIDController(3.0, 0.0, 0.15), PIDController(0.15, 0.0, 0.03))
  196. # controller_type = "PID"
  197. # controller_type = "MPC"
  198. controller_type = "LQR"
  199. position_controller = None
  200. if controller_type == 'PID':
  201. position_controller = posion_pos_controller(params).position_controller()
  202. elif controller_type == 'LQR':
  203. # 定义位置环的状态空间模型
  204. A_pos = np.zeros((3, 3))
  205. B_pos = np.eye(3)
  206. Q_pos = np.eye(3) * 1.65
  207. R_pos = np.eye(3) * 4.0
  208. # 定义速度环的状态空间模型
  209. A_vel = np.zeros((3, 3))
  210. B_vel = np.eye(3)
  211. Q_vel = np.eye(3) * 10.0
  212. R_vel = np.eye(3) * 2.4
  213. pos_controller = LQRController(A_pos, B_pos, Q_pos, R_pos)
  214. vel_controller = LQRController(A_vel, B_vel, Q_vel, R_vel)
  215. position_controller = [pos_controller, vel_controller]
  216. elif controller_type == 'MPC':
  217. # 定义离散位置环的状态空间模型
  218. A_pos = np.eye(3)
  219. B_pos = np.eye(3) * 0.01
  220. Q_pos = np.eye(3) * 3.95
  221. R_pos = np.eye(3) * 0.6
  222. # 定义离散速度环的状态空间模型
  223. # A_vel = np.zeros((3, 3))
  224. A_vel = np.eye(3)
  225. B_vel = np.eye(3) * 0.01
  226. Q_vel = np.eye(3) * 12.0
  227. R_vel = np.eye(3) * 0.6
  228. pos_controller = MPCController(A_pos, B_pos, None, 10, Q_pos, R_pos, -3, 3)
  229. vel_controller = MPCController(A_vel, B_vel, None, 10, Q_vel, R_vel, -1, 1)
  230. position_controller = [pos_controller, vel_controller]
  231. else:
  232. print('ERROR:Unknown Controller')
  233. pose_controller = posion_pos_controller(params).pos_controller()
  234. target_altitude = -5 # 目标高度
  235. square_corners = [
  236. [0, 0, 0],
  237. [0, 0, target_altitude],
  238. [5, 0, target_altitude],
  239. [5, 5, target_altitude],
  240. [0, 5, target_altitude],
  241. [0, 0, target_altitude],
  242. [0, 0, 0],
  243. [0, 0, 0]
  244. ]
  245. simulation = UAVSimulation(params, dynamics, position_controller=position_controller,
  246. pose_controller=pose_controller, controller_type=controller_type)
  247. simulation.run(square_corners, 35, 0.01)
  248. simulation.plot_trajectory()
  249. simulation.plot_state_error()
  250. plt.show()

三种控制算法效果如下:

可能由于任务较为简单,三种算法差异不大,同时LQR和MPC计算效率较低。

            </div><div data-report-view="{&quot;mod&quot;:&quot;1585297308_001&quot;,&quot;spm&quot;:&quot;1001.2101.3001.6548&quot;,&quot;dest&quot;:&quot;https://blog.csdn.net/perfectdisaster/article/details/143432386&quot;,&quot;extend1&quot;:&quot;pc&quot;,&quot;ab&quot;:&quot;new&quot;}"><div></div></div>
    </div>

文章作者: 郭昱鑫
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 郭昱鑫 !
  目录