#Theodore Bounds #Inverted Double Pendulum Control Optimization #Adapted from code by Everton Colling import math import matplotlib matplotlib.use("TkAgg") import matplotlib.animation as animation import numpy as np from gekko import GEKKO #Defining a model m = GEKKO(remote=True) ################################# #Define initial and final conditions and limits pi = math.pi; x0 = 0; xdot0 = 0 q10 = pi; q1dot0 = 0 #0=vertical, pi=inverted q20 = pi; q2dot0 = 0 #0=vertical, pi=inverted xf = 0; xdotf = 0 q1f = 0; q1dotf = 0 q2f = 0; q2dotf = 0 xmin = -2; xmax = 2 umin = -10; umax = 10 #Defining the time parameter (0, 1) N = 100 t = np.linspace(0,1,N) m.time = t #Final time TF = m.FV(12,lb=2,ub=25); TF.STATUS = 1 end_loc = len(m.time)-1 final = np.zeros(len(m.time)) for i in range(N): if i >=(N-2): final[i] = 1000 #final[end_loc] = 100 final = m.Param(value=final) #Parameters mc = m.Param(value=1) #cart mass m1 = m.Param(value=.1) #link 1 mass m2 = m.Param(value=.1) #link 2 mass L1 = m.Param(value=.5) #link 1 length LC1 = m.Param(value=.25) #link 1 CM pos L2 = m.Param(value=.5) #link 1 length LC2 = m.Param(value=.25) #link 1 CM pos I1 = m.Param(value=.01) #link 1 MOI I2 = m.Param(value=.01) #link 2 MOI g = m.Const(value=9.81) #gravity Bc = m.Const(value=.5) #cart friction B1 = m.Const(value=.001) #link 1 friction B2 = m.Const(value=.001) #link 2 friction #MV u = m.MV(lb=umin,ub=umax); u.STATUS = 1 #State Variables x, xdot, q1, q1dot, q2, q2dot = m.Array(m.Var, 6) x.value = x0; xdot.value = xdot0 q1.value = q10; q1dot.value = q1dot0 q2.value = q20; q2dot.value = q2dot0 x.LOWER = xmin; x.UPPER = xmax #Intermediates h1 = m.Intermediate(mc + m1 + m2) h2 = m.Intermediate(m1*LC1 + m2*L1) h3 = m.Intermediate(m2*LC2) h4 = m.Intermediate(m1*LC1**2 + m2*L1**2 + I1) h5 = m.Intermediate(m2*LC2*L1) h6 = m.Intermediate(m2*LC2**2 + I2) h7 = m.Intermediate(m1*LC1*g + m2*L1*g) h8 = m.Intermediate(m2*LC2*g) M = np.array([[h1, h2*m.cos(q1), h3*m.cos(q2)], [h2*m.cos(q1), h4, h5*m.cos(q1-q2)], [h3*m.cos(q2), h5*m.cos(q1-q2), h6]]) C = np.array([[Bc, -h2*q1dot*m.sin(q1), -h3*q2dot*m.sin(q2)], [0, B1+B2, h5*q2dot*m.sin(q1-q2)-B2], [0, -h5*q1dot*m.sin(q1-q2)-B2, B2]]) G = np.array([0, -h7*m.sin(q1), -h8*m.sin(q2)]) U = np.array([u, 0, 0]) DQ = np.array([xdot, q1dot, q2dot]) CDQ = C@DQ b = np.array([xdot.dt()/TF, q1dot.dt()/TF, q2dot.dt()/TF]) Mb = M@b #Defining the State Space Model m.Equations([(Mb[i] == U[i] - CDQ[i] - G[i]) for i in range(3)]) m.Equation(x.dt()/TF == xdot) m.Equation(q1.dt()/TF == q1dot) m.Equation(q2.dt()/TF == q2dot) m.Obj(final*(x-xf)**2) m.Obj(final*(xdot-xdotf)**2) m.Obj(final*(q1-q1f)**2) m.Obj(final*(q1dot-q1dotf)**2) m.Obj(final*(q2-q2f)**2) m.Obj(final*(q2dot-q2dotf)**2) #Try to minimize final time m.Obj(TF) m.options.IMODE = 6 #MPC m.options.SOLVER = 3 #IPOPT m.solve() m.time = np.multiply(TF, m.time) print('Final time: ', TF.value[0]) print(q1dot.value) #Plotting the results import matplotlib.pyplot as plt plt.close('all') fig1 = plt.figure() fig2 = plt.figure() ax1 = fig1.add_subplot() ax2 = fig2.add_subplot(321) ax3 = fig2.add_subplot(322) ax4 = fig2.add_subplot(323) ax5 = fig2.add_subplot(324) ax6 = fig2.add_subplot(325) ax7 = fig2.add_subplot(326) ax1.plot(m.time,u.value,'m',lw=2) ax1.legend([r'$u$'],loc=1) ax1.set_title('Control Input') ax1.set_ylabel('Force (N)') ax1.set_xlabel('Time (s)') ax1.set_xlim(m.time[0],m.time[-1]) ax1.grid(True) ax2.plot(m.time,x.value,'r',lw=2) ax2.set_ylabel('Position (m)') ax2.set_xlabel('Time (s)') ax2.legend([r'$x$'],loc='upper left') ax2.set_xlim(m.time[0],m.time[-1]) ax2.grid(True) ax2.set_title('Cart Position') ax3.plot(m.time,xdot.value,'g',lw=2) ax3.set_ylabel('Velocity (m/s)') ax3.set_xlabel('Time (s)') ax3.legend([r'$xdot$'],loc='upper left') ax3.set_xlim(m.time[0],m.time[-1]) ax3.grid(True) ax3.set_title('Cart Velocity') q1alt = np.zeros((N,1)); q2alt = np.zeros((N,1)); for i in range(N): q1alt[i] = q1.value[i] + math.pi/2 q2alt[i] = q2.value[i] + math.pi/2 ax4.plot(m.time,q1alt,'r',lw=2) ax4.set_ylabel('Angle (Rad)') ax4.set_xlabel('Time (s)') ax4.legend([r'$q1$'],loc='upper left') ax4.set_xlim(m.time[0],m.time[-1]) ax4.grid(True) ax4.set_title('Link 1 Position') ax5.plot(m.time,q1dot.value,'g',lw=2) ax5.set_ylabel('Angular Velocity (Rad/s)') ax5.set_xlabel('Time (s)') ax5.legend([r'$q1dot$'],loc='upper right') ax5.set_xlim(m.time[0],m.time[-1]) ax5.grid(True) ax5.set_title('Link 1 Velocity') ax6.plot(m.time,q2alt,'r',lw=2) ax6.set_ylabel('Angle (Rad)') ax6.set_xlabel('Time (s)') ax6.legend([r'$q2$'],loc='upper left') ax6.set_xlim(m.time[0],m.time[-1]) ax6.grid(True) ax6.set_title('Link 2 Position') ax7.plot(m.time,q2dot.value,'g',lw=2) ax7.set_ylabel('Angular Velocity (Rad/s)') ax7.set_xlabel('Time (s)') ax7.legend([r'$q2dot$'],loc='upper right') ax7.set_xlim(m.time[0],m.time[-1]) ax7.grid(True) ax7.set_title('Link 2 Velocity') plt.rcParams['animation.html'] = 'html5' x1 = x.value y1 = np.zeros(len(m.time)) x2 = L1.value*np.sin(q1.value)+x1 x2b = (1.05*L1.value[0])*np.sin(q1.value)+x1 y2 = L1.value[0]*np.cos(q1.value)+y1 y2b = (1.05*L1.value[0])*np.cos(q1.value)+y1 x3 = L2.value[0]*np.sin(q2.value)+x2 x3b = (1.05*L2.value[0])*np.sin(q2.value)+x2 y3 = L2.value[0]*np.cos(q2.value)+y2 y3b = (1.05*L2.value[0])*np.cos(q2.value)+y2 fig = plt.figure(figsize=(8,6.4)) ax = fig.add_subplot(111,autoscale_on=False,\ xlim=(-2.5,2.5),ylim=(-1.25,1.25)) ax.set_xlabel('position') ax.get_yaxis().set_visible(False) crane_rail, = ax.plot([-2.5,2.5],[-0.2,-0.2],'k-',lw=4) start, = ax.plot([-1.5,-1.5],[-1.5,1.5],'k:',lw=2) objective, = ax.plot([1.5,1.5],[-1.5,1.5],'k:',lw=2) mass1, = ax.plot([],[],linestyle='None',marker='s',\ markersize=40,markeredgecolor='k',\ color='orange',markeredgewidth=2) mass2, = ax.plot([],[],linestyle='None',marker='o',\ markersize=20,markeredgecolor='k',\ color='orange',markeredgewidth=2) mass3, = ax.plot([],[],linestyle='None',marker='o',\ markersize=20,markeredgecolor='k',\ color='orange',markeredgewidth=2) line12, = ax.plot([],[],'o-',color='black',lw=4,\ markersize=6,markeredgecolor='k',\ markerfacecolor='k') line23, = ax.plot([],[],'o-',color='black',lw=4,\ markersize=6,markeredgecolor='k',\ markerfacecolor='k') time_template = 'time = %.1fs' time_text = ax.text(0.05,0.9,'',transform=ax.transAxes) #start_text = ax.text(-1.1,-0.3,'start',ha='right') #end_text = ax.text(1.0,-0.3,'end',ha='left') def init(): mass1.set_data([],[]) mass2.set_data([],[]) mass3.set_data([],[]) line12.set_data([],[]) line23.set_data([],[]) time_text.set_text('') return line12, line23, mass1, mass2, mass3, time_text def animate(i): mass1.set_data([x1[i]], [y1[i]-0.1]) mass2.set_data([x2[i]], [y2[i]]) mass3.set_data([x3[i]], [y3[i]]) line12.set_data([x1[i],x2[i]],[y1[i],y2[i]]) line23.set_data([x2[i],x3[i]],[y2[i],y3[i]]) time_text.set_text(time_template % m.time[i]) return line12, line23, mass1, mass2, mass3, time_text ani_a = animation.FuncAnimation(fig, animate, \ np.arange(len(m.time)), \ interval=40,init_func=init) #blit=False, ani_a.save('Pendulum_Swing_Up.mp4',fps=30) plt.show() #Export Data import csv with open('U.csv', 'w', newline = '') as csvfile: my_writer = csv.writer(csvfile, delimiter = ' ') for i in range(N): input = np.array((m.time[i], u.value[i], x.value[i], q1.value[i], q2.value[i])) my_writer.writerow(input) csvfile.close