当前位置:   article > 正文

基于LQR的倒立摆控制——python代码——dlqr步骤推导

dlqr

推荐一个自动控制小车开源项目:本文结合老王自动驾驶控制算法第五讲的离散LQR进行学习复盘

Inverted Pendulum Control — PythonRobotics documentation

  • dlqr原理(老王的拉格朗日乘子法)

  • 由于函数较多,写了一个框架方便理解。

  •  代码如下代,内附官方和自己的注释:
  1. """
  2. Inverted Pendulum LQR control
  3. author: Trung Kien - letrungkien.k53.hut@gmail.com
  4. """
  5. import math
  6. import time
  7. import matplotlib.pyplot as plt
  8. import numpy as np
  9. from numpy.linalg import inv, eig
  10. # Model parameters
  11. l_bar = 2.0 # length of bar
  12. M = 1.0 # [kg]
  13. m = 0.3 # [kg]
  14. g = 9.8 # [m/s^2]
  15. nx = 4 # number of state
  16. nu = 1 # number of input
  17. Q = np.diag([0.0, 1.0, 1.0, 0.0]) # state cost matrix。返回所给元素组成的对角矩阵
  18. R = np.diag([0.01]) # input cost matrix
  19. delta_t = 0.1 # time tick [s]
  20. sim_time = 5.0 # simulation time [s]
  21. show_animation = True #一个为真的变量
  22. ## 1
  23. def main():
  24. # x=[x,dot_x,seta,dot_seita]
  25. x0 = np.array([
  26. [0.0],
  27. [0.0],
  28. [0.3],#倾斜角
  29. [0.0]
  30. ])
  31. # x = np.copy(x0)
  32. x = x0
  33. time = 0.0
  34. while sim_time > time:
  35. time += delta_t
  36. # calc control input
  37. u = lqr_control(x)#调用函数
  38. # simulate inverted pendulum cart
  39. x = simulation(x, u)#调用函数
  40. if show_animation:
  41. plt.clf()#Clear the current figure.清楚每一帧的动画
  42. print("X[0]:",x[0])
  43. # print("X:",x)
  44. px = float(x[0])#将一个字符串或数字转换为浮点数。输出位置
  45. theta = float(x[2])#输出角度
  46. plot_cart(px, theta)#调用函数
  47. plt.xlim([-5.0, 2.0])
  48. plt.pause(0.001)#暂停间隔
  49. print("Finish")
  50. print(f"x={float(x[0]):.2f} [m] , theta={math.degrees(x[2]):.2f} [deg]")
  51. if show_animation:
  52. plt.show()
  53. ## 2
  54. def simulation(x, u):
  55. '''
  56. 调整X
  57. '''
  58. A, B = get_model_matrix()#调用函数,得到AB矩阵
  59. x = A @ x + B @ u#矩阵乘法,必须声明numpy
  60. return x
  61. ## 3
  62. def solve_DARE(A, B, Q, R, maxiter=150, eps=0.01):
  63. """
  64. Solve a discrete time_Algebraic Riccati equation (DARE)求得离散时间黎卡提方程的解 矩阵P
  65. """
  66. P = Q#初始化
  67. for i in range(maxiter):
  68. #矩阵P进行迭代,4×4的矩阵
  69. Pn = A.T @ P @ A - A.T @ P @ B @ inv(R + B.T @ P @ B) @ B.T @ P @ A + Q
  70. # print("Pn:",Pn)
  71. #矩阵P收敛时,退出迭代循环
  72. # print("abs(Pn - P).max:",abs(Pn - P).max(),i)
  73. # print("Pn - P:",Pn - P,i)
  74. if (abs(Pn - P)).max() < eps:
  75. break
  76. P = Pn
  77. return Pn#收敛的矩阵P
  78. # P = Q
  79. # for i in range(50):
  80. # Pn = A.T @ P @ A - A.T @ P @ B @ inv(R + B.T @ P @ B ) @ B.T @ P @ A +Q
  81. # # print("Pn:",Pn)
  82. # # print("P:",P)
  83. # if (abs(Pn - P)).max() < 0.01:
  84. # break
  85. # return Pn
  86. ## 4
  87. def dlqr(A, B, Q, R):
  88. """
  89. Solve the discrete time lqr controller.
  90. x[k+1] = A x[k] + B u[k]
  91. cost = sum x[k].T*Q*x[k] + u[k].T*R*u[k]
  92. # ref Bertsekas, p.151
  93. """
  94. # first, try to solve the ricatti equation
  95. #先求解迭代后收敛的矩阵P
  96. P = solve_DARE(A, B, Q, R)
  97. # compute the LQR gain
  98. #计算矩阵K,u=-KX
  99. K = inv(B.T @ P @ B + R) @ (B.T @ P @ A)#u=-kx的k
  100. eigVals, eigVecs = eig(A - B @ K)#计算方阵的特征值和右特征向量
  101. return K, P, eigVals
  102. ## 5
  103. def lqr_control(x):
  104. '''
  105. 得到输入u
  106. '''
  107. A, B = get_model_matrix()
  108. start = time.time()
  109. K, _, _ = dlqr(A, B, Q, R)
  110. u = -K @ x
  111. elapsed_time = time.time() - start
  112. print(f"calc time:{elapsed_time:.6f} [sec]")
  113. return u#返回输入u
  114. ## 6
  115. def get_numpy_array_from_matrix(x):
  116. """
  117. get build-in list from matrix,将多维数组降为一维
  118. """
  119. return np.array(x).flatten()#将多维数组降为一维
  120. ## 7
  121. def get_model_matrix():
  122. '''
  123. 更新离散过程中的矩阵A、B
  124. '''
  125. A = np.array([
  126. [0.0, 1.0, 0.0, 0.0],
  127. [0.0, 0.0, m * g / M, 0.0],
  128. [0.0, 0.0, 0.0, 1.0],
  129. [0.0, 0.0, g * (M + m) / (l_bar * M), 0.0]
  130. ])
  131. # A = np.eye(nx) + delta_t * A#单位矩阵+△t*A,收敛速度慢
  132. A = inv(np.eye(4) - A *1/2 * delta_t) @ (np.eye(4) + A *1/2 * delta_t)#收敛更快
  133. B = np.array([
  134. [0.0],
  135. [1.0 / M],
  136. [0.0],
  137. [1.0 / (l_bar * M)]
  138. ])
  139. B = delta_t * B
  140. return A, B
  141. ## 8
  142. def flatten(a):
  143. """
  144. 将多维数组降为一维
  145. """
  146. return np.array(a).flatten()
  147. ## 9
  148. def plot_cart(xt, theta):#xt:小球位置
  149. """
  150. 画图
  151. """
  152. cart_w = 1.0#马车宽
  153. cart_h = 0.5#马车高
  154. radius = 0.1#马车轮半径
  155. cx = np.array([-cart_w / 2.0, cart_w / 2.0, cart_w /2.0, -cart_w / 2.0, -cart_w / 2.0])#马车顶点x坐标矩阵,闭合图形的顶点
  156. cy = np.array([0.0, 0.0, cart_h, cart_h, 0.0])#马车顶点y坐标
  157. cy += radius * 2.0#加轮高
  158. cx = cx + xt#调整马车位置,以球心坐标为基点变化
  159. bx = np.array([0.0, l_bar * math.sin(-theta)])#球心的x坐标
  160. bx += xt
  161. by = np.array([cart_h, l_bar * math.cos(-theta) + cart_h])#球心的y坐标
  162. by += radius * 2.0
  163. ##画车轮的圆
  164. #np.arange返回一个有终点和起点的固定步长的排列,第一个参数为起点,第二个参数为终点,第三个参数为步长
  165. angles = np.arange(0.0, math.pi * 2.0, math.radians(3.0))#math.radians()返回弧度制。离散化
  166. #每一步的x、y随球的旋转弧度变化
  167. ox = np.array([radius * math.cos(a) for a in angles])#np.array()的作用就是按照一定要求将object转换为数组
  168. oy = np.array([radius * math.sin(a) for a in angles])
  169. #右轮
  170. rwx = np.copy(ox) + cart_w / 4.0 + xt#右轮位置 = 离散矩阵 + 0.25 + 球位置
  171. rwy = np.copy(oy) + radius#离散矩阵
  172. #左轮
  173. lwx = np.copy(ox) - cart_w / 4.0 + xt
  174. lwy = np.copy(oy) + radius
  175. ##画球
  176. wx = np.copy(ox) + bx[-1]
  177. wy = np.copy(oy) + by[-1]
  178. plt.plot(flatten(cx), flatten(cy), "-b")#马车
  179. plt.plot(flatten(bx), flatten(by), "-k")#摆杆
  180. plt.plot(flatten(rwx), flatten(rwy), "-k")#右轮
  181. plt.plot(flatten(lwx), flatten(lwy), "-k")#左球
  182. plt.plot(flatten(wx), flatten(wy), "-k")#球
  183. plt.title(f"x: {xt:.2f} , theta: {math.degrees(theta):.2f}")
  184. # for stopping simulation with the esc key.
  185. plt.gcf().canvas.mpl_connect(
  186. 'key_release_event',
  187. lambda event: [exit(0) if event.key == 'escape' else None])
  188. plt.axis("equal")
  189. if __name__ == '__main__':
  190. main()

自己根据老王的dlqr推导过程,仿着开源代码修改了自己的代码,由于开源代码有些地方较为冗长,自己在函数顺序方面做了改进,图像plot部分未做修改

  1. import math
  2. import time
  3. import matplotlib.pyplot as plt
  4. import numpy as np
  5. from numpy.linalg import inv, eig
  6. l_bar = 2.0 # length of bar
  7. M = 1.0 # [kg]
  8. m = 0.3 # [kg]
  9. g = 9.8 # [m/s^2]
  10. nx = 4 # number of state
  11. nu = 1 # number of input
  12. Q = np.diag([0.0, 1.0, 1.0, 0.0]) # state cost matrix。返回所给元素组成的对角矩阵
  13. R = np.diag([0.01]) # input cost matrix
  14. delta_t = 0.1 # time tick [s]
  15. sim_time = 5.0 # simulation time [s]
  16. show_animation = True #一个为真的变量
  17. #主函数√
  18. def main():
  19. time = 0.0
  20. X0 = np.array([
  21. [0.0],
  22. [0.0],
  23. [0.3],
  24. [0.0]
  25. ])
  26. X = X0
  27. while sim_time > time:
  28. time += delta_t
  29. A,B = get_A_B()
  30. P = get_P(A,B,R,Q)
  31. K = get_K(A,B,R,P)
  32. u = get_u(K, X)
  33. X = A @ X + B @ u
  34. if show_animation:
  35. plt.clf()#Clear the current figure.清楚每一帧的动画
  36. px = float(X[0])#将一个字符串或数字转换为浮点数。输出位置
  37. theta = float(X[2])#输出角度
  38. plot_cart(px, theta)#调用函数
  39. plt.xlim([-5.0, 5.0])
  40. plt.pause(0.001)#暂停间隔
  41. print("Finish")
  42. print(f"x={float(X[0]):.2f} [m] , theta={math.degrees(X[2]):.2f} [deg]")
  43. if show_animation:
  44. plt.show()
  45. #获取p矩阵√
  46. def get_P(A,B,R,Q):
  47. P = Q
  48. # Pn = A.T @ P @ A - A.T @ P @ B @ inv(R + B.T @ P @ B ) @ B.T @ P @ A +Q
  49. # print("Pn:",Pn)
  50. for i in range(150):
  51. Pn = A.T @ P @ A - A.T @ P @ B @ inv(R + B.T @ P @ B ) @ B.T @ P @ A +Q
  52. # print("Pn:",Pn)
  53. # print("P:",P)
  54. if (abs(Pn - P)).max()< 0.01:
  55. break
  56. P = Pn
  57. return Pn
  58. #获取A,B√√√
  59. def get_A_B():
  60. A0 = np.array([
  61. [0.0, 1.0, 0.0, 0.0],
  62. [0.0, 0.0, m * g / M, 0.0],
  63. [0.0, 0.0, 0.0, 1.0],
  64. [0.0, 0.0, g * (M + m) / (l_bar * M), 0.0]
  65. ])
  66. B0 = np.array([
  67. [0.0],
  68. [1.0 / M],
  69. [0.0],
  70. [1.0 / (l_bar * M)]
  71. ])
  72. A = inv(np.eye(4) - A0 *1/2 * delta_t) @ (np.eye(4) + A0 *1/2 * delta_t)
  73. B = B0 * delta_t
  74. return A,B
  75. #获取K矩阵
  76. def get_K(A,B,R,P):
  77. K = inv(B.T @ P @ B + R) @(B.T @ P @ A)
  78. return K
  79. # 获取输入u
  80. def get_u(K, X):
  81. u = -1 * K @ X
  82. return u
  83. ## 8
  84. def flatten(a):
  85. """
  86. 将多维数组降为一维
  87. """
  88. return np.array(a).flatten()
  89. def plot_cart(xt, theta):
  90. """
  91. 画图
  92. """
  93. cart_w = 1.0
  94. cart_h = 0.5
  95. radius = 0.1
  96. cx = np.array([-cart_w / 2.0, cart_w / 2.0, cart_w /
  97. 2.0, -cart_w / 2.0, -cart_w / 2.0])
  98. cy = np.array([0.0, 0.0, cart_h, cart_h, 0.0])
  99. cy += radius * 2.0
  100. cx = cx + xt
  101. bx = np.array([0.0, l_bar * math.sin(-theta)])
  102. bx += xt
  103. by = np.array([cart_h, l_bar * math.cos(-theta) + cart_h])
  104. by += radius * 2.0
  105. angles = np.arange(0.0, math.pi * 2.0, math.radians(3.0))
  106. ox = np.array([radius * math.cos(a) for a in angles])
  107. oy = np.array([radius * math.sin(a) for a in angles])
  108. rwx = np.copy(ox) + cart_w / 4.0 + xt
  109. rwy = np.copy(oy) + radius
  110. lwx = np.copy(ox) - cart_w / 4.0 + xt
  111. lwy = np.copy(oy) + radius
  112. wx = np.copy(ox) + bx[-1]
  113. wy = np.copy(oy) + by[-1]
  114. plt.plot(flatten(cx), flatten(cy), "-b")
  115. plt.plot(flatten(bx), flatten(by), "-k")
  116. plt.plot(flatten(rwx), flatten(rwy), "-k")
  117. plt.plot(flatten(lwx), flatten(lwy), "-k")
  118. plt.plot(flatten(wx), flatten(wy), "-k")
  119. plt.title(f"x: {xt:.2f} , theta: {math.degrees(theta):.2f}")
  120. # for stopping simulation with the esc key.
  121. plt.gcf().canvas.mpl_connect(
  122. 'key_release_event',
  123. lambda event: [exit(0) if event.key == 'escape' else None])
  124. plt.axis("equal")
  125. if __name__ == '__main__':
  126. main()

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/510264
推荐阅读
相关标签
  

闽ICP备14008679号