From a9d3e4b3782d4297f8c24b2116f989ade41a442b Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Wed, 26 Jul 2023 23:54:18 -0400 Subject: [PATCH 01/93] dimensionless with an s --- dev/{dimentionless => dimensionless}/cases_master.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename dev/{dimentionless => dimensionless}/cases_master.py (99%) diff --git a/dev/dimentionless/cases_master.py b/dev/dimensionless/cases_master.py similarity index 99% rename from dev/dimentionless/cases_master.py rename to dev/dimensionless/cases_master.py index 7d9b5056..0184720c 100644 --- a/dev/dimentionless/cases_master.py +++ b/dev/dimensionless/cases_master.py @@ -18,7 +18,7 @@ def case( m , g , l , t_max_star , q_star , case_name = 'test ', show = True, rax = None , rax2 = None, res = 'reg', legend = 1): - # Additionnal fixed domain dimentionless parameters + # Additionnal fixed domain dimensionless parameters theta_star = 2.0 * np.pi dtheta_star = 1.0 * np.pi time_star = 2.0 * np.pi * 20.0 @@ -27,7 +27,7 @@ def case( m , g , l , t_max_star , q_star , case_name = 'test ', show = True, ra omega = np.sqrt( ( g / l ) ) mgl = m * g * l - # Dimentional parameters + # Dimensional parameters t_max = t_max_star * mgl q = q_star * mgl theta = theta_star From 67e9398e4a82794c1bdc0e37ec064651778dd1c8 Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Tue, 29 Aug 2023 10:04:15 -0400 Subject: [PATCH 02/93] 2D plane graphic output ok --- dev/plane/plane.py | 269 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 268 insertions(+), 1 deletion(-) diff --git a/dev/plane/plane.py b/dev/plane/plane.py index 43a31d07..2d823520 100644 --- a/dev/plane/plane.py +++ b/dev/plane/plane.py @@ -15,6 +15,69 @@ ############################################################################### +############################################################################### +def Transformation_Matrix_2D_from_base_angle( theta , bx , by ): + + s = np.sin( theta ) + c = np.cos( theta ) + + T = np.array([ [ c , -s , bx ] , + [ s , c , by ] , + [ 0 , 0 , 1 ] ]) + + return T + + +############################################################################### +def Transform_2D_Pts( pts , T ): + + pts_transformed = np.zeros( pts.shape ) + + for i in range(pts.shape[0]): + + pts_transformed[ i ] = T @ pts[ i ] + + return pts_transformed + + +############################################################################### +def arrow_from_base_angle( l , theta , bx , by ): + + d = l * 0.15 # length of arrow secondary lines + + pts_local = np.array([ [ 0 , 0 , 1 ] , + [ l , 0 , 1 ] , + [ l-d , d , 1 ] , + [ l , 0 , 1 ] , + [ l-d , -d , 1 ] ]) + + T = Transformation_Matrix_2D_from_base_angle( theta , bx , by ) + + pts_global = Transform_2D_Pts( pts_local , T ) + + + return pts_global + + +############################################################################### +def arrow_from_tip_angle( l , theta , bx , by ): + + d = l * 0.15 # length of arrow secondary lines + + pts_local = np.array([ [ -l , 0 , 1 ] , + [ 0 , 0 , 1 ] , + [ -d , d , 1 ] , + [ 0 , 0 , 1 ] , + [ -d , -d , 1 ] ]) + + T = Transformation_Matrix_2D_from_base_angle( theta , bx , by ) + + pts_global = Transform_2D_Pts( pts_local , T ) + + + return pts_global + + ############################################################################## @@ -60,6 +123,7 @@ def __init__(self): # Graphic output parameters + self.width = 1 self.dynamic_domain = True self.dynamic_range = 10 @@ -114,4 +178,207 @@ def g(self, q ): return g - \ No newline at end of file + + ########################################################################### + def forward_kinematic_domain(self, q ): + """ + """ + l = self.dynamic_range + + x = q[0] + self.width * 5 + y = q[1] + self.width * 1.5 + z = 0 + + if self.dynamic_domain: + + domain = [ ( -l + x , l + x ) , + ( -l + y , l + y ) , + ( -l + z , l + z ) ] + else: + + domain = [ ( -l , l ) , + ( -l , l ) , + ( -l , l ) ]# + + + return domain + + + ########################################################################### + def forward_kinematic_lines(self, q ): + """ + Compute points p = [x;y;z] positions given config q + ---------------------------------------------------- + - points of interest for ploting + + Outpus: + lines_pts = [] : a list of array (n_pts x 3) for each lines + + """ + + lines_pts = [] # list of array (n_pts x 3) for each lines + lines_style = [] + lines_color = [] + + + ########################### + # Dimensions + ########################### + + w = self.width # body width + l = w * 10 # body lenght + + ########################### + # Body + ########################### + + pts = np.zeros(( 5 , 3 )) + + x = q[0] + y = q[1] + theta = q[2] + + + body_pts_local = np.array([ [ 0 , 0 , 1 ] , + [ l , 0 , 1 ] , + [ l-w , w , 1 ] , + [ 2*w , w , 1 ] , + [ w , 3*w , 1 ] , + [ 0 , 3*w , 1 ] , + [ 0 , 0 , 1 ] ]) + + T_body = Transformation_Matrix_2D_from_base_angle( theta , x , y ) + + body_pts_global = Transform_2D_Pts( body_pts_local , T_body ) + + lines_pts.append( body_pts_global ) + lines_style.append( '-') + lines_color.append( 'b') + + ########################### + # Wings + ########################### + + pts = np.zeros(( 2 , 3 )) + + + wings_pts_local = np.array([ [ 3*w , 0.5 * w , 1 ] , + [ 6*w , 0.5 * w , 1 ] ]) + + + wings_pts_global = Transform_2D_Pts( wings_pts_local , T_body ) + + lines_pts.append( wings_pts_global ) + lines_style.append( '-') + lines_color.append( 'b') + + ########################### + # bottom line + ########################### + + pts = np.zeros((2,3)) + + pts[0,0] = -10000 + pts[1,0] = 10000 + pts[0,1] = 0 + pts[1,1] = 0 + + lines_pts.append( pts ) + lines_style.append('--') + lines_color.append('k') + + + + + + return lines_pts , lines_style , lines_color + + + ########################################################################### + def forward_kinematic_lines_plus(self, x , u , t ): + """ + plots the force vector + + """ + + lines_pts = [] # list of array (n_pts x 3) for each lines + lines_style = [] + lines_color = [] + + w = self.width + + q, dq = self.x2q(x) + + bx = q[0] + by = q[1] + theta = q[2] + + trust_vector_lenght = u[0] * 10 * self.width / (self.u_ub[0] - self.u_lb[0]) + + delta = u[1] + + ########################### + # Trust vector + ########################### + + pts = arrow_from_tip_angle( trust_vector_lenght , theta , bx , by ) + + + lines_pts.append( pts ) + lines_style.append( '-') + lines_color.append( 'r') + + ########################### + # Control surface + ########################### + + ctl_pts_local = np.array([ [ 0 , 0 , 1 ] , + [ -2*w , 0 , 1 ] ]) + + b_T_c = Transformation_Matrix_2D_from_base_angle( delta , w , 0.5 * w ) + a_T_b = Transformation_Matrix_2D_from_base_angle( theta , bx , by ) + + ctl_pts_global = Transform_2D_Pts( ctl_pts_local , a_T_b @ b_T_c ) + + lines_pts.append( ctl_pts_global ) + + lines_style.append( '-') + lines_color.append( 'b') + + + return lines_pts , lines_style , lines_color + + + + +''' +################################################################# +################## Main ######## +################################################################# +''' + + +if __name__ == "__main__": + """ MAIN TEST """ + + + if True: + + sys = Plane2D() + + sys.x0 = np.array([10,20,0.5,1,1,-0.2]) + + + + def t2u(t): + + u = np.array([ t , -0.2 + t * 0.1 ]) + + return u + + sys.t2u = t2u + #sys.ubar = np.array([ 3 , -0.05 ]) + + sys.compute_trajectory() + + sys.animate_simulation() \ No newline at end of file From a7313d8f2a8539fcb19731dc75c3c2c5fdd49bff Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Tue, 29 Aug 2023 13:49:22 -0400 Subject: [PATCH 03/93] debugin aero forces --- dev/plane/plane.py | 203 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 192 insertions(+), 11 deletions(-) diff --git a/dev/plane/plane.py b/dev/plane/plane.py index 2d823520..4b3c239a 100644 --- a/dev/plane/plane.py +++ b/dev/plane/plane.py @@ -77,6 +77,27 @@ def arrow_from_tip_angle( l , theta , bx , by ): return pts_global +############################################################################### +def arrow_from_components( vx , vy , bx , by ): + + l = np.sqrt( vx**2 + vy**2 ) + d = l * 0.15 # length of arrow secondary lines + + pts_local = np.array([ [ 0 , 0 , 1 ] , + [ l , 0 , 1 ] , + [ l-d , d , 1 ] , + [ l , 0 , 1 ] , + [ l-d , -d , 1 ] ]) + + theta = np.arctan2( vy , vx ) + + T = Transformation_Matrix_2D_from_base_angle( theta , bx , by ) + + pts_global = Transform_2D_Pts( pts_local , T ) + + + return pts_global + @@ -115,10 +136,15 @@ def __init__(self): self.x_lb = np.array([-50,-0,-2,-10,-10,-10]) # Model param - self.mass = 1000 - self.inertia = 100 + self.mass = 1 + self.inertia = 1 self.gravity = 9.8 + # Aero param + self.rho = 1 + self.A = 1 + self.alpha_stall = 0.35 + # Kinematic param @@ -126,6 +152,78 @@ def __init__(self): self.width = 1 self.dynamic_domain = True self.dynamic_range = 10 + self.static_range = 300 + + + ########################################################################### + def compute_velocity_vector(self, q , dq ): + + theta = q[2] + vx = dq[0] + vy = dq[1] + + V = np.sqrt( vx**2 + vy**2 ) # absolute velocity + gamma = np.arctan2( vy , vx ) # velocity vector angle + + alpha = theta - gamma # angle of attack + + return ( V , gamma , alpha ) + + + ########################################################################### + def Cl(self, alpha ): + + m = 4 + + if (alpha < self.alpha_stall ) and (alpha > -self.alpha_stall ): + + Cl = m * alpha + + elif (alpha < 2 * self.alpha_stall ) and (alpha > 0 ): + + Cl = 2 * self.alpha_stall * m - m * alpha + + elif (alpha < 0 ) and (alpha > -2 * self.alpha_stall ): + + Cl = -2 * self.alpha_stall * m - m * alpha + + else: + + Cl = 0 + + return Cl + + ########################################################################### + def Cd(self, alpha ): + + if (alpha < self.alpha_stall ) and (alpha > -self.alpha_stall ): + + Cd = 0.2 * alpha **2 + 0.1 + + else: + + Cd = 0.2 * self.alpha_stall ** 2 + 0.1 + + + return Cd + + ########################################################################### + def Cm(self, alpha , delta ): + + Cm = 0 * alpha + 0 * delta + + return Cm + + ########################################################################### + def compute_aerodynamic_forces( self, V , alpha , delta ): + + rav = 0.5 * self.rho * self.A * V**2 + + L = rav * self.Cl( alpha ) + D = rav * self.Cd( alpha ) + M = rav * self.Cm( alpha , delta ) + + return ( L , D , M ) @@ -179,26 +277,69 @@ def g(self, q ): return g + ########################################################################### + def d(self, q , dq ): + """ + State-dependent dissipative forces : dof x 1 + """ + + V , gamma , alpha = self.compute_velocity_vector( q , dq ) + + L, D, M = self.compute_aerodynamic_forces( V , alpha, 0 ) + + d_wind = np.array([ -D , L , M ]) # aero forces in wind aligned basis + + s = np.sin( gamma ) + c = np.cos( gamma ) + + R = np.array([ [ c , -s , 0 ] , + [ s , c , 0 ] , + [ 0 , 0 , 1 ] ]) + + d = - R @ d_wind # aero forces in global basis + + return d + + ########################################################################### + def B(self, q ): + """ + Actuator Matrix : dof x m + """ + + B = np.zeros((3,2)) + + theta = q[2] + + # TODO PLACE HOLDER + B[0,0] = np.cos( theta ) + B[1,0] = np.sin( theta ) + + return B + + ########################################################################### def forward_kinematic_domain(self, q ): """ """ - l = self.dynamic_range x = q[0] + self.width * 5 y = q[1] + self.width * 1.5 z = 0 if self.dynamic_domain: + + l = self.dynamic_range domain = [ ( -l + x , l + x ) , ( -l + y , l + y ) , ( -l + z , l + z ) ] else: - domain = [ ( -l , l ) , - ( -l , l ) , - ( -l , l ) ]# + l = self.static_range + + domain = [ ( -l * 0.01 , l ) , + ( -l * 0.01 , l ) , + ( -l * 0.01 , l ) ]# return domain @@ -341,10 +482,46 @@ def forward_kinematic_lines_plus(self, x , u , t ): ctl_pts_global = Transform_2D_Pts( ctl_pts_local , a_T_b @ b_T_c ) lines_pts.append( ctl_pts_global ) - lines_style.append( '-') lines_color.append( 'b') + ########################### + # Aero forces + ########################### + + + V , gamma , alpha = self.compute_velocity_vector( q , dq ) + + pts = arrow_from_components(V, 0, 0, 0) + + b_T_w = Transformation_Matrix_2D_from_base_angle( -alpha , 4.5 * w , 0.5 * w ) + + pts_global = Transform_2D_Pts( pts , a_T_b @ b_T_w ) + + lines_pts.append( pts_global ) + lines_style.append('-') + lines_color.append('k') + + L, D, M = self.compute_aerodynamic_forces( V , alpha , 0 ) + + pts = arrow_from_components(0, L, 0, 0) + + b_T_w = Transformation_Matrix_2D_from_base_angle( -alpha , 4.5 * w , 0.5 * w ) + + pts_global = Transform_2D_Pts( pts , a_T_b @ b_T_w ) + + lines_pts.append( pts_global ) + lines_style.append('-') + lines_color.append('c') + + pts = arrow_from_components(-D, 0, 0, 0) + + pts_global = Transform_2D_Pts( pts , a_T_b @ b_T_w ) + + lines_pts.append( pts_global ) + lines_style.append('-') + lines_color.append('r') + return lines_pts , lines_style , lines_color @@ -366,19 +543,23 @@ def forward_kinematic_lines_plus(self, x , u , t ): sys = Plane2D() - sys.x0 = np.array([10,20,0.5,1,1,-0.2]) + sys.x0 = np.array([0,0,0.0,0,0,0.0]) def t2u(t): - u = np.array([ t , -0.2 + t * 0.1 ]) + u = np.array([ 3* t , 0 ]) return u sys.t2u = t2u #sys.ubar = np.array([ 3 , -0.05 ]) - sys.compute_trajectory() + # sys.gravity = 0 + + sys.compute_trajectory( 10 , 20001 , 'euler' ) + sys.plot_trajectory('x') - sys.animate_simulation() \ No newline at end of file + # sys.dynamic_domain = False + sys.animate_simulation( time_factor_video=0.5 ) \ No newline at end of file From 64c45c6ba1ba70b8d9ff64c38ff78954367b9f3c Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Thu, 31 Aug 2023 22:11:28 -0400 Subject: [PATCH 04/93] plane cd curve dev --- dev/plane/plane.py | 117 +++++++++++++++++++++++++++++++++------------ 1 file changed, 86 insertions(+), 31 deletions(-) diff --git a/dev/plane/plane.py b/dev/plane/plane.py index 4b3c239a..005a705e 100644 --- a/dev/plane/plane.py +++ b/dev/plane/plane.py @@ -10,6 +10,7 @@ import numpy as np import matplotlib.pyplot as plt ############################################################################### +from pyro.analysis import graphical from pyro.dynamic import system from pyro.dynamic import mechanical ############################################################################### @@ -136,14 +137,22 @@ def __init__(self): self.x_lb = np.array([-50,-0,-2,-10,-10,-10]) # Model param - self.mass = 1 - self.inertia = 1 + self.mass = 1 # kg + self.inertia = 0.1 # kgm2 self.gravity = 9.8 # Aero param - self.rho = 1 - self.A = 1 - self.alpha_stall = 0.35 + self.rho = 1.29 # air density + self.Sw = 0.1 # wing aera + self.Cd0 = 0.02 # parasite drag + self.AR = 5.0 # aspect ratio + self.e_factor = 0.8 # oswald efficiency factor + + self.Sw_tail = self.Sw * 0.1 + self.l_tail = 0.1 + + + self.alpha_stall = np.pi / 12. # Kinematic param @@ -173,51 +182,95 @@ def compute_velocity_vector(self, q , dq ): ########################################################################### def Cl(self, alpha ): - m = 4 + # Rough fit on + # https://www.aerospaceweb.org/question/airfoils/q0150b.shtml - if (alpha < self.alpha_stall ) and (alpha > -self.alpha_stall ): - - Cl = m * alpha - - elif (alpha < 2 * self.alpha_stall ) and (alpha > 0 ): - - Cl = 2 * self.alpha_stall * m - m * alpha - - elif (alpha < 0 ) and (alpha > -2 * self.alpha_stall ): - - Cl = -2 * self.alpha_stall * m - m * alpha + Cl = np.sin( 2 * alpha ) # falt plate approx - else: + #If not stalled + if (alpha < self.alpha_stall ) and (alpha > -self.alpha_stall ): - Cl = 0 + Cl = Cl + 4 * alpha return Cl + ########################################################################### def Cd(self, alpha ): + Cl = self.Cl( alpha ) + + # Body parasite drag + Cd = self.Cd0 + + # Wing flat plate approx + Cd = Cd + ( 1 - np.cos( 2 * alpha )) + + #If not stalled: add induced drag if (alpha < self.alpha_stall ) and (alpha > -self.alpha_stall ): - Cd = 0.2 * alpha **2 + 0.1 - - else: - - Cd = 0.2 * self.alpha_stall ** 2 + 0.1 + Cd = Cd + Cl **2 / ( np.pi * self.e_factor * self.AR ) return Cd + ############################# + def plot_alpha2Cl(self, alpha_min = -3.15, alpha_max = 3.15 , delta = 0.0 ): + + alphas = np.arange( alpha_min, alpha_max, 0.05 ) + + n = alphas.shape[0] + Cls = np.zeros((n,1)) + Cds = np.zeros((n,1)) + Cms = np.zeros((n,1)) + + for i in range(n): + Cls[i] = self.Cl( alphas[i] ) + Cds[i] = self.Cd( alphas[i] ) + Cms[i] = self.Cm( alphas[i], delta ) + + fig , ax = plt.subplots(3, figsize=graphical.default_figsize, + dpi= graphical.default_dpi, frameon=True) + + fig.canvas.manager.set_window_title('Aero curve') + + ax[0].plot( alphas , Cls , 'b') + ax[0].set_ylabel('Cl', fontsize=graphical.default_fontsize) + ax[0].set_xlabel('alpha', fontsize=graphical.default_fontsize ) + ax[0].tick_params( labelsize = graphical.default_fontsize ) + ax[0].grid(True) + + ax[1].plot( alphas , Cds , 'b') + ax[1].set_ylabel('Cd', fontsize=graphical.default_fontsize) + ax[1].set_xlabel('alpha', fontsize=graphical.default_fontsize ) + ax[1].tick_params( labelsize = graphical.default_fontsize ) + ax[1].grid(True) + + ax[2].plot( alphas , Cms , 'b') + ax[2].set_ylabel('Cm', fontsize=graphical.default_fontsize) + ax[2].set_xlabel('alpha', fontsize=graphical.default_fontsize ) + ax[2].tick_params( labelsize = graphical.default_fontsize ) + ax[2].grid(True) + + fig.tight_layout() + fig.canvas.draw() + + plt.show() + ########################################################################### def Cm(self, alpha , delta ): - Cm = 0 * alpha + 0 * delta + Cl_tail = self.Cl( alpha + delta ) + Cd_tail = self.Cd( alpha + delta ) + + Cm = -( Cl_tail * np.cos( alpha ) + Cd_tail * np.sin( alpha ) ) * self.l_tail * self.Sw_tail / self.Sw return Cm ########################################################################### def compute_aerodynamic_forces( self, V , alpha , delta ): - rav = 0.5 * self.rho * self.A * V**2 + rav = 0.5 * self.rho * self.Sw * V**2 L = rav * self.Cl( alpha ) D = rav * self.Cd( alpha ) @@ -543,23 +596,25 @@ def forward_kinematic_lines_plus(self, x , u , t ): sys = Plane2D() - sys.x0 = np.array([0,0,0.0,0,0,0.0]) + sys.plot_alpha2Cl() + + sys.x0 = np.array([0,0,0.2,10,0,0]) def t2u(t): - u = np.array([ 3* t , 0 ]) + u = np.array([ 10 , 0 ]) return u sys.t2u = t2u #sys.ubar = np.array([ 3 , -0.05 ]) - # sys.gravity = 0 + #sys.gravity = 0 sys.compute_trajectory( 10 , 20001 , 'euler' ) - sys.plot_trajectory('x') + #sys.plot_trajectory('x') - # sys.dynamic_domain = False + #sys.dynamic_domain = False sys.animate_simulation( time_factor_video=0.5 ) \ No newline at end of file From 35519d9001081c9f6e1a715b94d3c17d1651b413 Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Thu, 7 Sep 2023 11:53:07 -0400 Subject: [PATCH 05/93] new aero forces for 2D plane --- dev/plane/plane.py | 283 +++++++++++++++++++++++++------------ pyro/dynamic/mechanical.py | 2 +- 2 files changed, 190 insertions(+), 95 deletions(-) diff --git a/dev/plane/plane.py b/dev/plane/plane.py index 005a705e..3b1079fc 100644 --- a/dev/plane/plane.py +++ b/dev/plane/plane.py @@ -17,13 +17,13 @@ ############################################################################### -def Transformation_Matrix_2D_from_base_angle( theta , bx , by ): +def Transformation_Matrix_2D_from_base_angle( theta , x , y ): s = np.sin( theta ) c = np.cos( theta ) - T = np.array([ [ c , -s , bx ] , - [ s , c , by ] , + T = np.array([ [ c , -s , x ] , + [ s , c , y ] , [ 0 , 0 , 1 ] ]) return T @@ -79,7 +79,7 @@ def arrow_from_tip_angle( l , theta , bx , by ): return pts_global ############################################################################### -def arrow_from_components( vx , vy , bx , by ): +def arrow_from_base_components( vx , vy , bx , by ): l = np.sqrt( vx**2 + vy**2 ) d = l * 0.15 # length of arrow secondary lines @@ -99,6 +99,27 @@ def arrow_from_components( vx , vy , bx , by ): return pts_global +############################################################################### +def arrow_from_tip_components( vx , vy , bx , by ): + + l = np.sqrt( vx**2 + vy**2 ) + d = l * 0.15 # length of arrow secondary lines + + pts_local = np.array([ [ -l , 0 , 1 ] , + [ 0 , 0 , 1 ] , + [ -d , d , 1 ] , + [ 0 , 0 , 1 ] , + [ -d , -d , 1 ] ]) + + theta = np.arctan2( vy , vx ) + + T = Transformation_Matrix_2D_from_base_angle( theta , bx , by ) + + pts_global = Transform_2D_Pts( pts_local , T ) + + + return pts_global + @@ -106,7 +127,7 @@ def arrow_from_components( vx , vy , bx , by ): # 2D planar drone ############################################################################## -class Plane2D( mechanical.MechanicalSystem ): +class Plane2D( mechanical.MechanicalSystemWithPositionInputs ): """ Equations of Motion @@ -133,35 +154,40 @@ def __init__(self): self.output_units = self.state_units # State working range - self.x_ub = np.array([+50,+100,+2,10,10,10]) - self.x_lb = np.array([-50,-0,-2,-10,-10,-10]) + self.x_ub = np.array([+100,+200,+2,30,30,10]) + self.x_lb = np.array([-100,-0,-2,-30,-30,-10]) + + self.u_ub = np.array([+100,+0.3]) + self.u_lb = np.array([-100,-0.3]) # Model param - self.mass = 1 # kg + self.mass = 2.0 # kg self.inertia = 0.1 # kgm2 self.gravity = 9.8 # Aero param self.rho = 1.29 # air density - self.Sw = 0.1 # wing aera - self.Cd0 = 0.02 # parasite drag - self.AR = 5.0 # aspect ratio - self.e_factor = 0.8 # oswald efficiency factor - - self.Sw_tail = self.Sw * 0.1 - self.l_tail = 0.1 - - self.alpha_stall = np.pi / 12. + self.S_w = 0.2 # wing ref. area + self.S_t = 0.05 # tail ref. area + self.l_w = 0.0 # wing a.c. position with respect to c.g., negative is behind c.g. + self.l_t = 1.0 # tail a.c. position with respect to c.g., negative is behind c.g. - # Kinematic param + # we assume same wing profile and geometry for wing and tail + self.Cd0 = 0.02 # parasite drag + self.AR = 5.0 # aspect ratio + self.e_factor = 0.8 # oswald efficiency factor + self.Cm0 = 0.0 # Aero moment coef. ac + self.alpha_stall = np.pi / 12. # Graphic output parameters - self.width = 1 + self.length = 2.0 + self.l_cg = self.length * 0.6 # distance from back of airplane to cg + self.width = self.length / 10.0 self.dynamic_domain = True - self.dynamic_range = 10 - self.static_range = 300 + self.dynamic_range = self.length * 1.0 + self.static_range = self.length * 30 ########################################################################### @@ -214,8 +240,17 @@ def Cd(self, alpha ): return Cd + + ########################################################################### + def Cm(self, alpha ): + + Cm = self.Cm0 + + return Cm + + ############################# - def plot_alpha2Cl(self, alpha_min = -3.15, alpha_max = 3.15 , delta = 0.0 ): + def plot_alpha2Cl(self, alpha_min = -3.15, alpha_max = 3.15 ): alphas = np.arange( alpha_min, alpha_max, 0.05 ) @@ -227,7 +262,7 @@ def plot_alpha2Cl(self, alpha_min = -3.15, alpha_max = 3.15 , delta = 0.0 ): for i in range(n): Cls[i] = self.Cl( alphas[i] ) Cds[i] = self.Cd( alphas[i] ) - Cms[i] = self.Cm( alphas[i], delta ) + Cms[i] = self.Cm( alphas[i] ) fig , ax = plt.subplots(3, figsize=graphical.default_figsize, dpi= graphical.default_dpi, frameon=True) @@ -257,26 +292,33 @@ def plot_alpha2Cl(self, alpha_min = -3.15, alpha_max = 3.15 , delta = 0.0 ): plt.show() + + ########################################################################### - def Cm(self, alpha , delta ): + def compute_aerodynamic_forces( self, V , alpha , delta ): - Cl_tail = self.Cl( alpha + delta ) - Cd_tail = self.Cd( alpha + delta ) + rv2 = 0.5 * self.rho * V**2 - Cm = -( Cl_tail * np.cos( alpha ) + Cd_tail * np.sin( alpha ) ) * self.l_tail * self.Sw_tail / self.Sw + c_w = np.sqrt( self.S_w / self.AR ) + c_t = np.sqrt( self.S_t / self.AR ) - return Cm - - ########################################################################### - def compute_aerodynamic_forces( self, V , alpha , delta ): + Cl_w = self.Cl( alpha ) + Cd_w = self.Cd( alpha ) + Cm_w = self.Cm( alpha ) + + L_w = rv2 * self.S_w * Cl_w + D_w = rv2 * self.S_w * Cd_w + M_w = rv2 * self.S_w * c_w * Cm_w - rav = 0.5 * self.rho * self.Sw * V**2 + Cl_t = self.Cl( alpha + delta ) + Cd_t = self.Cd( alpha + delta ) + Cm_t = self.Cm( alpha + delta ) - L = rav * self.Cl( alpha ) - D = rav * self.Cd( alpha ) - M = rav * self.Cm( alpha , delta ) + L_t = rv2 * self.S_t * Cl_t + D_t = rv2 * self.S_t * Cd_t + M_t = rv2 * self.S_t * c_t * Cm_t - return ( L , D , M ) + return ( L_w , D_w , M_w , L_t , D_t , M_t ) @@ -331,16 +373,33 @@ def g(self, q ): ########################################################################### - def d(self, q , dq ): + def d(self, q , dq , u ): """ State-dependent dissipative forces : dof x 1 """ V , gamma , alpha = self.compute_velocity_vector( q , dq ) - L, D, M = self.compute_aerodynamic_forces( V , alpha, 0 ) - - d_wind = np.array([ -D , L , M ]) # aero forces in wind aligned basis + delta = u[1] + + L_w, D_w, M_w, L_t, D_t, M_t = self.compute_aerodynamic_forces( V , alpha, delta ) + + ########################################################## + # Total aero forces vector at c.g. in wind-aligned basis + ########################################################## + + s = np.sin( alpha ) + c = np.cos( alpha ) + + L = L_w + L_t + D = D_w + D_t + M = M_w + M_t - self.l_w * ( L_w * c + D_w * s ) - self.l_t * ( L_t * c + D_t * s ) + + ########################################################## + # Transformation of aero forces in global inertial basis + ########################################################## + + d_wind = np.array([ -D , L , M ]) s = np.sin( gamma ) c = np.cos( gamma ) @@ -349,17 +408,17 @@ def d(self, q , dq ): [ s , c , 0 ] , [ 0 , 0 , 1 ] ]) - d = - R @ d_wind # aero forces in global basis + d = - R @ d_wind # aero forces in global inertial basis return d ########################################################################### - def B(self, q ): + def B(self, q , u ): """ Actuator Matrix : dof x m """ - B = np.zeros((3,2)) + B = np.zeros((3,1)) theta = q[2] @@ -375,8 +434,8 @@ def forward_kinematic_domain(self, q ): """ """ - x = q[0] + self.width * 5 - y = q[1] + self.width * 1.5 + x = q[0] + y = q[1] z = 0 if self.dynamic_domain: @@ -420,7 +479,7 @@ def forward_kinematic_lines(self, q ): ########################### w = self.width # body width - l = w * 10 # body lenght + l = self.length # body lenght ########################### # Body @@ -428,10 +487,13 @@ def forward_kinematic_lines(self, q ): pts = np.zeros(( 5 , 3 )) - x = q[0] - y = q[1] + x = q[0] + y = q[1] theta = q[2] + world_T_body = Transformation_Matrix_2D_from_base_angle( theta , x , y ) + #body_T_wind = Transformation_Matrix_2D_from_base_angle( -alpha , 0 , 0 ) + body_T_drawing = Transformation_Matrix_2D_from_base_angle( 0 , -self.l_cg , -w/2 ) body_pts_local = np.array([ [ 0 , 0 , 1 ] , [ l , 0 , 1 ] , @@ -440,29 +502,37 @@ def forward_kinematic_lines(self, q ): [ w , 3*w , 1 ] , [ 0 , 3*w , 1 ] , [ 0 , 0 , 1 ] ]) + - T_body = Transformation_Matrix_2D_from_base_angle( theta , x , y ) - - body_pts_global = Transform_2D_Pts( body_pts_local , T_body ) + body_pts_global = Transform_2D_Pts( body_pts_local , world_T_body @ body_T_drawing) lines_pts.append( body_pts_global ) lines_style.append( '-') lines_color.append( 'b') + + cg = np.array([ [ x , y , 1 ] ]) + + lines_pts.append( cg ) + lines_style.append( 'o') + lines_color.append( 'k') + ########################### # Wings ########################### pts = np.zeros(( 2 , 3 )) + + c_w = np.sqrt( self.S_w / self.AR ) - wings_pts_local = np.array([ [ 3*w , 0.5 * w , 1 ] , - [ 6*w , 0.5 * w , 1 ] ]) + wings_pts_body = np.array([ [ -self.l_w + c_w , 0 , 1 ] , + [ -self.l_w - c_w , 0 , 1 ] ]) - wings_pts_global = Transform_2D_Pts( wings_pts_local , T_body ) + wings_pts_world = Transform_2D_Pts( wings_pts_body , world_T_body ) - lines_pts.append( wings_pts_global ) + lines_pts.append( wings_pts_world ) lines_style.append( '-') lines_color.append( 'b') @@ -481,9 +551,6 @@ def forward_kinematic_lines(self, q ): lines_style.append('--') lines_color.append('k') - - - return lines_pts , lines_style , lines_color @@ -499,15 +566,18 @@ def forward_kinematic_lines_plus(self, x , u , t ): lines_style = [] lines_color = [] - w = self.width + #w = self.width q, dq = self.x2q(x) - bx = q[0] - by = q[1] + x = q[0] + y = q[1] theta = q[2] - trust_vector_lenght = u[0] * 10 * self.width / (self.u_ub[0] - self.u_lb[0]) + V , gamma , alpha = self.compute_velocity_vector( q , dq ) + + world_T_body = Transformation_Matrix_2D_from_base_angle( theta , x , y ) + body_T_wind = Transformation_Matrix_2D_from_base_angle( -alpha , 0 , 0 ) delta = u[1] @@ -515,10 +585,18 @@ def forward_kinematic_lines_plus(self, x , u , t ): # Trust vector ########################### - pts = arrow_from_tip_angle( trust_vector_lenght , theta , bx , by ) + # Max trust --> arrow is long as airplane + f_scale = self.length / (self.u_ub[0] - self.u_lb[0]) + trust_vector_lenght = u[0] * f_scale - lines_pts.append( pts ) + #pts = arrow_from_tip_angle( trust_vector_lenght , theta , bx , by ) + + trust_arrow_body = arrow_from_tip_components( trust_vector_lenght, 0, -self.l_cg, 0) + + trust_arrow_world = Transform_2D_Pts( trust_arrow_body , world_T_body ) + + lines_pts.append( trust_arrow_world ) lines_style.append( '-') lines_color.append( 'r') @@ -526,52 +604,69 @@ def forward_kinematic_lines_plus(self, x , u , t ): # Control surface ########################### - ctl_pts_local = np.array([ [ 0 , 0 , 1 ] , - [ -2*w , 0 , 1 ] ]) + c_t = np.sqrt( self.S_t / self.AR ) + + # NOT TO scale to better see the elevator + tail_pts_tail = np.array([ [ 1.0 * c_t , 0 , 1 ] , + [ -1.0 * c_t , 0 , 1 ] ]) - b_T_c = Transformation_Matrix_2D_from_base_angle( delta , w , 0.5 * w ) - a_T_b = Transformation_Matrix_2D_from_base_angle( theta , bx , by ) + body_T_tail = Transformation_Matrix_2D_from_base_angle( delta , - self.l_t , 0 ) - ctl_pts_global = Transform_2D_Pts( ctl_pts_local , a_T_b @ b_T_c ) + tail_pts_global = Transform_2D_Pts( tail_pts_tail , world_T_body @ body_T_tail) - lines_pts.append( ctl_pts_global ) + lines_pts.append( tail_pts_global ) lines_style.append( '-') lines_color.append( 'b') - ########################### - # Aero forces - ########################### - - - V , gamma , alpha = self.compute_velocity_vector( q , dq ) + # ########################### + # # Velocity vector + # ########################### - pts = arrow_from_components(V, 0, 0, 0) + v_length = V * self.length / sys.x_ub[3] + if v_length > self.length: v_length = self.length - b_T_w = Transformation_Matrix_2D_from_base_angle( -alpha , 4.5 * w , 0.5 * w ) + v_pts = arrow_from_base_components( v_length , 0, 0, 0) - pts_global = Transform_2D_Pts( pts , a_T_b @ b_T_w ) + v_world = Transform_2D_Pts( v_pts , world_T_body @ body_T_wind ) - lines_pts.append( pts_global ) + lines_pts.append( v_world ) lines_style.append('-') lines_color.append('k') - L, D, M = self.compute_aerodynamic_forces( V , alpha , 0 ) + # ########################### + # # Aero forces + # ########################### + + + L_w, D_w, M_w, L_t, D_t, M_t = self.compute_aerodynamic_forces( V , alpha , delta ) - pts = arrow_from_components(0, L, 0, 0) + L_w_pts = arrow_from_base_components(0, L_w * f_scale, 0, 0) + D_w_pts = arrow_from_base_components(-D_w * f_scale, 0, 0, 0) + L_t_pts = arrow_from_base_components(0, L_t * f_scale, 0, 0) + D_t_pts = arrow_from_base_components(-D_t * f_scale, 0, 0, 0) - b_T_w = Transformation_Matrix_2D_from_base_angle( -alpha , 4.5 * w , 0.5 * w ) + body_T_acw = Transformation_Matrix_2D_from_base_angle( 0 , -self.l_w , 0 ) + body_T_act = Transformation_Matrix_2D_from_base_angle( 0 , -self.l_t , 0 ) - pts_global = Transform_2D_Pts( pts , a_T_b @ b_T_w ) + L_w_pts_global = Transform_2D_Pts( L_w_pts , world_T_body @ body_T_acw @ body_T_wind ) + D_w_pts_global = Transform_2D_Pts( D_w_pts , world_T_body @ body_T_acw @ body_T_wind ) - lines_pts.append( pts_global ) + lines_pts.append( L_w_pts_global ) lines_style.append('-') - lines_color.append('c') + lines_color.append('b') - pts = arrow_from_components(-D, 0, 0, 0) + lines_pts.append( D_w_pts_global ) + lines_style.append('-') + lines_color.append('r') - pts_global = Transform_2D_Pts( pts , a_T_b @ b_T_w ) + L_t_pts_global = Transform_2D_Pts( L_t_pts , world_T_body @ body_T_act @ body_T_wind ) + D_t_pts_global = Transform_2D_Pts( D_t_pts , world_T_body @ body_T_act @ body_T_wind ) + + lines_pts.append( L_t_pts_global ) + lines_style.append('-') + lines_color.append('b') - lines_pts.append( pts_global ) + lines_pts.append( D_t_pts_global ) lines_style.append('-') lines_color.append('r') @@ -596,15 +691,15 @@ def forward_kinematic_lines_plus(self, x , u , t ): sys = Plane2D() - sys.plot_alpha2Cl() + #sys.plot_alpha2Cl() - sys.x0 = np.array([0,0,0.2,10,0,0]) + sys.x0 = np.array([0,0,0.0,30,0,0]) def t2u(t): - u = np.array([ 10 , 0 ]) + u = np.array([ 20 , -0.01 ]) return u @@ -613,8 +708,8 @@ def t2u(t): #sys.gravity = 0 - sys.compute_trajectory( 10 , 20001 , 'euler' ) - #sys.plot_trajectory('x') + sys.compute_trajectory( 10 , 10001 , 'euler' ) + sys.plot_trajectory('x') #sys.dynamic_domain = False sys.animate_simulation( time_factor_video=0.5 ) \ No newline at end of file diff --git a/pyro/dynamic/mechanical.py b/pyro/dynamic/mechanical.py index 10fd8896..687b3b7c 100644 --- a/pyro/dynamic/mechanical.py +++ b/pyro/dynamic/mechanical.py @@ -422,7 +422,7 @@ def ddq(self, q , dq , u , t = 0 ): H = self.H( q ) C = self.C( q , dq ) g = self.g( q ) - d = self.d( q , dq) + d = self.d( q , dq, u ) B = self.B( q , u ) e = self.u2e( u ) From 51af56df4c61ec92fc5160c2ad61d0b3e765cbd9 Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Thu, 7 Sep 2023 12:19:24 -0400 Subject: [PATCH 06/93] sim with trim and stall --- dev/plane/plane.py | 50 ++++++++++++++++++++++++++++++---------------- 1 file changed, 33 insertions(+), 17 deletions(-) diff --git a/dev/plane/plane.py b/dev/plane/plane.py index 3b1079fc..a3d109ab 100644 --- a/dev/plane/plane.py +++ b/dev/plane/plane.py @@ -157,8 +157,8 @@ def __init__(self): self.x_ub = np.array([+100,+200,+2,30,30,10]) self.x_lb = np.array([-100,-0,-2,-30,-30,-10]) - self.u_ub = np.array([+100,+0.3]) - self.u_lb = np.array([-100,-0.3]) + self.u_ub = np.array([+10,+0.3]) + self.u_lb = np.array([-10,-0.3]) # Model param self.mass = 2.0 # kg @@ -510,12 +510,13 @@ def forward_kinematic_lines(self, q ): lines_style.append( '-') lines_color.append( 'b') - - cg = np.array([ [ x , y , 1 ] ]) - - lines_pts.append( cg ) - lines_style.append( 'o') - lines_color.append( 'k') + if self.dynamic_domain : + + cg = np.array([ [ x , y , 1 ] ]) + + lines_pts.append( cg ) + lines_style.append( 'o') + lines_color.append( 'k') ########################### # Wings @@ -651,24 +652,40 @@ def forward_kinematic_lines_plus(self, x , u , t ): L_w_pts_global = Transform_2D_Pts( L_w_pts , world_T_body @ body_T_acw @ body_T_wind ) D_w_pts_global = Transform_2D_Pts( D_w_pts , world_T_body @ body_T_acw @ body_T_wind ) + #Change color if stalled: + if (alpha < self.alpha_stall ) and (alpha > -self.alpha_stall ): + L_color = 'b' + D_color = 'r' + else: + L_color = 'c' + D_color = 'm' + lines_pts.append( L_w_pts_global ) lines_style.append('-') - lines_color.append('b') + lines_color.append( L_color ) lines_pts.append( D_w_pts_global ) lines_style.append('-') - lines_color.append('r') + lines_color.append( D_color ) L_t_pts_global = Transform_2D_Pts( L_t_pts , world_T_body @ body_T_act @ body_T_wind ) D_t_pts_global = Transform_2D_Pts( D_t_pts , world_T_body @ body_T_act @ body_T_wind ) + #Change color if stalled: + if (( alpha + delta ) < self.alpha_stall ) and ( ( alpha + delta ) > -self.alpha_stall ): + L_color = 'b' + D_color = 'r' + else: + L_color = 'c' + D_color = 'm' + lines_pts.append( L_t_pts_global ) lines_style.append('-') - lines_color.append('b') + lines_color.append( L_color ) lines_pts.append( D_t_pts_global ) lines_style.append('-') - lines_color.append('r') + lines_color.append( D_color ) return lines_pts , lines_style , lines_color @@ -693,23 +710,22 @@ def forward_kinematic_lines_plus(self, x , u , t ): #sys.plot_alpha2Cl() - sys.x0 = np.array([0,0,0.0,30,0,0]) + sys.x0 = np.array([0,0,0.2,15,0,0]) def t2u(t): - u = np.array([ 20 , -0.01 ]) + u = np.array([ 2 * t , -0.12 * t ]) return u sys.t2u = t2u - #sys.ubar = np.array([ 3 , -0.05 ]) #sys.gravity = 0 - sys.compute_trajectory( 10 , 10001 , 'euler' ) + sys.compute_trajectory( 10 , 20001 , 'euler' ) sys.plot_trajectory('x') - #sys.dynamic_domain = False + # sys.dynamic_domain = False sys.animate_simulation( time_factor_video=0.5 ) \ No newline at end of file From 9caca231a6743d65b948022168159443f0064647 Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Fri, 8 Sep 2023 10:54:21 -0400 Subject: [PATCH 07/93] moved plane to pyro core and new test traj opt --- dev/plane/plane.py | 731 --------------------- dev/plane/plane_trajectory_optimisation.py | 48 ++ pyro/dynamic/plane.py | 630 +++++++++++++++++- 3 files changed, 670 insertions(+), 739 deletions(-) delete mode 100644 dev/plane/plane.py create mode 100644 dev/plane/plane_trajectory_optimisation.py diff --git a/dev/plane/plane.py b/dev/plane/plane.py deleted file mode 100644 index a3d109ab..00000000 --- a/dev/plane/plane.py +++ /dev/null @@ -1,731 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Created on Tue Jun 20 10:29:50 2023 - -@author: alex -""" - -############################################################################### -import numpy as np -import matplotlib.pyplot as plt -############################################################################### -from pyro.analysis import graphical -from pyro.dynamic import system -from pyro.dynamic import mechanical -############################################################################### - - -############################################################################### -def Transformation_Matrix_2D_from_base_angle( theta , x , y ): - - s = np.sin( theta ) - c = np.cos( theta ) - - T = np.array([ [ c , -s , x ] , - [ s , c , y ] , - [ 0 , 0 , 1 ] ]) - - return T - - -############################################################################### -def Transform_2D_Pts( pts , T ): - - pts_transformed = np.zeros( pts.shape ) - - for i in range(pts.shape[0]): - - pts_transformed[ i ] = T @ pts[ i ] - - return pts_transformed - - -############################################################################### -def arrow_from_base_angle( l , theta , bx , by ): - - d = l * 0.15 # length of arrow secondary lines - - pts_local = np.array([ [ 0 , 0 , 1 ] , - [ l , 0 , 1 ] , - [ l-d , d , 1 ] , - [ l , 0 , 1 ] , - [ l-d , -d , 1 ] ]) - - T = Transformation_Matrix_2D_from_base_angle( theta , bx , by ) - - pts_global = Transform_2D_Pts( pts_local , T ) - - - return pts_global - - -############################################################################### -def arrow_from_tip_angle( l , theta , bx , by ): - - d = l * 0.15 # length of arrow secondary lines - - pts_local = np.array([ [ -l , 0 , 1 ] , - [ 0 , 0 , 1 ] , - [ -d , d , 1 ] , - [ 0 , 0 , 1 ] , - [ -d , -d , 1 ] ]) - - T = Transformation_Matrix_2D_from_base_angle( theta , bx , by ) - - pts_global = Transform_2D_Pts( pts_local , T ) - - - return pts_global - -############################################################################### -def arrow_from_base_components( vx , vy , bx , by ): - - l = np.sqrt( vx**2 + vy**2 ) - d = l * 0.15 # length of arrow secondary lines - - pts_local = np.array([ [ 0 , 0 , 1 ] , - [ l , 0 , 1 ] , - [ l-d , d , 1 ] , - [ l , 0 , 1 ] , - [ l-d , -d , 1 ] ]) - - theta = np.arctan2( vy , vx ) - - T = Transformation_Matrix_2D_from_base_angle( theta , bx , by ) - - pts_global = Transform_2D_Pts( pts_local , T ) - - - return pts_global - -############################################################################### -def arrow_from_tip_components( vx , vy , bx , by ): - - l = np.sqrt( vx**2 + vy**2 ) - d = l * 0.15 # length of arrow secondary lines - - pts_local = np.array([ [ -l , 0 , 1 ] , - [ 0 , 0 , 1 ] , - [ -d , d , 1 ] , - [ 0 , 0 , 1 ] , - [ -d , -d , 1 ] ]) - - theta = np.arctan2( vy , vx ) - - T = Transformation_Matrix_2D_from_base_angle( theta , bx , by ) - - pts_global = Transform_2D_Pts( pts_local , T ) - - - return pts_global - - - - -############################################################################## -# 2D planar drone -############################################################################## - -class Plane2D( mechanical.MechanicalSystemWithPositionInputs ): - - """ - Equations of Motion - ------------------------- - - """ - - ############################ - def __init__(self): - """ """ - - # initialize standard params - mechanical.MechanicalSystemWithPositionInputs.__init__( self, 3 , 1 , 1 ) - - # Labels - self.name = '2D plane model' - self.state_label = ['x','y','theta','vx','vy','w'] - self.input_label = ['Trust', 'delta'] - self.output_label = self.state_label - - # Units - self.state_units = ['[m]','[m]','[rad]','[m/sec]','[m/sec]','[rad/sec]'] - self.input_units = ['[N]', '[Rad]'] - self.output_units = self.state_units - - # State working range - self.x_ub = np.array([+100,+200,+2,30,30,10]) - self.x_lb = np.array([-100,-0,-2,-30,-30,-10]) - - self.u_ub = np.array([+10,+0.3]) - self.u_lb = np.array([-10,-0.3]) - - # Model param - self.mass = 2.0 # kg - self.inertia = 0.1 # kgm2 - self.gravity = 9.8 - - # Aero param - self.rho = 1.29 # air density - - self.S_w = 0.2 # wing ref. area - self.S_t = 0.05 # tail ref. area - self.l_w = 0.0 # wing a.c. position with respect to c.g., negative is behind c.g. - self.l_t = 1.0 # tail a.c. position with respect to c.g., negative is behind c.g. - - # we assume same wing profile and geometry for wing and tail - self.Cd0 = 0.02 # parasite drag - self.AR = 5.0 # aspect ratio - self.e_factor = 0.8 # oswald efficiency factor - self.Cm0 = 0.0 # Aero moment coef. ac - self.alpha_stall = np.pi / 12. - - - # Graphic output parameters - self.length = 2.0 - self.l_cg = self.length * 0.6 # distance from back of airplane to cg - self.width = self.length / 10.0 - self.dynamic_domain = True - self.dynamic_range = self.length * 1.0 - self.static_range = self.length * 30 - - - ########################################################################### - def compute_velocity_vector(self, q , dq ): - - theta = q[2] - vx = dq[0] - vy = dq[1] - - V = np.sqrt( vx**2 + vy**2 ) # absolute velocity - gamma = np.arctan2( vy , vx ) # velocity vector angle - - alpha = theta - gamma # angle of attack - - return ( V , gamma , alpha ) - - - ########################################################################### - def Cl(self, alpha ): - - # Rough fit on - # https://www.aerospaceweb.org/question/airfoils/q0150b.shtml - - Cl = np.sin( 2 * alpha ) # falt plate approx - - #If not stalled - if (alpha < self.alpha_stall ) and (alpha > -self.alpha_stall ): - - Cl = Cl + 4 * alpha - - return Cl - - - ########################################################################### - def Cd(self, alpha ): - - Cl = self.Cl( alpha ) - - # Body parasite drag - Cd = self.Cd0 - - # Wing flat plate approx - Cd = Cd + ( 1 - np.cos( 2 * alpha )) - - #If not stalled: add induced drag - if (alpha < self.alpha_stall ) and (alpha > -self.alpha_stall ): - - Cd = Cd + Cl **2 / ( np.pi * self.e_factor * self.AR ) - - - return Cd - - - ########################################################################### - def Cm(self, alpha ): - - Cm = self.Cm0 - - return Cm - - - ############################# - def plot_alpha2Cl(self, alpha_min = -3.15, alpha_max = 3.15 ): - - alphas = np.arange( alpha_min, alpha_max, 0.05 ) - - n = alphas.shape[0] - Cls = np.zeros((n,1)) - Cds = np.zeros((n,1)) - Cms = np.zeros((n,1)) - - for i in range(n): - Cls[i] = self.Cl( alphas[i] ) - Cds[i] = self.Cd( alphas[i] ) - Cms[i] = self.Cm( alphas[i] ) - - fig , ax = plt.subplots(3, figsize=graphical.default_figsize, - dpi= graphical.default_dpi, frameon=True) - - fig.canvas.manager.set_window_title('Aero curve') - - ax[0].plot( alphas , Cls , 'b') - ax[0].set_ylabel('Cl', fontsize=graphical.default_fontsize) - ax[0].set_xlabel('alpha', fontsize=graphical.default_fontsize ) - ax[0].tick_params( labelsize = graphical.default_fontsize ) - ax[0].grid(True) - - ax[1].plot( alphas , Cds , 'b') - ax[1].set_ylabel('Cd', fontsize=graphical.default_fontsize) - ax[1].set_xlabel('alpha', fontsize=graphical.default_fontsize ) - ax[1].tick_params( labelsize = graphical.default_fontsize ) - ax[1].grid(True) - - ax[2].plot( alphas , Cms , 'b') - ax[2].set_ylabel('Cm', fontsize=graphical.default_fontsize) - ax[2].set_xlabel('alpha', fontsize=graphical.default_fontsize ) - ax[2].tick_params( labelsize = graphical.default_fontsize ) - ax[2].grid(True) - - fig.tight_layout() - fig.canvas.draw() - - plt.show() - - - - ########################################################################### - def compute_aerodynamic_forces( self, V , alpha , delta ): - - rv2 = 0.5 * self.rho * V**2 - - c_w = np.sqrt( self.S_w / self.AR ) - c_t = np.sqrt( self.S_t / self.AR ) - - Cl_w = self.Cl( alpha ) - Cd_w = self.Cd( alpha ) - Cm_w = self.Cm( alpha ) - - L_w = rv2 * self.S_w * Cl_w - D_w = rv2 * self.S_w * Cd_w - M_w = rv2 * self.S_w * c_w * Cm_w - - Cl_t = self.Cl( alpha + delta ) - Cd_t = self.Cd( alpha + delta ) - Cm_t = self.Cm( alpha + delta ) - - L_t = rv2 * self.S_t * Cl_t - D_t = rv2 * self.S_t * Cd_t - M_t = rv2 * self.S_t * c_t * Cm_t - - return ( L_w , D_w , M_w , L_t , D_t , M_t ) - - - - ########################################################################### - def H(self, q ): - """ - Inertia matrix - ---------------------------------- - dim( H ) = ( dof , dof ) - - such that --> Kinetic Energy = 0.5 * dq^T * H(q) * dq - - """ - - H = np.zeros((3,3)) - - H[0,0] = self.mass - H[1,1] = self.mass - H[2,2] = self.inertia - - return H - - - ########################################################################### - def C(self, q , dq ): - """ - Corriolis and Centrifugal Matrix - ------------------------------------ - dim( C ) = ( dof , dof ) - - such that --> d H / dt = C + C^T - - - """ - - C = np.zeros((3,3)) - - return C - - - ########################################################################### - def g(self, q ): - """ - Gravitationnal forces vector : dof x 1 - """ - - g = np.zeros(3) - - g[1] = self.mass * self.gravity - - return g - - - ########################################################################### - def d(self, q , dq , u ): - """ - State-dependent dissipative forces : dof x 1 - """ - - V , gamma , alpha = self.compute_velocity_vector( q , dq ) - - delta = u[1] - - L_w, D_w, M_w, L_t, D_t, M_t = self.compute_aerodynamic_forces( V , alpha, delta ) - - ########################################################## - # Total aero forces vector at c.g. in wind-aligned basis - ########################################################## - - s = np.sin( alpha ) - c = np.cos( alpha ) - - L = L_w + L_t - D = D_w + D_t - M = M_w + M_t - self.l_w * ( L_w * c + D_w * s ) - self.l_t * ( L_t * c + D_t * s ) - - ########################################################## - # Transformation of aero forces in global inertial basis - ########################################################## - - d_wind = np.array([ -D , L , M ]) - - s = np.sin( gamma ) - c = np.cos( gamma ) - - R = np.array([ [ c , -s , 0 ] , - [ s , c , 0 ] , - [ 0 , 0 , 1 ] ]) - - d = - R @ d_wind # aero forces in global inertial basis - - return d - - ########################################################################### - def B(self, q , u ): - """ - Actuator Matrix : dof x m - """ - - B = np.zeros((3,1)) - - theta = q[2] - - # TODO PLACE HOLDER - B[0,0] = np.cos( theta ) - B[1,0] = np.sin( theta ) - - return B - - - ########################################################################### - def forward_kinematic_domain(self, q ): - """ - """ - - x = q[0] - y = q[1] - z = 0 - - if self.dynamic_domain: - - l = self.dynamic_range - - domain = [ ( -l + x , l + x ) , - ( -l + y , l + y ) , - ( -l + z , l + z ) ] - else: - - l = self.static_range - - domain = [ ( -l * 0.01 , l ) , - ( -l * 0.01 , l ) , - ( -l * 0.01 , l ) ]# - - - return domain - - - ########################################################################### - def forward_kinematic_lines(self, q ): - """ - Compute points p = [x;y;z] positions given config q - ---------------------------------------------------- - - points of interest for ploting - - Outpus: - lines_pts = [] : a list of array (n_pts x 3) for each lines - - """ - - lines_pts = [] # list of array (n_pts x 3) for each lines - lines_style = [] - lines_color = [] - - - ########################### - # Dimensions - ########################### - - w = self.width # body width - l = self.length # body lenght - - ########################### - # Body - ########################### - - pts = np.zeros(( 5 , 3 )) - - x = q[0] - y = q[1] - theta = q[2] - - world_T_body = Transformation_Matrix_2D_from_base_angle( theta , x , y ) - #body_T_wind = Transformation_Matrix_2D_from_base_angle( -alpha , 0 , 0 ) - body_T_drawing = Transformation_Matrix_2D_from_base_angle( 0 , -self.l_cg , -w/2 ) - - body_pts_local = np.array([ [ 0 , 0 , 1 ] , - [ l , 0 , 1 ] , - [ l-w , w , 1 ] , - [ 2*w , w , 1 ] , - [ w , 3*w , 1 ] , - [ 0 , 3*w , 1 ] , - [ 0 , 0 , 1 ] ]) - - - body_pts_global = Transform_2D_Pts( body_pts_local , world_T_body @ body_T_drawing) - - lines_pts.append( body_pts_global ) - lines_style.append( '-') - lines_color.append( 'b') - - if self.dynamic_domain : - - cg = np.array([ [ x , y , 1 ] ]) - - lines_pts.append( cg ) - lines_style.append( 'o') - lines_color.append( 'k') - - ########################### - # Wings - ########################### - - pts = np.zeros(( 2 , 3 )) - - c_w = np.sqrt( self.S_w / self.AR ) - - - wings_pts_body = np.array([ [ -self.l_w + c_w , 0 , 1 ] , - [ -self.l_w - c_w , 0 , 1 ] ]) - - - wings_pts_world = Transform_2D_Pts( wings_pts_body , world_T_body ) - - lines_pts.append( wings_pts_world ) - lines_style.append( '-') - lines_color.append( 'b') - - ########################### - # bottom line - ########################### - - pts = np.zeros((2,3)) - - pts[0,0] = -10000 - pts[1,0] = 10000 - pts[0,1] = 0 - pts[1,1] = 0 - - lines_pts.append( pts ) - lines_style.append('--') - lines_color.append('k') - - - return lines_pts , lines_style , lines_color - - - ########################################################################### - def forward_kinematic_lines_plus(self, x , u , t ): - """ - plots the force vector - - """ - - lines_pts = [] # list of array (n_pts x 3) for each lines - lines_style = [] - lines_color = [] - - #w = self.width - - q, dq = self.x2q(x) - - x = q[0] - y = q[1] - theta = q[2] - - V , gamma , alpha = self.compute_velocity_vector( q , dq ) - - world_T_body = Transformation_Matrix_2D_from_base_angle( theta , x , y ) - body_T_wind = Transformation_Matrix_2D_from_base_angle( -alpha , 0 , 0 ) - - delta = u[1] - - ########################### - # Trust vector - ########################### - - # Max trust --> arrow is long as airplane - f_scale = self.length / (self.u_ub[0] - self.u_lb[0]) - - trust_vector_lenght = u[0] * f_scale - - #pts = arrow_from_tip_angle( trust_vector_lenght , theta , bx , by ) - - trust_arrow_body = arrow_from_tip_components( trust_vector_lenght, 0, -self.l_cg, 0) - - trust_arrow_world = Transform_2D_Pts( trust_arrow_body , world_T_body ) - - lines_pts.append( trust_arrow_world ) - lines_style.append( '-') - lines_color.append( 'r') - - ########################### - # Control surface - ########################### - - c_t = np.sqrt( self.S_t / self.AR ) - - # NOT TO scale to better see the elevator - tail_pts_tail = np.array([ [ 1.0 * c_t , 0 , 1 ] , - [ -1.0 * c_t , 0 , 1 ] ]) - - body_T_tail = Transformation_Matrix_2D_from_base_angle( delta , - self.l_t , 0 ) - - tail_pts_global = Transform_2D_Pts( tail_pts_tail , world_T_body @ body_T_tail) - - lines_pts.append( tail_pts_global ) - lines_style.append( '-') - lines_color.append( 'b') - - # ########################### - # # Velocity vector - # ########################### - - v_length = V * self.length / sys.x_ub[3] - if v_length > self.length: v_length = self.length - - v_pts = arrow_from_base_components( v_length , 0, 0, 0) - - v_world = Transform_2D_Pts( v_pts , world_T_body @ body_T_wind ) - - lines_pts.append( v_world ) - lines_style.append('-') - lines_color.append('k') - - # ########################### - # # Aero forces - # ########################### - - - L_w, D_w, M_w, L_t, D_t, M_t = self.compute_aerodynamic_forces( V , alpha , delta ) - - L_w_pts = arrow_from_base_components(0, L_w * f_scale, 0, 0) - D_w_pts = arrow_from_base_components(-D_w * f_scale, 0, 0, 0) - L_t_pts = arrow_from_base_components(0, L_t * f_scale, 0, 0) - D_t_pts = arrow_from_base_components(-D_t * f_scale, 0, 0, 0) - - body_T_acw = Transformation_Matrix_2D_from_base_angle( 0 , -self.l_w , 0 ) - body_T_act = Transformation_Matrix_2D_from_base_angle( 0 , -self.l_t , 0 ) - - L_w_pts_global = Transform_2D_Pts( L_w_pts , world_T_body @ body_T_acw @ body_T_wind ) - D_w_pts_global = Transform_2D_Pts( D_w_pts , world_T_body @ body_T_acw @ body_T_wind ) - - #Change color if stalled: - if (alpha < self.alpha_stall ) and (alpha > -self.alpha_stall ): - L_color = 'b' - D_color = 'r' - else: - L_color = 'c' - D_color = 'm' - - lines_pts.append( L_w_pts_global ) - lines_style.append('-') - lines_color.append( L_color ) - - lines_pts.append( D_w_pts_global ) - lines_style.append('-') - lines_color.append( D_color ) - - L_t_pts_global = Transform_2D_Pts( L_t_pts , world_T_body @ body_T_act @ body_T_wind ) - D_t_pts_global = Transform_2D_Pts( D_t_pts , world_T_body @ body_T_act @ body_T_wind ) - - #Change color if stalled: - if (( alpha + delta ) < self.alpha_stall ) and ( ( alpha + delta ) > -self.alpha_stall ): - L_color = 'b' - D_color = 'r' - else: - L_color = 'c' - D_color = 'm' - - lines_pts.append( L_t_pts_global ) - lines_style.append('-') - lines_color.append( L_color ) - - lines_pts.append( D_t_pts_global ) - lines_style.append('-') - lines_color.append( D_color ) - - - return lines_pts , lines_style , lines_color - - - - -''' -################################################################# -################## Main ######## -################################################################# -''' - - -if __name__ == "__main__": - """ MAIN TEST """ - - - if True: - - sys = Plane2D() - - #sys.plot_alpha2Cl() - - sys.x0 = np.array([0,0,0.2,15,0,0]) - - - - def t2u(t): - - u = np.array([ 2 * t , -0.12 * t ]) - - return u - - sys.t2u = t2u - - #sys.gravity = 0 - - sys.compute_trajectory( 10 , 20001 , 'euler' ) - sys.plot_trajectory('x') - - # sys.dynamic_domain = False - sys.animate_simulation( time_factor_video=0.5 ) \ No newline at end of file diff --git a/dev/plane/plane_trajectory_optimisation.py b/dev/plane/plane_trajectory_optimisation.py new file mode 100644 index 00000000..973d7138 --- /dev/null +++ b/dev/plane/plane_trajectory_optimisation.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Created on Fri Sep 8 10:38:30 2023 + +@author: alex +""" + +import numpy as np + +from pyro.dynamic import plane + +from pyro.planning.trajectoryoptimisation import DirectCollocationTrajectoryOptimisation + +sys = plane.Plane2D() + +sys.x0 = np.array([0,0,0,20,0,0]) + + + +def t2u(t): + + u = np.array([ 10 , -0.2 ]) + + return u + +sys.t2u = t2u + +#sys.gravity = 0 + +sys.compute_trajectory( 2 , 2001 , 'euler' ) +# sys.plot_trajectory('x') +# sys.animate_simulation( time_factor_video=0.5 ) + +planner = DirectCollocationTrajectoryOptimisation( sys ) + +planner.x_start = sys.x0 +planner.x_goal = np.array([25,20,1.6,0,10,0]) + +planner.grid = 20 +planner.dt = 0.1 +planner.maxiter = 200 + +planner.set_initial_trajectory_guest( sys.traj ) + +planner.compute_optimal_trajectory() +planner.show_solution() +planner.animate_solution() \ No newline at end of file diff --git a/pyro/dynamic/plane.py b/pyro/dynamic/plane.py index 43a31d07..5442ec11 100644 --- a/pyro/dynamic/plane.py +++ b/pyro/dynamic/plane.py @@ -10,18 +10,124 @@ import numpy as np import matplotlib.pyplot as plt ############################################################################### +from pyro.analysis import graphical from pyro.dynamic import system from pyro.dynamic import mechanical ############################################################################### +############################################################################### +def Transformation_Matrix_2D_from_base_angle( theta , x , y ): + + s = np.sin( theta ) + c = np.cos( theta ) + + T = np.array([ [ c , -s , x ] , + [ s , c , y ] , + [ 0 , 0 , 1 ] ]) + + return T + + +############################################################################### +def Transform_2D_Pts( pts , T ): + + pts_transformed = np.zeros( pts.shape ) + + for i in range(pts.shape[0]): + + pts_transformed[ i ] = T @ pts[ i ] + + return pts_transformed + + +############################################################################### +def arrow_from_base_angle( l , theta , bx , by ): + + d = l * 0.15 # length of arrow secondary lines + + pts_local = np.array([ [ 0 , 0 , 1 ] , + [ l , 0 , 1 ] , + [ l-d , d , 1 ] , + [ l , 0 , 1 ] , + [ l-d , -d , 1 ] ]) + + T = Transformation_Matrix_2D_from_base_angle( theta , bx , by ) + + pts_global = Transform_2D_Pts( pts_local , T ) + + + return pts_global + + +############################################################################### +def arrow_from_tip_angle( l , theta , bx , by ): + + d = l * 0.15 # length of arrow secondary lines + + pts_local = np.array([ [ -l , 0 , 1 ] , + [ 0 , 0 , 1 ] , + [ -d , d , 1 ] , + [ 0 , 0 , 1 ] , + [ -d , -d , 1 ] ]) + + T = Transformation_Matrix_2D_from_base_angle( theta , bx , by ) + + pts_global = Transform_2D_Pts( pts_local , T ) + + + return pts_global + +############################################################################### +def arrow_from_base_components( vx , vy , bx , by ): + + l = np.sqrt( vx**2 + vy**2 ) + d = l * 0.15 # length of arrow secondary lines + + pts_local = np.array([ [ 0 , 0 , 1 ] , + [ l , 0 , 1 ] , + [ l-d , d , 1 ] , + [ l , 0 , 1 ] , + [ l-d , -d , 1 ] ]) + + theta = np.arctan2( vy , vx ) + + T = Transformation_Matrix_2D_from_base_angle( theta , bx , by ) + + pts_global = Transform_2D_Pts( pts_local , T ) + + + return pts_global + +############################################################################### +def arrow_from_tip_components( vx , vy , bx , by ): + + l = np.sqrt( vx**2 + vy**2 ) + d = l * 0.15 # length of arrow secondary lines + + pts_local = np.array([ [ -l , 0 , 1 ] , + [ 0 , 0 , 1 ] , + [ -d , d , 1 ] , + [ 0 , 0 , 1 ] , + [ -d , -d , 1 ] ]) + + theta = np.arctan2( vy , vx ) + + T = Transformation_Matrix_2D_from_base_angle( theta , bx , by ) + + pts_global = Transform_2D_Pts( pts_local , T ) + + + return pts_global + + ############################################################################## # 2D planar drone ############################################################################## -class Plane2D( mechanical.MechanicalSystem ): +class Plane2D( mechanical.MechanicalSystemWithPositionInputs ): """ Equations of Motion @@ -48,20 +154,171 @@ def __init__(self): self.output_units = self.state_units # State working range - self.x_ub = np.array([+50,+100,+2,10,10,10]) - self.x_lb = np.array([-50,-0,-2,-10,-10,-10]) + self.x_ub = np.array([+100,+200,+2,30,30,10]) + self.x_lb = np.array([-100,-0,-2,-30,-30,-10]) + + self.u_ub = np.array([+10,+0.3]) + self.u_lb = np.array([ 0, -0.3]) # Model param - self.mass = 1000 - self.inertia = 100 + self.mass = 2.0 # kg + self.inertia = 0.1 # kgm2 self.gravity = 9.8 - # Kinematic param + # Aero param + self.rho = 1.29 # air density + + self.S_w = 0.2 # wing ref. area + self.S_t = 0.05 # tail ref. area + self.l_w = 0.0 # wing a.c. position with respect to c.g., negative is behind c.g. + self.l_t = 1.0 # tail a.c. position with respect to c.g., negative is behind c.g. + + # we assume same wing profile and geometry for wing and tail + self.Cd0 = 0.02 # parasite drag + self.AR = 5.0 # aspect ratio + self.e_factor = 0.8 # oswald efficiency factor + self.Cm0 = 0.0 # Aero moment coef. ac + self.alpha_stall = np.pi / 12. # Graphic output parameters + self.length = 2.0 + self.l_cg = self.length * 0.6 # distance from back of airplane to cg + self.width = self.length / 10.0 self.dynamic_domain = True - self.dynamic_range = 10 + self.dynamic_range = self.length * 1.0 + self.static_range = self.length * 30 + + + ########################################################################### + def compute_velocity_vector(self, q , dq ): + + theta = q[2] + vx = dq[0] + vy = dq[1] + + V = np.sqrt( vx**2 + vy**2 ) # absolute velocity + gamma = np.arctan2( vy , vx ) # velocity vector angle + + alpha = theta - gamma # angle of attack + + return ( V , gamma , alpha ) + + + ########################################################################### + def Cl(self, alpha ): + + # Rough fit on + # https://www.aerospaceweb.org/question/airfoils/q0150b.shtml + + Cl = np.sin( 2 * alpha ) # falt plate approx + + #If not stalled + if (alpha < self.alpha_stall ) and (alpha > -self.alpha_stall ): + + Cl = Cl + 4 * alpha + + return Cl + + + ########################################################################### + def Cd(self, alpha ): + + Cl = self.Cl( alpha ) + + # Body parasite drag + Cd = self.Cd0 + + # Wing flat plate approx + Cd = Cd + ( 1 - np.cos( 2 * alpha )) + + #If not stalled: add induced drag + if (alpha < self.alpha_stall ) and (alpha > -self.alpha_stall ): + + Cd = Cd + Cl **2 / ( np.pi * self.e_factor * self.AR ) + + + return Cd + + + ########################################################################### + def Cm(self, alpha ): + + Cm = self.Cm0 + + return Cm + + + ############################# + def plot_alpha2Cl(self, alpha_min = -3.15, alpha_max = 3.15 ): + + alphas = np.arange( alpha_min, alpha_max, 0.05 ) + + n = alphas.shape[0] + Cls = np.zeros((n,1)) + Cds = np.zeros((n,1)) + Cms = np.zeros((n,1)) + + for i in range(n): + Cls[i] = self.Cl( alphas[i] ) + Cds[i] = self.Cd( alphas[i] ) + Cms[i] = self.Cm( alphas[i] ) + + fig , ax = plt.subplots(3, figsize=graphical.default_figsize, + dpi= graphical.default_dpi, frameon=True) + + fig.canvas.manager.set_window_title('Aero curve') + + ax[0].plot( alphas , Cls , 'b') + ax[0].set_ylabel('Cl', fontsize=graphical.default_fontsize) + ax[0].set_xlabel('alpha', fontsize=graphical.default_fontsize ) + ax[0].tick_params( labelsize = graphical.default_fontsize ) + ax[0].grid(True) + + ax[1].plot( alphas , Cds , 'b') + ax[1].set_ylabel('Cd', fontsize=graphical.default_fontsize) + ax[1].set_xlabel('alpha', fontsize=graphical.default_fontsize ) + ax[1].tick_params( labelsize = graphical.default_fontsize ) + ax[1].grid(True) + + ax[2].plot( alphas , Cms , 'b') + ax[2].set_ylabel('Cm', fontsize=graphical.default_fontsize) + ax[2].set_xlabel('alpha', fontsize=graphical.default_fontsize ) + ax[2].tick_params( labelsize = graphical.default_fontsize ) + ax[2].grid(True) + + fig.tight_layout() + fig.canvas.draw() + + plt.show() + + + + ########################################################################### + def compute_aerodynamic_forces( self, V , alpha , delta ): + + rv2 = 0.5 * self.rho * V**2 + + c_w = np.sqrt( self.S_w / self.AR ) + c_t = np.sqrt( self.S_t / self.AR ) + + Cl_w = self.Cl( alpha ) + Cd_w = self.Cd( alpha ) + Cm_w = self.Cm( alpha ) + + L_w = rv2 * self.S_w * Cl_w + D_w = rv2 * self.S_w * Cd_w + M_w = rv2 * self.S_w * c_w * Cm_w + + Cl_t = self.Cl( alpha + delta ) + Cd_t = self.Cd( alpha + delta ) + Cm_t = self.Cm( alpha + delta ) + + L_t = rv2 * self.S_t * Cl_t + D_t = rv2 * self.S_t * Cd_t + M_t = rv2 * self.S_t * c_t * Cm_t + + return ( L_w , D_w , M_w , L_t , D_t , M_t ) @@ -114,4 +371,361 @@ def g(self, q ): return g - \ No newline at end of file + + ########################################################################### + def d(self, q , dq , u ): + """ + State-dependent dissipative forces : dof x 1 + """ + + V , gamma , alpha = self.compute_velocity_vector( q , dq ) + + delta = u[1] + + L_w, D_w, M_w, L_t, D_t, M_t = self.compute_aerodynamic_forces( V , alpha, delta ) + + ########################################################## + # Total aero forces vector at c.g. in wind-aligned basis + ########################################################## + + s = np.sin( alpha ) + c = np.cos( alpha ) + + L = L_w + L_t + D = D_w + D_t + M = M_w + M_t - self.l_w * ( L_w * c + D_w * s ) - self.l_t * ( L_t * c + D_t * s ) + + ########################################################## + # Transformation of aero forces in global inertial basis + ########################################################## + + d_wind = np.array([ -D , L , M ]) + + s = np.sin( gamma ) + c = np.cos( gamma ) + + R = np.array([ [ c , -s , 0 ] , + [ s , c , 0 ] , + [ 0 , 0 , 1 ] ]) + + d = - R @ d_wind # aero forces in global inertial basis + + return d + + ########################################################################### + def B(self, q , u ): + """ + Actuator Matrix : dof x m + """ + + B = np.zeros((3,1)) + + theta = q[2] + + # TODO PLACE HOLDER + B[0,0] = np.cos( theta ) + B[1,0] = np.sin( theta ) + + return B + + + ########################################################################### + def forward_kinematic_domain(self, q ): + """ + """ + + x = q[0] + y = q[1] + z = 0 + + if self.dynamic_domain: + + l = self.dynamic_range + + domain = [ ( -l + x , l + x ) , + ( -l + y , l + y ) , + ( -l + z , l + z ) ] + else: + + l = self.static_range + + domain = [ ( -l * 0.01 , l ) , + ( -l * 0.01 , l ) , + ( -l * 0.01 , l ) ]# + + + return domain + + + ########################################################################### + def forward_kinematic_lines(self, q ): + """ + Compute points p = [x;y;z] positions given config q + ---------------------------------------------------- + - points of interest for ploting + + Outpus: + lines_pts = [] : a list of array (n_pts x 3) for each lines + + """ + + lines_pts = [] # list of array (n_pts x 3) for each lines + lines_style = [] + lines_color = [] + + + ########################### + # Dimensions + ########################### + + w = self.width # body width + l = self.length # body lenght + + ########################### + # Body + ########################### + + pts = np.zeros(( 5 , 3 )) + + x = q[0] + y = q[1] + theta = q[2] + + world_T_body = Transformation_Matrix_2D_from_base_angle( theta , x , y ) + #body_T_wind = Transformation_Matrix_2D_from_base_angle( -alpha , 0 , 0 ) + body_T_drawing = Transformation_Matrix_2D_from_base_angle( 0 , -self.l_cg , -w/2 ) + + body_pts_local = np.array([ [ 0 , 0 , 1 ] , + [ l , 0 , 1 ] , + [ l-w , w , 1 ] , + [ 2*w , w , 1 ] , + [ w , 3*w , 1 ] , + [ 0 , 3*w , 1 ] , + [ 0 , 0 , 1 ] ]) + + + body_pts_global = Transform_2D_Pts( body_pts_local , world_T_body @ body_T_drawing) + + lines_pts.append( body_pts_global ) + lines_style.append( '-') + lines_color.append( 'b') + + if self.dynamic_domain : + + cg = np.array([ [ x , y , 1 ] ]) + + lines_pts.append( cg ) + lines_style.append( 'o') + lines_color.append( 'k') + + ########################### + # Wings + ########################### + + pts = np.zeros(( 2 , 3 )) + + c_w = np.sqrt( self.S_w / self.AR ) + + + wings_pts_body = np.array([ [ -self.l_w + c_w , 0 , 1 ] , + [ -self.l_w - c_w , 0 , 1 ] ]) + + + wings_pts_world = Transform_2D_Pts( wings_pts_body , world_T_body ) + + lines_pts.append( wings_pts_world ) + lines_style.append( '-') + lines_color.append( 'b') + + ########################### + # bottom line + ########################### + + pts = np.zeros((2,3)) + + pts[0,0] = -10000 + pts[1,0] = 10000 + pts[0,1] = 0 + pts[1,1] = 0 + + lines_pts.append( pts ) + lines_style.append('--') + lines_color.append('k') + + + return lines_pts , lines_style , lines_color + + + ########################################################################### + def forward_kinematic_lines_plus(self, x , u , t ): + """ + plots the force vector + + """ + + lines_pts = [] # list of array (n_pts x 3) for each lines + lines_style = [] + lines_color = [] + + #w = self.width + + q, dq = self.x2q(x) + + x = q[0] + y = q[1] + theta = q[2] + + V , gamma , alpha = self.compute_velocity_vector( q , dq ) + + world_T_body = Transformation_Matrix_2D_from_base_angle( theta , x , y ) + body_T_wind = Transformation_Matrix_2D_from_base_angle( -alpha , 0 , 0 ) + + delta = u[1] + + ########################### + # Trust vector + ########################### + + # Max trust --> arrow is long as airplane + f_scale = self.length / (self.u_ub[0] - self.u_lb[0]) + + trust_vector_lenght = u[0] * f_scale + + #pts = arrow_from_tip_angle( trust_vector_lenght , theta , bx , by ) + + trust_arrow_body = arrow_from_tip_components( trust_vector_lenght, 0, -self.l_cg, 0) + + trust_arrow_world = Transform_2D_Pts( trust_arrow_body , world_T_body ) + + lines_pts.append( trust_arrow_world ) + lines_style.append( '-') + lines_color.append( 'r') + + ########################### + # Control surface + ########################### + + c_t = np.sqrt( self.S_t / self.AR ) + + # NOT TO scale to better see the elevator + tail_pts_tail = np.array([ [ 1.0 * c_t , 0 , 1 ] , + [ -1.0 * c_t , 0 , 1 ] ]) + + body_T_tail = Transformation_Matrix_2D_from_base_angle( delta , - self.l_t , 0 ) + + tail_pts_global = Transform_2D_Pts( tail_pts_tail , world_T_body @ body_T_tail) + + lines_pts.append( tail_pts_global ) + lines_style.append( '-') + lines_color.append( 'b') + + # ########################### + # # Velocity vector + # ########################### + + v_length = V * self.length / self.x_ub[3] + if v_length > self.length: v_length = self.length + + v_pts = arrow_from_base_components( v_length , 0, 0, 0) + + v_world = Transform_2D_Pts( v_pts , world_T_body @ body_T_wind ) + + lines_pts.append( v_world ) + lines_style.append('-') + lines_color.append('k') + + # ########################### + # # Aero forces + # ########################### + + + L_w, D_w, M_w, L_t, D_t, M_t = self.compute_aerodynamic_forces( V , alpha , delta ) + + L_w_pts = arrow_from_base_components(0, L_w * f_scale, 0, 0) + D_w_pts = arrow_from_base_components(-D_w * f_scale, 0, 0, 0) + L_t_pts = arrow_from_base_components(0, L_t * f_scale, 0, 0) + D_t_pts = arrow_from_base_components(-D_t * f_scale, 0, 0, 0) + + body_T_acw = Transformation_Matrix_2D_from_base_angle( 0 , -self.l_w , 0 ) + body_T_act = Transformation_Matrix_2D_from_base_angle( 0 , -self.l_t , 0 ) + + L_w_pts_global = Transform_2D_Pts( L_w_pts , world_T_body @ body_T_acw @ body_T_wind ) + D_w_pts_global = Transform_2D_Pts( D_w_pts , world_T_body @ body_T_acw @ body_T_wind ) + + #Change color if stalled: + if (alpha < self.alpha_stall ) and (alpha > -self.alpha_stall ): + L_color = 'b' + D_color = 'r' + else: + L_color = 'c' + D_color = 'm' + + lines_pts.append( L_w_pts_global ) + lines_style.append('-') + lines_color.append( L_color ) + + lines_pts.append( D_w_pts_global ) + lines_style.append('-') + lines_color.append( D_color ) + + L_t_pts_global = Transform_2D_Pts( L_t_pts , world_T_body @ body_T_act @ body_T_wind ) + D_t_pts_global = Transform_2D_Pts( D_t_pts , world_T_body @ body_T_act @ body_T_wind ) + + #Change color if stalled: + if (( alpha + delta ) < self.alpha_stall ) and ( ( alpha + delta ) > -self.alpha_stall ): + L_color = 'b' + D_color = 'r' + else: + L_color = 'c' + D_color = 'm' + + lines_pts.append( L_t_pts_global ) + lines_style.append('-') + lines_color.append( L_color ) + + lines_pts.append( D_t_pts_global ) + lines_style.append('-') + lines_color.append( D_color ) + + + return lines_pts , lines_style , lines_color + + + + +''' +################################################################# +################## Main ######## +################################################################# +''' + + +if __name__ == "__main__": + """ MAIN TEST """ + + + if True: + + sys = Plane2D() + + #sys.plot_alpha2Cl() + + sys.x0 = np.array([0,0,0.2,15,0,0]) + + + + def t2u(t): + + u = np.array([ 2 * t , -0.12 * t ]) + + return u + + sys.t2u = t2u + + #sys.gravity = 0 + + sys.compute_trajectory( 10 , 20001 , 'euler' ) + sys.plot_trajectory('x') + + # sys.dynamic_domain = False + sys.animate_simulation( time_factor_video=0.5 ) From f007fc0f4b46c2424e758807d6b39f442e8d7555 Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Tue, 12 Sep 2023 23:23:59 -0400 Subject: [PATCH 08/93] test --- dev/dimentionless/dev_car.py | 287 +++++++++++++++++++++++++++++++++++ 1 file changed, 287 insertions(+) create mode 100644 dev/dimentionless/dev_car.py diff --git a/dev/dimentionless/dev_car.py b/dev/dimentionless/dev_car.py new file mode 100644 index 00000000..3da1b5b1 --- /dev/null +++ b/dev/dimentionless/dev_car.py @@ -0,0 +1,287 @@ +# -*- coding: utf-8 -*- +""" +Created on Mon Nov 12 20:28:17 2018 + +@author: Alexandre +""" +############################################################################## +import numpy as np +import matplotlib.pyplot as plt +############################################################################## +from pyro.dynamic import longitudinal_vehicule +from pyro.planning import discretizer +from pyro.analysis import costfunction +from pyro.planning import dynamicprogramming +from pyro.analysis import graphical +############################################################################## + + +m = 1 +g = 1 +l = 1 +t_max_star = 1 +q_star = 1 +case_name = 'test' +show = True +res = 'std' +legend = 1 + +# Additionnal fixed domain dimentionless parameters +theta_star = 2.0 * np.pi +dtheta_star = 1.0 * np.pi +time_star = 2.0 * np.pi * 20.0 + +# Combined system parameters +omega = np.sqrt( ( g / l ) ) +mgl = m * g * l + +# Dimentional parameters +t_max = t_max_star * mgl +q = q_star * mgl +theta = theta_star +dtheta = dtheta_star * omega +time = time_star / omega +J_max = mgl**2 / omega * time_star * ( ( q_star * theta_star )**2 + t_max_star**2 ) + +print('\n\nCase :' + case_name ) +print('----------------------------------------------------') +print(' m=',m,' g=',g,' l=',l,' t_max=', t_max, ' q=', q) + +################################ +# Dynamic system definition +################################ + +sys = longitudinal_vehicule. + +# kinematic +sys.lc1 = l + +sys.l1 = sys.lc1 +sys.l_domain = sys.lc1 * 2 + +# dynamic +sys.m1 = m +sys.I1 = 0 +sys.gravity = g +sys.d1 = 0 + +sys.u_ub[0] = + t_max +sys.u_lb[0] = - t_max + +sys.x_ub = np.array([ + theta , + dtheta ]) +sys.x_lb = np.array([ - theta , - dtheta ]) + +################################ +# Discritized grid +################################ + +if res == 'test' : + + dt = 0.5 + nx = 21 + nu = 3 + +elif res == 'plus' : + + dt = 0.05 + nx = 301 + nu = 101 + +elif res == 'hi' : + + dt = 0.025 + nx = 501 + nu = 101 + +else: + + dt = 0.05 + nx = 101 + nu = 11 + +grid_sys = discretizer.GridDynamicSystem( sys , [nx,nx] , [nu] , dt , True ) + +################################ +# Cost function +################################ + +qcf = costfunction.QuadraticCostFunction.from_sys(sys) + +qcf.xbar = np.array([ 0 , 0 ]) # target +qcf.INF = J_max + +qcf.Q[0,0] = q ** 2 +qcf.Q[1,1] = 0.0 + +qcf.R[0,0] = 1.0 + +################################ +# Computing optimal policy +################################ + +dp = dynamicprogramming.DynamicProgrammingWithLookUpTable( grid_sys, qcf ) + +steps = int( time / dt ) + +dp.compute_steps( steps ) + + +#grid_sys.fontsize = 10 +qcf.INF = 0.1 * J_max +dp.clean_infeasible_set() + + +################################## +# Fig param +################################## + +dpi = 300 +fontsize = 10 +figsize = (4, 3) + +################################## +# Dimensional policy plot +################################## + +fig = plt.figure( figsize = figsize, dpi=dpi, frameon=True) +fig.canvas.manager.set_window_title( 'dimentionless policy' ) +ax = fig.add_subplot(1, 1, 1) + +xname = r'$\theta \; [rad]$' +yname = r'$\dot{\theta} \; [rad/sec]$' +zname = r'$\tau \; [Nm]$' + +sys.state_label[0] = r'$\theta$' +sys.state_label[1] = r'$\dot{\theta}$' +sys.input_label[0] = r'$\tau$' + +xrange = 2.0 * np.pi +yrange = np.pi * np.sqrt( 10 / 1. ) +zrange = 20. + +ax.set_ylabel( yname, fontsize = fontsize ) +ax.set_xlabel( xname, fontsize = fontsize ) + +x_level = grid_sys.x_level[ 0 ] +y_level = grid_sys.x_level[ 1 ] + + +u = grid_sys.get_input_from_policy( dp.pi , 0 ) +J_grid_nd = grid_sys.get_grid_from_array( u ) +J_grid_2d = grid_sys.get_2D_slice_of_grid( J_grid_nd , 0 , 1 ) + +mesh = ax.pcolormesh( x_level, + y_level, + J_grid_2d.T, + shading='gouraud', + cmap = 'bwr', + vmin = -zrange, + vmax = zrange, + rasterized = True ) + +ax.tick_params( labelsize = fontsize ) +ax.grid(True) +ax.set_ylim( -yrange, +yrange) +ax.set_xlim( -xrange, xrange) + +cbar = fig.colorbar( mesh ) + +cbar.set_label(zname, fontsize = fontsize , rotation = 90 ) + +fig.tight_layout() +#fig.show() +fig.savefig( case_name + '_policy.pdf') +fig.savefig( case_name + '_policy.png') +fig.savefig( case_name + '_policy.jpg') + +if show: + plt.show() +else: + plt.close( fig ) + + +################################## +# Trajectory plot +################################## + +ctl = dp.get_lookup_table_controller() + +# Simulation +cl_sys = ctl + sys +cl_sys.x0 = np.array([-3.14, 0.]) +cl_sys.compute_trajectory( 10 , 6001, 'euler') + +tp = graphical.TrajectoryPlotter( sys ) +tp.fontsize = fontsize +tp.plot( cl_sys.traj , 'xu' , show = False ) +tp.plots[1].set_ylim([-5.5, 5.5]) +tp.plots[2].set_ylim([-zrange, zrange]) +tp.fig.savefig( case_name + '_traj.pdf') +tp.fig.savefig( case_name + '_traj.png') +tp.fig.savefig( case_name + '_traj.jpg') + +if show: + plt.show() +else: + plt.close( tp.fig ) + + +################################## +# Dimensionless policy plot +################################## + +fig = plt.figure( figsize= figsize, dpi=dpi, frameon=True) +fig.canvas.manager.set_window_title( 'dimentionless policy' ) +ax = fig.add_subplot(1, 1, 1) + +xname = r'$\theta^*$'#self.sys.state_label[x] #+ ' ' + self.sys.state_units[x] +yname = r'$\dot{\theta}^* = \frac{\dot{\theta}}{\omega}$'#self.sys.state_label[y] #+ ' ' + self.sys.state_units[y] +zname = r'$\tau^*=\frac{\tau}{mgl}$' + +ax.set_ylabel(yname, fontsize = fontsize ) +ax.set_xlabel(xname, fontsize = fontsize ) + +x_level = grid_sys.x_level[ 0 ] * 1 +y_level = grid_sys.x_level[ 1 ] * (1 / omega) + + +u = grid_sys.get_input_from_policy( dp.pi , 0 ) + +u2 = u * (1/mgl) + +J_grid_nd = grid_sys.get_grid_from_array( u2 ) + +J_grid_2d = grid_sys.get_2D_slice_of_grid( J_grid_nd , 0 , 1 ) + +mesh = ax.pcolormesh( x_level, + y_level, + J_grid_2d.T, + shading='gouraud', + cmap = 'bwr', + rasterized = True ) + +ax.tick_params( labelsize = fontsize ) +ax.grid(True) + +cbar = fig.colorbar( mesh ) + +cbar.set_label(zname, fontsize = fontsize , rotation = 90 ) + +fig.tight_layout() +#fig.show() +fig.savefig( case_name + '_dimpolicy.pdf') +fig.savefig( case_name + '_dimpolicy.png') +fig.savefig( case_name + '_dimpolicy.jpg') + +if show: + plt.show() +else: + plt.close( fig ) + + + + + + + + \ No newline at end of file From 11f6ba63bb5375c54cbb080e00b2c5971b228a6c Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Wed, 13 Sep 2023 15:26:42 -0400 Subject: [PATCH 09/93] test traj opt --- dev/plane/plane_trajectory_optimisation.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/dev/plane/plane_trajectory_optimisation.py b/dev/plane/plane_trajectory_optimisation.py index 973d7138..715865de 100644 --- a/dev/plane/plane_trajectory_optimisation.py +++ b/dev/plane/plane_trajectory_optimisation.py @@ -20,7 +20,7 @@ def t2u(t): - u = np.array([ 10 , -0.2 ]) + u = np.array([ 20 , -0.1 ]) return u @@ -29,17 +29,17 @@ def t2u(t): #sys.gravity = 0 sys.compute_trajectory( 2 , 2001 , 'euler' ) -# sys.plot_trajectory('x') -# sys.animate_simulation( time_factor_video=0.5 ) +#sys.plot_trajectory('x') +sys.animate_simulation( time_factor_video=0.5 ) planner = DirectCollocationTrajectoryOptimisation( sys ) planner.x_start = sys.x0 -planner.x_goal = np.array([25,20,1.6,0,10,0]) +planner.x_goal = sys.traj.x[-1,:] planner.grid = 20 planner.dt = 0.1 -planner.maxiter = 200 +planner.maxiter = 400 planner.set_initial_trajectory_guest( sys.traj ) From 58cd87575cd7074336c469507b66843442e4b503 Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Wed, 13 Sep 2023 22:57:17 -0400 Subject: [PATCH 10/93] car analysis --- dev/dimentionless/dev_car.py | 190 ++++++++++++++++++----------------- 1 file changed, 97 insertions(+), 93 deletions(-) diff --git a/dev/dimentionless/dev_car.py b/dev/dimentionless/dev_car.py index 3da1b5b1..2313a8fd 100644 --- a/dev/dimentionless/dev_car.py +++ b/dev/dimentionless/dev_car.py @@ -15,61 +15,65 @@ from pyro.analysis import graphical ############################################################################## - -m = 1 -g = 1 -l = 1 -t_max_star = 1 -q_star = 1 -case_name = 'test' +case_name = 'car_test_l5_w50' show = True res = 'std' legend = 1 -# Additionnal fixed domain dimentionless parameters -theta_star = 2.0 * np.pi -dtheta_star = 1.0 * np.pi -time_star = 2.0 * np.pi * 20.0 -# Combined system parameters -omega = np.sqrt( ( g / l ) ) -mgl = m * g * l +#dim context + +x_c_star = 0.5 +y_c_star = 0.5 +x_w_star = 50.0 + +# param +length = 5.0 +gravity = 9.8 + +# context +x_c = x_c_star * length +y_c = y_c_star * length +x_w = x_w_star * length + +# additionnal domain +x_max = 10 * length +v_max = 2 * np.sqrt( gravity * length ) +t_max = 10 * x_max / v_max +s_max = 0.3 +j_max = t_max * ( ( x_max / x_w ) **2 + s_max ** 2 ) -# Dimentional parameters -t_max = t_max_star * mgl -q = q_star * mgl -theta = theta_star -dtheta = dtheta_star * omega -time = time_star / omega -J_max = mgl**2 / omega * time_star * ( ( q_star * theta_star )**2 + t_max_star**2 ) print('\n\nCase :' + case_name ) print('----------------------------------------------------') -print(' m=',m,' g=',g,' l=',l,' t_max=', t_max, ' q=', q) +print(' l=',length,' x_c=', x_c, ' y_c=', y_c,'x_w=', x_w) ################################ # Dynamic system definition ################################ -sys = longitudinal_vehicule. +sys = longitudinal_vehicule.LongitudinalFrontWheelDriveCarWithWheelSlipInput() + +# Model param +sys.lenght = length # distance between front wheel and back wheel [m] +sys.xc = x_c # distance from back wheel to c.g. [m] +sys.yc = y_c # height from ground to c.g. [m] -# kinematic -sys.lc1 = l +sys.gravity = gravity # gravity constant [N/kg] -sys.l1 = sys.lc1 -sys.l_domain = sys.lc1 * 2 +sys.mass = 1. # total car mass [kg] +sys.rho = 1. # air density [kg/m3] +sys.cdA = 0. # drag coef time area [m2] -# dynamic -sys.m1 = m -sys.I1 = 0 -sys.gravity = g -sys.d1 = 0 +# Ground traction curve parameters +sys.mu_max = 0.5 +sys.mu_slope = 70. -sys.u_ub[0] = + t_max -sys.u_lb[0] = - t_max +sys.u_ub[0] = + s_max +sys.u_lb[0] = - s_max -sys.x_ub = np.array([ + theta , + dtheta ]) -sys.x_lb = np.array([ - theta , - dtheta ]) +sys.x_ub = np.array([ + x_max , + v_max]) +sys.x_lb = np.array([ - x_max , - v_max]) ################################ # Discritized grid @@ -97,7 +101,7 @@ dt = 0.05 nx = 101 - nu = 11 + nu = 21 grid_sys = discretizer.GridDynamicSystem( sys , [nx,nx] , [nu] , dt , True ) @@ -108,9 +112,11 @@ qcf = costfunction.QuadraticCostFunction.from_sys(sys) qcf.xbar = np.array([ 0 , 0 ]) # target -qcf.INF = J_max -qcf.Q[0,0] = q ** 2 +qcf.INF = j_max + + +qcf.Q[0,0] = (1./x_w) ** 2 qcf.Q[1,1] = 0.0 qcf.R[0,0] = 1.0 @@ -121,15 +127,17 @@ dp = dynamicprogramming.DynamicProgrammingWithLookUpTable( grid_sys, qcf ) -steps = int( time / dt ) +steps = int( t_max / dt ) dp.compute_steps( steps ) #grid_sys.fontsize = 10 -qcf.INF = 0.1 * J_max +# qcf.INF = 0.1 * qcf.INF dp.clean_infeasible_set() +dp.plot_policy() + ################################## # Fig param @@ -139,25 +147,25 @@ fontsize = 10 figsize = (4, 3) -################################## -# Dimensional policy plot -################################## +# ################################## +# # Dimensional policy plot +# ################################## fig = plt.figure( figsize = figsize, dpi=dpi, frameon=True) fig.canvas.manager.set_window_title( 'dimentionless policy' ) ax = fig.add_subplot(1, 1, 1) -xname = r'$\theta \; [rad]$' -yname = r'$\dot{\theta} \; [rad/sec]$' -zname = r'$\tau \; [Nm]$' +xname = r'$x \; [m]$' +yname = r'$\dot{x} \; [m/sec]$' +zname = r'$s \; [-]$' -sys.state_label[0] = r'$\theta$' -sys.state_label[1] = r'$\dot{\theta}$' -sys.input_label[0] = r'$\tau$' +sys.state_label[0] = r'$x$' +sys.state_label[1] = r'$\dot{x}$' +sys.input_label[0] = r'$s$' -xrange = 2.0 * np.pi -yrange = np.pi * np.sqrt( 10 / 1. ) -zrange = 20. +xrange = 50 +yrange = 15 +zrange = 0.3 ax.set_ylabel( yname, fontsize = fontsize ) ax.set_xlabel( xname, fontsize = fontsize ) @@ -191,41 +199,14 @@ fig.tight_layout() #fig.show() fig.savefig( case_name + '_policy.pdf') -fig.savefig( case_name + '_policy.png') -fig.savefig( case_name + '_policy.jpg') +# fig.savefig( case_name + '_policy.png') +# fig.savefig( case_name + '_policy.jpg') if show: plt.show() else: plt.close( fig ) - - -################################## -# Trajectory plot -################################## - -ctl = dp.get_lookup_table_controller() - -# Simulation -cl_sys = ctl + sys -cl_sys.x0 = np.array([-3.14, 0.]) -cl_sys.compute_trajectory( 10 , 6001, 'euler') - -tp = graphical.TrajectoryPlotter( sys ) -tp.fontsize = fontsize -tp.plot( cl_sys.traj , 'xu' , show = False ) -tp.plots[1].set_ylim([-5.5, 5.5]) -tp.plots[2].set_ylim([-zrange, zrange]) -tp.fig.savefig( case_name + '_traj.pdf') -tp.fig.savefig( case_name + '_traj.png') -tp.fig.savefig( case_name + '_traj.jpg') - -if show: - plt.show() -else: - plt.close( tp.fig ) - - + ################################## # Dimensionless policy plot ################################## @@ -234,20 +215,20 @@ fig.canvas.manager.set_window_title( 'dimentionless policy' ) ax = fig.add_subplot(1, 1, 1) -xname = r'$\theta^*$'#self.sys.state_label[x] #+ ' ' + self.sys.state_units[x] -yname = r'$\dot{\theta}^* = \frac{\dot{\theta}}{\omega}$'#self.sys.state_label[y] #+ ' ' + self.sys.state_units[y] -zname = r'$\tau^*=\frac{\tau}{mgl}$' +xname = r'$x^*$'#self.sys.state_label[x] #+ ' ' + self.sys.state_units[x] +yname = r'$\dot{x}^* = \frac{\dot{x}}{\sqrt{gl}}$'#self.sys.state_label[y] #+ ' ' + self.sys.state_units[y] +zname = r'$s$' ax.set_ylabel(yname, fontsize = fontsize ) ax.set_xlabel(xname, fontsize = fontsize ) -x_level = grid_sys.x_level[ 0 ] * 1 -y_level = grid_sys.x_level[ 1 ] * (1 / omega) +x_level = grid_sys.x_level[ 0 ] * 1./length +y_level = grid_sys.x_level[ 1 ] * (1 / np.sqrt( gravity * length )) u = grid_sys.get_input_from_policy( dp.pi , 0 ) -u2 = u * (1/mgl) +u2 = u J_grid_nd = grid_sys.get_grid_from_array( u2 ) @@ -270,15 +251,38 @@ fig.tight_layout() #fig.show() fig.savefig( case_name + '_dimpolicy.pdf') -fig.savefig( case_name + '_dimpolicy.png') -fig.savefig( case_name + '_dimpolicy.jpg') +# fig.savefig( case_name + '_dimpolicy.png') +# fig.savefig( case_name + '_dimpolicy.jpg') + + +# ################################## +# # Trajectory plot +# ################################## + +ctl = dp.get_lookup_table_controller() + +# Simulation +cl_sys = ctl + sys +cl_sys.x0 = np.array([-x_max * 0.5, 0.]) +cl_sys.compute_trajectory( 10 , 6001, 'euler') + +tp = graphical.TrajectoryPlotter( sys ) +tp.fontsize = fontsize +tp.plot( cl_sys.traj , 'xu' , show = False ) +tp.plots[1].set_ylim([-5.5, 5.5]) +tp.plots[2].set_ylim([-zrange, zrange]) +tp.fig.savefig( case_name + '_traj.pdf') +# tp.fig.savefig( case_name + '_traj.png') +# tp.fig.savefig( case_name + '_traj.jpg') if show: plt.show() else: - plt.close( fig ) + plt.close( tp.fig ) + +cl_sys.plot_trajectory('xu') +cl_sys.plot_phase_plane_trajectory() - From 7f48dad2c6b25704a20b212268f5196eabe97be4 Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Thu, 14 Sep 2023 10:04:43 -0400 Subject: [PATCH 11/93] started drawing lib --- pyro/dynamic/plane.py | 81 ++++++++++++++------------------------ pyro/dynamic/rocket.py | 2 +- pyro/kinematic/drawing.py | 46 ++++++++++++++++++++++ pyro/kinematic/geometry.py | 53 +++++++++++++++++++++++++ 4 files changed, 130 insertions(+), 52 deletions(-) create mode 100644 pyro/kinematic/drawing.py create mode 100644 pyro/kinematic/geometry.py diff --git a/pyro/dynamic/plane.py b/pyro/dynamic/plane.py index 5442ec11..185705aa 100644 --- a/pyro/dynamic/plane.py +++ b/pyro/dynamic/plane.py @@ -10,35 +10,13 @@ import numpy as np import matplotlib.pyplot as plt ############################################################################### -from pyro.analysis import graphical -from pyro.dynamic import system -from pyro.dynamic import mechanical +from pyro.analysis import graphical +from pyro.dynamic import mechanical +from pyro.kinematic import geometry +from pyro.kinematic import drawing ############################################################################### -############################################################################### -def Transformation_Matrix_2D_from_base_angle( theta , x , y ): - - s = np.sin( theta ) - c = np.cos( theta ) - - T = np.array([ [ c , -s , x ] , - [ s , c , y ] , - [ 0 , 0 , 1 ] ]) - - return T - - -############################################################################### -def Transform_2D_Pts( pts , T ): - - pts_transformed = np.zeros( pts.shape ) - - for i in range(pts.shape[0]): - - pts_transformed[ i ] = T @ pts[ i ] - - return pts_transformed ############################################################################### @@ -52,9 +30,9 @@ def arrow_from_base_angle( l , theta , bx , by ): [ l , 0 , 1 ] , [ l-d , -d , 1 ] ]) - T = Transformation_Matrix_2D_from_base_angle( theta , bx , by ) + T = geometry.transformation_matrix_2D( theta , bx , by ) - pts_global = Transform_2D_Pts( pts_local , T ) + pts_global = drawing.transform_points_2D( T , pts_local ) return pts_global @@ -71,9 +49,9 @@ def arrow_from_tip_angle( l , theta , bx , by ): [ 0 , 0 , 1 ] , [ -d , -d , 1 ] ]) - T = Transformation_Matrix_2D_from_base_angle( theta , bx , by ) + T = geometry.transformation_matrix_2D( theta , bx , by ) - pts_global = Transform_2D_Pts( pts_local , T ) + pts_global = drawing.transform_points_2D( T , pts_local ) return pts_global @@ -92,9 +70,9 @@ def arrow_from_base_components( vx , vy , bx , by ): theta = np.arctan2( vy , vx ) - T = Transformation_Matrix_2D_from_base_angle( theta , bx , by ) + T = geometry.transformation_matrix_2D( theta , bx , by ) - pts_global = Transform_2D_Pts( pts_local , T ) + pts_global = drawing.transform_points_2D( T , pts_local ) return pts_global @@ -113,9 +91,9 @@ def arrow_from_tip_components( vx , vy , bx , by ): theta = np.arctan2( vy , vx ) - T = Transformation_Matrix_2D_from_base_angle( theta , bx , by ) + T = geometry.transformation_matrix_2D( theta , bx , by ) - pts_global = Transform_2D_Pts( pts_local , T ) + pts_global = drawing.transform_points_2D( T , pts_local ) return pts_global @@ -491,9 +469,9 @@ def forward_kinematic_lines(self, q ): y = q[1] theta = q[2] - world_T_body = Transformation_Matrix_2D_from_base_angle( theta , x , y ) - #body_T_wind = Transformation_Matrix_2D_from_base_angle( -alpha , 0 , 0 ) - body_T_drawing = Transformation_Matrix_2D_from_base_angle( 0 , -self.l_cg , -w/2 ) + world_T_body = geometry.transformation_matrix_2D( theta , x , y ) + #body_T_wind = transformation_matrix_2D_from_base_angle( -alpha , 0 , 0 ) + body_T_drawing = geometry.transformation_matrix_2D( 0 , -self.l_cg , -w/2 ) body_pts_local = np.array([ [ 0 , 0 , 1 ] , [ l , 0 , 1 ] , @@ -504,7 +482,7 @@ def forward_kinematic_lines(self, q ): [ 0 , 0 , 1 ] ]) - body_pts_global = Transform_2D_Pts( body_pts_local , world_T_body @ body_T_drawing) + body_pts_global = drawing.transform_points_2D( world_T_body @ body_T_drawing , body_pts_local ) lines_pts.append( body_pts_global ) lines_style.append( '-') @@ -531,7 +509,7 @@ def forward_kinematic_lines(self, q ): [ -self.l_w - c_w , 0 , 1 ] ]) - wings_pts_world = Transform_2D_Pts( wings_pts_body , world_T_body ) + wings_pts_world = drawing.transform_points_2D( world_T_body , wings_pts_body ) lines_pts.append( wings_pts_world ) lines_style.append( '-') @@ -577,8 +555,9 @@ def forward_kinematic_lines_plus(self, x , u , t ): V , gamma , alpha = self.compute_velocity_vector( q , dq ) - world_T_body = Transformation_Matrix_2D_from_base_angle( theta , x , y ) - body_T_wind = Transformation_Matrix_2D_from_base_angle( -alpha , 0 , 0 ) + + world_T_body = geometry.transformation_matrix_2D( theta , x , y ) + body_T_wind = geometry.transformation_matrix_2D( -alpha , 0 , 0 ) delta = u[1] @@ -595,7 +574,7 @@ def forward_kinematic_lines_plus(self, x , u , t ): trust_arrow_body = arrow_from_tip_components( trust_vector_lenght, 0, -self.l_cg, 0) - trust_arrow_world = Transform_2D_Pts( trust_arrow_body , world_T_body ) + trust_arrow_world = drawing.transform_points_2D( world_T_body , trust_arrow_body ) lines_pts.append( trust_arrow_world ) lines_style.append( '-') @@ -611,9 +590,9 @@ def forward_kinematic_lines_plus(self, x , u , t ): tail_pts_tail = np.array([ [ 1.0 * c_t , 0 , 1 ] , [ -1.0 * c_t , 0 , 1 ] ]) - body_T_tail = Transformation_Matrix_2D_from_base_angle( delta , - self.l_t , 0 ) + body_T_tail = geometry.transformation_matrix_2D( delta , - self.l_t , 0 ) - tail_pts_global = Transform_2D_Pts( tail_pts_tail , world_T_body @ body_T_tail) + tail_pts_global = drawing.transform_points_2D( world_T_body @ body_T_tail , tail_pts_tail ) lines_pts.append( tail_pts_global ) lines_style.append( '-') @@ -628,7 +607,7 @@ def forward_kinematic_lines_plus(self, x , u , t ): v_pts = arrow_from_base_components( v_length , 0, 0, 0) - v_world = Transform_2D_Pts( v_pts , world_T_body @ body_T_wind ) + v_world = drawing.transform_points_2D( world_T_body @ body_T_wind , v_pts ) lines_pts.append( v_world ) lines_style.append('-') @@ -646,11 +625,11 @@ def forward_kinematic_lines_plus(self, x , u , t ): L_t_pts = arrow_from_base_components(0, L_t * f_scale, 0, 0) D_t_pts = arrow_from_base_components(-D_t * f_scale, 0, 0, 0) - body_T_acw = Transformation_Matrix_2D_from_base_angle( 0 , -self.l_w , 0 ) - body_T_act = Transformation_Matrix_2D_from_base_angle( 0 , -self.l_t , 0 ) + body_T_acw = geometry.transformation_matrix_2D( 0 , -self.l_w , 0 ) + body_T_act = geometry.transformation_matrix_2D( 0 , -self.l_t , 0 ) - L_w_pts_global = Transform_2D_Pts( L_w_pts , world_T_body @ body_T_acw @ body_T_wind ) - D_w_pts_global = Transform_2D_Pts( D_w_pts , world_T_body @ body_T_acw @ body_T_wind ) + L_w_pts_global = drawing.transform_points_2D( world_T_body @ body_T_acw @ body_T_wind , L_w_pts ) + D_w_pts_global = drawing.transform_points_2D( world_T_body @ body_T_acw @ body_T_wind , D_w_pts ) #Change color if stalled: if (alpha < self.alpha_stall ) and (alpha > -self.alpha_stall ): @@ -668,8 +647,8 @@ def forward_kinematic_lines_plus(self, x , u , t ): lines_style.append('-') lines_color.append( D_color ) - L_t_pts_global = Transform_2D_Pts( L_t_pts , world_T_body @ body_T_act @ body_T_wind ) - D_t_pts_global = Transform_2D_Pts( D_t_pts , world_T_body @ body_T_act @ body_T_wind ) + L_t_pts_global = drawing.transform_points_2D( world_T_body @ body_T_act @ body_T_wind , L_t_pts ) + D_t_pts_global = drawing.transform_points_2D( world_T_body @ body_T_act @ body_T_wind , D_t_pts ) #Change color if stalled: if (( alpha + delta ) < self.alpha_stall ) and ( ( alpha + delta ) > -self.alpha_stall ): diff --git a/pyro/dynamic/rocket.py b/pyro/dynamic/rocket.py index 2d864d47..2e7073bc 100644 --- a/pyro/dynamic/rocket.py +++ b/pyro/dynamic/rocket.py @@ -132,7 +132,7 @@ def g(self, q ): ########################################################################### - def d(self, q , dq ): + def d(self, q , dq , u ): """ State-dependent dissipative forces : dof x 1 """ diff --git a/pyro/kinematic/drawing.py b/pyro/kinematic/drawing.py new file mode 100644 index 00000000..2098a088 --- /dev/null +++ b/pyro/kinematic/drawing.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Created on Thu Sep 14 09:12:12 2023 + +@author: alex +""" + +############################################################################### +import numpy as np +import matplotlib.pyplot as plt +############################################################################### +from pyro.kinematic import geometry +############################################################################### + + + +############################################################################### +def transform_points_2D( A_T_B , pts_B ): + """ + + Take a list of pts in a given frame B and express them in frame A base on + transformation matrix A_T_B + + Parameters + ---------- + pts : TYPE + DESCRIPTION. + T : TYPE + DESCRIPTION. + + Returns + ------- + pts_transformed : TYPE + DESCRIPTION. + + """ + + pts_A = np.zeros( pts_B.shape ) + + # For all pts in the list + for i in range(pts_B.shape[0]): + + pts_A[ i ] = A_T_B @ pts_B[ i ] + + return pts_A diff --git a/pyro/kinematic/geometry.py b/pyro/kinematic/geometry.py new file mode 100644 index 00000000..5425b7f3 --- /dev/null +++ b/pyro/kinematic/geometry.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Created on Thu Sep 14 09:12:12 2023 + +@author: alex +""" + +############################################################################### +import numpy as np +import matplotlib.pyplot as plt +############################################################################### + + +############################################################################### + +# Notation + +# aRb : rotation matrix of basis b in basis a +# ATB : transformation matrix of frame b in frame a +# v_a : vector components in basis a + + +############################################################################### +def transformation_matrix_2D( theta , x , y ): + """ + + Transformation Matrix between 2 frame in 2D + + Parameters + ---------- + theta : float + roation angle (arround z) + x : float + translation along x-axis + y : float + translation along y-axis + + Returns + ------- + T : 3 x 3 np.array + Transformation matrix + + """ + + s = np.sin( theta ) + c = np.cos( theta ) + + T = np.array([ [ c , -s , x ] , + [ s , c , y ] , + [ 0 , 0 , 1 ] ]) + + return T \ No newline at end of file From 6f75ee94da64900eb1f5fc994b0bd613066b3a9b Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Thu, 14 Sep 2023 10:35:53 -0400 Subject: [PATCH 12/93] plane now using v0 of drawing lib --- pyro/dynamic/plane.py | 98 +++------------------------------------ pyro/kinematic/drawing.py | 77 ++++++++++++++++++++++++++++-- 2 files changed, 80 insertions(+), 95 deletions(-) diff --git a/pyro/dynamic/plane.py b/pyro/dynamic/plane.py index 185705aa..d3c81935 100644 --- a/pyro/dynamic/plane.py +++ b/pyro/dynamic/plane.py @@ -18,91 +18,8 @@ - -############################################################################### -def arrow_from_base_angle( l , theta , bx , by ): - - d = l * 0.15 # length of arrow secondary lines - - pts_local = np.array([ [ 0 , 0 , 1 ] , - [ l , 0 , 1 ] , - [ l-d , d , 1 ] , - [ l , 0 , 1 ] , - [ l-d , -d , 1 ] ]) - - T = geometry.transformation_matrix_2D( theta , bx , by ) - - pts_global = drawing.transform_points_2D( T , pts_local ) - - - return pts_global - - -############################################################################### -def arrow_from_tip_angle( l , theta , bx , by ): - - d = l * 0.15 # length of arrow secondary lines - - pts_local = np.array([ [ -l , 0 , 1 ] , - [ 0 , 0 , 1 ] , - [ -d , d , 1 ] , - [ 0 , 0 , 1 ] , - [ -d , -d , 1 ] ]) - - T = geometry.transformation_matrix_2D( theta , bx , by ) - - pts_global = drawing.transform_points_2D( T , pts_local ) - - - return pts_global - -############################################################################### -def arrow_from_base_components( vx , vy , bx , by ): - - l = np.sqrt( vx**2 + vy**2 ) - d = l * 0.15 # length of arrow secondary lines - - pts_local = np.array([ [ 0 , 0 , 1 ] , - [ l , 0 , 1 ] , - [ l-d , d , 1 ] , - [ l , 0 , 1 ] , - [ l-d , -d , 1 ] ]) - - theta = np.arctan2( vy , vx ) - - T = geometry.transformation_matrix_2D( theta , bx , by ) - - pts_global = drawing.transform_points_2D( T , pts_local ) - - - return pts_global - -############################################################################### -def arrow_from_tip_components( vx , vy , bx , by ): - - l = np.sqrt( vx**2 + vy**2 ) - d = l * 0.15 # length of arrow secondary lines - - pts_local = np.array([ [ -l , 0 , 1 ] , - [ 0 , 0 , 1 ] , - [ -d , d , 1 ] , - [ 0 , 0 , 1 ] , - [ -d , -d , 1 ] ]) - - theta = np.arctan2( vy , vx ) - - T = geometry.transformation_matrix_2D( theta , bx , by ) - - pts_global = drawing.transform_points_2D( T , pts_local ) - - - return pts_global - - - - ############################################################################## -# 2D planar drone +# 2D planar plane ############################################################################## class Plane2D( mechanical.MechanicalSystemWithPositionInputs ): @@ -572,7 +489,7 @@ def forward_kinematic_lines_plus(self, x , u , t ): #pts = arrow_from_tip_angle( trust_vector_lenght , theta , bx , by ) - trust_arrow_body = arrow_from_tip_components( trust_vector_lenght, 0, -self.l_cg, 0) + trust_arrow_body = drawing.arrow_from_length_angle( trust_vector_lenght, 0, -self.l_cg, 0 , origin = 'tip') trust_arrow_world = drawing.transform_points_2D( world_T_body , trust_arrow_body ) @@ -605,7 +522,7 @@ def forward_kinematic_lines_plus(self, x , u , t ): v_length = V * self.length / self.x_ub[3] if v_length > self.length: v_length = self.length - v_pts = arrow_from_base_components( v_length , 0, 0, 0) + v_pts = drawing.arrow_from_length_angle( v_length , 0 ) v_world = drawing.transform_points_2D( world_T_body @ body_T_wind , v_pts ) @@ -617,13 +534,12 @@ def forward_kinematic_lines_plus(self, x , u , t ): # # Aero forces # ########################### - L_w, D_w, M_w, L_t, D_t, M_t = self.compute_aerodynamic_forces( V , alpha , delta ) - L_w_pts = arrow_from_base_components(0, L_w * f_scale, 0, 0) - D_w_pts = arrow_from_base_components(-D_w * f_scale, 0, 0, 0) - L_t_pts = arrow_from_base_components(0, L_t * f_scale, 0, 0) - D_t_pts = arrow_from_base_components(-D_t * f_scale, 0, 0, 0) + L_w_pts = drawing.arrow_from_components( 0, L_w * f_scale ) + D_w_pts = drawing.arrow_from_components( -D_w * f_scale, 0 ) + L_t_pts = drawing.arrow_from_components( 0, L_t * f_scale ) + D_t_pts = drawing.arrow_from_components( -D_t * f_scale, 0 ) body_T_acw = geometry.transformation_matrix_2D( 0 , -self.l_w , 0 ) body_T_act = geometry.transformation_matrix_2D( 0 , -self.l_t , 0 ) diff --git a/pyro/kinematic/drawing.py b/pyro/kinematic/drawing.py index 2098a088..b77e5c18 100644 --- a/pyro/kinematic/drawing.py +++ b/pyro/kinematic/drawing.py @@ -14,20 +14,27 @@ ############################################################################### +############################################################################### +### Transformation tools +############################################################################### + ############################################################################### def transform_points_2D( A_T_B , pts_B ): """ Take a list of pts in a given frame B and express them in frame A base on - transformation matrix A_T_B + transformation matrix A_T_B that describe a 2D transform in the x-y plane Parameters ---------- pts : TYPE DESCRIPTION. - T : TYPE - DESCRIPTION. + T : 3 x 3 numpy array + A 2D transformation matrix + T = np.array([ [ c , -s , x ] , + [ s , c , y ] , + [ 0 , 0 , 1 ] ]) Returns ------- @@ -36,11 +43,73 @@ def transform_points_2D( A_T_B , pts_B ): """ + # Init output array of same dimension pts_A = np.zeros( pts_B.shape ) - # For all pts in the list + # save z values + z = pts_B[:,2] + + # set last component to 1 for homegeneous transform operations + pts_B[:,2] = 1. + + # Transform all (x,y) pts in the list for i in range(pts_B.shape[0]): pts_A[ i ] = A_T_B @ pts_B[ i ] + + # reset original z values + pts_A[:,2] = z return pts_A + + +############################################################################### +### drawing shorcuts tools +############################################################################### + + +############################################################################### +def arrow_from_length_angle( l , theta , x = 0 , y = 0 , w = None , origin = 'base'): + + # width of arrow secondary lines + if w is None: + d = l * 0.15 + else: + d = w + + # Local points + + if origin == 'tip': + + pts_local = np.array([ [ -l , 0 , 0. ] , + [ 0 , 0 , 0. ] , + [ -d , d , 0. ] , + [ 0 , 0 , 0. ] , + [ -d , -d , 0. ] ]) + + # elif origin == 'base': + else: + + pts_local = np.array([ [ 0 , 0 , 0. ] , + [ l , 0 , 0. ] , + [ l-d , d , 0. ] , + [ l , 0 , 0. ] , + [ l-d , -d , 0. ] ]) + + T = geometry.transformation_matrix_2D( theta , x , y ) + + pts_global = transform_points_2D( T , pts_local ) + + + return pts_global + + +############################################################################### +def arrow_from_components( vx , vy , x = 0 , y = 0 , w = None , origin = 'base' ): + + l = np.sqrt( vx**2 + vy**2 ) + theta = np.arctan2( vy , vx ) + + return arrow_from_length_angle( l , theta , x , y , w , origin ) + + From c8ebce4f3e4d2bdb6936604c6125a90f7ec08c00 Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Thu, 14 Sep 2023 11:04:13 -0400 Subject: [PATCH 13/93] rocket using drawing lib --- pyro/dynamic/rocket.py | 82 ++++++++++++++++++++---------------------- 1 file changed, 38 insertions(+), 44 deletions(-) diff --git a/pyro/dynamic/rocket.py b/pyro/dynamic/rocket.py index 2e7073bc..203f1e3d 100644 --- a/pyro/dynamic/rocket.py +++ b/pyro/dynamic/rocket.py @@ -10,8 +10,9 @@ import numpy as np import matplotlib.pyplot as plt ############################################################################### -from pyro.dynamic import system -from pyro.dynamic import mechanical +from pyro.dynamic import mechanical +from pyro.kinematic import geometry +from pyro.kinematic import drawing ############################################################################### @@ -63,6 +64,24 @@ def __init__(self): self.dynamic_domain = True self.dynamic_range = 10 + # rocket drawing + pts = np.zeros(( 10 , 3 )) + l = self.height + w = self.width + + pts[0,:] = np.array([ 0, -l,0]) + pts[1,:] = np.array([-w, -l,0]) + pts[2,:] = np.array([-w, +l,0]) + pts[3,:] = np.array([ 0,l+w,0]) + pts[4,:] = np.array([+w, +l,0]) + pts[5,:] = np.array([+w, -l,0]) + pts[6,:] = pts[0,:] + pts[7,:] = pts[0,:] + np.array([-w,-w,0]) + pts[8,:] = pts[0,:] + np.array([+w,-w,0]) + pts[9,:] = pts[0,:] + + self.drawing_body_pts = pts + ########################################################################### def H(self, q ): @@ -206,32 +225,21 @@ def forward_kinematic_lines(self, q ): # body ########################### - x = q[0] - y = q[1] - s = np.sin(q[2]) - c = np.cos(q[2]) - l = self.height - w = self.width - - pts = np.zeros(( 10 , 3 )) - pts[0,:] = np.array([x+l*s,y-l*c,0]) - pts[1,:] = pts[0,:] + np.array([-w*c,-w*s,0]) - pts[2,:] = pts[1,:] + np.array([-2*l*s,2*l*c,0]) - pts[3,:] = np.array([x-(l+w)*s,y+(l+w)*c,0]) - pts[4,:] = np.array([x-l*s+w*c,y+l*c+w*s,0]) - pts[5,:] = pts[4,:] + np.array([2*l*s,-2*l*c,0]) - pts[6,:] = pts[0,:] - pts[7,:] = pts[0,:] + np.array([w*s-w*c,-w*c-w*s,0]) - pts[8,:] = pts[0,:] + np.array([w*s+w*c,-w*c+w*s,0]) - pts[9,:] = pts[0,:] + x = q[0] + y = q[1] + theta = q[2] + W_T_B = geometry.transformation_matrix_2D( theta , x , y ) - lines_pts.append( pts ) + pts_B = self.drawing_body_pts + pts_W = drawing.transform_points_2D( W_T_B , pts_B ) + + lines_pts.append( pts_W ) lines_style.append( '-') lines_color.append( 'b' ) ########################### - # cg + # C.G. ########################### pts = np.zeros(( 1 , 3 )) @@ -257,32 +265,18 @@ def forward_kinematic_lines_plus(self, x , u , t ): lines_color = [] ########################### - # trust force vectors + # trust force vector ########################### - l = self.height - - s = np.sin(x[2]) - c = np.cos(x[2]) - + length = u[0] * 0.0002 # arrow length + theta = u[1] - 0.5 * np.pi # arrow angle (body frame) + y_offset = -self.height - self.width - xb = x[0]+l*s - yb = x[1]-l*c + pts_body = drawing.arrow_from_length_angle( length, theta, y = y_offset ) + W_T_B = geometry.transformation_matrix_2D( x[2], x[0] , x[1] ) + pts_W = drawing.transform_points_2D( W_T_B , pts_body ) - s = np.sin(x[2]+u[1]) - c = np.cos(x[2]+u[1]) - - T = u[0] * 0.0002 - h = self.width - - pts = np.zeros(( 5 , 3 )) - pts[0,:] = np.array([xb,yb,0]) - pts[1,:] = pts[0,:] + np.array([T*s,-T*c,0]) - pts[2,:] = pts[1,:] + np.array([h*c-h*s,h*s+h*c,0]) - pts[3,:] = pts[1,:] - pts[4,:] = pts[1,:] + np.array([-h*c-h*s,-h*s+h*c,0]) - - lines_pts.append( pts ) + lines_pts.append( pts_W ) lines_style.append( '-') lines_color.append( 'r' ) From cadcbb3c4b14ae4ab7077c4a93c0ece50a551970 Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Thu, 14 Sep 2023 15:05:00 -0400 Subject: [PATCH 14/93] plane with controller --- dev/plane/plane_controller.py | 86 +++++++++++++++++++++++++++++++++++ pyro/dynamic/plane.py | 23 ++-------- pyro/dynamic/rocket.py | 2 +- 3 files changed, 91 insertions(+), 20 deletions(-) create mode 100644 dev/plane/plane_controller.py diff --git a/dev/plane/plane_controller.py b/dev/plane/plane_controller.py new file mode 100644 index 00000000..49774193 --- /dev/null +++ b/dev/plane/plane_controller.py @@ -0,0 +1,86 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Created on Fri Sep 8 10:38:30 2023 + +@author: alex +""" + +import numpy as np + +from pyro.dynamic import plane + +from pyro.control import controller + + + + +############################################################################### + +class PLaneController( controller.StaticController ) : + + ############################ + def __init__( self ): + """ """ + + # Dimensions + self.k = 1 + self.m = 2 + self.p = 6 + + super().__init__(self.k, self.m, self.p) + + # Label + self.name = 'Plane Controller' + + self.v_ref = 40 + self.y_ref = 0.0 + + + ############################# + def c( self , y , r , t = 0 ): + """ + Feedback static computation u = c(y,r,t) + + INPUTS + y : sensor signal_proc vector p x 1 + r : reference signal_proc vector k x 1 + t : time 1 x 1 + + OUPUTS + u : control inputs vector m x 1 + + """ + + v = y[3] + theta = y[2] + y = y[1] + + T = 10 * ( self.v_ref - v ) + + theta_ref = 0.1 * ( self.y_ref - y ) + + delta = -0.5 * ( theta_ref - theta ) + + u = np.array([ T , delta ]) + + + return u + + +sys = plane.Plane2D() +sys.l_w = 0.0 + +sys.x0 = np.array([0,0,0.2,15,0,0]) + +ctl = PLaneController() + +cl_sys = ctl + sys + +cl_sys.compute_trajectory( 2 ) + +cl_sys.plot_trajectory() +cl_sys.animate_simulation( time_factor_video = 0.2 ) + + + diff --git a/pyro/dynamic/plane.py b/pyro/dynamic/plane.py index d3c81935..38fe4e2d 100644 --- a/pyro/dynamic/plane.py +++ b/pyro/dynamic/plane.py @@ -547,40 +547,25 @@ def forward_kinematic_lines_plus(self, x , u , t ): L_w_pts_global = drawing.transform_points_2D( world_T_body @ body_T_acw @ body_T_wind , L_w_pts ) D_w_pts_global = drawing.transform_points_2D( world_T_body @ body_T_acw @ body_T_wind , D_w_pts ) - #Change color if stalled: - if (alpha < self.alpha_stall ) and (alpha > -self.alpha_stall ): - L_color = 'b' - D_color = 'r' - else: - L_color = 'c' - D_color = 'm' - lines_pts.append( L_w_pts_global ) lines_style.append('-') - lines_color.append( L_color ) + lines_color.append('b') lines_pts.append( D_w_pts_global ) lines_style.append('-') - lines_color.append( D_color ) + lines_color.append( 'r' ) L_t_pts_global = drawing.transform_points_2D( world_T_body @ body_T_act @ body_T_wind , L_t_pts ) D_t_pts_global = drawing.transform_points_2D( world_T_body @ body_T_act @ body_T_wind , D_t_pts ) - #Change color if stalled: - if (( alpha + delta ) < self.alpha_stall ) and ( ( alpha + delta ) > -self.alpha_stall ): - L_color = 'b' - D_color = 'r' - else: - L_color = 'c' - D_color = 'm' lines_pts.append( L_t_pts_global ) lines_style.append('-') - lines_color.append( L_color ) + lines_color.append('b') lines_pts.append( D_t_pts_global ) lines_style.append('-') - lines_color.append( D_color ) + lines_color.append('r') return lines_pts , lines_style , lines_color diff --git a/pyro/dynamic/rocket.py b/pyro/dynamic/rocket.py index 203f1e3d..c5c5461d 100644 --- a/pyro/dynamic/rocket.py +++ b/pyro/dynamic/rocket.py @@ -299,7 +299,7 @@ def forward_kinematic_lines_plus(self, x , u , t ): sys.x0[0] = 0 sys.ubar[0] = sys.mass * sys.gravity * 1.1 - sys.ubar[1] = -0.005 + sys.ubar[1] = -0.001 sys.plot_trajectory() sys.animate_simulation() \ No newline at end of file From ba84f2565db43d44972b252fa609f1c7bdf625aa Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Thu, 14 Sep 2023 21:39:11 -0400 Subject: [PATCH 15/93] added negative normal forces constraints --- dev/dimentionless/dev_car.py | 8 +++--- pyro/dynamic/longitudinal_vehicule.py | 41 +++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/dev/dimentionless/dev_car.py b/dev/dimentionless/dev_car.py index 2313a8fd..ee277709 100644 --- a/dev/dimentionless/dev_car.py +++ b/dev/dimentionless/dev_car.py @@ -15,7 +15,7 @@ from pyro.analysis import graphical ############################################################################## -case_name = 'car_test_l5_w50' +case_name = 'car_test' show = True res = 'std' legend = 1 @@ -24,7 +24,7 @@ #dim context x_c_star = 0.5 -y_c_star = 0.5 +y_c_star = 0.7 x_w_star = 50.0 # param @@ -40,7 +40,7 @@ x_max = 10 * length v_max = 2 * np.sqrt( gravity * length ) t_max = 10 * x_max / v_max -s_max = 0.3 +s_max = 0.1 j_max = t_max * ( ( x_max / x_w ) **2 + s_max ** 2 ) @@ -66,7 +66,7 @@ sys.cdA = 0. # drag coef time area [m2] # Ground traction curve parameters -sys.mu_max = 0.5 +sys.mu_max = 1.0 sys.mu_slope = 70. sys.u_ub[0] = + s_max diff --git a/pyro/dynamic/longitudinal_vehicule.py b/pyro/dynamic/longitudinal_vehicule.py index cc8a360b..fb9d081b 100644 --- a/pyro/dynamic/longitudinal_vehicule.py +++ b/pyro/dynamic/longitudinal_vehicule.py @@ -183,6 +183,47 @@ def f(self, x , u , t = 0 ): return dx + ############################# + def isavalidinput(self , x , u): + """ check if u is in the control inputs domain given x """ + + ans = False + + # Min-Max Slip + + for i in range(self.m): + ans = ans or ( u[i] < self.u_lb[i] ) + ans = ans or ( u[i] > self.u_ub[i] ) + + # Normal Forces Negatives? + + slip = u + v = x[1] + + # compute ratio of horizontal/vertical force + mu = self.slip2force( slip ) + + # constant params local vairables + ry, rr, rf = self.compute_ratios() + m = self.mass + g = self.gravity + rcda = self.rho * self.cdA + + # Drag froce + fd = 0.5 * rcda * v * np.abs( v ) # drag froce with the right sign + + # Acceleration (equation considering weight transfer) + a = (mu * m * g * rr - fd )/( m * (1 + mu * ry )) + + # Normal force check + fn_front = m * g * rr - m * a * ry + fn_rear = m * g * rf + m * a * ry + + ans = ans or ( fn_front < 0. ) + ans = ans or ( fn_rear < 0. ) + + return not(ans) + ########################################################################### # For graphical output From 4d6d7b9bb3052ebf010e1ba982d75f1e6de93d11 Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Fri, 15 Sep 2023 15:42:10 -0400 Subject: [PATCH 16/93] added new demo script --- .../pendulum_double/double_pendulum.py | 4 +- examples/demos_by_system/plane/plane_cobra.py | 1 + .../plane/plane_simple_controller.py | 86 +++++++++++++++++++ .../demos_by_system/suspension/suspension.py | 25 ++++++ pyro/dynamic/drone.py | 5 +- pyro/dynamic/pendulum.py | 3 - 6 files changed, 118 insertions(+), 6 deletions(-) create mode 100644 examples/demos_by_system/plane/plane_cobra.py create mode 100644 examples/demos_by_system/plane/plane_simple_controller.py create mode 100644 examples/demos_by_system/suspension/suspension.py diff --git a/examples/demos_by_system/pendulum_double/double_pendulum.py b/examples/demos_by_system/pendulum_double/double_pendulum.py index 6fe77040..80675298 100644 --- a/examples/demos_by_system/pendulum_double/double_pendulum.py +++ b/examples/demos_by_system/pendulum_double/double_pendulum.py @@ -15,7 +15,9 @@ sys = pendulum.DoublePendulum() # Simultation -sys.x0 = np.array([-0.1,0,0,0]) +sys.x0 = np.array([-0.1,0,0,0]) +sys.ubar = np.array([-0.1,0.0]) + sys.plot_trajectory() sys.plot_phase_plane_trajectory(0, 2) sys.animate_simulation() \ No newline at end of file diff --git a/examples/demos_by_system/plane/plane_cobra.py b/examples/demos_by_system/plane/plane_cobra.py new file mode 100644 index 00000000..d0677ba9 --- /dev/null +++ b/examples/demos_by_system/plane/plane_cobra.py @@ -0,0 +1 @@ +#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ Created on Fri Sep 8 10:38:30 2023 @author: alex """ import numpy as np from pyro.dynamic import plane sys = plane.Plane2D() sys.plot_alpha2Cl() sys.x0 = np.array([0,0,0.2,15,0,0]) def t2u(t): u = np.array([ 2 * t , -0.12 * t ]) return u sys.t2u = t2u #sys.gravity = 0 sys.compute_trajectory( 10 , 20001 , 'euler' ) sys.plot_trajectory('x') # sys.dynamic_domain = False sys.animate_simulation( time_factor_video=0.5 ) \ No newline at end of file diff --git a/examples/demos_by_system/plane/plane_simple_controller.py b/examples/demos_by_system/plane/plane_simple_controller.py new file mode 100644 index 00000000..49774193 --- /dev/null +++ b/examples/demos_by_system/plane/plane_simple_controller.py @@ -0,0 +1,86 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Created on Fri Sep 8 10:38:30 2023 + +@author: alex +""" + +import numpy as np + +from pyro.dynamic import plane + +from pyro.control import controller + + + + +############################################################################### + +class PLaneController( controller.StaticController ) : + + ############################ + def __init__( self ): + """ """ + + # Dimensions + self.k = 1 + self.m = 2 + self.p = 6 + + super().__init__(self.k, self.m, self.p) + + # Label + self.name = 'Plane Controller' + + self.v_ref = 40 + self.y_ref = 0.0 + + + ############################# + def c( self , y , r , t = 0 ): + """ + Feedback static computation u = c(y,r,t) + + INPUTS + y : sensor signal_proc vector p x 1 + r : reference signal_proc vector k x 1 + t : time 1 x 1 + + OUPUTS + u : control inputs vector m x 1 + + """ + + v = y[3] + theta = y[2] + y = y[1] + + T = 10 * ( self.v_ref - v ) + + theta_ref = 0.1 * ( self.y_ref - y ) + + delta = -0.5 * ( theta_ref - theta ) + + u = np.array([ T , delta ]) + + + return u + + +sys = plane.Plane2D() +sys.l_w = 0.0 + +sys.x0 = np.array([0,0,0.2,15,0,0]) + +ctl = PLaneController() + +cl_sys = ctl + sys + +cl_sys.compute_trajectory( 2 ) + +cl_sys.plot_trajectory() +cl_sys.animate_simulation( time_factor_video = 0.2 ) + + + diff --git a/examples/demos_by_system/suspension/suspension.py b/examples/demos_by_system/suspension/suspension.py new file mode 100644 index 00000000..5efab0c3 --- /dev/null +++ b/examples/demos_by_system/suspension/suspension.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Created on Sun Oct 16 22:27:47 2022 + +@author: alex +""" + +import numpy as np + +from pyro.dynamic import suspension +from pyro.analysis import costfunction +from pyro.planning import dynamicprogramming +from pyro.planning import discretizer + +sys = suspension.QuarterCarOnRoughTerrain() + +sys.k = 10.0 +sys.vx = 10.0 + + +# Simulation and animation +sys.compute_trajectory( 10, 10001, 'euler') +sys.plot_trajectory('xu') +sys.animate_simulation() \ No newline at end of file diff --git a/pyro/dynamic/drone.py b/pyro/dynamic/drone.py index 059d6dff..9c06b546 100644 --- a/pyro/dynamic/drone.py +++ b/pyro/dynamic/drone.py @@ -794,8 +794,9 @@ def forward_kinematic_lines_plus(self, x , u , t ): if __name__ == "__main__": """ MAIN TEST """ + #sys = Drone2D() - if False: + if True: sys = Drone2D() @@ -807,7 +808,7 @@ def forward_kinematic_lines_plus(self, x , u , t ): sys.plot_trajectory() sys.animate_simulation() - if True: + if False: sys = SpeedControlledDrone2D() diff --git a/pyro/dynamic/pendulum.py b/pyro/dynamic/pendulum.py index e1fbaa62..a5fad64f 100644 --- a/pyro/dynamic/pendulum.py +++ b/pyro/dynamic/pendulum.py @@ -1163,9 +1163,6 @@ def forward_kinematic_lines_plus(self, x , u , t ): if __name__ == "__main__": """ MAIN TEST """ - sys = DoublePendulum() - - #sys = SinglePendulum() if False: From c03d54c60166b19018f87414be4f34eb9a094c9e Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Fri, 22 Sep 2023 10:13:33 -0400 Subject: [PATCH 17/93] rename folder and fast demo --- .../cases_master.py | 2 +- .../dev_car.py | 4 +- .../pendulum_optimal_swingup_demo.py | 69 +++++++++++++++++++ 3 files changed, 72 insertions(+), 3 deletions(-) rename dev/{dimentionless => dimensionless}/cases_master.py (99%) rename dev/{dimentionless => dimensionless}/dev_car.py (99%) create mode 100644 examples/demos_by_tool/dynamicprogramming/pendulum_optimal_swingup_demo.py diff --git a/dev/dimentionless/cases_master.py b/dev/dimensionless/cases_master.py similarity index 99% rename from dev/dimentionless/cases_master.py rename to dev/dimensionless/cases_master.py index 7d9b5056..df00ef99 100644 --- a/dev/dimentionless/cases_master.py +++ b/dev/dimensionless/cases_master.py @@ -381,7 +381,7 @@ def sensitivity( ts , qs , res = 'mid' , name = 'sensitivity' , legend = 1): ### Quick tests #################################### -res = 'test' +res = 'std' dp , cl_sys = case( m=1 , g=10 , l=1 , t_max_star=0.5 , q_star= 0.1 , case_name = 'test', res = res, show = True ) diff --git a/dev/dimentionless/dev_car.py b/dev/dimensionless/dev_car.py similarity index 99% rename from dev/dimentionless/dev_car.py rename to dev/dimensionless/dev_car.py index ee277709..19ccd64b 100644 --- a/dev/dimentionless/dev_car.py +++ b/dev/dimensionless/dev_car.py @@ -24,8 +24,8 @@ #dim context x_c_star = 0.5 -y_c_star = 0.7 -x_w_star = 50.0 +y_c_star = 1.2 +x_w_star = 10.0 # param length = 5.0 diff --git a/examples/demos_by_tool/dynamicprogramming/pendulum_optimal_swingup_demo.py b/examples/demos_by_tool/dynamicprogramming/pendulum_optimal_swingup_demo.py new file mode 100644 index 00000000..260afca4 --- /dev/null +++ b/examples/demos_by_tool/dynamicprogramming/pendulum_optimal_swingup_demo.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Created on Sun Oct 16 22:27:47 2022 + +@author: alex +""" + +import numpy as np + +from pyro.dynamic import pendulum +from pyro.analysis import costfunction +from pyro.planning import dynamicprogramming +from pyro.planning import discretizer + +sys = pendulum.SinglePendulum() + +sys.x_ub = np.array([+10, +10]) +sys.x_lb = np.array([-10, -10]) + +# Discrete world +grid_sys = discretizer.GridDynamicSystem( sys , [201,201] , [21] ) + +# Cost Function +qcf = costfunction.QuadraticCostFunction.from_sys(sys) + +qcf.xbar = np.array([ -3.14 , 0 ]) # target +qcf.INF = 500 + +qcf.R[0,0] = 1.0 + +qcf.S[0,0] = 10.0 +qcf.S[1,1] = 10.0 + + +# DP algo +dp = dynamicprogramming.DynamicProgrammingWithLookUpTable( grid_sys, qcf) +#dp = dprog.DynamicProgramming2DRectBivariateSpline(grid_sys, qcf) + +#dp.solve_bellman_equation( animate_cost2go = True ) + +#dp.compute_steps(200) +# dp.plot_policy() + +#dp.solve_bellman_equation( tol = 1) +dp.solve_bellman_equation( tol = 0.1 , animate_cost2go = True ) +#dp.solve_bellman_equation( tol = 1 , animate_policy = True ) +#dp.plot_cost2go(150) + +#dp.animate_cost2go( show = False , save = True ) +#dp.animate_policy( show = False , save = True ) + +dp.clean_infeasible_set() +dp.plot_cost2go_3D() +dp.plot_policy() + +ctl = dp.get_lookup_table_controller() + + +#ctl.plot_control_law( sys = sys , n = 100) + + +#asign controller +cl_sys = ctl + sys +cl_sys.x0 = np.array([0., 0.]) +cl_sys.compute_trajectory( 10, 10001, 'euler') +cl_sys.plot_trajectory('xu') +cl_sys.plot_phase_plane_trajectory() +cl_sys.animate_simulation() From 046c3e94de0b423365fd64d3b801a13cb7c38bec Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Fri, 29 Sep 2023 14:37:13 -0400 Subject: [PATCH 18/93] quick n dirty traj plot update function --- pyro/analysis/graphical.py | 85 ++++++++++++++++++++++++++++++++++---- 1 file changed, 78 insertions(+), 7 deletions(-) diff --git a/pyro/analysis/graphical.py b/pyro/analysis/graphical.py index 475c9ab3..011f27cf 100644 --- a/pyro/analysis/graphical.py +++ b/pyro/analysis/graphical.py @@ -114,6 +114,8 @@ def plot(self, traj, plot = 'x' , show = True ): simfig , plots = plt.subplots(l, sharex=True, figsize=self.figsize, dpi=self.dpi, frameon=True) + + lines = [None] * l ####################################################################### #Fix bug for single variable plotting @@ -128,7 +130,7 @@ def plot(self, traj, plot = 'x' , show = True ): if plot=='All' or plot=='x' or plot=='xu' or plot=='xy' or plot=='xuj': # For all states for i in range( sys.n ): - plots[j].plot( traj.t , traj.x[:,i] , 'b') + lines[j] = plots[j].plot( traj.t , traj.x[:,i] , 'b')[0] plots[j].set_ylabel(sys.state_label[i] +'\n'+ sys.state_units[i] , fontsize=self.fontsize ) plots[j].grid(True) @@ -138,7 +140,7 @@ def plot(self, traj, plot = 'x' , show = True ): if plot == 'All' or plot == 'u' or plot == 'xu' or plot == 'xuj': # For all inputs for i in range( sys.m ): - plots[j].plot( traj.t , traj.u[:,i] , 'r') + lines[j] = plots[j].plot( traj.t , traj.u[:,i] , 'r')[0] plots[j].set_ylabel(sys.input_label[i] + '\n' + sys.input_units[i] , fontsize=self.fontsize ) plots[j].grid(True) @@ -148,7 +150,7 @@ def plot(self, traj, plot = 'x' , show = True ): if plot == 'All' or plot == 'y' or plot == 'xy': # For all outputs for i in range( sys.p ): - plots[j].plot( traj.t , traj.y[:,i] , 'k') + lines[j] = plots[j].plot( traj.t , traj.y[:,i] , 'k')[0] plots[j].set_ylabel(sys.output_label[i] + '\n' + sys.output_units[i] , fontsize=self.fontsize ) plots[j].grid(True) @@ -157,12 +159,12 @@ def plot(self, traj, plot = 'x' , show = True ): if plot == 'All' or plot == 'j' or plot == 'xuj': # Cost function - plots[j].plot( traj.t , traj.dJ[:] , 'b') + lines[j] = plots[j].plot( traj.t , traj.dJ[:] , 'b')[0] plots[j].set_ylabel('dJ', fontsize=self.fontsize ) plots[j].grid(True) plots[j].tick_params( labelsize = self.fontsize ) j = j + 1 - plots[j].plot( traj.t , traj.J[:] , 'r') + lines[j] = plots[j].plot( traj.t , traj.J[:] , 'r')[0] plots[j].set_ylabel('J', fontsize=self.fontsize ) plots[j].grid(True) plots[j].tick_params( labelsize = self.fontsize ) @@ -172,7 +174,7 @@ def plot(self, traj, plot = 'x' , show = True ): # Internal states n = sys.n - sys.controller.l for i in range( l ): - plots[j].plot( traj.t , traj.x[:,i+n] , 'b') + lines[j] = plots[j].plot( traj.t , traj.x[:,i+n] , 'b')[0] plots[j].set_ylabel(sys.state_label[i+n] +'\n'+ sys.state_units[i+n] , fontsize=self.fontsize ) plots[j].grid(True) @@ -189,8 +191,64 @@ def plot(self, traj, plot = 'x' , show = True ): self.fig = simfig self.plots = plots + self.lines = lines + self.l = l + + return (simfig, plots, lines) + + ########################################################################## + def update_plot(self, traj, plot = 'x'): + """ + Create a figure with trajectories for states, inputs, outputs and cost + ---------------------------------------------------------------------- + plot = 'All' + plot = 'xu' + plot = 'xy' + plot = 'x' + plot = 'u' + plot = 'y' + plot = 'j' + plot = 'z' + """ + + lines = self.lines + + j = 0 + + if plot=='All' or plot=='x' or plot=='xu' or plot=='xy' or plot=='xuj': + + # For all states + for i in range( sys.n ): + lines[j].set_data( traj.t , traj.x[:,i] ) + j = j + 1 + + if plot == 'All' or plot == 'u' or plot == 'xu' or plot == 'xuj': + # For all inputs + for i in range( sys.m ): + lines[j].set_data( traj.t , traj.u[:,i] ) + j = j + 1 + + if plot == 'All' or plot == 'y' or plot == 'xy': + # For all outputs + for i in range( sys.p ): + lines[j].set_data( traj.t , traj.y[:,i] ) + j = j + 1 + + if plot == 'All' or plot == 'j' or plot == 'xuj': + # Cost function + lines[j].set_data( traj.t , traj.dJ[:,i] ) + j = j + 1 + lines[j].set_data( traj.t , traj.J[:,i] ) + j = j + 1 + + if plot == 'z': + # Internal states + n = sys.n - sys.controller.l + for i in range( self.l ): + lines[j].set_data( traj.t , traj.x[:,i+n] ) + j = j + 1 - return (simfig, plots) + ########################################################################## def phase_plane_trajectory(self, traj, x_axis=0, y_axis=1): @@ -843,6 +901,19 @@ def __animate__(self,i): sys = pendulum.DoublePendulum() sys.x0 = np.array([0.1,0.1,0,0]) + + + + traj = sys.compute_trajectory( 2.0 ) + + + plotter = TrajectoryPlotter( sys ) + plotter.plot( traj, 'xu' ) + + sys.x0 = np.array([0.2,0.1,0,0]) + traj2 = sys.compute_trajectory( 2.0 ) + + plotter.update_plot( traj2 ) is_3d = False From c5ea31f80edd347f6f3fa3f783762ee566a550a4 Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Fri, 29 Sep 2023 15:22:59 -0400 Subject: [PATCH 19/93] added dynamic plot to show convergence step with trajectory optimization --- .../car_trajectory_optimisation.py | 3 +- .../cartpole_with_trajectory_optimization.py | 3 +- .../planar_drone_trajectory_optimisation.py | 6 ++- .../linear_trajectory_optimisation.py | 3 +- .../mountain_car_trajectory_optimization.py | 45 +++++++++++++++++++ pyro/analysis/graphical.py | 14 ++++++ pyro/planning/trajectoryoptimisation.py | 42 +++++++++++++++-- 7 files changed, 109 insertions(+), 7 deletions(-) create mode 100644 examples/demos_by_tool/trajectory_planning/mountain_car_trajectory_optimization.py diff --git a/examples/demos_by_system/car_steering/car_trajectory_optimisation.py b/examples/demos_by_system/car_steering/car_trajectory_optimisation.py index 72af987b..fed1da49 100644 --- a/examples/demos_by_system/car_steering/car_trajectory_optimisation.py +++ b/examples/demos_by_system/car_steering/car_trajectory_optimisation.py @@ -34,7 +34,8 @@ planner.x_goal = np.array([ 0,0,0]) planner.maxiter = 1000 +# planner.ini planner.compute_optimal_trajectory() -planner.show_solution() +# planner.show_solution() planner.animate_solution() diff --git a/examples/demos_by_system/cartpole/cartpole_with_trajectory_optimization.py b/examples/demos_by_system/cartpole/cartpole_with_trajectory_optimization.py index 2327a207..467343c7 100644 --- a/examples/demos_by_system/cartpole/cartpole_with_trajectory_optimization.py +++ b/examples/demos_by_system/cartpole/cartpole_with_trajectory_optimization.py @@ -43,7 +43,8 @@ planner.x_start = np.array([0,0,0,0]) planner.x_goal = np.array([0,np.pi,0,0]) +planner.init_dynamic_plot() planner.maxiter = 500 planner.compute_optimal_trajectory() -planner.show_solution() +# planner.show_solution() planner.animate_solution() \ No newline at end of file diff --git a/examples/demos_by_system/drone/planar_drone_trajectory_optimisation.py b/examples/demos_by_system/drone/planar_drone_trajectory_optimisation.py index 4046621b..2899c344 100644 --- a/examples/demos_by_system/drone/planar_drone_trajectory_optimisation.py +++ b/examples/demos_by_system/drone/planar_drone_trajectory_optimisation.py @@ -14,12 +14,16 @@ sys = Drone2D() +sys.u_ub = np.array([10.0, 10.0]) + planner = DirectCollocationTrajectoryOptimisation( sys ) planner.x_start = np.array([-5,0,0,0,0,0]) planner.x_goal = np.array([0,0,0,0,0,0]) +planner.maxiter = 200 +planner.init_dynamic_plot() planner.compute_optimal_trajectory() -planner.show_solution() +# planner.show_solution() planner.animate_solution() diff --git a/examples/demos_by_tool/trajectory_planning/linear_trajectory_optimisation.py b/examples/demos_by_tool/trajectory_planning/linear_trajectory_optimisation.py index 192f12d8..01748825 100644 --- a/examples/demos_by_tool/trajectory_planning/linear_trajectory_optimisation.py +++ b/examples/demos_by_tool/trajectory_planning/linear_trajectory_optimisation.py @@ -22,8 +22,9 @@ planner.x_start = np.array([0.5,0.5,0,0]) planner.x_goal = np.array([0,0,0,0]) +planner.init_dynamic_plot() planner.compute_optimal_trajectory() -planner.show_solution() +# planner.show_solution() planner.animate_solution() diff --git a/examples/demos_by_tool/trajectory_planning/mountain_car_trajectory_optimization.py b/examples/demos_by_tool/trajectory_planning/mountain_car_trajectory_optimization.py new file mode 100644 index 00000000..1908bc45 --- /dev/null +++ b/examples/demos_by_tool/trajectory_planning/mountain_car_trajectory_optimization.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Created on Fri Sep 29 15:03:52 2023 + +@author: alex +""" + +############################################################################## +import numpy as np +############################################################################## +from pyro.dynamic import mountaincar +from pyro.planning import trajectoryoptimisation +from pyro.analysis import costfunction +############################################################################## + +sys = mountaincar.MountainCar() + +sys.x_ub = np.array([+0.2,+2.0]) +sys.x_lb = np.array([-1.7,-2.0]) + +sys.u_ub[0] = +1.5 +sys.u_lb[0] = -1.5 + + +# Cost Function +qcf = costfunction.QuadraticCostFunction.from_sys(sys) + +qcf.xbar = np.array([ 0 , 0 ]) # target +qcf.INF = 30 + +qcf.R[0,0] = 10.0 + +qcf.S[0,0] = 10.0 +qcf.S[1,1] = 10.0 + +planner = trajectoryoptimisation.DirectCollocationTrajectoryOptimisation( sys , 0.1, 40 ) + +planner.x_start = np.array([-1.0,+0.0]) +planner.x_goal = np.array([+0.0,+0.0]) + +planner.init_dynamic_plot() +planner.compute_optimal_trajectory() +# planner.show_solution() +planner.animate_solution() diff --git a/pyro/analysis/graphical.py b/pyro/analysis/graphical.py index 011f27cf..f23db56d 100644 --- a/pyro/analysis/graphical.py +++ b/pyro/analysis/graphical.py @@ -212,6 +212,8 @@ def update_plot(self, traj, plot = 'x'): """ lines = self.lines + sys = self.sys + plots = self.plots j = 0 @@ -220,25 +222,35 @@ def update_plot(self, traj, plot = 'x'): # For all states for i in range( sys.n ): lines[j].set_data( traj.t , traj.x[:,i] ) + plots[j].relim() + plots[j].autoscale_view(True,True,True) j = j + 1 if plot == 'All' or plot == 'u' or plot == 'xu' or plot == 'xuj': # For all inputs for i in range( sys.m ): lines[j].set_data( traj.t , traj.u[:,i] ) + plots[j].relim() + plots[j].autoscale_view(True,True,True) j = j + 1 if plot == 'All' or plot == 'y' or plot == 'xy': # For all outputs for i in range( sys.p ): lines[j].set_data( traj.t , traj.y[:,i] ) + plots[j].relim() + plots[j].autoscale_view(True,True,True) j = j + 1 if plot == 'All' or plot == 'j' or plot == 'xuj': # Cost function lines[j].set_data( traj.t , traj.dJ[:,i] ) + plots[j].relim() + plots[j].autoscale_view(True,True,True) j = j + 1 lines[j].set_data( traj.t , traj.J[:,i] ) + plots[j].relim() + plots[j].autoscale_view(True,True,True) j = j + 1 if plot == 'z': @@ -246,6 +258,8 @@ def update_plot(self, traj, plot = 'x'): n = sys.n - sys.controller.l for i in range( self.l ): lines[j].set_data( traj.t , traj.x[:,i+n] ) + plots[j].relim() + plots[j].autoscale_view(True,True,True) j = j + 1 diff --git a/pyro/planning/trajectoryoptimisation.py b/pyro/planning/trajectoryoptimisation.py index 06fe86fa..40b88f7e 100644 --- a/pyro/planning/trajectoryoptimisation.py +++ b/pyro/planning/trajectoryoptimisation.py @@ -7,10 +7,13 @@ """ import numpy as np +import matplotlib.pyplot as plt + from scipy.optimize import minimize from pyro.analysis import simulation from pyro.planning import plan +from pyro.analysis import graphical ############################################################################### @@ -27,7 +30,7 @@ class DirectCollocationTrajectoryOptimisation( plan.Planner ): """ ############################ - def __init__(self, sys , dt = 0.2 , grid = 20 , cost_function = None ): + def __init__(self, sys , dt = 0.2 , grid = 20 , cost_function = None, dynamic_plot = False ): # Set sys, default cost function x_start and x_goal @@ -48,6 +51,24 @@ def __init__(self, sys , dt = 0.2 , grid = 20 , cost_function = None ): # Memory variable self.iter_count = 0 + # Optional Convergence Graph + self.dynamic_plot = dynamic_plot + + if dynamic_plot: + + self.init_dynamic_plot() + + + ############################ + def init_dynamic_plot(self): + + # Graphic option + self.dynamic_plot = True + + traj = self.decisionvariables2traj( self.dec_init ) + self.plotter = graphical.TrajectoryPlotter( self.sys ) + self.plotter.plot( traj, 'xu' ) + ############################ def set_initial_trajectory_guest(self, traj): @@ -55,6 +76,12 @@ def set_initial_trajectory_guest(self, traj): new_traj = traj.re_sample( self.grid ) self.dec_init = self.traj2decisionvariables( new_traj ) + if self.dynamic_plot: + + traj = self.decisionvariables2traj( self.dec_init ) + self.plotter.update_plot( traj, 'xu' ) + plt.pause( 0.001 ) + ############################ def decisionvariables2xu(self, dec ): @@ -291,12 +318,19 @@ def compute_bounds(self): ############################## - def display_callback(self, a ): + def display_callback(self, x ): self.iter_count = self.iter_count + 1 print('Optimizing trajectory: Iteration', self.iter_count) + if self.dynamic_plot: + + traj = self.decisionvariables2traj( x ) + self.plotter.update_plot( traj, 'xu' ) + plt.pause( 0.001 ) + + ############################## def compute_optimal_trajectory(self): @@ -346,5 +380,7 @@ def compute_solution(self): planner.x_start = np.array([0.1,0]) planner.x_goal = np.array([-3.14,0]) + planner.init_dynamic_plot() + planner.compute_solution() - planner.show_solution() \ No newline at end of file + # planner.show_solution() \ No newline at end of file From 76c6fc78d646d9d23d7193e0be16ea6fba909d14 Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Mon, 2 Oct 2023 10:40:14 -0400 Subject: [PATCH 20/93] added time print and vectorized residues computation --- ...et_landing_with_trajectory_optimization.py | 62 +++++++++++++++++++ .../plane/plane_simple_controller.py | 4 +- pyro/planning/trajectoryoptimisation.py | 25 +++++--- 3 files changed, 79 insertions(+), 12 deletions(-) create mode 100644 dev/trajectory_optim_test/rocket_landing_with_trajectory_optimization.py diff --git a/dev/trajectory_optim_test/rocket_landing_with_trajectory_optimization.py b/dev/trajectory_optim_test/rocket_landing_with_trajectory_optimization.py new file mode 100644 index 00000000..fde41570 --- /dev/null +++ b/dev/trajectory_optim_test/rocket_landing_with_trajectory_optimization.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Created on Sun Oct 3 08:27:06 2021 + +@author: alex +""" + +import numpy as np + +from pyro.dynamic.rocket import Rocket +from pyro.analysis.costfunction import QuadraticCostFunction +from pyro.dynamic.statespace import linearize +from pyro.control.lqr import synthesize_lqr_controller +from pyro.planning import trajectoryoptimisation +from pyro.analysis import costfunction + +# Non-linear model +sys = Rocket() + +sys.inertia = 400 + +sys.xbar = np.array([0,2.2,0,0,0,0]) +sys.ubar = np.array([1,0]) * sys.mass * sys.gravity # Nominal trust = weight + +# Linear model +ss = linearize( sys , 0.01 ) + +# Cost function +cf = QuadraticCostFunction.from_sys( sys ) +cf.Q[0,0] = 1 +cf.Q[1,1] = 10000 +cf.Q[2,2] = 0.1 +cf.Q[3,3] = 0 +cf.Q[4,4] = 10000 +cf.Q[5,5] = 0 +cf.R[0,0] = 0.01 +cf.R[1,1] = 10.0 + +# LQR controller +ctl = synthesize_lqr_controller( ss , cf , sys.xbar , sys.ubar ) + +ctl.ubar + +# Simulation Closed-Loop Non-linear with LQR controller +cl_sys = ctl + sys +cl_sys.x0 = np.array([1,10,-0.8,0,0,0]) +traj = cl_sys.compute_trajectory(10) + +cl_sys.plot_trajectory('xu') + +planner = trajectoryoptimisation.DirectCollocationTrajectoryOptimisation( sys , 0.1, 40 ) + +planner.x_start = np.array([1.,10.,-0.8,0.,0.,0.]) +planner.x_goal = np.array([0.,0.,0.,0.,0.,0.]) + +planner.init_dynamic_plot() +planner.set_initial_trajectory_guest( traj ) +# planner.compute_optimal_trajectory() +# planner.show_solution() +# planner.animate_solution() + diff --git a/examples/demos_by_system/plane/plane_simple_controller.py b/examples/demos_by_system/plane/plane_simple_controller.py index 49774193..1c48dfb1 100644 --- a/examples/demos_by_system/plane/plane_simple_controller.py +++ b/examples/demos_by_system/plane/plane_simple_controller.py @@ -56,9 +56,9 @@ def c( self , y , r , t = 0 ): theta = y[2] y = y[1] - T = 10 * ( self.v_ref - v ) + T = +10 * ( self.v_ref - v ) - theta_ref = 0.1 * ( self.y_ref - y ) + theta_ref = +0.1 * ( self.y_ref - y ) delta = -0.5 * ( theta_ref - theta ) diff --git a/pyro/planning/trajectoryoptimisation.py b/pyro/planning/trajectoryoptimisation.py index 40b88f7e..f4697235 100644 --- a/pyro/planning/trajectoryoptimisation.py +++ b/pyro/planning/trajectoryoptimisation.py @@ -9,6 +9,8 @@ import numpy as np import matplotlib.pyplot as plt +import time + from scipy.optimize import minimize from pyro.analysis import simulation @@ -50,6 +52,7 @@ def __init__(self, sys , dt = 0.2 , grid = 20 , cost_function = None, dynamic_p # Memory variable self.iter_count = 0 + self.start_time = time.time() # Optional Convergence Graph self.dynamic_plot = dynamic_plot @@ -74,7 +77,7 @@ def init_dynamic_plot(self): def set_initial_trajectory_guest(self, traj): new_traj = traj.re_sample( self.grid ) - self.dec_init = self.traj2decisionvariables( new_traj ) + self.dec_init = self.traj2decisionvariables( new_traj )[:,0] if self.dynamic_plot: @@ -256,7 +259,7 @@ def dynamic_constraints(self, dec): x , u = self.decisionvariables2xu( dec ) - residues_vec = np.zeros( (self.grid-1) * self.sys.n ) + residues = np.zeros( ( self.grid - 1 , self.sys.n )) for i in range(self.grid-1): @@ -278,13 +281,9 @@ def dynamic_constraints(self, dec): #num diff delta_x_num = x[:,i+1] - x[:,i] # numerical delta in trajectory data - diff = delta_x_num - delta_x_eqs - - for j in range(self.sys.n): - #TODO numpy manip for replacing slow for loop - residues_vec[i + (self.grid-1) * j ] = diff[j] + residues[i,:] = delta_x_num - delta_x_eqs - return residues_vec + return residues.flatten() ############################## @@ -322,7 +321,8 @@ def display_callback(self, x ): self.iter_count = self.iter_count + 1 - print('Optimizing trajectory: Iteration', self.iter_count) + print('Optimizing trajectory: iteration no', self.iter_count , + ' elapsed time = %.2f' % (time.time() - self.start_time ) ) if self.dynamic_plot: @@ -335,6 +335,8 @@ def display_callback(self, x ): ############################## def compute_optimal_trajectory(self): + self.start_time = time.time() + self.compute_bounds() dynamic_constraints = {'type': 'eq', 'fun': self.dynamic_constraints } @@ -383,4 +385,7 @@ def compute_solution(self): planner.init_dynamic_plot() planner.compute_solution() - # planner.show_solution() \ No newline at end of file + planner.show_solution() + + + \ No newline at end of file From f977f1d561945c3537dee8175c8aff943e8f358c Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Mon, 2 Oct 2023 11:46:12 -0400 Subject: [PATCH 21/93] started vectorized traj opt --- pyro/analysis/costfunction.py | 66 +++++++++++++ pyro/dynamic/statespace.py | 2 + pyro/planning/trajectoryoptimisation.py | 125 +++++++++++++++--------- 3 files changed, 149 insertions(+), 44 deletions(-) diff --git a/pyro/analysis/costfunction.py b/pyro/analysis/costfunction.py index 1e8691e2..dccf6a02 100644 --- a/pyro/analysis/costfunction.py +++ b/pyro/analysis/costfunction.py @@ -206,6 +206,72 @@ def g(self, x, u, t): return dJ + +############################################################################# + +class QuadraticCostFunctionVectorized( CostFunction ): + """ + Vectorized: (x, u , t) can be trajectory of time matrices + + Quadratic cost functions of continuous dynamical systems + ---------------------------------------------- + n : number of states + m : number of control inputs + --------------------------------------- + J = int( g(x,u,t) * dt ) + h( x(T) , T ) + + g = xQx + uRu + h = xSx + + """ + + ############################ + def __init__(self, n, m): + + CostFunction.__init__(self) + + # dimensions + self.n = n + self.m = m + + # Quadratic cost weights + self.Q = np.diag( np.ones(n) ) + self.R = np.diag( np.ones(m) ) + self.S = np.diag( np.zeros(n) ) + + self.is_vectorized = True + + + ############################ + @classmethod + def from_sys(cls, sys): + """ From ContinuousDynamicSystem instance """ + + instance = cls( sys.n , sys.m ) + + return instance + + + ############################# + def h(self, x , t = 0): + """ Final cost function with zero value """ + + # Quadratic terminal cost + J_f = np.diag( x.T @ self.S @ x ) + + return J_f + + + ############################# + def g(self, x, u, t): + """ Quadratic additive cost """ + + + dJ = np.diag( x.T @ self.Q @ x ) + + return dJ + + ############################################################################## diff --git a/pyro/dynamic/statespace.py b/pyro/dynamic/statespace.py index 47725db4..81627a03 100644 --- a/pyro/dynamic/statespace.py +++ b/pyro/dynamic/statespace.py @@ -35,6 +35,8 @@ def __init__(self, A, B, C, D): ContinuousDynamicSystem.__init__( self, n, m, p) + self.is_vectorized = True + ############################################ def _check_dimensions(self): diff --git a/pyro/planning/trajectoryoptimisation.py b/pyro/planning/trajectoryoptimisation.py index f4697235..4ab2565c 100644 --- a/pyro/planning/trajectoryoptimisation.py +++ b/pyro/planning/trajectoryoptimisation.py @@ -50,6 +50,16 @@ def __init__(self, sys , dt = 0.2 , grid = 20 , cost_function = None, dynamic_p self.compute_bounds() self.dec_init = np.zeros( grid * ( sys.n + sys.m ) ) + + # Check if vectorized operation are available + try: + is_vectorized = self.sys.is_vectorized & self.cost_function.is_vectorized + except: + is_vectorized = False + self.is_vectorized = is_vectorized + + + # Memory variable self.iter_count = 0 self.start_time = time.time() @@ -110,17 +120,20 @@ def decisionvariables2xu(self, dec ): m = self.sys.m # number of inputs grid = self.grid # number of time steps - x = np.zeros( ( n , grid ) ) - u = np.zeros( ( m , grid ) ) + # x = np.zeros( ( n , grid ) ) + # u = np.zeros( ( m , grid ) ) - # Extract states variables - for i in range(self.sys.n): - x[i,:] = dec[ i * grid : (i+1) * grid ] + # # Extract states variables + # for i in range(self.sys.n): + # x[i,:] = dec[ i * grid : (i+1) * grid ] - # Extract input variables - for j in range(self.sys.m): - k = n + j - u[j,:] = dec[ k * grid : (k+1) * grid ] + # # Extract input variables + # for j in range(self.sys.m): + # k = n + j + # u[j,:] = dec[ k * grid : (k+1) * grid ] + + x = dec[: n * grid ].reshape( n , grid ) + u = dec[ n * grid :].reshape( m , grid ) return x,u @@ -229,26 +242,39 @@ def cost(self, dec): x,u = self.decisionvariables2xu( dec ) - J = 0 - - for i in range(self.grid -1): - #i - x_i = x[:,i] - u_i = u[:,i] - t_i = i*self.dt - dJi = self.cost_function.g( x_i , u_i, t_i ) + if self.is_vectorized: + + # Vectorized operation version + t = np.linspace(0, ( self.grid - 1 )* self.dt, self.grid) + + dJ = self.cost_function.g( x , u , t ) - #i+1 - x_i1 = x[:,i+1] - u_i1 = u[:,i+1] - t_i1 = (i+1)*self.dt - dJi1 = self.cost_function.g( x_i1 , u_i1, t_i1 ) + J = np.trapz( dJ , t ) - #trapez - dJ = 0.5 * ( dJi + dJi1 ) + else: + + # Loop version + + J = 0 - #integral - J = J + dJ * self.dt + for i in range(self.grid -1): + #i + x_i = x[:,i] + u_i = u[:,i] + t_i = i*self.dt + dJi = self.cost_function.g( x_i , u_i, t_i ) + + #i+1 + x_i1 = x[:,i+1] + u_i1 = u[:,i+1] + t_i1 = (i+1)*self.dt + dJi1 = self.cost_function.g( x_i1 , u_i1, t_i1 ) + + #trapez + dJ = 0.5 * ( dJi + dJi1 ) + + #integral + J = J + dJ * self.dt return J @@ -259,29 +285,40 @@ def dynamic_constraints(self, dec): x , u = self.decisionvariables2xu( dec ) - residues = np.zeros( ( self.grid - 1 , self.sys.n )) - - for i in range(self.grid-1): + if self.is_vectorized: - #i - x_i = x[:,i] - u_i = u[:,i] - t_i = i*self.dt - dx_i = self.sys.f(x_i,u_i,t_i) # analytical state derivatives + # Vectorized operation version - #i+1 - x_i1 = x[:,i+1] - u_i1 = u[:,i+1] - t_i1 = (i+1)*self.dt - dx_i1 = self.sys.f(x_i1,u_i1,t_i1) # analytical state derivatives + pass - #trapez - delta_x_eqs = 0.5 * self.dt * (dx_i + dx_i1) + + else: - #num diff - delta_x_num = x[:,i+1] - x[:,i] # numerical delta in trajectory data + # Loop version + + residues = np.zeros( ( self.grid - 1 , self.sys.n )) - residues[i,:] = delta_x_num - delta_x_eqs + for i in range(self.grid-1): + + #i + x_i = x[:,i] + u_i = u[:,i] + t_i = i*self.dt + dx_i = self.sys.f(x_i,u_i,t_i) # analytical state derivatives + + #i+1 + x_i1 = x[:,i+1] + u_i1 = u[:,i+1] + t_i1 = (i+1)*self.dt + dx_i1 = self.sys.f(x_i1,u_i1,t_i1) # analytical state derivatives + + #trapez + delta_x_eqs = 0.5 * self.dt * (dx_i + dx_i1) + + #num diff + delta_x_num = x[:,i+1] - x[:,i] # numerical delta in trajectory data + + residues[i,:] = delta_x_num - delta_x_eqs return residues.flatten() From 05027cd8930aa0bfc55151106009cde4919c2f31 Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Mon, 2 Oct 2023 12:17:31 -0400 Subject: [PATCH 22/93] working vectorized traj opt for linear sys --- ...near_trajectory_optimisation_vectorized.py | 34 +++++++++++++++++++ pyro/analysis/costfunction.py | 20 ++++++++--- pyro/planning/trajectoryoptimisation.py | 10 +++++- 3 files changed, 59 insertions(+), 5 deletions(-) create mode 100644 examples/demos_by_tool/trajectory_planning/linear_trajectory_optimisation_vectorized.py diff --git a/examples/demos_by_tool/trajectory_planning/linear_trajectory_optimisation_vectorized.py b/examples/demos_by_tool/trajectory_planning/linear_trajectory_optimisation_vectorized.py new file mode 100644 index 00000000..e8fcb3a4 --- /dev/null +++ b/examples/demos_by_tool/trajectory_planning/linear_trajectory_optimisation_vectorized.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Created on Sun Oct 3 08:27:06 2021 + +@author: alex +""" + +import numpy as np + +from pyro.dynamic.massspringdamper import TwoMass +from pyro.planning.trajectoryoptimisation import DirectCollocationTrajectoryOptimisation +from pyro.analysis.costfunction import QuadraticCostFunctionVectorized + + +sys = TwoMass() + +sys.u_ub[0] = +2 +sys.u_lb[0] = 0 + +cf = QuadraticCostFunctionVectorized( 4, 1) +# cf = sys.cost_function + +planner = DirectCollocationTrajectoryOptimisation( sys , 0.1, 40 , cost_function = cf) + +planner.x_start = np.array([0.5,0.5,0,0]) +planner.x_goal = np.array([0,0,0,0]) + +# planner.init_dynamic_plot() +planner.compute_optimal_trajectory() +# planner.show_solution() +planner.animate_solution() + + diff --git a/pyro/analysis/costfunction.py b/pyro/analysis/costfunction.py index dccf6a02..e4129097 100644 --- a/pyro/analysis/costfunction.py +++ b/pyro/analysis/costfunction.py @@ -256,8 +256,15 @@ def from_sys(cls, sys): def h(self, x , t = 0): """ Final cost function with zero value """ - # Quadratic terminal cost - J_f = np.diag( x.T @ self.S @ x ) + if x.ndim == 1 : + + J_f = x.T @ self.S @ x + + + else: + + # Quadratic terminal cost + J_f = np.diag( x.T @ self.S @ x ) return J_f @@ -265,9 +272,14 @@ def h(self, x , t = 0): ############################# def g(self, x, u, t): """ Quadratic additive cost """ - - dJ = np.diag( x.T @ self.Q @ x ) + if x.ndim == 1 : + + dJ = x.T @ self.Q @ x + + else: + + dJ = np.diag( x.T @ self.Q @ x ) return dJ diff --git a/pyro/planning/trajectoryoptimisation.py b/pyro/planning/trajectoryoptimisation.py index 4ab2565c..ae1d20f2 100644 --- a/pyro/planning/trajectoryoptimisation.py +++ b/pyro/planning/trajectoryoptimisation.py @@ -289,7 +289,15 @@ def dynamic_constraints(self, dec): # Vectorized operation version - pass + t = np.linspace(0, ( self.grid - 1 )* self.dt, self.grid) + + dx = self.sys.f( x ,u , t ) + + dx_eqs = 0.5 * ( dx[:,0:-1] + dx[:,1:] ) * self.dt + + dx_num = np.diff( x ) + + residues = dx_num - dx_eqs else: From 325141b7ce324ed8513ea6376089dcabecd7d690 Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Tue, 3 Oct 2023 16:03:59 -0400 Subject: [PATCH 23/93] working on traj opt --- dev/2Ddrone/2Ddrone_trajopt | 158 ++++++++++++++++++ dev/linedrone/linedrone.py | 32 ---- ...rone_trajectory_optimisation_linearized.py | 5 +- ...near_trajectory_optimisation_vectorized.py | 6 +- pyro/analysis/costfunction.py | 4 +- pyro/dynamic/drone.py | 30 ++-- pyro/planning/trajectoryoptimisation.py | 4 +- 7 files changed, 184 insertions(+), 55 deletions(-) create mode 100644 dev/2Ddrone/2Ddrone_trajopt delete mode 100644 dev/linedrone/linedrone.py diff --git a/dev/2Ddrone/2Ddrone_trajopt b/dev/2Ddrone/2Ddrone_trajopt new file mode 100644 index 00000000..8263d972 --- /dev/null +++ b/dev/2Ddrone/2Ddrone_trajopt @@ -0,0 +1,158 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Created on Thu May 18 12:30:17 2023 + +@author: alex +""" + +import numpy as np + +import matplotlib.pyplot as plt + +from pyro.dynamic import drone +from pyro.analysis import costfunction +from pyro.planning import trajectoryoptimisation + +# from pyro.planning import dynamicprogramming + +############################################################################# + +class drone2Dcostfunction( costfunction.CostFunction ): + """ + + + """ + + ############################ + def __init__(self, obstacles ): + + costfunction.CostFunction.__init__(self) + + # dimensions + self.n = 2 + self.m = 2 + + # Quadratic cost weights + self.Q = np.diag( np.ones(self.n) ) + self.R = np.diag( np.ones(self.m) ) + self.S = np.diag( np.zeros(self.n) ) + + self.obstacles = obstacles + + self.is_vectorized = False + + + ############################ + @classmethod + def from_sys(cls, sys): + """ From ContinuousDynamicSystem instance """ + + instance = cls( sys.n , sys.m ) + + return instance + + + ############################# + def h(self, x , t = 0): + """ Final cost function with zero value """ + + if x.ndim == 1 : + + J_f = x.T @ self.S @ x + + + else: + + # Quadratic terminal cost + J_f = np.diag( x.T @ self.S @ x ) + + return J_f + + + ############################# + def g(self, x, u, t): + """ Quadratic additive cost """ + + if x.ndim == 1 : + + dJ = x.T @ self.Q @ x + u.T @ self.R @ u + + # deltas = self.obstacles - x + + # distances = np.linalg.norm( deltas , axis = 1 ) + + # costs = 1.0 / distances + + # dJ = dJ + costs.sum() + + else: + + dJ = np.diag( x.T @ self.Q @ x ) + + # plt.pause( 0.01 ) + # input("Press Enter to continue...") + + + return dJ + +sys = drone.SpeedControlledDrone2D() + + +obstacles = np.array([[ 0. , 20. ] , + [ 0. , 25. ] , + [ 0. , 30. ] , + [ 5. , 20. ] , + [ 5. , 25. ] , + [ 5. , 30. ] , + [ 10. , 20. ] , + [ 10. , 25. ] , + [ 10. , 30. ] , + [ 2.5 , 35 ] , + [ 7.5 , 35 ] ] ) + +sys.obstacles = obstacles + +sys.x_ub = np.array([+100,+100]) +sys.x_lb = np.array([-10,-10]) + +sys.u_ub = np.array([+10,+10]) +sys.u_lb = np.array([-10,-10]) + + + +cf = drone2Dcostfunction( obstacles ) + +cf.Q[0,0] = 0.0 +cf.Q[1,1] = 0.0 +cf.R[0,0] = 100.0 +cf.R[1,1] = 100.0 + + + +x0 = 0.0 +y0 = 0.0 +xf = 10.0 +yf = 30.0 +tf = 10.0 +vx = (xf-x0)/tf +vy = (yf-y0)/tf + +grid = 20 +dt = tf / grid + +planner = trajectoryoptimisation.DirectCollocationTrajectoryOptimisation( sys , dt , grid , cf) + +planner.dec_init[ 0 * grid : 1 * grid ] = np.linspace( x0 , xf , grid ) +planner.dec_init[ 1 * grid : 2 * grid ] = np.linspace( y0 , yf , grid ) +planner.dec_init[ 2 * grid : 3 * grid ] = vx +planner.dec_init[ 3 * grid : 4 * grid ] = vy + + +planner.x_start = np.array([x0,y0]) +planner.x_goal = np.array([xf,yf]) + +planner.init_dynamic_plot() +planner.compute_solution() +planner.animate_solution() + diff --git a/dev/linedrone/linedrone.py b/dev/linedrone/linedrone.py deleted file mode 100644 index d8de9233..00000000 --- a/dev/linedrone/linedrone.py +++ /dev/null @@ -1,32 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Created on Thu May 18 12:30:17 2023 - -@author: alex -""" - -import numpy as np - -from pyro.dynamic import drone -from pyro.planning import dynamicprogramming - -sys = drone.SpeedControlledDrone2D() - - -sys.obstacles = [ - np.array([ 0. , 20. ]) , - np.array([ 0. , 25. ]) , - np.array([ 0. , 30. ]) , - np.array([ 5. , 20. ]) , - np.array([ 5. , 25. ]) , - np.array([ 5. , 30. ]) , - np.array([ 10. , 20. ]) , - np.array([ 10. , 25. ]) , - np.array([ 10. , 30. ]) , - np.array([ 2.5 , 35 ]) , - np.array([ 7.5 , 35 ]) , - ] - -x_start = np.array([8.0,0]) -x_goal = np.array([0.0,0]) \ No newline at end of file diff --git a/examples/demos_by_system/drone/planar_drone_trajectory_optimisation_linearized.py b/examples/demos_by_system/drone/planar_drone_trajectory_optimisation_linearized.py index cf0ae964..5b2c2be1 100644 --- a/examples/demos_by_system/drone/planar_drone_trajectory_optimisation_linearized.py +++ b/examples/demos_by_system/drone/planar_drone_trajectory_optimisation_linearized.py @@ -12,6 +12,8 @@ from pyro.planning.trajectoryoptimisation import DirectCollocationTrajectoryOptimisation from pyro.dynamic.statespace import linearize +from pyro.analysis.costfunction import QuadraticCostFunctionVectorized + # Non-linear model sys = Drone2D() @@ -22,8 +24,9 @@ # Linear model ss = linearize( sys , 0.01 ) +cf = QuadraticCostFunctionVectorized( sys.n, sys.m ) -planner = DirectCollocationTrajectoryOptimisation( ss ) +planner = DirectCollocationTrajectoryOptimisation( ss , cost_function = cf ) planner.x_start = np.array([-5,0,0,0,0,0]) planner.x_goal = np.array([0,0,0,0,0,0]) diff --git a/examples/demos_by_tool/trajectory_planning/linear_trajectory_optimisation_vectorized.py b/examples/demos_by_tool/trajectory_planning/linear_trajectory_optimisation_vectorized.py index e8fcb3a4..2f908370 100644 --- a/examples/demos_by_tool/trajectory_planning/linear_trajectory_optimisation_vectorized.py +++ b/examples/demos_by_tool/trajectory_planning/linear_trajectory_optimisation_vectorized.py @@ -10,7 +10,7 @@ from pyro.dynamic.massspringdamper import TwoMass from pyro.planning.trajectoryoptimisation import DirectCollocationTrajectoryOptimisation -from pyro.analysis.costfunction import QuadraticCostFunctionVectorized +from pyro.analysis.costfunction import QuadraticCostFunctionVectorized sys = TwoMass() @@ -26,9 +26,9 @@ planner.x_start = np.array([0.5,0.5,0,0]) planner.x_goal = np.array([0,0,0,0]) -# planner.init_dynamic_plot() +planner.init_dynamic_plot() planner.compute_optimal_trajectory() # planner.show_solution() -planner.animate_solution() +# planner.animate_solution() diff --git a/pyro/analysis/costfunction.py b/pyro/analysis/costfunction.py index e4129097..12bd3d02 100644 --- a/pyro/analysis/costfunction.py +++ b/pyro/analysis/costfunction.py @@ -275,11 +275,11 @@ def g(self, x, u, t): if x.ndim == 1 : - dJ = x.T @ self.Q @ x + dJ = x.T @ self.Q @ x + u.T @ self.R @ u else: - dJ = np.diag( x.T @ self.Q @ x ) + dJ = np.diag( x.T @ self.Q @ x ) + np.diag( u.T @ self.R @ u ) return dJ diff --git a/pyro/dynamic/drone.py b/pyro/dynamic/drone.py index 9c06b546..4db89a23 100644 --- a/pyro/dynamic/drone.py +++ b/pyro/dynamic/drone.py @@ -327,19 +327,19 @@ def __init__(self): self.dynamic_range = 10 # Point Obstacles - self.obstacles = [ - np.array([ 0. , 20. ]) , - np.array([ 0. , 25. ]) , - np.array([ 0. , 30. ]) , - np.array([ 5. , 20. ]) , - np.array([ 5. , 25. ]) , - np.array([ 5. , 30. ]) , - np.array([ 10. , 20. ]) , - np.array([ 10. , 25. ]) , - np.array([ 10. , 30. ]) , - np.array([ 2.5 , 35 ]) , - np.array([ 7.5 , 35 ]) , - ] + self.obstacles = np.array([[ 0. , 20. ] , + [ 0. , 25. ] , + [ 0. , 30. ] , + [ 5. , 20. ] , + [ 5. , 25. ] , + [ 5. , 30. ] , + [ 10. , 20. ] , + [ 10. , 25. ] , + [ 10. , 30. ] , + [ 2.5 , 35 ] , + [ 7.5 , 35 ] ] ) + + self.is_vectorized = True ########################################################################### def f(self, x , u , t = 0 ): @@ -796,7 +796,7 @@ def forward_kinematic_lines_plus(self, x , u , t ): #sys = Drone2D() - if True: + if False: sys = Drone2D() @@ -808,7 +808,7 @@ def forward_kinematic_lines_plus(self, x , u , t ): sys.plot_trajectory() sys.animate_simulation() - if False: + if True: sys = SpeedControlledDrone2D() diff --git a/pyro/planning/trajectoryoptimisation.py b/pyro/planning/trajectoryoptimisation.py index ae1d20f2..dc39fa8a 100644 --- a/pyro/planning/trajectoryoptimisation.py +++ b/pyro/planning/trajectoryoptimisation.py @@ -249,7 +249,7 @@ def cost(self, dec): dJ = self.cost_function.g( x , u , t ) - J = np.trapz( dJ , t ) + J = np.trapz( dJ , t ) else: @@ -430,7 +430,7 @@ def compute_solution(self): planner.init_dynamic_plot() planner.compute_solution() - planner.show_solution() + planner.animate_solution() \ No newline at end of file From 6a27bfebf06ff4dedec2ae4837deec52fe9c634a Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Wed, 4 Oct 2023 12:06:29 -0400 Subject: [PATCH 24/93] drone with obs traj opt working prelim --- dev/2Ddrone/2Ddrone_trajopt | 59 ++++-- dev/2Ddrone/2Ddrone_trajopt_simple_cost_test | 158 ++++++++++++++++ dev/2Ddrone/2Ddrone_trajopt_succes_v1 | 183 +++++++++++++++++++ 3 files changed, 383 insertions(+), 17 deletions(-) create mode 100644 dev/2Ddrone/2Ddrone_trajopt_simple_cost_test create mode 100644 dev/2Ddrone/2Ddrone_trajopt_succes_v1 diff --git a/dev/2Ddrone/2Ddrone_trajopt b/dev/2Ddrone/2Ddrone_trajopt index 8263d972..41412508 100644 --- a/dev/2Ddrone/2Ddrone_trajopt +++ b/dev/2Ddrone/2Ddrone_trajopt @@ -40,6 +40,10 @@ class drone2Dcostfunction( costfunction.CostFunction ): self.obstacles = obstacles + self.d_tol = 2.0 + self.nab = 10.0 + self.buffer = 1.0 + self.is_vectorized = False @@ -78,20 +82,35 @@ class drone2Dcostfunction( costfunction.CostFunction ): dJ = x.T @ self.Q @ x + u.T @ self.R @ u - # deltas = self.obstacles - x + deltas = self.obstacles - x + + distances = np.linalg.norm( deltas , axis = 1 ) - self.buffer - # distances = np.linalg.norm( deltas , axis = 1 ) + distances[ distances < 0 ] = 0 - # costs = 1.0 / distances + costs = self.nab * np.exp( - ( distances / self.d_tol ) ** 2 ) - # dJ = dJ + costs.sum() + dJ = dJ + costs.sum() else: - dJ = np.diag( x.T @ self.Q @ x ) + dJ = np.diag( x.T @ self.Q @ x ) + np.diag( u.T @ self.R @ u ) + + deltas = self.obstacles.T[:,:,np.newaxis] - x[:,np.newaxis,:] + + distances = np.linalg.norm( deltas , axis = 1 ) - self.buffer + + distances[ distances < 0 ] = 0 - # plt.pause( 0.01 ) - # input("Press Enter to continue...") + # d_min = distances.min( axis = 0 ) + + # costs = self.nab * ( 1 / ( distances ) - 1. / self.d_tol ) ** 2 + # costs = self.nab * ( 1 / d_min ) ** 2 + costs = self.nab * np.exp( - ( distances / self.d_tol ) ** 2 ) + + # costs[ distances > self.d_tol ] = 0 + + dJ = dJ + costs.sum() return dJ @@ -116,24 +135,27 @@ sys.obstacles = obstacles sys.x_ub = np.array([+100,+100]) sys.x_lb = np.array([-10,-10]) -sys.u_ub = np.array([+10,+10]) -sys.u_lb = np.array([-10,-10]) +sys.u_ub = np.array([+5,+5]) +sys.u_lb = np.array([-5,-5]) cf = drone2Dcostfunction( obstacles ) -cf.Q[0,0] = 0.0 -cf.Q[1,1] = 0.0 -cf.R[0,0] = 100.0 -cf.R[1,1] = 100.0 +cf.Q[0,0] = 1.0 +cf.Q[1,1] = 1.0 +cf.R[0,0] = 100.0 +cf.R[1,1] = 100.0 +cf.buffer = 1.0 +cf.d_tol = 1.0 +cf.nab = 10000.0 -x0 = 0.0 -y0 = 0.0 -xf = 10.0 -yf = 30.0 +x0 = 0.0 +y0 = 16.0 +xf = 10.2 +yf = 27.5 tf = 10.0 vx = (xf-x0)/tf vy = (yf-y0)/tf @@ -152,7 +174,10 @@ planner.dec_init[ 3 * grid : 4 * grid ] = vy planner.x_start = np.array([x0,y0]) planner.x_goal = np.array([xf,yf]) +planner.maxiter = 200 + planner.init_dynamic_plot() planner.compute_solution() +planner.show_solution('j') planner.animate_solution() diff --git a/dev/2Ddrone/2Ddrone_trajopt_simple_cost_test b/dev/2Ddrone/2Ddrone_trajopt_simple_cost_test new file mode 100644 index 00000000..97736de9 --- /dev/null +++ b/dev/2Ddrone/2Ddrone_trajopt_simple_cost_test @@ -0,0 +1,158 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Created on Thu May 18 12:30:17 2023 + +@author: alex +""" + +import numpy as np + +import matplotlib.pyplot as plt + +from pyro.dynamic import drone +from pyro.analysis import costfunction +from pyro.planning import trajectoryoptimisation + +# from pyro.planning import dynamicprogramming + +############################################################################# + +class drone2Dcostfunction( costfunction.CostFunction ): + """ + + + """ + + ############################ + def __init__(self, obstacles ): + + costfunction.CostFunction.__init__(self) + + # dimensions + self.n = 2 + self.m = 2 + + # Quadratic cost weights + self.Q = np.diag( np.ones(self.n) ) + self.R = np.diag( np.ones(self.m) ) + self.S = np.diag( np.zeros(self.n) ) + + self.obstacles = obstacles + + self.is_vectorized = False + + + ############################ + @classmethod + def from_sys(cls, sys): + """ From ContinuousDynamicSystem instance """ + + instance = cls( sys.n , sys.m ) + + return instance + + + ############################# + def h(self, x , t = 0): + """ Final cost function with zero value """ + + if x.ndim == 1 : + + J_f = x.T @ self.S @ x + + + else: + + # Quadratic terminal cost + J_f = np.diag( x.T @ self.S @ x ) + + return J_f + + + ############################# + def g(self, x, u, t): + """ Quadratic additive cost """ + + if x.ndim == 1 : + + dJ = x.T @ self.Q @ x + u.T @ self.R @ u + + deltas = self.obstacles - x + + distances = np.linalg.norm( deltas , axis = 1 ) + + costs = -distances + + dJ = dJ + costs.sum() + + else: + + dJ = np.diag( x.T @ self.Q @ x ) + + # plt.pause( 0.01 ) + # input("Press Enter to continue...") + + + return dJ + +sys = drone.SpeedControlledDrone2D() + + +obstacles = np.array([[ 0. , 20. ] , + [ 0. , 25. ] , + [ 0. , 30. ] , + [ 5. , 20. ] , + [ 5. , 25. ] , + [ 5. , 30. ] , + [ 10. , 20. ] , + [ 10. , 25. ] , + [ 10. , 30. ] , + [ 2.5 , 35 ] , + [ 7.5 , 35 ] ] ) + +sys.obstacles = obstacles + +sys.x_ub = np.array([+100,+100]) +sys.x_lb = np.array([-10,-10]) + +sys.u_ub = np.array([+5,+5]) +sys.u_lb = np.array([-5,-5]) + + + +cf = drone2Dcostfunction( obstacles ) + +cf.Q[0,0] = 0.0 +cf.Q[1,1] = 0.0 +cf.R[0,0] = 1.0 +cf.R[1,1] = 1.0 + + + +x0 = 0.0 +y0 = 16.0 +xf = 10.0 +yf = 27.5 +tf = 10.0 +vx = (xf-x0)/tf +vy = (yf-y0)/tf + +grid = 20 +dt = tf / grid + +planner = trajectoryoptimisation.DirectCollocationTrajectoryOptimisation( sys , dt , grid , cf) + +planner.dec_init[ 0 * grid : 1 * grid ] = np.linspace( x0 , xf , grid ) +planner.dec_init[ 1 * grid : 2 * grid ] = np.linspace( y0 , yf , grid ) +planner.dec_init[ 2 * grid : 3 * grid ] = vx +planner.dec_init[ 3 * grid : 4 * grid ] = vy + + +planner.x_start = np.array([x0,y0]) +planner.x_goal = np.array([xf,yf]) + +planner.init_dynamic_plot() +planner.compute_solution() +planner.animate_solution() + diff --git a/dev/2Ddrone/2Ddrone_trajopt_succes_v1 b/dev/2Ddrone/2Ddrone_trajopt_succes_v1 new file mode 100644 index 00000000..41412508 --- /dev/null +++ b/dev/2Ddrone/2Ddrone_trajopt_succes_v1 @@ -0,0 +1,183 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Created on Thu May 18 12:30:17 2023 + +@author: alex +""" + +import numpy as np + +import matplotlib.pyplot as plt + +from pyro.dynamic import drone +from pyro.analysis import costfunction +from pyro.planning import trajectoryoptimisation + +# from pyro.planning import dynamicprogramming + +############################################################################# + +class drone2Dcostfunction( costfunction.CostFunction ): + """ + + + """ + + ############################ + def __init__(self, obstacles ): + + costfunction.CostFunction.__init__(self) + + # dimensions + self.n = 2 + self.m = 2 + + # Quadratic cost weights + self.Q = np.diag( np.ones(self.n) ) + self.R = np.diag( np.ones(self.m) ) + self.S = np.diag( np.zeros(self.n) ) + + self.obstacles = obstacles + + self.d_tol = 2.0 + self.nab = 10.0 + self.buffer = 1.0 + + self.is_vectorized = False + + + ############################ + @classmethod + def from_sys(cls, sys): + """ From ContinuousDynamicSystem instance """ + + instance = cls( sys.n , sys.m ) + + return instance + + + ############################# + def h(self, x , t = 0): + """ Final cost function with zero value """ + + if x.ndim == 1 : + + J_f = x.T @ self.S @ x + + + else: + + # Quadratic terminal cost + J_f = np.diag( x.T @ self.S @ x ) + + return J_f + + + ############################# + def g(self, x, u, t): + """ Quadratic additive cost """ + + if x.ndim == 1 : + + dJ = x.T @ self.Q @ x + u.T @ self.R @ u + + deltas = self.obstacles - x + + distances = np.linalg.norm( deltas , axis = 1 ) - self.buffer + + distances[ distances < 0 ] = 0 + + costs = self.nab * np.exp( - ( distances / self.d_tol ) ** 2 ) + + dJ = dJ + costs.sum() + + else: + + dJ = np.diag( x.T @ self.Q @ x ) + np.diag( u.T @ self.R @ u ) + + deltas = self.obstacles.T[:,:,np.newaxis] - x[:,np.newaxis,:] + + distances = np.linalg.norm( deltas , axis = 1 ) - self.buffer + + distances[ distances < 0 ] = 0 + + # d_min = distances.min( axis = 0 ) + + # costs = self.nab * ( 1 / ( distances ) - 1. / self.d_tol ) ** 2 + # costs = self.nab * ( 1 / d_min ) ** 2 + costs = self.nab * np.exp( - ( distances / self.d_tol ) ** 2 ) + + # costs[ distances > self.d_tol ] = 0 + + dJ = dJ + costs.sum() + + + return dJ + +sys = drone.SpeedControlledDrone2D() + + +obstacles = np.array([[ 0. , 20. ] , + [ 0. , 25. ] , + [ 0. , 30. ] , + [ 5. , 20. ] , + [ 5. , 25. ] , + [ 5. , 30. ] , + [ 10. , 20. ] , + [ 10. , 25. ] , + [ 10. , 30. ] , + [ 2.5 , 35 ] , + [ 7.5 , 35 ] ] ) + +sys.obstacles = obstacles + +sys.x_ub = np.array([+100,+100]) +sys.x_lb = np.array([-10,-10]) + +sys.u_ub = np.array([+5,+5]) +sys.u_lb = np.array([-5,-5]) + + + +cf = drone2Dcostfunction( obstacles ) + +cf.Q[0,0] = 1.0 +cf.Q[1,1] = 1.0 +cf.R[0,0] = 100.0 +cf.R[1,1] = 100.0 + +cf.buffer = 1.0 +cf.d_tol = 1.0 +cf.nab = 10000.0 + + +x0 = 0.0 +y0 = 16.0 +xf = 10.2 +yf = 27.5 +tf = 10.0 +vx = (xf-x0)/tf +vy = (yf-y0)/tf + +grid = 20 +dt = tf / grid + +planner = trajectoryoptimisation.DirectCollocationTrajectoryOptimisation( sys , dt , grid , cf) + +planner.dec_init[ 0 * grid : 1 * grid ] = np.linspace( x0 , xf , grid ) +planner.dec_init[ 1 * grid : 2 * grid ] = np.linspace( y0 , yf , grid ) +planner.dec_init[ 2 * grid : 3 * grid ] = vx +planner.dec_init[ 3 * grid : 4 * grid ] = vy + + +planner.x_start = np.array([x0,y0]) +planner.x_goal = np.array([xf,yf]) + +planner.maxiter = 200 + +planner.init_dynamic_plot() +planner.compute_solution() +planner.show_solution('j') +planner.animate_solution() + From b3477b6ed60104e67066778125a856d7e31f56c3 Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Wed, 4 Oct 2023 12:12:23 -0400 Subject: [PATCH 25/93] vectorized version still as a bug --- dev/2Ddrone/2Ddrone_trajopt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dev/2Ddrone/2Ddrone_trajopt b/dev/2Ddrone/2Ddrone_trajopt index 41412508..64179237 100644 --- a/dev/2Ddrone/2Ddrone_trajopt +++ b/dev/2Ddrone/2Ddrone_trajopt @@ -110,7 +110,7 @@ class drone2Dcostfunction( costfunction.CostFunction ): # costs[ distances > self.d_tol ] = 0 - dJ = dJ + costs.sum() + dJ = dJ + costs.sum( axis = 0 ) return dJ @@ -154,7 +154,7 @@ cf.nab = 10000.0 x0 = 0.0 y0 = 16.0 -xf = 10.2 +xf = 0.2 yf = 27.5 tf = 10.0 vx = (xf-x0)/tf From f980ad578840503d4323d906ef5b82055940ee39 Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Wed, 4 Oct 2023 12:43:12 -0400 Subject: [PATCH 26/93] vi drone 2d test --- dev/2Ddrone/2Ddrone_vi | 193 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 193 insertions(+) create mode 100644 dev/2Ddrone/2Ddrone_vi diff --git a/dev/2Ddrone/2Ddrone_vi b/dev/2Ddrone/2Ddrone_vi new file mode 100644 index 00000000..669ec51a --- /dev/null +++ b/dev/2Ddrone/2Ddrone_vi @@ -0,0 +1,193 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Created on Thu May 18 12:30:17 2023 + +@author: alex +""" + +import numpy as np + +import matplotlib.pyplot as plt + +from pyro.dynamic import drone +from pyro.analysis import costfunction +# from pyro.planning import trajectoryoptimisation + +from pyro.planning import dynamicprogramming +from pyro.planning import discretizer + +############################################################################# + +class drone2Dcostfunction( costfunction.CostFunction ): + """ + + + """ + + ############################ + def __init__(self, obstacles ): + + costfunction.CostFunction.__init__(self) + + # dimensions + self.n = 2 + self.m = 2 + + # Quadratic cost weights + self.Q = np.diag( np.ones(self.n) ) + self.R = np.diag( np.ones(self.m) ) + self.S = np.diag( np.zeros(self.n) ) + + self.obstacles = obstacles + + self.d_tol = 2.0 + self.nab = 10.0 + self.buffer = 1.0 + + self.xbar = np.array([0,0]) + + self.is_vectorized = False + + + ############################ + @classmethod + def from_sys(cls, sys): + """ From ContinuousDynamicSystem instance """ + + instance = cls( sys.n , sys.m ) + + return instance + + + ############################# + def h(self, x , t = 0): + """ Final cost function with zero value """ + + dx = x - self.xbar + + if x.ndim == 1 : + + J_f = dx.T @ self.S @ dx + + + else: + + # Quadratic terminal cost + J_f = np.diag( dx.T @ self.S @ dx ) + + return J_f + + + ############################# + def g(self, x, u, t): + """ Quadratic additive cost """ + + dx = x - self.xbar + + if x.ndim == 1 : + + dJ = dx.T @ self.Q @ dx + u.T @ self.R @ u + + deltas = self.obstacles - x + + distances = np.linalg.norm( deltas , axis = 1 ) - self.buffer + + distances[ distances < 0 ] = 0 + + costs = self.nab * np.exp( - ( distances / self.d_tol ) ** 2 ) + + dJ = dJ + costs.sum() + + else: + + dJ = np.diag( dx.T @ self.Q @ dx ) + np.diag( u.T @ self.R @ u ) + + deltas = self.obstacles.T[:,:,np.newaxis] - x[:,np.newaxis,:] + + distances = np.linalg.norm( deltas , axis = 1 ) - self.buffer + + distances[ distances < 0 ] = 0 + + # d_min = distances.min( axis = 0 ) + + # costs = self.nab * ( 1 / ( distances ) - 1. / self.d_tol ) ** 2 + # costs = self.nab * ( 1 / d_min ) ** 2 + costs = self.nab * np.exp( - ( distances / self.d_tol ) ** 2 ) + + # costs[ distances > self.d_tol ] = 0 + + dJ = dJ + costs.sum( axis = 0 ) + + + return dJ + +sys = drone.SpeedControlledDrone2D() + + +obstacles = np.array([[ 0. , 20. ] , + [ 0. , 25. ] , + [ 0. , 30. ] , + [ 5. , 20. ] , + [ 5. , 25. ] , + [ 5. , 30. ] , + [ 10. , 20. ] , + [ 10. , 25. ] , + [ 10. , 30. ] , + [ 2.5 , 35 ] , + [ 7.5 , 35 ] ] ) + +sys.obstacles = obstacles + +sys.x_ub = np.array([+20,+40]) +sys.x_lb = np.array([-10,10]) + +sys.u_ub = np.array([+5,+5]) +sys.u_lb = np.array([-5,-5]) + + + +cf = drone2Dcostfunction( obstacles ) + +cf.Q[0,0] = 1.0 +cf.Q[1,1] = 1.0 +cf.R[0,0] = 100.0 +cf.R[1,1] = 100.0 + +cf.buffer = 1.0 +cf.d_tol = 1.0 +cf.nab = 5000.0 + +cf.xbar = np.array([0.0,15.0]) + +cf.INF = 20000 + +# Discrete world +grid_sys = discretizer.GridDynamicSystem( sys , [201,201] , [5,5] , dt = 0.2 ) + + +# DP alg + +dp = dynamicprogramming.DynamicProgrammingWithLookUpTable( grid_sys, cf) + +# dp.solve_bellman_equation( tol = 0.1 ) + +dp.compute_steps( 200 , animate_cost2go= True ) + +dp.plot_cost2go() +dp.plot_policy() + + +#asign controller +ctl = dp.get_lookup_table_controller() +cl_sys = ctl + sys + +# Simulation and animation +cl_sys.x0 = np.array([7.5,27.5]) +cl_sys.compute_trajectory( 10, 10001, 'euler') +cl_sys.plot_trajectory('xu') +cl_sys.plot_phase_plane_trajectory() +cl_sys.animate_simulation() + + + From 79c7253c15f9bda52a399f5af8aa03c0a13bce17 Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Thu, 5 Oct 2023 11:52:34 -0400 Subject: [PATCH 27/93] started time varying lqr --- dev/lqr/lqr_cartpole_dev.py | 75 ++++++++++++ dev/lqr/lqr_dev.py | 220 ++++++++++++++++++++++++++++++++++++ dev/lqr/lqr_swing_up_dev.py | 89 +++++++++++++++ pyro/analysis/simulation.py | 16 ++- pyro/control/lqr.py | 3 + pyro/dynamic/statespace.py | 9 +- pyro/dynamic/system.py | 1 + 7 files changed, 403 insertions(+), 10 deletions(-) create mode 100644 dev/lqr/lqr_cartpole_dev.py create mode 100644 dev/lqr/lqr_dev.py create mode 100644 dev/lqr/lqr_swing_up_dev.py diff --git a/dev/lqr/lqr_cartpole_dev.py b/dev/lqr/lqr_cartpole_dev.py new file mode 100644 index 00000000..6aa5ec31 --- /dev/null +++ b/dev/lqr/lqr_cartpole_dev.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Created on Thu May 4 10:01:30 2023 + +@author: alex +""" + +############################################################################### +import numpy as np +############################################################################### +from pyro.dynamic import cartpole +from pyro.analysis import costfunction +from pyro.planning import trajectoryoptimisation + +from pyro.analysis import simulation +############################################################################### + +import lqr_dev as lqr + + +# sys +sys = cartpole.CartPole() + +sys.xbar[1] = np.pi # Up-right position + +#Max/Min torque +sys.u_ub[0] = +20 +sys.u_lb[0] = -20 + + +# Cost function +cf = costfunction.QuadraticCostFunction.from_sys( sys ) + +cf.Q[0,0] = 1.0 +cf.Q[1,1] = 1.0 +cf.R[0,0] = 1.0 + +planner = trajectoryoptimisation.DirectCollocationTrajectoryOptimisation( + sys , + dt = 0.2 , + grid = 20 , + cost_function = cf + ) + + +planner.x_start = np.array([0,0,0,0]) +planner.x_goal = np.array([0,np.pi,0,0]) + +planner.init_dynamic_plot() +planner.maxiter = 500 +planner.compute_optimal_trajectory() +# planner.show_solution() +# planner.animate_solution() + +traj = planner.traj + +# traj = simulation.Trajectory.load('trajectory.npz') +# traj = simulation.Trajectory.load('cartpole_hidef_swingup_traj.npy') + +cf.R[0,0] = 0.1 + +ctl = lqr.TrajectoryLQRController( sys , traj , cf ) + + +# Simulation +cl_sys = ctl + sys + +cl_sys.x0[0] = 0.0 +cl_sys.x0[1] = 0.0 + +cl_sys.compute_trajectory( tf = 5.0 , n = 20001, solver = 'euler') +cl_sys.plot_trajectory('xu') +cl_sys.animate_simulation( time_factor_video=1.0 ) + diff --git a/dev/lqr/lqr_dev.py b/dev/lqr/lqr_dev.py new file mode 100644 index 00000000..a9d4a8e3 --- /dev/null +++ b/dev/lqr/lqr_dev.py @@ -0,0 +1,220 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Created on Tue Mar 24 13:05:15 2020 + +@author: alex +""" + +############################################################################## +import numpy as np +from scipy.linalg import solve_continuous_are + +############################################################################## +from pyro.control import linear +from pyro.control import controller +from pyro.dynamic import statespace +from pyro.analysis import costfunction +############################################################################## + + + +class TrajectoryLQRController( controller.StaticController ): + """ + General (SISO or MIMO) proportional controller + ------------------------------------------------- + + u = u_d(t) + K(t) ( xd(t) - x ) + + + K = - R^(-1) B(t) S(t) + + S(t) = integral of differential riccati equation + + A(t) = df/dx(t) + B(t) = df/du(t) + + x = y : state feedback is assumed + + ----------------------------------------- + r : reference signal vector k x 1 + y : sensor signal vector p x 1 + u : control inputs vector m x 1 + ----------------------------------------- + + """ + + ############################### + def __init__(self, sys , traj , cf = None ): + + n = sys.n # states dimensions + m = sys.m # input dimenstions + + controller.StaticController.__init__( self, n, m, n) + + self.name = "Trajectory LQR Controller" + + self.sys = sys + self.traj = traj + + if cf is None: + + self.cf = sys.cost_function + + else: + + self.cf = cf + + self.compute_linear_dynamic() + self.compute_cost_matrices() + self.compute_cost2go() + self.compute_gains() + + + ############################### + def compute_linear_dynamic(self): + + steps = self.traj.time_steps + + self.As = np.zeros(( self.sys.n , self.sys.n , steps )) + self.Bs = np.zeros(( self.sys.n , self.sys.m , steps )) + + for i in range(steps): + + self.sys.tbar = self.traj.t[i] + self.sys.xbar = self.traj.x[i,:] + self.sys.ubar = self.traj.u[i,:] + + # TODO more intelligent gradient computation + ss = statespace.linearize( self.sys , 0.01 ) + + self.As[:,:,i] = ss.A + self.Bs[:,:,i] = ss.B + + + + ############################### + def compute_cost_matrices(self): + + steps = self.traj.time_steps + + self.Qs = np.zeros(( self.sys.n , self.sys.n , steps )) + self.Rs = np.zeros(( self.sys.m , self.sys.m , steps )) + + for i in range(steps): + + # ToDo Add Time Varying Quadratic cost object + self.Qs[:,:,i] = self.cf.Q + self.Rs[:,:,i] = self.cf.R + + + + ############################### + def compute_cost2go(self): + + steps = self.traj.time_steps + + self.Ss = np.zeros(( self.sys.n , self.sys.n , steps )) + self.Ps = np.zeros(( self.sys.n , self.sys.n , steps )) + + # Boundary conditions + Af = self.As[:,:,-1] + Bf = self.Bs[:,:,-1] + Qf = self.Qs[:,:,-1] + Rf = self.Rs[:,:,-1] + + self.Ss[:,:,-1] = solve_continuous_are( Af , Bf , Qf , Rf ) + + # self.Ss[:,:,-1] = np.eye( self.sys.n ) + # self.Ps[:,:,-1] = np.eye( self.sys.n ) + + for i in range( steps -1 , 0, -1 ): + + A = self.As[:,:,i] + B = self.Bs[:,:,i] + Q = self.Qs[:,:,i] + R = self.Rs[:,:,i] + S = self.Ss[:,:,i] + # P = self.Ps[:,:,i] + + # dS = S @ A + A.T @ S - S @ B @ np.linalg.inv(R) @ B.T @ S + Q + dS = Q + + # dP = A.T @ P - 0.5 * S @ B @ np.linalg.inv(R) @ B.T @ P + 0.5 * Q @ np.linalg.inv(P).T + + dt = self.traj.t[i] - self.traj.t[i-1] + + # Euler integration (maybe is not engouh precision) + self.Ss[:,:,i-1] = self.Ss[:,:,i] + dS * dt + + # self.Ps[:,:,i-1] = self.Ps[:,:,i] + dP * dt + # self.Ss[:,:,i-1] = self.Ps[:,:,i-1] @ self.Ps[:,:,i-1].T + + print('t = ', self.traj.t[i]) + print('\ndS = \n' , dS) + print('\nS = \n' , S) + + + ############################### + def compute_gains(self): + + steps = self.traj.time_steps + + self.Ks = np.zeros(( self.sys.m , self.sys.n , steps )) + + for i in range( steps ): + + A = self.As[:,:,i] + B = self.Bs[:,:,i] + Q = self.Qs[:,:,i] + R = self.Rs[:,:,i] + S = self.Ss[:,:,i] + + K = np.linalg.inv(R) @ B.T @ S + + print('t = ', self.traj.t[i]) + print('\nK = \n' , K) + + self.Ks[:,:,i] = K + + + + + + + ############################## + def c(self, x, r, t): + """ Feedback law """ + + # Nominal control input + u_bar = self.traj.t2u( t ) + x_bar = self.traj.t2x( t ) + + # Find closest traj point + i = (np.abs(self.traj.t - t)).argmin() + + K = self.Ks[:,:,i] # Time varying linear gain + + u_e = K @ ( x_bar - x ) + + if t < self.traj.time_final: + u = u_bar + u_e + else: + u = u_e + + return u + + + +''' +################################################################# +################## Main ######## +################################################################# +''' + + +if __name__ == "__main__": + + + pass + \ No newline at end of file diff --git a/dev/lqr/lqr_swing_up_dev.py b/dev/lqr/lqr_swing_up_dev.py new file mode 100644 index 00000000..919f8428 --- /dev/null +++ b/dev/lqr/lqr_swing_up_dev.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Created on Thu May 4 10:01:30 2023 + +@author: alex +""" + +############################################################################### +import numpy as np +############################################################################### +from pyro.dynamic import pendulum +from pyro.analysis import costfunction +from pyro.planning import trajectoryoptimisation +from pyro.control import lqr +from pyro.analysis import simulation +############################################################################### + +import lqr_dev as lqr + + +# # sys +# sys = pendulum.InvertedPendulum() +sys = pendulum.SinglePendulum() + + +# #Max/Min torque +# sys.u_ub[0] = +4 +# sys.u_lb[0] = -6 + + +# Cost function +cf = costfunction.QuadraticCostFunction.from_sys( sys ) + +cf.Q[0,0] = 1.0 +cf.Q[1,1] = 1.0 +cf.R[0,0] = 0.1 + +# planner = trajectoryoptimisation.DirectCollocationTrajectoryOptimisation( +# sys , +# dt = 0.2 , +# grid = 20 , +# cost_function = cf +# ) + + +# planner.x_start = np.array([+np.pi,0]) +# planner.x_goal = np.array([0,0]) + +# planner.init_dynamic_plot() +# planner.maxiter = 500 +# planner.compute_optimal_trajectory() + + + +#Max/Min torque +sys.u_ub[0] = +4 +sys.u_lb[0] = -6 + +planner = trajectoryoptimisation.DirectCollocationTrajectoryOptimisation( sys ) + +planner.x_start = np.array([0,0]) +planner.x_goal = np.array([-3.14,0]) + +planner.compute_optimal_trajectory() +planner.show_solution() +planner.animate_solution() + + +# planner.show_solution() +# planner.animate_solution() + +traj = planner.traj + +# # traj = simulation.Trajectory.load('trajectory.npz') + +ctl = lqr.TrajectoryLQRController( sys , traj , cf ) + + +# Simulation +cl_sys = ctl + sys + +cl_sys.x0[0] = 0.0 +cl_sys.x0[1] = 0.0 + +cl_sys.compute_trajectory( tf = 5.0 , n = 20001, solver = 'euler') +cl_sys.plot_trajectory('xu') +cl_sys.animate_simulation( time_factor_video=1.0 ) + diff --git a/pyro/analysis/simulation.py b/pyro/analysis/simulation.py index 21f3497f..e195953d 100644 --- a/pyro/analysis/simulation.py +++ b/pyro/analysis/simulation.py @@ -72,7 +72,8 @@ def save(self, name = 'trajectory.npy' ): def load(cls, name): try: # try to load as new format (np.savez) - with np.load(name) as data: + # with np.load(name) as data: + with np.load(name, allow_pickle=True) as data: return cls(**data) except ValueError: @@ -85,7 +86,7 @@ def load(cls, name): ############################ def _compute_size(self): - #print(self.t) + # print(self.t) self.time_final = self.t.max() self.time_steps = self.t.size @@ -95,10 +96,13 @@ def _compute_size(self): self.ubar = np.zeros( self.m ) - # Check consistency between signals - for arr in [self.x, self.y, self.u, self.dx, self.r, self.J, self.dJ]: - if (arr is not None) and (arr.shape[0] != self.time_steps): - raise ValueError("Result arrays must have same length along axis 0") + # # Check consistency between signals + # for arr in [self.x, self.y, self.u, self.dx, self.r, self.J, self.dJ]: + + # if arr is not None: + + # if arr.shape[0] != self.time_steps: + # raise ValueError("Result arrays must have same length along axis 0") ############################ diff --git a/pyro/control/lqr.py b/pyro/control/lqr.py index c9b3733b..fc7f2584 100755 --- a/pyro/control/lqr.py +++ b/pyro/control/lqr.py @@ -12,6 +12,7 @@ ############################################################################## from pyro.control import linear +from pyro.control import controller from pyro.dynamic import statespace from pyro.analysis import costfunction ############################################################################## @@ -102,6 +103,8 @@ def linearize_and_synthesize_lqr_controller( sys , cf ): ctl = synthesize_lqr_controller( ss , cf , sys.xbar , sys.ubar ) return ctl + + diff --git a/pyro/dynamic/statespace.py b/pyro/dynamic/statespace.py index 81627a03..b3dcd701 100644 --- a/pyro/dynamic/statespace.py +++ b/pyro/dynamic/statespace.py @@ -207,6 +207,7 @@ def linearize(sys, epsilon_x=0.001, epsilon_u=None): xbar = sys.xbar.astype(float) ubar = sys.ubar.astype(float) + tbar = sys.tbar epsilon_x = np.asarray(epsilon_x) @@ -225,16 +226,16 @@ def linearize(sys, epsilon_x=0.001, epsilon_u=None): def f_x(x): - return sys.f(x, ubar, 0) + return sys.f(x, ubar, tbar) def f_u(u): - return sys.f(xbar, u, 0) + return sys.f(xbar, u, tbar) def h_x(x): - return sys.h(x, ubar, 0) + return sys.h(x, ubar, tbar) def h_u(u): - return sys.h(xbar, u, 0) + return sys.h(xbar, u, tbar) A = _approx_jacobian(f_x, xbar, epsilon_x) B = _approx_jacobian(f_u, ubar, epsilon_u) diff --git a/pyro/dynamic/system.py b/pyro/dynamic/system.py index dd746fba..242b88b1 100644 --- a/pyro/dynamic/system.py +++ b/pyro/dynamic/system.py @@ -94,6 +94,7 @@ def __init__(self, n = 1, m = 1, p = 1): # Default state and inputs values self.xbar = np.zeros(self.n) self.ubar = np.zeros(self.m) + self.tbar = 0 # Plot params self.domain = [ (-10,10) , (-10,10) , (-10,10) ] From 04b410849ff86249c9911852489555b252b6118d Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Thu, 5 Oct 2023 13:50:23 -0400 Subject: [PATCH 28/93] alpha version of time-varying trajectory following LQR controller --- dev/lqr/lqr_dev.py | 220 ------------------ ...artpole_swing_up_with_lqr_stabilization.py | 43 ++-- ...ith_trajectory_following_lqr_controller.py | 29 +++ ...endulum_swing_up_with_lqr_stabilization.py | 39 +--- pyro/analysis/simulation.py | 2 +- pyro/control/lqr.py | 206 ++++++++++++++++ 6 files changed, 265 insertions(+), 274 deletions(-) delete mode 100644 dev/lqr/lqr_dev.py rename dev/lqr/lqr_cartpole_dev.py => examples/demos_by_tool/trajectory_stabilization/cartpole_swing_up_with_lqr_stabilization.py (71%) create mode 100644 examples/demos_by_tool/trajectory_stabilization/double_pendulum_with_trajectory_following_lqr_controller.py rename dev/lqr/lqr_swing_up_dev.py => examples/demos_by_tool/trajectory_stabilization/pendulum_swing_up_with_lqr_stabilization.py (61%) diff --git a/dev/lqr/lqr_dev.py b/dev/lqr/lqr_dev.py deleted file mode 100644 index a9d4a8e3..00000000 --- a/dev/lqr/lqr_dev.py +++ /dev/null @@ -1,220 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Created on Tue Mar 24 13:05:15 2020 - -@author: alex -""" - -############################################################################## -import numpy as np -from scipy.linalg import solve_continuous_are - -############################################################################## -from pyro.control import linear -from pyro.control import controller -from pyro.dynamic import statespace -from pyro.analysis import costfunction -############################################################################## - - - -class TrajectoryLQRController( controller.StaticController ): - """ - General (SISO or MIMO) proportional controller - ------------------------------------------------- - - u = u_d(t) + K(t) ( xd(t) - x ) - - - K = - R^(-1) B(t) S(t) - - S(t) = integral of differential riccati equation - - A(t) = df/dx(t) - B(t) = df/du(t) - - x = y : state feedback is assumed - - ----------------------------------------- - r : reference signal vector k x 1 - y : sensor signal vector p x 1 - u : control inputs vector m x 1 - ----------------------------------------- - - """ - - ############################### - def __init__(self, sys , traj , cf = None ): - - n = sys.n # states dimensions - m = sys.m # input dimenstions - - controller.StaticController.__init__( self, n, m, n) - - self.name = "Trajectory LQR Controller" - - self.sys = sys - self.traj = traj - - if cf is None: - - self.cf = sys.cost_function - - else: - - self.cf = cf - - self.compute_linear_dynamic() - self.compute_cost_matrices() - self.compute_cost2go() - self.compute_gains() - - - ############################### - def compute_linear_dynamic(self): - - steps = self.traj.time_steps - - self.As = np.zeros(( self.sys.n , self.sys.n , steps )) - self.Bs = np.zeros(( self.sys.n , self.sys.m , steps )) - - for i in range(steps): - - self.sys.tbar = self.traj.t[i] - self.sys.xbar = self.traj.x[i,:] - self.sys.ubar = self.traj.u[i,:] - - # TODO more intelligent gradient computation - ss = statespace.linearize( self.sys , 0.01 ) - - self.As[:,:,i] = ss.A - self.Bs[:,:,i] = ss.B - - - - ############################### - def compute_cost_matrices(self): - - steps = self.traj.time_steps - - self.Qs = np.zeros(( self.sys.n , self.sys.n , steps )) - self.Rs = np.zeros(( self.sys.m , self.sys.m , steps )) - - for i in range(steps): - - # ToDo Add Time Varying Quadratic cost object - self.Qs[:,:,i] = self.cf.Q - self.Rs[:,:,i] = self.cf.R - - - - ############################### - def compute_cost2go(self): - - steps = self.traj.time_steps - - self.Ss = np.zeros(( self.sys.n , self.sys.n , steps )) - self.Ps = np.zeros(( self.sys.n , self.sys.n , steps )) - - # Boundary conditions - Af = self.As[:,:,-1] - Bf = self.Bs[:,:,-1] - Qf = self.Qs[:,:,-1] - Rf = self.Rs[:,:,-1] - - self.Ss[:,:,-1] = solve_continuous_are( Af , Bf , Qf , Rf ) - - # self.Ss[:,:,-1] = np.eye( self.sys.n ) - # self.Ps[:,:,-1] = np.eye( self.sys.n ) - - for i in range( steps -1 , 0, -1 ): - - A = self.As[:,:,i] - B = self.Bs[:,:,i] - Q = self.Qs[:,:,i] - R = self.Rs[:,:,i] - S = self.Ss[:,:,i] - # P = self.Ps[:,:,i] - - # dS = S @ A + A.T @ S - S @ B @ np.linalg.inv(R) @ B.T @ S + Q - dS = Q - - # dP = A.T @ P - 0.5 * S @ B @ np.linalg.inv(R) @ B.T @ P + 0.5 * Q @ np.linalg.inv(P).T - - dt = self.traj.t[i] - self.traj.t[i-1] - - # Euler integration (maybe is not engouh precision) - self.Ss[:,:,i-1] = self.Ss[:,:,i] + dS * dt - - # self.Ps[:,:,i-1] = self.Ps[:,:,i] + dP * dt - # self.Ss[:,:,i-1] = self.Ps[:,:,i-1] @ self.Ps[:,:,i-1].T - - print('t = ', self.traj.t[i]) - print('\ndS = \n' , dS) - print('\nS = \n' , S) - - - ############################### - def compute_gains(self): - - steps = self.traj.time_steps - - self.Ks = np.zeros(( self.sys.m , self.sys.n , steps )) - - for i in range( steps ): - - A = self.As[:,:,i] - B = self.Bs[:,:,i] - Q = self.Qs[:,:,i] - R = self.Rs[:,:,i] - S = self.Ss[:,:,i] - - K = np.linalg.inv(R) @ B.T @ S - - print('t = ', self.traj.t[i]) - print('\nK = \n' , K) - - self.Ks[:,:,i] = K - - - - - - - ############################## - def c(self, x, r, t): - """ Feedback law """ - - # Nominal control input - u_bar = self.traj.t2u( t ) - x_bar = self.traj.t2x( t ) - - # Find closest traj point - i = (np.abs(self.traj.t - t)).argmin() - - K = self.Ks[:,:,i] # Time varying linear gain - - u_e = K @ ( x_bar - x ) - - if t < self.traj.time_final: - u = u_bar + u_e - else: - u = u_e - - return u - - - -''' -################################################################# -################## Main ######## -################################################################# -''' - - -if __name__ == "__main__": - - - pass - \ No newline at end of file diff --git a/dev/lqr/lqr_cartpole_dev.py b/examples/demos_by_tool/trajectory_stabilization/cartpole_swing_up_with_lqr_stabilization.py similarity index 71% rename from dev/lqr/lqr_cartpole_dev.py rename to examples/demos_by_tool/trajectory_stabilization/cartpole_swing_up_with_lqr_stabilization.py index 6aa5ec31..8a1f2e9c 100644 --- a/dev/lqr/lqr_cartpole_dev.py +++ b/examples/demos_by_tool/trajectory_stabilization/cartpole_swing_up_with_lqr_stabilization.py @@ -12,34 +12,36 @@ from pyro.dynamic import cartpole from pyro.analysis import costfunction from pyro.planning import trajectoryoptimisation - -from pyro.analysis import simulation +from pyro.control import lqr ############################################################################### -import lqr_dev as lqr - +############## +# System +############## -# sys sys = cartpole.CartPole() -sys.xbar[1] = np.pi # Up-right position - -#Max/Min torque sys.u_ub[0] = +20 sys.u_lb[0] = -20 - +################ # Cost function +################ + cf = costfunction.QuadraticCostFunction.from_sys( sys ) cf.Q[0,0] = 1.0 cf.Q[1,1] = 1.0 cf.R[0,0] = 1.0 +########################### +# Trajectory optimization +########################## + planner = trajectoryoptimisation.DirectCollocationTrajectoryOptimisation( sys , - dt = 0.2 , - grid = 20 , + dt = 0.1 , + grid = 40 , cost_function = cf ) @@ -50,26 +52,29 @@ planner.init_dynamic_plot() planner.maxiter = 500 planner.compute_optimal_trajectory() -# planner.show_solution() -# planner.animate_solution() traj = planner.traj -# traj = simulation.Trajectory.load('trajectory.npz') -# traj = simulation.Trajectory.load('cartpole_hidef_swingup_traj.npy') -cf.R[0,0] = 0.1 +########################### +# LQR Controller +########################## + +cf.R[0,0] = 5.0 ctl = lqr.TrajectoryLQRController( sys , traj , cf ) +########################### # Simulation +########################## + cl_sys = ctl + sys -cl_sys.x0[0] = 0.0 -cl_sys.x0[1] = 0.0 +cl_sys.x0[0] = 0.5 +cl_sys.x0[1] = 0.5 -cl_sys.compute_trajectory( tf = 5.0 , n = 20001, solver = 'euler') +cl_sys.compute_trajectory( tf = 10.0 ) cl_sys.plot_trajectory('xu') cl_sys.animate_simulation( time_factor_video=1.0 ) diff --git a/examples/demos_by_tool/trajectory_stabilization/double_pendulum_with_trajectory_following_lqr_controller.py b/examples/demos_by_tool/trajectory_stabilization/double_pendulum_with_trajectory_following_lqr_controller.py new file mode 100644 index 00000000..316386bf --- /dev/null +++ b/examples/demos_by_tool/trajectory_stabilization/double_pendulum_with_trajectory_following_lqr_controller.py @@ -0,0 +1,29 @@ +# -*- coding: utf-8 -*- +""" +Created on Fri Nov 16 12:05:08 2018 + +@author: Alexandre +""" +############################################################################### +import numpy as np +############################################################################### +from pyro.dynamic import pendulum +from pyro.planning import plan +from pyro.analysis import simulation +from pyro.control import lqr +############################################################################### + +sys = pendulum.DoublePendulum() + +traj = simulation.Trajectory.load('double_pendulum_directcollocation_hires.npy') + +ctl = lqr.TrajectoryLQRController( sys , traj ) + +# New cl-dynamic +cl_sys = ctl + sys + +# Simultation +cl_sys.x0 = traj.x[0,:] + np.array([-0.2,0.1,0.1,-0.02]) +cl_sys.compute_trajectory(10) +cl_sys.plot_trajectory('xu') +cl_sys.animate_simulation() \ No newline at end of file diff --git a/dev/lqr/lqr_swing_up_dev.py b/examples/demos_by_tool/trajectory_stabilization/pendulum_swing_up_with_lqr_stabilization.py similarity index 61% rename from dev/lqr/lqr_swing_up_dev.py rename to examples/demos_by_tool/trajectory_stabilization/pendulum_swing_up_with_lqr_stabilization.py index 919f8428..1ceb6eaa 100644 --- a/dev/lqr/lqr_swing_up_dev.py +++ b/examples/demos_by_tool/trajectory_stabilization/pendulum_swing_up_with_lqr_stabilization.py @@ -16,19 +16,9 @@ from pyro.analysis import simulation ############################################################################### -import lqr_dev as lqr - - -# # sys -# sys = pendulum.InvertedPendulum() sys = pendulum.SinglePendulum() -# #Max/Min torque -# sys.u_ub[0] = +4 -# sys.u_lb[0] = -6 - - # Cost function cf = costfunction.QuadraticCostFunction.from_sys( sys ) @@ -36,22 +26,6 @@ cf.Q[1,1] = 1.0 cf.R[0,0] = 0.1 -# planner = trajectoryoptimisation.DirectCollocationTrajectoryOptimisation( -# sys , -# dt = 0.2 , -# grid = 20 , -# cost_function = cf -# ) - - -# planner.x_start = np.array([+np.pi,0]) -# planner.x_goal = np.array([0,0]) - -# planner.init_dynamic_plot() -# planner.maxiter = 500 -# planner.compute_optimal_trajectory() - - #Max/Min torque sys.u_ub[0] = +4 @@ -62,17 +36,14 @@ planner.x_start = np.array([0,0]) planner.x_goal = np.array([-3.14,0]) +planner.init_dynamic_plot() planner.compute_optimal_trajectory() -planner.show_solution() -planner.animate_solution() +traj = planner.traj -# planner.show_solution() -# planner.animate_solution() -traj = planner.traj +cf.R[0,0] = 1.0 -# # traj = simulation.Trajectory.load('trajectory.npz') ctl = lqr.TrajectoryLQRController( sys , traj , cf ) @@ -80,10 +51,10 @@ # Simulation cl_sys = ctl + sys -cl_sys.x0[0] = 0.0 +cl_sys.x0[0] = 0.0 + 0.2 cl_sys.x0[1] = 0.0 -cl_sys.compute_trajectory( tf = 5.0 , n = 20001, solver = 'euler') +cl_sys.compute_trajectory( tf = 5.0 ) cl_sys.plot_trajectory('xu') cl_sys.animate_simulation( time_factor_video=1.0 ) diff --git a/pyro/analysis/simulation.py b/pyro/analysis/simulation.py index e195953d..ca29cb2f 100644 --- a/pyro/analysis/simulation.py +++ b/pyro/analysis/simulation.py @@ -73,7 +73,7 @@ def load(cls, name): try: # try to load as new format (np.savez) # with np.load(name) as data: - with np.load(name, allow_pickle=True) as data: + with np.load(name) as data: return cls(**data) except ValueError: diff --git a/pyro/control/lqr.py b/pyro/control/lqr.py index fc7f2584..91a7cb57 100755 --- a/pyro/control/lqr.py +++ b/pyro/control/lqr.py @@ -105,6 +105,212 @@ def linearize_and_synthesize_lqr_controller( sys , cf ): return ctl + +############################################################################## +### Time varying LQR controller +############################################################################## + +class TrajectoryLQRController( controller.StaticController ): + """ + General (SISO or MIMO) proportional controller + ------------------------------------------------- + + u = u_d(t) + K(t) ( xd(t) - x ) + + + K = R^(-1) B(t) S(t) + + S(t) = integral of differential riccati equation + + A(t) = df/dx(t) + B(t) = df/du(t) + + x = y : state feedback is assumed + + ----------------------------------------- + r : reference signal vector k x 1 + y : sensor signal vector p x 1 + u : control inputs vector m x 1 + ----------------------------------------- + + """ + + ############################### + def __init__(self, sys , traj , cf = None ): + + n = sys.n # states dimensions + m = sys.m # input dimenstions + + controller.StaticController.__init__( self, n, m, n) + + self.name = "Trajectory LQR Controller" + + self.sys = sys + self.traj = traj + + if cf is None: + + self.cf = sys.cost_function + + else: + + self.cf = cf + + self.compute_linear_dynamic() + self.compute_cost_matrices() + self.compute_cost2go() + self.compute_gains() + + self.traj.generate_interpol_functions() + + print('\n\n-------------------------------------------------------------------------') + print('Warning: this method is an approximation of the riccati solution for now') + print('ToDo @Alex : fix differential riccati integration') + print('-------------------------------------------------------------------------\n') + + + ############################### + def compute_linear_dynamic(self): + + steps = self.traj.time_steps + + self.As = np.zeros(( self.sys.n , self.sys.n , steps )) + self.Bs = np.zeros(( self.sys.n , self.sys.m , steps )) + + for i in range(steps): + + self.sys.tbar = self.traj.t[i] + self.sys.xbar = self.traj.x[i,:] + self.sys.ubar = self.traj.u[i,:] + + # TODO more intelligent gradient computation + ss = statespace.linearize( self.sys , 0.01 ) + + self.As[:,:,i] = ss.A + self.Bs[:,:,i] = ss.B + + + + ############################### + def compute_cost_matrices(self): + + steps = self.traj.time_steps + + self.Qs = np.zeros(( self.sys.n , self.sys.n , steps )) + self.Rs = np.zeros(( self.sys.m , self.sys.m , steps )) + + for i in range(steps): + + # ToDo Add Time Varying Quadratic cost object + self.Qs[:,:,i] = self.cf.Q + self.Rs[:,:,i] = self.cf.R + + + + ############################### + def compute_cost2go(self): + + steps = self.traj.time_steps + + self.Ss = np.zeros(( self.sys.n , self.sys.n , steps )) + self.Ps = np.zeros(( self.sys.n , self.sys.n , steps )) + + # Boundary conditions + Af = self.As[:,:,-1] + Bf = self.Bs[:,:,-1] + Qf = self.Qs[:,:,-1] + Rf = self.Rs[:,:,-1] + + self.Ss[:,:,-1] = solve_continuous_are( Af , Bf , Qf , Rf ) + + # self.Ss[:,:,-1] = np.eye( self.sys.n ) + # self.Ps[:,:,-1] = np.eye( self.sys.n ) + + for i in range( steps -1 , 0, -1 ): + + A = self.As[:,:,i] + B = self.Bs[:,:,i] + Q = self.Qs[:,:,i] + R = self.Rs[:,:,i] + S = self.Ss[:,:,i] + # P = self.Ps[:,:,i] + + # Quick approx for testing + dS = Q + + ############## + ## TODO: Make a real integraiton of differential riccati equation + ############# + + # dS = S @ A + A.T @ S - S @ B @ np.linalg.inv(R) @ B.T @ S + Q + # dP = A.T @ P - 0.5 * S @ B @ np.linalg.inv(R) @ B.T @ P + 0.5 * Q @ np.linalg.inv(P).T + + dt = self.traj.t[i] - self.traj.t[i-1] + + # Euler integration (maybe is not engouh precision) + self.Ss[:,:,i-1] = self.Ss[:,:,i] + dS * dt + + # self.Ps[:,:,i-1] = self.Ps[:,:,i] + dP * dt + # self.Ss[:,:,i-1] = self.Ps[:,:,i-1] @ self.Ps[:,:,i-1].T + + # print('t = ', self.traj.t[i]) + # print('\ndS = \n' , dS) + # print('\nS = \n' , S) + + + ############################### + def compute_gains(self): + + steps = self.traj.time_steps + + self.Ks = np.zeros(( self.sys.m , self.sys.n , steps )) + + for i in range( steps ): + + A = self.As[:,:,i] + B = self.Bs[:,:,i] + Q = self.Qs[:,:,i] + R = self.Rs[:,:,i] + S = self.Ss[:,:,i] + + K = np.linalg.inv(R) @ B.T @ S + + print('\n-------------------\nt = ', self.traj.t[i]) + print('K = \n' , K) + + self.Ks[:,:,i] = K + + + ############################## + def c(self, x, r, t): + """ Feedback law """ + + if t < self.traj.time_final: + + # Nominal control input + u_bar = self.traj.inter_t2u( t ) + x_bar = self.traj.inter_t2x( t ) + + # Find closest traj point + i = (np.abs(self.traj.t - t)).argmin() + + K = self.Ks[:,:,i] # Time varying linear gain + + u_e = K @ ( x_bar - x ) + + u = u_bar + u_e + + else: + + x_bar = self.traj.x[-1,:] + + K = self.Ks[:,:,-1] + + u = K @ ( x_bar - x ) # this assume final position is a fixed point + + return u + + From 957f36f9e5ada1a953fd68d15858e0dcc9053cde Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Fri, 15 Dec 2023 12:19:35 -0500 Subject: [PATCH 29/93] demo --- dev/2Ddrone/2Ddrone_vi | 25 +++++++++++++++---- examples/demos_by_system/plane/plane_cobra.py | 2 +- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/dev/2Ddrone/2Ddrone_vi b/dev/2Ddrone/2Ddrone_vi index 669ec51a..e7c6a265 100644 --- a/dev/2Ddrone/2Ddrone_vi +++ b/dev/2Ddrone/2Ddrone_vi @@ -139,7 +139,7 @@ obstacles = np.array([[ 0. , 20. ] , sys.obstacles = obstacles -sys.x_ub = np.array([+20,+40]) +sys.x_ub = np.array([+20,+35]) sys.x_lb = np.array([-10,10]) sys.u_ub = np.array([+5,+5]) @@ -160,10 +160,22 @@ cf.nab = 5000.0 cf.xbar = np.array([0.0,15.0]) + +# sys.x_ub = np.array([+15,+35]) +# sys.x_lb = np.array([-5,15]) +# sys.u_ub = np.array([+2,+2]) +# sys.u_lb = np.array([-2,-2]) +# cf.d_tol = 0.5 +# cf.Q[0,0] = 5.0 +# cf.Q[1,1] = 5.0 +# # cf.xbar = np.array([0.0,22.0]) +# cf.xbar = np.array([5.0,22.0]) +# # cf.xbar = np.array([10.0,22.0]) + cf.INF = 20000 # Discrete world -grid_sys = discretizer.GridDynamicSystem( sys , [201,201] , [5,5] , dt = 0.2 ) +grid_sys = discretizer.GridDynamicSystem( sys , [201,201] , [5,5] , dt = 0.1 ) # DP alg @@ -172,10 +184,12 @@ dp = dynamicprogramming.DynamicProgrammingWithLookUpTable( grid_sys, cf) # dp.solve_bellman_equation( tol = 0.1 ) -dp.compute_steps( 200 , animate_cost2go= True ) +dp.compute_steps( 400 , animate_cost2go= False ) + +dp.plot_cost2go_3D() -dp.plot_cost2go() -dp.plot_policy() +dp.plot_policy( k = 0 ) +dp.plot_policy( k = 1 ) #asign controller @@ -184,6 +198,7 @@ cl_sys = ctl + sys # Simulation and animation cl_sys.x0 = np.array([7.5,27.5]) +cl_sys.x0 = np.array([5.0,32.0]) cl_sys.compute_trajectory( 10, 10001, 'euler') cl_sys.plot_trajectory('xu') cl_sys.plot_phase_plane_trajectory() diff --git a/examples/demos_by_system/plane/plane_cobra.py b/examples/demos_by_system/plane/plane_cobra.py index d0677ba9..6e3f9ea8 100644 --- a/examples/demos_by_system/plane/plane_cobra.py +++ b/examples/demos_by_system/plane/plane_cobra.py @@ -1 +1 @@ -#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ Created on Fri Sep 8 10:38:30 2023 @author: alex """ import numpy as np from pyro.dynamic import plane sys = plane.Plane2D() sys.plot_alpha2Cl() sys.x0 = np.array([0,0,0.2,15,0,0]) def t2u(t): u = np.array([ 2 * t , -0.12 * t ]) return u sys.t2u = t2u #sys.gravity = 0 sys.compute_trajectory( 10 , 20001 , 'euler' ) sys.plot_trajectory('x') # sys.dynamic_domain = False sys.animate_simulation( time_factor_video=0.5 ) \ No newline at end of file +#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ Created on Fri Sep 8 10:38:30 2023 @author: alex """ import numpy as np from pyro.dynamic import plane sys = plane.Plane2D() sys.plot_alpha2Cl() sys.x0 = np.array([0,0,0.2,15,0,0]) sys.inertia = 1.0 def t2u(t): u = np.array([ 2 * t , -0.12 * t ]) return u sys.t2u = t2u #sys.gravity = 0 sys.compute_trajectory( 10 , 20001 , 'euler' ) sys.plot_trajectory('x') # sys.dynamic_domain = False sys.animate_simulation( time_factor_video=0.5 ) \ No newline at end of file From ae03bdf660d94ba018afdebcf978cf0bcbac86c9 Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Fri, 15 Dec 2023 13:03:22 -0500 Subject: [PATCH 30/93] move readme img location --- README.md | 41 ++++++++++++++++++++--------------------- pyro/dynamic/plane.py | 2 +- 2 files changed, 21 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index ceb416f8..7002f1af 100644 --- a/README.md +++ b/README.md @@ -6,19 +6,18 @@ An object-based toolbox for robot dynamic simulation, analysis, control and plan
- rocket + rocket - cartpole + cartpole
- - cartpole_swing_up + cartpole_swing_up - mass-spring + mass-spring
@@ -28,31 +27,31 @@ An object-based toolbox for robot dynamic simulation, analysis, control and plan Computed torque controller - cost2go + ctc Sliding mode controller - policy + smc Dynamic programming - cost2go + cost2go Optimal torque policy - policy + policy Rapidly-exploring random tree planning - cost2go + rrt Direct collocation trajectory optimisation - policy + double pendulum @@ -62,39 +61,39 @@ An object-based toolbox for robot dynamic simulation, analysis, control and plan Simulation (computing trajectories) - traj + traj Phase plane analysis - phase-plane + phase-plane Generating animated simulations - ani + ani Robot arm manipulability ellipsoid - ani + elp Bode plot or output/input - ani + bode Pole zero map of output/input - ani + pz Modal analysis (mode 1) - ani + mode1 Modal analysis (mode 2) - ani + mode2 @@ -102,11 +101,11 @@ An object-based toolbox for robot dynamic simulation, analysis, control and plan The concept of this toolbox is a hierachy of "dynamic system" objects, from the most generic representation (any non-linear differential equations) to more system specific representations such as mechanical system (second order equations), linear state space, manipulator equations, etc. This structure is then leveraged by analysis tools, from generic tools that work for all sub-class of dynamic systems such as running simulation and phase-plane analysis, to system-specific tools that leverage specific system propreties such as modal analysis for linear sub-class: - + The core of the library is a mother "dynamic system" class defined by a differential equation $\dot{x} = f(x,u,t)$, an output equation $y = h(x,u,t)$ and a foward kinematic equation $lines = f_{kinematic}(x,u,t)$ that is used for generating animations: - + # How to use # diff --git a/pyro/dynamic/plane.py b/pyro/dynamic/plane.py index 38fe4e2d..83559756 100644 --- a/pyro/dynamic/plane.py +++ b/pyro/dynamic/plane.py @@ -604,7 +604,7 @@ def t2u(t): #sys.gravity = 0 - sys.compute_trajectory( 10 , 20001 , 'euler' ) + sys.compute_trajectory( 2 , 1001 , 'euler' ) sys.plot_trajectory('x') # sys.dynamic_domain = False From adc86300beee0aa9b2b464ddf0700091dd59c22d Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Fri, 15 Dec 2023 13:14:30 -0500 Subject: [PATCH 31/93] readme --- README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 7002f1af..035ce631 100644 --- a/README.md +++ b/README.md @@ -6,18 +6,18 @@ An object-based toolbox for robot dynamic simulation, analysis, control and plan
- rocket + rocket - cartpole + cartpole
- cartpole_swing_up + cartpole_swing_up - mass-spring + mass-spring
@@ -51,7 +51,7 @@ An object-based toolbox for robot dynamic simulation, analysis, control and plan Direct collocation trajectory optimisation - double pendulum + doublependulum @@ -97,13 +97,13 @@ An object-based toolbox for robot dynamic simulation, analysis, control and plan -### Unified by a standardized "dynamic system" and "controller" class hierarchy +### Unified by a standardized *dynamic system*, *controller* and *planner* classes hierarchy -The concept of this toolbox is a hierachy of "dynamic system" objects, from the most generic representation (any non-linear differential equations) to more system specific representations such as mechanical system (second order equations), linear state space, manipulator equations, etc. This structure is then leveraged by analysis tools, from generic tools that work for all sub-class of dynamic systems such as running simulation and phase-plane analysis, to system-specific tools that leverage specific system propreties such as modal analysis for linear sub-class: +The concept of this toolbox is a hierachy of *dynamic system* objects, from the most generic representation (any non-linear differential equations) to more system specific representations such as mechanical system (second order equations), linear state space, manipulator equations, etc. This structure is then leveraged by analysis tools, from generic tools that work for all sub-class of dynamic systems such as running simulation and phase-plane analysis, to system-specific tools that leverage specific system propreties such as modal analysis for linear sub-class: -The core of the library is a mother "dynamic system" class defined by a differential equation $\dot{x} = f(x,u,t)$, an output equation $y = h(x,u,t)$ and a foward kinematic equation $lines = f_{kinematic}(x,u,t)$ that is used for generating animations: +The core of the library is a mother *dynamic system* class defined by a differential equation $\dot{x} = f(x,u,t)$, an optionnaly an output equation $y = h(x,u,t)$ and a foward kinematic equation that is used for generating animations: From 485b95a244685754aa288e717e23f1aba2d6ebd0 Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Fri, 15 Dec 2023 13:18:40 -0500 Subject: [PATCH 32/93] fix bug --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 035ce631..49c9982c 100644 --- a/README.md +++ b/README.md @@ -89,7 +89,7 @@ An object-based toolbox for robot dynamic simulation, analysis, control and plan Modal analysis (mode 1) - mode1 + mode1 Modal analysis (mode 2) From 83e6c17a2d2617652d74dc3b716101f33699ff48 Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Fri, 15 Dec 2023 13:23:53 -0500 Subject: [PATCH 33/93] and --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 49c9982c..cb7f5f4e 100644 --- a/README.md +++ b/README.md @@ -103,7 +103,7 @@ The concept of this toolbox is a hierachy of *dynamic system* objects, from the -The core of the library is a mother *dynamic system* class defined by a differential equation $\dot{x} = f(x,u,t)$, an optionnaly an output equation $y = h(x,u,t)$ and a foward kinematic equation that is used for generating animations: +The core of the library is a mother *dynamic system* class defined by a differential equation $\dot{x} = f(x,u,t)$, and optionnaly an output equation $y = h(x,u,t)$ and a foward kinematic equation that is used for generating animations: From ca13d8cbf84fd3c0a2b0c186f1b637382145d641 Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Fri, 15 Dec 2023 13:29:18 -0500 Subject: [PATCH 34/93] default fig param --- README.md | 8 ++++---- pyro/analysis/graphical.py | 14 +++++++------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index cb7f5f4e..f11c48af 100644 --- a/README.md +++ b/README.md @@ -6,18 +6,18 @@ An object-based toolbox for robot dynamic simulation, analysis, control and plan
- rocket + rocket - cartpole + cartpole
- cartpole_swing_up + cartpole_swing_up - mass-spring + mass-spring
diff --git a/pyro/analysis/graphical.py b/pyro/analysis/graphical.py index f23db56d..937dde00 100644 --- a/pyro/analysis/graphical.py +++ b/pyro/analysis/graphical.py @@ -57,9 +57,9 @@ def __init__(self, sys): self.sys = sys # Ploting - self.fontsize = 5 - self.figsize = (4, 3) - self.dpi = 300 + self.fontsize = default_fontsize + self.figsize = default_figsize + self.dpi = default_dpi ########################################################################## @@ -388,11 +388,11 @@ def __init__(self, sys ): self.x_axis = 0 self.y_axis = 1 - # Params - self.figsize = (4, 3) - self.dpi = 300 + # Ploting Param + self.fontsize = default_fontsize + self.figsize = default_figsize + self.dpi = default_dpi self.linestyle = sys.linestyle - self.fontsize = 5 # Label self.top_right_label = None From 371c926a4bdb046eb97f84ba78e31a11979dcc91 Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Fri, 15 Dec 2023 13:46:51 -0500 Subject: [PATCH 35/93] default dpi change --- pyro/analysis/graphical.py | 6 ++++-- pyro/analysis/phaseanalysis.py | 8 +++++--- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/pyro/analysis/graphical.py b/pyro/analysis/graphical.py index 937dde00..223c933f 100644 --- a/pyro/analysis/graphical.py +++ b/pyro/analysis/graphical.py @@ -30,7 +30,7 @@ # Default figure settings default_figsize = (4, 3) -default_dpi = 300 +default_dpi = 250 default_linestyle = '-' default_fontsize = 5 @@ -916,7 +916,7 @@ def __animate__(self,i): sys = pendulum.DoublePendulum() sys.x0 = np.array([0.1,0.1,0,0]) - + sys.plot_phase_plane(0,2) traj = sys.compute_trajectory( 2.0 ) @@ -928,6 +928,8 @@ def __animate__(self,i): traj2 = sys.compute_trajectory( 2.0 ) plotter.update_plot( traj2 ) + + plotter.phase_plane_trajectory( traj2 , 0 , 2 ) is_3d = False diff --git a/pyro/analysis/phaseanalysis.py b/pyro/analysis/phaseanalysis.py index 558515f5..eb00682b 100644 --- a/pyro/analysis/phaseanalysis.py +++ b/pyro/analysis/phaseanalysis.py @@ -9,6 +9,8 @@ import matplotlib import matplotlib.pyplot as plt +import graphical + # Embed font type in PDF matplotlib.rcParams['pdf.fonttype'] = 42 matplotlib.rcParams['ps.fonttype'] = 42 @@ -50,13 +52,13 @@ def __init__(self, ContinuousDynamicSystem , x_axis = 0 , y_axis = 1): # Plotting params self.color = 'b' - self.figsize = (3, 2) - self.dpi = 300 + self.figsize = graphical.default_figsize + self.dpi = graphical.default_dpi self.linewidth = 0.005 self.streamplot = False self.arrowstyle = '->' self.headlength = 4.5 - self.fontsize = 5 + self.fontsize = graphical.default_fontsize ########################################################################### From 1b3f8f1a29fa188db12d7e1dd521783a30d32bc3 Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Fri, 15 Dec 2023 13:50:02 -0500 Subject: [PATCH 36/93] path bug --- pyro/analysis/phaseanalysis.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyro/analysis/phaseanalysis.py b/pyro/analysis/phaseanalysis.py index eb00682b..9ff2d4a0 100644 --- a/pyro/analysis/phaseanalysis.py +++ b/pyro/analysis/phaseanalysis.py @@ -9,7 +9,7 @@ import matplotlib import matplotlib.pyplot as plt -import graphical +from pyro.analysis import graphical # Embed font type in PDF matplotlib.rcParams['pdf.fonttype'] = 42 From eb0e51add0bdafff2840314108a5218cb6aedc6a Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Mon, 18 Dec 2023 22:46:48 -0500 Subject: [PATCH 37/93] working on car dimensionless analysis --- dev/dimensionless/cases_car.py | 318 ++++++++++++++++++++++++++ dev/dimensionless/dev_car.py | 9 +- pyro/dynamic/longitudinal_vehicule.py | 20 +- 3 files changed, 334 insertions(+), 13 deletions(-) create mode 100644 dev/dimensionless/cases_car.py diff --git a/dev/dimensionless/cases_car.py b/dev/dimensionless/cases_car.py new file mode 100644 index 00000000..9fd07036 --- /dev/null +++ b/dev/dimensionless/cases_car.py @@ -0,0 +1,318 @@ +# -*- coding: utf-8 -*- +""" +Created on Mon Nov 12 20:28:17 2018 + +@author: Alexandre +""" +############################################################################## +import numpy as np +import matplotlib.pyplot as plt +############################################################################## +from pyro.dynamic import longitudinal_vehicule +from pyro.planning import discretizer +from pyro.analysis import costfunction +from pyro.planning import dynamicprogramming +from pyro.analysis import graphical +############################################################################## + + + +def case( length , x_c_star , y_c_star , x_w_star , case_name = 'test ', show = True, rax = None , rax2 = None, res = 'reg', legend = 1): + + + gravity = 9.8 + + # context + x_c = x_c_star * length + y_c = y_c_star * length + x_w = x_w_star * length + + # additionnal domain + x_max = 10 * length + v_max = 2 * np.sqrt( gravity * length ) + t_max = 10 * x_max / v_max + s_max = 0.07 + j_max = t_max * ( ( x_max / x_w ) **2 + s_max ** 2 ) + + + print('\n\nCase :' + case_name ) + print('----------------------------------------------------') + print(' l=',length,' x_c=', x_c, ' y_c=', y_c,'x_w=', x_w) + + ################################ + # Dynamic system definition + ################################ + + sys = longitudinal_vehicule.LongitudinalFrontWheelDriveCarWithWheelSlipInput() + + # Model param + sys.lenght = length # distance between front wheel and back wheel [m] + sys.xc = x_c # distance from back wheel to c.g. [m] + sys.yc = y_c # height from ground to c.g. [m] + + sys.gravity = gravity # gravity constant [N/kg] + + sys.mass = 1. # total car mass [kg] + sys.rho = 1. # air density [kg/m3] + sys.cdA = 0. # drag coef time area [m2] + + # Ground traction curve parameters + sys.mu_max = 1.0 + sys.mu_slope = 70. + + sys.u_ub[0] = + s_max + sys.u_lb[0] = - s_max + + sys.x_ub = np.array([ + x_max , + v_max]) + sys.x_lb = np.array([ - x_max , - v_max]) + + ################################ + # Discritized grid + ################################ + + if res == 'test' : + + dt = 0.5 + nx = 21 + nu = 3 + + elif res == 'plus' : + + dt = 0.05 + nx = 301 + nu = 101 + + elif res == 'hi' : + + dt = 0.025 + nx = 501 + nu = 101 + + else: + + dt = 0.05 + nx = 101 + nu = 21 + + grid_sys = discretizer.GridDynamicSystem( sys , [nx,nx] , [nu] , dt , True ) + + ################################ + # Cost function + ################################ + + qcf = costfunction.QuadraticCostFunction.from_sys(sys) + + qcf.xbar = np.array([ 0 , 0 ]) # target + + qcf.INF = j_max + + + qcf.Q[0,0] = (1./x_w) ** 2 + qcf.Q[1,1] = 0.0 + + qcf.R[0,0] = 1.0 + + ################################ + # Computing optimal policy + ################################ + + dp = dynamicprogramming.DynamicProgrammingWithLookUpTable( grid_sys, qcf ) + + steps = int( t_max / dt ) + + dp.compute_steps( steps ) + + + #grid_sys.fontsize = 10 + # qcf.INF = 0.1 * qcf.INF + dp.clean_infeasible_set() + + dp.plot_policy() + + + ################################## + # Fig param + ################################## + + dpi = 300 + fontsize = 10 + figsize = (4, 3) + + # ################################## + # # Dimensional policy plot + # ################################## + + fig = plt.figure( figsize = figsize, dpi=dpi, frameon=True) + fig.canvas.manager.set_window_title( 'dimentionless policy' ) + ax = fig.add_subplot(1, 1, 1) + + xname = r'$x \; [m]$' + yname = r'$\dot{x} \; [m/sec]$' + zname = r'$s \; [-]$' + + sys.state_label[0] = r'$x$' + sys.state_label[1] = r'$\dot{x}$' + sys.input_label[0] = r'$s$' + + xrange = 50 + yrange = 15 + zrange = 0.08 + + ax.set_ylabel( yname, fontsize = fontsize ) + ax.set_xlabel( xname, fontsize = fontsize ) + + x_level = grid_sys.x_level[ 0 ] + y_level = grid_sys.x_level[ 1 ] + + + u = grid_sys.get_input_from_policy( dp.pi , 0 ) + J_grid_nd = grid_sys.get_grid_from_array( u ) + J_grid_2d = grid_sys.get_2D_slice_of_grid( J_grid_nd , 0 , 1 ) + + mesh = ax.pcolormesh( x_level, + y_level, + J_grid_2d.T, + shading='gouraud', + cmap = 'bwr', + vmin = -zrange, + vmax = zrange, + rasterized = True ) + + ax.tick_params( labelsize = fontsize ) + ax.grid(True) + ax.set_ylim( -yrange, +yrange) + ax.set_xlim( -xrange, xrange) + + cbar = fig.colorbar( mesh ) + + cbar.set_label(zname, fontsize = fontsize , rotation = 90 ) + + fig.tight_layout() + #fig.show() + fig.savefig( case_name + '_policy.pdf') + # fig.savefig( case_name + '_policy.png') + # fig.savefig( case_name + '_policy.jpg') + + if show: + plt.show() + else: + plt.close( fig ) + + ################################## + # Dimensionless policy plot + ################################## + + fig = plt.figure( figsize= figsize, dpi=dpi, frameon=True) + fig.canvas.manager.set_window_title( 'dimentionless policy' ) + ax = fig.add_subplot(1, 1, 1) + + xname = r'$x^*$'#self.sys.state_label[x] #+ ' ' + self.sys.state_units[x] + yname = r'$\dot{x}^* = \frac{\dot{x}}{\sqrt{gl}}$'#self.sys.state_label[y] #+ ' ' + self.sys.state_units[y] + zname = r'$s$' + + ax.set_ylabel(yname, fontsize = fontsize ) + ax.set_xlabel(xname, fontsize = fontsize ) + + x_level = grid_sys.x_level[ 0 ] * 1./length + y_level = grid_sys.x_level[ 1 ] * (1 / np.sqrt( gravity * length )) + + + u = grid_sys.get_input_from_policy( dp.pi , 0 ) + + u2 = u + + J_grid_nd = grid_sys.get_grid_from_array( u2 ) + + J_grid_2d = grid_sys.get_2D_slice_of_grid( J_grid_nd , 0 , 1 ) + + mesh = ax.pcolormesh( x_level, + y_level, + J_grid_2d.T, + shading='gouraud', + cmap = 'bwr', + vmin = -zrange, + vmax = zrange, + rasterized = True ) + + ax.tick_params( labelsize = fontsize ) + ax.grid(True) + + cbar = fig.colorbar( mesh ) + + cbar.set_label(zname, fontsize = fontsize , rotation = 90 ) + + fig.tight_layout() + #fig.show() + fig.savefig( case_name + '_dimpolicy.pdf') + # fig.savefig( case_name + '_dimpolicy.png') + # fig.savefig( case_name + '_dimpolicy.jpg') + + + # ################################## + # # Trajectory plot + # ################################## + + ctl = dp.get_lookup_table_controller() + + # Simulation + cl_sys = ctl + sys + cl_sys.x0 = np.array([-x_max * 0.5, 0.]) + cl_sys.compute_trajectory( 10 , 6001, 'euler') + + tp = graphical.TrajectoryPlotter( sys ) + tp.fontsize = fontsize + tp.plot( cl_sys.traj , 'xu' , show = False ) + tp.plots[0].set_ylim([-15.5, 15.5]) + tp.plots[1].set_ylim([-15.5, 15.5]) + tp.plots[2].set_ylim([-zrange, zrange]) + tp.fig.savefig( case_name + '_traj.pdf') + + # Simulation + cl_sys = ctl + sys + cl_sys.x0 = np.array([x_max * 0.5, 0.]) + cl_sys.compute_trajectory( 10 , 6001, 'euler') + + tp = graphical.TrajectoryPlotter( sys ) + tp.fontsize = fontsize + tp.plot( cl_sys.traj , 'xu' , show = False ) + tp.plots[0].set_ylim([-15.5, 15.5]) + tp.plots[1].set_ylim([-15.5, 15.5]) + tp.plots[2].set_ylim([-zrange, zrange]) + tp.fig.savefig( case_name + '_traj2.pdf') + # tp.fig.savefig( case_name + '_traj.png') + # tp.fig.savefig( case_name + '_traj.jpg') + + if show: + plt.show() + else: + plt.close( tp.fig ) + + cl_sys.plot_trajectory('xu') + cl_sys.plot_phase_plane_trajectory() + + + +#################################### +### Main figures +#################################### + +# res = 'plus' +res = 'hi' + +# case( length = 2.0 , x_c_star = 0.5 , y_c_star = 0.5 , x_w_star = 20.0 , case_name = 'car1', res = res) +# case( length = 1.0 , x_c_star = 0.5 , y_c_star = 0.5 , x_w_star = 20.0 , case_name = 'car2', res = res) +# case( length = 3.0 , x_c_star = 0.5 , y_c_star = 0.5 , x_w_star = 20.0 , case_name = 'car3', res = res) + +# case( length = 2.0 , x_c_star = 0.5 , y_c_star = 1.5 , x_w_star = 10.0 , case_name = 'car4', res = res) +# case( length = 1.0 , x_c_star = 0.5 , y_c_star = 1.5 , x_w_star = 10.0 , case_name = 'car5', res = res) +# case( length = 3.0 , x_c_star = 0.5 , y_c_star = 1.5 , x_w_star = 10.0 , case_name = 'car6', res = res) + +# case( length = 2.0 , x_c_star = 0.5 , y_c_star = 0.1 , x_w_star = 2.0 , case_name = 'car7', res = res) +# case( length = 1.0 , x_c_star = 0.5 , y_c_star = 0.1 , x_w_star = 2.0 , case_name = 'car8', res = res) +case( length = 3.0 , x_c_star = 0.5 , y_c_star = 0.1 , x_w_star = 2.0 , case_name = 'car9', res = res) + + + + + + \ No newline at end of file diff --git a/dev/dimensionless/dev_car.py b/dev/dimensionless/dev_car.py index 19ccd64b..25cd7179 100644 --- a/dev/dimensionless/dev_car.py +++ b/dev/dimensionless/dev_car.py @@ -15,20 +15,23 @@ from pyro.analysis import graphical ############################################################################## -case_name = 'car_test' +case_name = 'car_05_12' show = True res = 'std' +res = 'plus' legend = 1 #dim context +case_name = 'car_05_12' +length = 5.0 x_c_star = 0.5 y_c_star = 1.2 x_w_star = 10.0 -# param -length = 5.0 + + gravity = 9.8 # context diff --git a/pyro/dynamic/longitudinal_vehicule.py b/pyro/dynamic/longitudinal_vehicule.py index fb9d081b..d29e7358 100644 --- a/pyro/dynamic/longitudinal_vehicule.py +++ b/pyro/dynamic/longitudinal_vehicule.py @@ -69,7 +69,7 @@ def __init__(self): self.cdA = 0.3 * 2 # drag coef time area [m2] # Ground traction curve parameters - self.mu_max = 0.5 + self.mu_max = 1.0 self.mu_slope = 70. @@ -171,15 +171,15 @@ def f(self, x , u , t = 0 ): dx[0] = v # velocity dx[1] = a # acc - ################### - # Normal force check - fn_front = m * g * rr - m * a * ry - fn_rear = m * g * rf + m * a * ry - if (fn_front<0) : - print('Normal force on front wheel is negative: fn = ', fn_front) - if (fn_rear<0) : - print('Normal force on rear wheel is negative: fn = ', fn_rear) - ################### + # ################### + # # Normal force check + # fn_front = m * g * rr - m * a * ry + # fn_rear = m * g * rf + m * a * ry + # if (fn_front<0) : + # print('Normal force on front wheel is negative: fn = ', fn_front) + # if (fn_rear<0) : + # print('Normal force on rear wheel is negative: fn = ', fn_rear) + # ################### return dx From d0cc022b275543c4ab2c0f8f51f53e0fd2fac4c0 Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Thu, 21 Dec 2023 23:02:04 -0500 Subject: [PATCH 38/93] dimensionless cases --- dev/dimensionless/cases_car.py | 11 +- .../{cases_master.py => cases_pendulum.py} | 0 dev/dimensionless/dev_car.py | 294 ------------------ ...ndulum_with_valueiteration_minimum_time.py | 2 +- 4 files changed, 8 insertions(+), 299 deletions(-) rename dev/dimensionless/{cases_master.py => cases_pendulum.py} (100%) delete mode 100644 dev/dimensionless/dev_car.py diff --git a/dev/dimensionless/cases_car.py b/dev/dimensionless/cases_car.py index 9fd07036..e4ecece4 100644 --- a/dev/dimensionless/cases_car.py +++ b/dev/dimensionless/cases_car.py @@ -296,10 +296,10 @@ def case( length , x_c_star , y_c_star , x_w_star , case_name = 'test ', show = ### Main figures #################################### -# res = 'plus' -res = 'hi' +res = 'plus' +# res = 'hi' -# case( length = 2.0 , x_c_star = 0.5 , y_c_star = 0.5 , x_w_star = 20.0 , case_name = 'car1', res = res) +case( length = 2.0 , x_c_star = 0.5 , y_c_star = 0.5 , x_w_star = 20.0 , case_name = 'car1', res = res) # case( length = 1.0 , x_c_star = 0.5 , y_c_star = 0.5 , x_w_star = 20.0 , case_name = 'car2', res = res) # case( length = 3.0 , x_c_star = 0.5 , y_c_star = 0.5 , x_w_star = 20.0 , case_name = 'car3', res = res) @@ -309,8 +309,11 @@ def case( length , x_c_star , y_c_star , x_w_star , case_name = 'test ', show = # case( length = 2.0 , x_c_star = 0.5 , y_c_star = 0.1 , x_w_star = 2.0 , case_name = 'car7', res = res) # case( length = 1.0 , x_c_star = 0.5 , y_c_star = 0.1 , x_w_star = 2.0 , case_name = 'car8', res = res) -case( length = 3.0 , x_c_star = 0.5 , y_c_star = 0.1 , x_w_star = 2.0 , case_name = 'car9', res = res) +# case( length = 3.0 , x_c_star = 0.5 , y_c_star = 0.1 , x_w_star = 2.0 , case_name = 'car9', res = res) +# case( length = 2.0 , x_c_star = 0.5 , y_c_star = 0.9 , x_w_star = 20.0 , case_name = 'car10', res = res) +# case( length = 1.0 , x_c_star = 0.5 , y_c_star = 0.9 , x_w_star = 20.0 , case_name = 'car11', res = res) +# case( length = 3.0 , x_c_star = 0.5 , y_c_star = 0.9 , x_w_star = 20.0 , case_name = 'car12', res = res) diff --git a/dev/dimensionless/cases_master.py b/dev/dimensionless/cases_pendulum.py similarity index 100% rename from dev/dimensionless/cases_master.py rename to dev/dimensionless/cases_pendulum.py diff --git a/dev/dimensionless/dev_car.py b/dev/dimensionless/dev_car.py deleted file mode 100644 index 25cd7179..00000000 --- a/dev/dimensionless/dev_car.py +++ /dev/null @@ -1,294 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Mon Nov 12 20:28:17 2018 - -@author: Alexandre -""" -############################################################################## -import numpy as np -import matplotlib.pyplot as plt -############################################################################## -from pyro.dynamic import longitudinal_vehicule -from pyro.planning import discretizer -from pyro.analysis import costfunction -from pyro.planning import dynamicprogramming -from pyro.analysis import graphical -############################################################################## - -case_name = 'car_05_12' -show = True -res = 'std' -res = 'plus' -legend = 1 - - -#dim context - -case_name = 'car_05_12' -length = 5.0 -x_c_star = 0.5 -y_c_star = 1.2 -x_w_star = 10.0 - - - -gravity = 9.8 - -# context -x_c = x_c_star * length -y_c = y_c_star * length -x_w = x_w_star * length - -# additionnal domain -x_max = 10 * length -v_max = 2 * np.sqrt( gravity * length ) -t_max = 10 * x_max / v_max -s_max = 0.1 -j_max = t_max * ( ( x_max / x_w ) **2 + s_max ** 2 ) - - -print('\n\nCase :' + case_name ) -print('----------------------------------------------------') -print(' l=',length,' x_c=', x_c, ' y_c=', y_c,'x_w=', x_w) - -################################ -# Dynamic system definition -################################ - -sys = longitudinal_vehicule.LongitudinalFrontWheelDriveCarWithWheelSlipInput() - -# Model param -sys.lenght = length # distance between front wheel and back wheel [m] -sys.xc = x_c # distance from back wheel to c.g. [m] -sys.yc = y_c # height from ground to c.g. [m] - -sys.gravity = gravity # gravity constant [N/kg] - -sys.mass = 1. # total car mass [kg] -sys.rho = 1. # air density [kg/m3] -sys.cdA = 0. # drag coef time area [m2] - -# Ground traction curve parameters -sys.mu_max = 1.0 -sys.mu_slope = 70. - -sys.u_ub[0] = + s_max -sys.u_lb[0] = - s_max - -sys.x_ub = np.array([ + x_max , + v_max]) -sys.x_lb = np.array([ - x_max , - v_max]) - -################################ -# Discritized grid -################################ - -if res == 'test' : - - dt = 0.5 - nx = 21 - nu = 3 - -elif res == 'plus' : - - dt = 0.05 - nx = 301 - nu = 101 - -elif res == 'hi' : - - dt = 0.025 - nx = 501 - nu = 101 - -else: - - dt = 0.05 - nx = 101 - nu = 21 - -grid_sys = discretizer.GridDynamicSystem( sys , [nx,nx] , [nu] , dt , True ) - -################################ -# Cost function -################################ - -qcf = costfunction.QuadraticCostFunction.from_sys(sys) - -qcf.xbar = np.array([ 0 , 0 ]) # target - -qcf.INF = j_max - - -qcf.Q[0,0] = (1./x_w) ** 2 -qcf.Q[1,1] = 0.0 - -qcf.R[0,0] = 1.0 - -################################ -# Computing optimal policy -################################ - -dp = dynamicprogramming.DynamicProgrammingWithLookUpTable( grid_sys, qcf ) - -steps = int( t_max / dt ) - -dp.compute_steps( steps ) - - -#grid_sys.fontsize = 10 -# qcf.INF = 0.1 * qcf.INF -dp.clean_infeasible_set() - -dp.plot_policy() - - -################################## -# Fig param -################################## - -dpi = 300 -fontsize = 10 -figsize = (4, 3) - -# ################################## -# # Dimensional policy plot -# ################################## - -fig = plt.figure( figsize = figsize, dpi=dpi, frameon=True) -fig.canvas.manager.set_window_title( 'dimentionless policy' ) -ax = fig.add_subplot(1, 1, 1) - -xname = r'$x \; [m]$' -yname = r'$\dot{x} \; [m/sec]$' -zname = r'$s \; [-]$' - -sys.state_label[0] = r'$x$' -sys.state_label[1] = r'$\dot{x}$' -sys.input_label[0] = r'$s$' - -xrange = 50 -yrange = 15 -zrange = 0.3 - -ax.set_ylabel( yname, fontsize = fontsize ) -ax.set_xlabel( xname, fontsize = fontsize ) - -x_level = grid_sys.x_level[ 0 ] -y_level = grid_sys.x_level[ 1 ] - - -u = grid_sys.get_input_from_policy( dp.pi , 0 ) -J_grid_nd = grid_sys.get_grid_from_array( u ) -J_grid_2d = grid_sys.get_2D_slice_of_grid( J_grid_nd , 0 , 1 ) - -mesh = ax.pcolormesh( x_level, - y_level, - J_grid_2d.T, - shading='gouraud', - cmap = 'bwr', - vmin = -zrange, - vmax = zrange, - rasterized = True ) - -ax.tick_params( labelsize = fontsize ) -ax.grid(True) -ax.set_ylim( -yrange, +yrange) -ax.set_xlim( -xrange, xrange) - -cbar = fig.colorbar( mesh ) - -cbar.set_label(zname, fontsize = fontsize , rotation = 90 ) - -fig.tight_layout() -#fig.show() -fig.savefig( case_name + '_policy.pdf') -# fig.savefig( case_name + '_policy.png') -# fig.savefig( case_name + '_policy.jpg') - -if show: - plt.show() -else: - plt.close( fig ) - -################################## -# Dimensionless policy plot -################################## - -fig = plt.figure( figsize= figsize, dpi=dpi, frameon=True) -fig.canvas.manager.set_window_title( 'dimentionless policy' ) -ax = fig.add_subplot(1, 1, 1) - -xname = r'$x^*$'#self.sys.state_label[x] #+ ' ' + self.sys.state_units[x] -yname = r'$\dot{x}^* = \frac{\dot{x}}{\sqrt{gl}}$'#self.sys.state_label[y] #+ ' ' + self.sys.state_units[y] -zname = r'$s$' - -ax.set_ylabel(yname, fontsize = fontsize ) -ax.set_xlabel(xname, fontsize = fontsize ) - -x_level = grid_sys.x_level[ 0 ] * 1./length -y_level = grid_sys.x_level[ 1 ] * (1 / np.sqrt( gravity * length )) - - -u = grid_sys.get_input_from_policy( dp.pi , 0 ) - -u2 = u - -J_grid_nd = grid_sys.get_grid_from_array( u2 ) - -J_grid_2d = grid_sys.get_2D_slice_of_grid( J_grid_nd , 0 , 1 ) - -mesh = ax.pcolormesh( x_level, - y_level, - J_grid_2d.T, - shading='gouraud', - cmap = 'bwr', - rasterized = True ) - -ax.tick_params( labelsize = fontsize ) -ax.grid(True) - -cbar = fig.colorbar( mesh ) - -cbar.set_label(zname, fontsize = fontsize , rotation = 90 ) - -fig.tight_layout() -#fig.show() -fig.savefig( case_name + '_dimpolicy.pdf') -# fig.savefig( case_name + '_dimpolicy.png') -# fig.savefig( case_name + '_dimpolicy.jpg') - - -# ################################## -# # Trajectory plot -# ################################## - -ctl = dp.get_lookup_table_controller() - -# Simulation -cl_sys = ctl + sys -cl_sys.x0 = np.array([-x_max * 0.5, 0.]) -cl_sys.compute_trajectory( 10 , 6001, 'euler') - -tp = graphical.TrajectoryPlotter( sys ) -tp.fontsize = fontsize -tp.plot( cl_sys.traj , 'xu' , show = False ) -tp.plots[1].set_ylim([-5.5, 5.5]) -tp.plots[2].set_ylim([-zrange, zrange]) -tp.fig.savefig( case_name + '_traj.pdf') -# tp.fig.savefig( case_name + '_traj.png') -# tp.fig.savefig( case_name + '_traj.jpg') - -if show: - plt.show() -else: - plt.close( tp.fig ) - -cl_sys.plot_trajectory('xu') -cl_sys.plot_phase_plane_trajectory() - - - - - - - \ No newline at end of file diff --git a/examples/demos_by_system/pendulum_simple/simple_pendulum_with_valueiteration_minimum_time.py b/examples/demos_by_system/pendulum_simple/simple_pendulum_with_valueiteration_minimum_time.py index fdb47404..ae9668d5 100755 --- a/examples/demos_by_system/pendulum_simple/simple_pendulum_with_valueiteration_minimum_time.py +++ b/examples/demos_by_system/pendulum_simple/simple_pendulum_with_valueiteration_minimum_time.py @@ -46,7 +46,7 @@ #dp.animate_policy( show = True , save = False ) #dp.animate_cost2go( show = False , save = True ) -#dp.animate_policy( show = False , save = True ) +dp.animate_policy( show = False , save = True ) ctl = dp.get_lookup_table_controller() From 8bd13a577aceae1e82b2fc68d6f028654e849243 Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Fri, 19 Jan 2024 12:44:11 -0500 Subject: [PATCH 39/93] added linear initial guess to planner and boat class init dev --- dev/boat/boat.py | 308 ++++++++++++++++++ dev/boat/boat_parking_with_lqr.py | 48 +++ dev/boat/boat_trajectory_planning.py | 35 ++ dev/dimensionless/car1_dimpolicy.pdf | Bin 0 -> 131345 bytes dev/dimensionless/car1_policy.pdf | Bin 0 -> 54698 bytes dev/dimensionless/car1_traj.pdf | Bin 0 -> 15380 bytes dev/dimensionless/car1_traj2.pdf | Bin 0 -> 15686 bytes ...e_pendulum_with_trajectory_optimization.py | 4 +- .../mountain_car_trajectory_optimization.py | 1 + pyro/planning/trajectoryoptimisation.py | 38 ++- 10 files changed, 431 insertions(+), 3 deletions(-) create mode 100644 dev/boat/boat.py create mode 100644 dev/boat/boat_parking_with_lqr.py create mode 100644 dev/boat/boat_trajectory_planning.py create mode 100644 dev/dimensionless/car1_dimpolicy.pdf create mode 100644 dev/dimensionless/car1_policy.pdf create mode 100644 dev/dimensionless/car1_traj.pdf create mode 100644 dev/dimensionless/car1_traj2.pdf diff --git a/dev/boat/boat.py b/dev/boat/boat.py new file mode 100644 index 00000000..925996ea --- /dev/null +++ b/dev/boat/boat.py @@ -0,0 +1,308 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Created on Sun Oct 3 05:43:08 2021 + +@author: alex +""" + +############################################################################### +import numpy as np +import matplotlib.pyplot as plt +############################################################################### +from pyro.dynamic import mechanical +from pyro.kinematic import geometry +from pyro.kinematic import drawing +############################################################################### + + +############################################################################## +# 2D planar drone +############################################################################## + +class Boat( mechanical.MechanicalSystemWithPositionInputs ): + """ + Equations of Motion + ------------------------- + + """ + + ############################ + def __init__(self): + """ """ + + # initialize standard params + mechanical.MechanicalSystemWithPositionInputs.__init__( self, 3 , 1 , 1 ) + + # Labels + self.name = '2D planar boat model' + self.state_label = ['x','y','theta','vx','vy','w'] + self.input_label = ['Trust', 'delta'] + self.output_label = self.state_label + + # Units + self.state_units = ['[m]','[m]','[rad]','[m/sec]','[m/sec]','[rad/sec]'] + self.input_units = ['[N]', '[Rad]'] + self.output_units = self.state_units + + # State working range + self.x_ub = np.array([+50,+50,+3,+2,+2,+1]) + self.x_lb = np.array([-50,-50,-3,-2,-2,-1]) + + # State working range + self.u_ub = np.array([+500,+1]) + self.u_lb = np.array([-10,-1]) + + # Model param + self.mass = 1000 + self.inertia = self.mass * 0.3 ** 2 + self.ycg = 1 + + self.cd_sway = 0.0 + self.cd_surge = 0.0 + self.cd_yaw = 0.0 + + # Kinematic param + self.width = 1.0 + self.height = 2.0 + + # Graphic output parameters + self.dynamic_domain = True + self.dynamic_range = 10 + + # rocket drawing + pts = np.zeros(( 10 , 3 )) + l = self.height + w = self.width + + pts[0,:] = np.array([ 0, -l,0]) + pts[1,:] = np.array([-w, -l,0]) + pts[2,:] = np.array([-w, +l,0]) + pts[3,:] = np.array([ 0,l+w,0]) + pts[4,:] = np.array([+w, +l,0]) + pts[5,:] = np.array([+w, -l,0]) + pts[6,:] = pts[0,:] + + self.drawing_body_pts = pts + + + ########################################################################### + def H(self, q ): + """ + Inertia matrix + ---------------------------------- + dim( H ) = ( dof , dof ) + + such that --> Kinetic Energy = 0.5 * dq^T * H(q) * dq + + """ + + H = np.zeros((3,3)) + + H[0,0] = self.mass + H[1,1] = self.mass + H[2,2] = self.inertia + + return H + + + ########################################################################### + def C(self, q , dq ): + """ + Corriolis and Centrifugal Matrix + ------------------------------------ + dim( C ) = ( dof , dof ) + + such that --> d H / dt = C + C^T + + + """ + + C = np.zeros((3,3)) + + return C + + + ########################################################################### + def B(self, q , u ): + """ + Actuator Matrix : dof x m + """ + + B = np.zeros((3,1)) + + delta = u[1] + + B[0,0] = -np.sin( q[2] + delta ) + B[1,0] = np.cos( q[2] + delta) + B[2,0] = - self.ycg * np.sin( delta ) + + return B + + + ########################################################################### + def g(self, q ): + """ + Gravitationnal forces vector : dof x 1 + """ + + g = np.zeros(3) + + return g + + + ########################################################################### + def d(self, q , dq , u ): + """ + State-dependent dissipative forces : dof x 1 + """ + + d = np.zeros(3) + + # Linear damping for now: + D = np.diag([ 100 , 100, 100 ]) + + d = D @ dq + + return d + + + ########################################################################### + # Graphical output + ########################################################################### + + ########################################################################### + def forward_kinematic_domain(self, q ): + """ + """ + l = self.height * 3 + + x = q[0] + y = q[1] + z = 0 + + if self.dynamic_domain: + + domain = [ ( -l + x , l + x ) , + ( -l + y , l + y ) , + ( -l + z , l + z ) ]# + else: + + domain = [ ( -l , l ) , + ( -l , l ) , + ( -l , l ) ]# + + return domain + + + ########################################################################### + def forward_kinematic_lines(self, q ): + """ + Compute points p = [x;y;z] positions given config q + ---------------------------------------------------- + - points of interest for ploting + + Outpus: + lines_pts = [] : a list of array (n_pts x 3) for each lines + + """ + + lines_pts = [] # list of array (n_pts x 3) for each lines + lines_style = [] + lines_color = [] + + ############################### + # ground line + ############################### + + pts = np.zeros(( 2 , 3 )) + pts[0,:] = np.array([-10,0,0]) + pts[1,:] = np.array([+10,0,0]) + + lines_pts.append( pts ) + lines_style.append( '--') + lines_color.append( 'k' ) + + ########################### + # body + ########################### + + x = q[0] + y = q[1] + theta = q[2] + + W_T_B = geometry.transformation_matrix_2D( theta , x , y ) + + pts_B = self.drawing_body_pts + pts_W = drawing.transform_points_2D( W_T_B , pts_B ) + + lines_pts.append( pts_W ) + lines_style.append( '-') + lines_color.append( 'b' ) + + ########################### + # C.G. + ########################### + + pts = np.zeros(( 1 , 3 )) + pts[0,:] = np.array([x,y,0]) + + lines_pts.append( pts ) + lines_style.append( 'o') + lines_color.append( 'b' ) + + return lines_pts , lines_style , lines_color + + + ########################################################################### + def forward_kinematic_lines_plus(self, x , u , t ): + """ + show trust vectors + + + """ + + lines_pts = [] # list of array (n_pts x 3) for each lines + lines_style = [] + lines_color = [] + + ########################### + # trust force vector + ########################### + + length = u[0] * 0.01 # arrow length + theta = u[1] - 0.5 * np.pi # arrow angle (body frame) + y_offset = -self.height + + pts_body = drawing.arrow_from_length_angle( length, theta, y = y_offset ) + W_T_B = geometry.transformation_matrix_2D( x[2], x[0] , x[1] ) + pts_W = drawing.transform_points_2D( W_T_B , pts_body ) + + lines_pts.append( pts_W ) + lines_style.append( '-') + lines_color.append( 'r' ) + + return lines_pts , lines_style , lines_color + + + +''' +################################################################# +################## Main ######## +################################################################# +''' + + +if __name__ == "__main__": + """ MAIN TEST """ + + sys = Boat() + + sys.x0 = np.array([0,-10,0,0,2,0]) + + sys.ubar[0] = 00 + sys.ubar[1] = 0 + + sys.compute_trajectory( tf = 100 ) + sys.plot_trajectory() + sys.animate_simulation() \ No newline at end of file diff --git a/dev/boat/boat_parking_with_lqr.py b/dev/boat/boat_parking_with_lqr.py new file mode 100644 index 00000000..6ef0474f --- /dev/null +++ b/dev/boat/boat_parking_with_lqr.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Created on Sun Oct 3 08:27:06 2021 + +@author: alex +""" + +import numpy as np + +from boat import Boat + +from pyro.analysis.costfunction import QuadraticCostFunction +from pyro.dynamic.statespace import linearize +from pyro.control.lqr import synthesize_lqr_controller + + +# Non-linear model +sys = Boat() + +sys.ubar = np.array([100,0]) + +# Linear model +ss = linearize( sys , 0.01 ) + +# Cost function +cf = QuadraticCostFunction.from_sys( sys ) +cf.Q[0,0] = 10000 +cf.Q[1,1] = 0 +cf.Q[2,2] = 10000 +cf.Q[3,3] = 0 +cf.Q[4,4] = 0 +cf.Q[5,5] = 0 +cf.R[0,0] = 1 +cf.R[1,1] = 10000 + +# LQR controller +ctl = synthesize_lqr_controller( ss , cf , sys.xbar , sys.ubar ) + +# Instable when trust reverse!!! + + +# Simulation Closed-Loop Non-linear with LQR controller +cl_sys = ctl + sys +cl_sys.x0 = np.array([1,-2,-0.2,0,0,0]) +cl_sys.compute_trajectory(20) +cl_sys.plot_trajectory('xu') +cl_sys.animate_simulation() \ No newline at end of file diff --git a/dev/boat/boat_trajectory_planning.py b/dev/boat/boat_trajectory_planning.py new file mode 100644 index 00000000..1a99719f --- /dev/null +++ b/dev/boat/boat_trajectory_planning.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Created on Sun Oct 3 08:27:06 2021 + +@author: alex +""" + +import numpy as np + +from boat import Boat + +from pyro.planning.trajectoryoptimisation import DirectCollocationTrajectoryOptimisation + +# DO NOT CONVERGE!!! + +sys = Boat() + +planner = DirectCollocationTrajectoryOptimisation( sys , 1.0 , 10 ) + + +planner.init_dynamic_plot() + +planner.x_start = np.array([0,-10,0,0,2,0]) +planner.x_goal = np.array([0,0,0,0,0,0]) + +planner.set_linear_initial_guest() +# planner.set_linear_initial_guest( True ) + +planner.maxiter = 500 + +planner.compute_optimal_trajectory() +# planner.show_solution() +# planner.animate_solution() + diff --git a/dev/dimensionless/car1_dimpolicy.pdf b/dev/dimensionless/car1_dimpolicy.pdf new file mode 100644 index 0000000000000000000000000000000000000000..6fe4bd43399ccfe865306f0b03bc418eeb61902c GIT binary patch literal 131345 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 L$9G*%mGXZ8yBJ-` diff --git a/dev/dimensionless/car1_policy.pdf b/dev/dimensionless/car1_policy.pdf deleted file mode 100644 index 8a7a8c0a3f32c3b0b5694c6842cd926f59a8caa7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 54698 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 IJQ4N$2hlx~$p8QV diff --git a/dev/dimensionless/car1_traj.pdf b/dev/dimensionless/car1_traj.pdf deleted file mode 100644 index 4e6e6f316d4467307c17125d95eed0fc692d5a80..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15380 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 diff --git a/dev/gym/pyro_gym_tests.py b/dev/gym/pyro_gym_tests.py deleted file mode 100644 index af2fa2ab..00000000 --- a/dev/gym/pyro_gym_tests.py +++ /dev/null @@ -1,50 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Created on Sun Oct 3 08:27:06 2021 - -@author: alex -""" - -import numpy as np - -from pyro.dynamic.rocket import Rocket -from pyro.dynamic.drone import Drone2D -from pyro.dynamic.boat import Boat2D - -from stable_baselines3 import PPO - -# Non-linear model -sys = Rocket() -sys = Drone2D() -sys = Boat2D() - -# sys.x_ub = np.array([10, 10, 10, 10, 10, 10]) -# sys.x_lb = -sys.x_ub - -# sys.u_ub = np.array([1000, 100]) -# sys.u_lb = np.array([0, -100]) - -env = sys.convert_to_gymnasium() - -env.reset_mode = "noisy_x0" - -#env.render_mode = "human" - -model = PPO("MlpPolicy", env, verbose=1) -model.learn(total_timesteps=250000) - -env = sys.convert_to_gymnasium() -env.render_mode = "human" -env.reset_mode = "noisy_x0" - -episodes = 10 -for episode in range(episodes): - y, info = env.reset() - terminated = False - truncated = False - - print("\n Episode:", episode) - while not (terminated or truncated): - u, _states = model.predict(y, deterministic=True) - y, r, terminated, truncated, info = env.step(u) diff --git a/dev/hybrid_systems/hybrid.py b/dev/hybrid_systems/hybrid.py deleted file mode 100755 index e5e43a00..00000000 --- a/dev/hybrid_systems/hybrid.py +++ /dev/null @@ -1,69 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Created on Wed Dec 9 22:04:53 2020 - -@author: alex -""" - -import numpy as np - -from pyro.dynamic import ContinuousDynamicSystem - -from pyro.analysis import simulation - - -############################################################################### -class SwitchedSystem( ContinuousDynamicSystem): - """ - Class in construction !!!!!! - - """ - ############################################ - def __init__(self, n, m, p, k): - - super().__init__(n, m, p) - - - self.k = k # Number of discrte modes - - - ############################################# - def f(self, x, u, t): - - u_mode = int(u[0]) - u_cont = u[1:] - - ################ - if u_mode == 0: - - dx = u_cont - - ################ - if u_mode == 1: - - dx = u_cont - - ################ - else: - - dx = np.zeros( self.n ) - - return dx - - - ############################# - def compute_trajectory( - self, tf=10, n=100001): - """ - Simulation of time evolution of the system - ------------------------------------------------ - tf : final time - n : time steps - """ - - sim = simulation.Simulator(self, tf, n, 'euler') - - self.traj = sim.compute() # save the result in the instance - - return self.traj \ No newline at end of file diff --git a/dev/hybrid_systems/hybrid_mechanical.py b/dev/hybrid_systems/hybrid_mechanical.py deleted file mode 100644 index beca11d9..00000000 --- a/dev/hybrid_systems/hybrid_mechanical.py +++ /dev/null @@ -1,462 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Created on Wed Apr 6 13:46:06 2022 -@author: alex -""" - -############################################################################### -import numpy as np -############################################################################### -from pyro.dynamic import system -from pyro.dynamic import mechanical -from hybrid import SwitchedSystem -############################################################################### - -############################################################################### - -class HybridMechanicalSystem( mechanical.MechanicalSystem , SwitchedSystem ): - """ - Mechanical system with Equation of Motion in the form of - ------------------------------------------------------- - H(q,k) ddq + C(q,dq) dq + d(q,dq,k) + g(q) = B(q,k) e - ------------------------------------------------------- - u : dim = (m+1, 1) : mode + force input variables - k=u[0] : integer : operating mode - e = u[1,:] : dim = (m, 1) : force/torque input variables - q : dim = (dof, 1) : position variables - dq : dim = (dof, 1) : velocity variables - ddq : dim = (dof, 1) : acceleration variables - H(q,k) : dim = (dof, dof) : inertia matrix - C(q) : dim = (dof, dof) : corriolis matrix - B(q,k) : dim = (dof, m) : actuator matrix - ddq : dim = (dof, 1) : acceleration variables - d(q,dq,k): dim = (dof, 1) : state-dependent dissipative forces - g(q) : dim = (dof, 1) : state-dependent conservatives forces - - """ - - ############################ - def __init__(self, dof = 1 , actuators = None, k = 2): - """ """ - - # Degree of Freedom - self.dof = dof - self.k = k - - # Nb of actuators - if actuators == None: # If not specifyied the sys is fully actuated - actuators = dof - - # Dimensions - n = dof * 2 - m = actuators + 1 - p = dof * 2 - - # initialize standard params - system.ContinuousDynamicSystem.__init__(self, n, m, p) - - # Name - self.name = str(dof) + 'DoF Hybrid Mechanical System' - - # Labels, bounds and units - for i in range(dof): - # joint angle states - self.x_ub[i] = + np.pi * 2 - self.x_lb[i] = - np.pi * 2 - self.state_label[i] = 'Angle '+ str(i) - self.state_units[i] = '[rad]' - # joint velocity states - self.x_ub[i+dof] = + np.pi * 2 - self.x_lb[i+dof] = - np.pi * 2 - self.state_label[i+dof] = 'Velocity ' + str(i) - self.state_units[i+dof] = '[rad/sec]' - for i in range(actuators): - self.u_ub[i+1] = + 5 - self.u_lb[i+1] = - 5 - self.input_label[i+1] = 'Torque ' + str(i) - self.input_units[i+1] ='[Nm]' - self.output_label = self.state_label - self.output_units = self.state_units - - self.u_ub[0] = k - 1 - self.u_lb[0] = 0 - self.input_label[0] = 'Mode' - self.input_units[0] ='' - - ########################################################################### - # The following functions needs to be overloaded by child classes - # to represent the dynamic of the system - ########################################################################### - - ########################################################################### - def H(self, q , k ): - """ - Inertia matrix - ---------------------------------- - dim( H ) = ( dof , dof ) - - such that --> Kinetic Energy = 0.5 * dq^T * H(q) * dq - - """ - if k == 0: - H = np.diag( np.ones( self.dof ) ) * 100 # Default is identity matrix - else: - H = np.diag( np.ones( self.dof ) ) # Default is identity matrix - - return H - - - ########################################################################### - def B(self, q , k ): - """ - Actuator Matrix : dof x m - """ - - B = np.zeros( ( self.dof , self.m ) ) - - if k == 0: - for i in range(min(self.m,self.dof)): - B[i,i] = 10 # Diag matrix for the first m rows - else: - for i in range(min(self.m,self.dof)): - B[i,i] = 1 # Diag matrix for the first m rows - - return B - - - ########################################################################### - def d(self, q , dq , k ): - """ - State-dependent dissipative forces : dof x 1 - """ - - if k == 0: - D = np.ones( self.dof ) * 100 # Default is zero vector - else: - D = np.zeros(self.dof ) # Default is zero vector - - d = np.dot( D , dq ) - - return d - - - ########################################################################### - # No need to overwrite the following functions for custom system - ########################################################################### - - ############################## - def u2k(self, u ): - """ Compute mode k based on u vector """ - - return int(u[0]) - - - ############################## - def u2e(self, u ): - """ Compute actuator effort e based on u vector """ - - return u[1:] - - - ############################## - def generalized_forces(self, q , dq , ddq , k , t = 0 ): - """ Computed generalized forces given a trajectory """ - - H = self.H( q , k ) - C = self.C( q , dq ) - g = self.g( q ) - d = self.d( q , dq , k ) - - # Generalized forces - forces = np.dot( H , ddq ) + np.dot( C , dq ) + g + d - - return forces - - - ############################## - def actuator_forces(self, q , dq , ddq , k , t = 0 ): - """ Computed actuator forces given a trajectory (inverse dynamic) """ - - if self.dof == (self.m - 1): - - B = self.B( q , k ) - - # Generalized forces - forces = self.generalized_forces( q , dq , ddq , k , t ) - - # Actuator forces - u = np.dot( np.linalg.inv( B ) , forces ) - - return u - - else: - - raise NotImplementedError - - - ############################## - def ddq(self, q , dq , u , t = 0 ): - """ Computed accelerations given actuator forces (foward dynamic) """ - - k = self.u2k(u) - e = self.u2e(u) - - H = self.H( q , k ) - C = self.C( q , dq ) - g = self.g( q ) - d = self.d( q , dq , k ) - B = self.B( q , k ) - - ddq = np.dot( np.linalg.inv( H ) , ( np.dot( B , e ) - - np.dot( C , dq ) - g - d ) ) - - return ddq - - - - ########################################################################### - def kinetic_energy(self, q , dq , k ): - """ Compute kinetic energy of manipulator """ - - e_k = 0.5 * np.dot( dq , np.dot( self.H( q , k ) , dq ) ) - - return e_k - - -############################################################################## -# Hybrid Mechanica lSystem -############################################################################## - -class MultipleSpeedMechanicalSystem( HybridMechanicalSystem ): - """ - Mechanical system with Equation of Motion in the form of - ------------------------------------------------------- - [ H(q) + R(k)^T I R(k) ] ddq + C(q,dq) dq + [d_mech(q,dq) + R(k)^T B R(k) dq] + g(q) = R(k)^T e - ------------------------------------------------------- - u : dim = (m+1, 1) : mode + force input variables - k=u[0] : integer : operating mode - e = u[1,:] : dim = (m, 1) : force/torque input variables - q : dim = (dof, 1) : position variables - dq : dim = (dof, 1) : velocity variables - ddq : dim = (dof, 1) : acceleration variables - H_mech(q) : dim = (dof, dof) : mechanism inertia matrix - I_actuator : dim = (dof, dof) : actuator inertia matrix - B_actuator : dim = (dof, dof) : actuator damping matrix - C(q) : dim = (dof, dof) : corriolis matrix - R : dim = (dof, m) : actuator matrix - ddq : dim = (dof, 1) : acceleration variables - d_mech(q,dq): dim = (dof, 1) : state-dependent dissipative forces - g(q) : dim = (dof, 1) : state-dependent conservatives forces - - """ - - ############################ - def __init__(self, dof = 1 , k = 2): - """ """ - - super().__init__(dof, dof, k) - - # Name - self.name = str(dof) + ' DoF Multiple Speed Mechanical System' - - # Number of discrete modes - self.k = k - - # Actuator - self.I_actuators = np.diag( np.ones( self.dof ) ) - self.B_actuators = np.diag( np.ones( self.dof ) ) - - # Transmissions - self.R_options = [ np.diag( np.ones( self.dof ) ) , - np.diag( np.ones( self.dof ) ) * 10 ] - - - - ########################################################################### - # The following functions needs to be overloaded by child classes - # to represent the dynamic of the system - ########################################################################### - - ########################################################################### - def H_mech(self, q ): - """ - Inertia matrix of arm mechanism only - ---------------------------------- - dim( H ) = ( dof , dof ) - - """ - - H = np.diag( np.ones( self.dof ) ) # Default is identity matrix - - return H - - - ########################################################################### - def H(self, q , k ): - """ """ - - R = self.R_options[k] - - H = self.H_mech(q) + R.T @ self.I_actuators @ R - - return H - - - ########################################################################### - def B(self, q , k ): - """ - Actuator Matrix : dof x m - """ - - R = self.R_options[k] - - return R.T - - ########################################################################### - def d_mech(self, q , dq ): - """ - State-dependent dissipative forces : dof x 1 - """ - - d = np.zeros( self.dof ) - - return d - - - ########################################################################### - def d(self, q , dq , k ): - """ - State-dependent dissipative forces : dof x 1 - """ - - R = self.R_options[k] - - d = self.d_mech(q, dq) + R.T @ self.B_actuators @ R @ dq - - return d - - - - -########################################################################### -class TwoSpeedLinearActuator( HybridMechanicalSystem ): - """ - - - """ - - ############################ - def __init__(self): - """ """ - - super().__init__( 1 , 1 ) - - self.mass_output = 10 - - self.motor_inertia = 1 - - self.ratios = np.array([1,10]) - - self.motor_damping = 1 - - self.l2 = 1 - - ########################################################################### - # The following functions needs to be overloaded by child classes - # to represent the dynamic of the system - ########################################################################### - - ########################################################################### - def H(self, q , k ): - """ - Inertia matrix - ---------------------------------- - dim( H ) = ( dof , dof ) - - such that --> Kinetic Energy = 0.5 * dq^T * H(q) * dq - - """ - H = np.array([[ self.mass_output + self.motor_inertia * self.ratios[k]**2 ]]) - - return H - - - ########################################################################### - def B(self, q , k ): - """ - Actuator Matrix : dof x m - """ - - B = np.array([[ self.ratios[k] ]]) - - return B - - - ########################################################################### - def d(self, q , dq , k ): - """ - State-dependent dissipative forces : dof x 1 - """ - - D = np.array([[ self.motor_damping * self.ratios[k]**2 ]]) - - d = np.dot( D , dq ) - - return d - - - ########################################################################### - def forward_kinematic_lines(self, q ): - """ - Compute points p = [x;y;z] positions given config q - ---------------------------------------------------- - - points of interest for ploting - - Outpus: - lines_pts = [] : a list of array (n_pts x 3) for each lines - - """ - - lines_pts = [] # list of array (n_pts x 3) for each lines - lines_style = [] - lines_color = [] - - # mass - pts = np.zeros(( 5 , 3 )) - - - pts[0,:] = np.array([q[0] - self.l2/2,+self.l2/2,0]) - pts[1,:] = np.array([q[0] + self.l2/2,+self.l2/2,0]) - pts[2,:] = np.array([q[0] + self.l2/2,-self.l2/2,0]) - pts[3,:] = np.array([q[0] - self.l2/2,-self.l2/2,0]) - pts[4,:] = pts[0,:] - - lines_pts.append( pts ) - lines_style.append( '-') - lines_color.append( 'k') - - return lines_pts , lines_style , lines_color - - -''' -################################################################# -################## Main ######## -################################################################# -''' - - -if __name__ == "__main__": - - sys = MultipleSpeedMechanicalSystem(2,2) - - sys.x0[1] = 0 - - sys.ubar[0] = 1 - sys.ubar[1] = 5 - - sys.compute_trajectory() - - sys.plot_trajectory('xu') - - \ No newline at end of file diff --git a/dev/hybrid_systems/rmincontroller b/dev/hybrid_systems/rmincontroller deleted file mode 100644 index 73fd800c..00000000 --- a/dev/hybrid_systems/rmincontroller +++ /dev/null @@ -1,260 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Created on Fri Apr 8 00:14:29 2022 - -@author: alex -""" - -############################################################################### -import numpy as np -from scipy.interpolate import interp1d -############################################################################### -from pyro.control.nonlinear import ComputedTorqueController -############################################################################### - - -############################################################################### -# Computed Torque -############################################################################### - -class RminComputedTorqueController( ComputedTorqueController ) : - """ - Inverse dynamic controller for mechanical system - - """ - - ############################ - def __init__(self, model , traj = None ): - """ - --------------------------------------- - r : reference signal vector k x 1 - y : sensor signal vector p x 1 - u : control inputs vector m x 1 - t : time 1 x 1 - --------------------------------------- - u = c( y , r , t ) - - """ - - super().__init__( model , traj ) - - # Label - self.name = 'R min Computed Torque Controller' - - self.modes_options = model.k - - - ############################# - def c_fixed_goal( self , y , r , t = 0 ): - """ - Feedback static computation u = c(y,r,t) - - INPUTS - y : sensor signal vector p x 1 - r : reference signal vector k x 1 - t : time 1 x 1 - - OUPUTS - u : control inputs vector m x 1 - - """ - - x = y - q_d = r - - u = self.fixed_goal_ctl( x , q_d , t ) - - return u - - - - ############################ - def fixed_goal_ctl( self , x , q_d , t = 0 ): - """ - - Given desired fixed goal state and actual state, compute torques - - """ - [ q , dq ] = self.model.x2q( x ) - - ddq_d = np.zeros( self.model.dof ) - dq_d = np.zeros( self.model.dof ) - - ddq_r = self.compute_ddq_r( ddq_d , dq_d , q_d , dq , q ) - - u = self.rmin_forces( q , dq , ddq_r ) - - return u - - - ############################ - def traj_following_ctl( self , x , t = 0 ): - """ - - Given desired loaded trajectory and actual state, compute torques - - """ - - [ q , dq ] = self.model.x2q( x ) - - ddq_d , dq_d , q_d = self.get_traj( t ) - - ddq_r = self.compute_ddq_r( ddq_d , dq_d , q_d , dq , q ) - - u = self.rmin_forces( q , dq , ddq_r ) - - return u - - - ############################ - def rmin_forces( self, q , dq , ddq_r ): - """ """ - - # Cost is Q - costs = np.zeros( self.modes_options ) - efforts = np.zeros( ( self.modes_options , self.model.dof ) ) - - #for all gear ratio options - for k in range( self.modes_options ): - - efforts[k] = self.model.actuator_forces( q , dq , ddq_r , k ) - - - costs[k] = np.dot( efforts[k] , efforts[k] ) - - """ - u = np.append( efforts[k] , k ) - x = self.model.q2x(q, dq) - - if self.model.isavalidinput( x , k ): - # Cost is norm of torque - costs[k] = np.dot( efforts[k] , efforts[k] ) - else: - # Bad option - costs[k] = 9999999999 # INF - """ - - # Optimal dsicrete mode - k_star = costs.argmin() - e_star = efforts[ k_star ] - - u = np.append( k_star , e_star ) - - return u - - - ############################ - def compute_ddq_r( self , ddq_d , dq_d , q_d , dq , q ): - """ - - Given desired trajectory and actual state, compute ddq_r - - """ - - q_e = q - q_d - dq_e = dq - dq_d - - ddq_r = ddq_d - 2 * self.zeta * self.w0 * dq_e - self.w0 ** 2 * q_e - - return ddq_r - - - ############################ - def load_trajectory( self , traj ): - """ - - Load Open-Loop trajectory solution to use as reference trajectory - - """ - - self.trajectory = traj - - q = traj.x[ :, 0 : self.model.dof ] - dq = traj.x[ :, self.model.dof : 2 * self.model.dof ] - ddq = traj.dx[:, self.model.dof : 2 * self.model.dof ] - t = traj.t - - # Create interpol functions - self.q = interp1d(t,q.T) - self.dq = interp1d(t,dq.T) - self.ddq = interp1d(t,ddq.T) - - - ############################ - def get_traj( self , t ): - """ - - Find closest point on the trajectory - - """ - - if t < self.trajectory.time_final : - - # Load trajectory - q = self.q( t ) - dq = self.dq( t ) - ddq = self.ddq( t ) - - else: - - q = self.rbar - dq = np.zeros( self.model.dof ) - ddq = np.zeros( self.model.dof ) - - return ddq , dq , q - - - ############################# - def c_trajectory_following( self , y , r , t ): - """ - Feedback static computation u = c(y,r,t) - - INPUTS - y : sensor signal vector p x 1 - r : reference signal vector k x 1 - t : time 1 x 1 - - OUPUTS - u : control inputs vector m x 1 - - """ - - x = y - - u = self.traj_following_ctl( x , t ) - - - return u - - - - -''' -################################################################# -################## Main ######## -################################################################# -''' - - -if __name__ == "__main__": - """ MAIN TEST """ - - from hybrid_mechanical import MultipleSpeedMechanicalSystem - - - sys = MultipleSpeedMechanicalSystem(1,2) - - sys.B_actuators[0,0] = 0.1 - sys.I_actuators[0,0] = 0.1 - - ctl = RminComputedTorqueController( sys ) - - # New cl-dynamic - cl_sys = ctl + sys - - cl_sys.x0 = np.array([2,0]) - cl_sys.compute_trajectory() - #cl_sys.animate_simulation() - cl_sys.plot_trajectory('xu') - \ No newline at end of file diff --git a/dev/hybrid_systems/test_hybrid_vi.py b/dev/hybrid_systems/test_hybrid_vi.py deleted file mode 100644 index 7372761d..00000000 --- a/dev/hybrid_systems/test_hybrid_vi.py +++ /dev/null @@ -1,59 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Created on Wed Apr 6 14:53:54 2022 - -@author: alex -""" - -############################################################################### -import numpy as np -############################################################################### -from hybrid_mechanical import TwoSpeedLinearActuator - -from pyro.planning import discretizer -from pyro.analysis import costfunction -from pyro.planning import valueiteration -############################################################################### - -sys = TwoSpeedLinearActuator() - -############################################################################### - -# Planning - -# Set domain -sys.x_ub = np.array([+3, +3]) -sys.x_lb = np.array([-3, -3]) - -sys.u_ub = np.array([+1, +1]) -sys.u_lb = np.array([-0, -1]) - -# Discrete world -grid_sys = discretizer.GridDynamicSystem(sys, (51, 51), (2, 11), 0.1) - -# Cost Function -cf = costfunction.QuadraticCostFunction.from_sys( sys ) -cf.xbar = np.array( [1, 0] ) # target -cf.INF = 1E9 -cf.EPS = 0.2 -cf.R = np.array([[0,0],[0,1]]) - -# VI algo -vi = valueiteration.ValueIteration_ND( grid_sys , cf ) - -vi.uselookuptable = True -vi.initialize() - - -vi.compute_steps(100) - -vi.assign_interpol_controller() -vi.plot_policy(0) -vi.plot_policy(1) - -cl_sys = vi.ctl + sys - -cl_sys.x0 = np.array([0, 0]) -cl_sys.compute_trajectory( 10, 10001, 'euler') -cl_sys.plot_trajectory('xu') diff --git a/dev/kinematic/__init__.py b/dev/kinematic/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/dev/kinematic/kinematic.py b/dev/kinematic/kinematic.py deleted file mode 100644 index 65fa95bb..00000000 --- a/dev/kinematic/kinematic.py +++ /dev/null @@ -1,659 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Sat Feb 11 17:07:22 2012 - -@author: gira2403 -""" - - -""" -########################################################### -# Modules -########################################################### -""" - -# Numpy -import numpy as np -from numpy.linalg import norm - - -""" -########################################################### -## Constantes -########################################################### -""" - -epsilon = 1e-12 - - -""" -########################################################### -## Class -########################################################### -""" - -########################################################### -## 3D Vector -########################################################### - -class Vector: - """ 3D Vector """ - - def __init__(self, x = 0, y = 0, z = 0): - """ Default is a null vector """ - - self.x = x - self.y = y - self.z = z - - """ Numpy matrix (COLUNM) """ - self.col = np.matrix([[x],[y],[z]]) - - """ Numpy skew matrix """ - self.skew = np.matrix([[0,-z,y],[z,0,-x],[-y,x,0]]) - - """ Vectorial norm """ - self.norm = norm(self.col) - - - ################################################## - def __call__(self): - - print('3D Vector : [',self.x,';',self.y,';',self.z,']') - - - ################################# - def __add__(self,other): - """ Vectorial addition """ - - x = self.x + other.x - y = self.y + other.y - z = self.z + other.z - - return Vector(x,y,z) - - - ################################# - def __sub__(self,other): - """ Vectorial substraction """ - - x = self.x - other.x - y = self.y - other.y - z = self.z - other.z - - return Vector(x,y,z) - - - ################################# - def __neg__(self): - """ Vectorial inversion """ - - x = - self.x - y = - self.y - z = - self.z - - return Vector(x,y,z) - - - ################################# - def __mul__(self,other): - """ Scalar multiplication with a scalar or dot product with a vector """ - - """ Check if its a scalar multiplication or a dot product """ - isvector = isinstance(other,Vector) - - ############## - if isvector: - - # Dot product - ans = other.col.T * self.col - - return ans[0,0] - - ############## - else: - - # Scalar multiplication - x = self.x * other - y = self.y * other - z = self.z * other - - return Vector(x,y,z) - - - ################################# - def __pow__(self,other): - """ Vectorial product """ - - col = self.skew * other.col - - return col2vec(col) - - - ################################# - def normalize(self): - """ Return normalized vector """ - - ############################ - if self.norm > 0 : - - direction = self * ( 1 / self.norm ) - - else : - print('Kinematic warning : not able to normalize a zero vector') - direction = Vector(0,0,0) - - - return direction - - - ################################# - def copy(self): - """ Return a copy of the current vector """ - - copy = self * 1 - - return copy - - - -########################################################### -## Rotation Matrix -########################################################### - -class RotationMatrix: - """ Matrix representation of a 3D rotation """ - - ########################### - def __init__(self, matrix = np.matrix(np.eye(3,3)) ): - """ Default is a identity matrix """ - - self.C = matrix - - - ####################### - def __call__(self): - - print('Rotation Matrix : \n', self.C) - - - ################################# - def __mul__(self,other): - """ Matrix multiplication """ - - """ Check if other is RotationMatrix or a vector """ - isvector = isinstance(other,Vector) - ismatrix = isinstance(other,RotationMatrix) - - ################## - if isvector: - - col = self.C * other.col - - return col2vec(col) - - ################## - elif ismatrix: - - mat = self.C * other.C - - return RotationMatrix(mat) - - ################## - else: - """ Scale the rotation arround the same axis with a scalar """ - - new_rotation = self.toAngleAxis() * other - - return AngleAxis2RotationMatrix(new_rotation) - - - ################################# - def __neg__(self): - """ Inverse Matrix """ - - return RotationMatrix(self.C.T) - - - ################################# - def toAngleAxis(self): - """ Compute equivalent Angle-Axis representation """ - - return RotationMatrix2AngleAxis(self) - - - ################################# - def toQuaternion(self): - """ Compute equivalent quaternion representation """ - - return RotationMatrix2Quaternion(self) - - - ################################# - def toRotationVector(self): - """ Compute equivalent Rotation Vector """ - - a = self.toAngleAxis() - - return a.toRotationVector() - - -########################################################### -## Angle-Axis 3D rotation -########################################################### - -class AngleAxis: - """ Angle-Axis representation of a rotation in 3D """ - - ################ - def __init__(self, rad = 0, axis = Vector(1,0,0) ): - """ Default is a rotation of 0 degree arround x """ - - self.rad = rad - self.deg = np.rad2deg(rad) - - self.axis = axis - - - ################ - def __call__(self): - """ Print Angle Axis """ - - print('AngleAxis: \n deg : ',self.deg,' axis : [',self.axis.x,';',self.axis.y,';',self.axis.z,']') - - - ################################# - def __mul__(self,other): - """ Scale the rotation around the same axis """ - - rad = self.rad * other - axis = self.axis.copy() - - return AngleAxis(rad, axis) - - - ################ - def toRotationMatrix(self): - """ Convert to 3x3 rotation matrix """ - - return AngleAxis2RotationMatrix(self) - - - ################ - def toQuaternion(self): - """ Convert to Quaternion """ - - return AngleAxis2Quaternion(self) - - - ################ - def toRotationVector(self): - """ Convert to Rotation Vector """ - - vector = self.axis * self.rad - - return RotationVector( vector.x , vector.y , vector.z ) - - -########################################################### -## Vecteur Rotation Rodrigues ( axis * angle ) -########################################################### - -class RotationVector(Vector): - """ 3D Vector representation of a rotation in 3D (angle axis : axis * angle) """ - - #################### - def __call__(self): - """ Print Rotation Vector """ - - print('3D Rotation Vector : [',self.x,';',self.y,';',self.z,']') - - - #################### - def toAngleAxis(self): - """ Convert to Angle-Axis """ - - rad = self.norm - axis = self.normalize() - - return AngleAxis(rad,axis) - - ################ - def toRotationMatrix(self): - """ Convert to 3x3 rotation matrix """ - - a = self.toAngleAxis() - - return AngleAxis2RotationMatrix(a) - - - ################ - def toQuaternion(self): - """ Convert to Quaternion """ - - a = self.toAngleAxis() - - return AngleAxis2Quaternion(a) - - -########################################################### -## Quaternion -########################################################### - -class Quaternion: - """ Quaternion representation of a 3D rotation """ - - ################# - def __init__(self, e = Vector(), n = 1 ): - """ Default is a zero rotation """ - - self.e = e - self.n = n - - - ################ - def __call__(self): - """ Print Quaternion """ - - print('Quaternion: \n e : [',self.e.x,';',self.e.y,';',self.e.z,'] n : ',self.n) - - - ################################# - def __mul__(self,other): - """ Quaternion multiplication or vector rotation with the Quaternion """ - - - """ Check if other is RotationMatrix or a vector """ - isvector = isinstance(other,Vector) - isquaternion = isinstance(other,Quaternion) - - ################ - if isquaternion: - """ Quaternion Multiplication """ - # Q = Qa * Qb - - na = self.n - nb = other.n - - ea = self.e - eb = other.e - - n = na * nb - ea * eb # * operator = scalar product for vector - e = (eb * na) + (ea * nb) + (ea ** eb) # ** operator = vectorial product for vector - - # Attention définition right handed, donc R2*R1 = Q1*Q2 - - """ - # 4x4 matrix to compute quaternion multiplication - qskew = np.matrix(np.zeros((4,4))) - - # Identitie matrix - I = np.matrix(np.eye(3,3)) - - qskew[0:3,0:3] = self.n * I - self.e.skew - qskew[3,0:3] = - self.e.col.T - qskew[0:3,3] = self.e.col - qskew[3,3] = self.n - """ - - return Quaternion(e,n) - - ################ - if isvector: - """ Rotate vector computing the rotation matrix """ - - new_vector = self.toRotationMatrix() * other - - return new_vector - - ############ - else: - """ Scale the rotation arround the same axis (other * angle arround the same axis) """ - - new_rotation = self.toAngleAxis() * other - - return AngleAxis2Quaternion(new_rotation) - - - ################################# - def __neg__(self): - """ Inverse Quaternion """ - - return Quaternion(-self.e,self.n) - - - ################################# - def toRotationMatrix(self): - """ Compute equivalent rotation matrix """ - - return Quaternion2RotationMatrix(self) - - - ################################# - def toAngleAxis(self): - """ Compute equivalent Angle-Axis """ - - return Quaternion2AngleAxis(self) - - - ################################# - def toRotationVector(self): - """ Compute equivalent Rotation Vector """ - - a = self.toAngleAxis() - - return a.toRotationVector() - - - -""" -############################################################################## -############# Functions ########################################### -############################################################################## -""" - -######################################## -def col2vec(col): - """ - Create a vector class from a numpy matrix - - col = - matrix([[1], - [2], - [1]]) - - """ - - x = col[0,0] - y = col[1,0] - z = col[2,0] - - return Vector(x,y,z) - - -######################################## -def list2vec(l): - """ - Create a vector class from a list - - l = [x,y,z] - - """ - - x = l[0] - y = l[1] - z = l[2] - - return Vector(x,y,z) - - -######################################## -def euler2RotationMatrix( teta_1 , teta_2 , teta_3 ): - """ Convert 3 euler angle to a 321 rotation matrix """ - - """ Convert degree to radian """ - r1 = np.deg2rad(teta_1) - r2 = np.deg2rad(teta_2) - r3 = np.deg2rad(teta_3) - - """ Compute cosinus """ - c1 = np.cos(r1) - c2 = np.cos(r2) - c3 = np.cos(r3) - - """ Compute sinus """ - s1 = np.sin(r1) - s2 = np.sin(r2) - s3 = np.sin(r3) - - """ Compute rotation matrix """ - R1 = np.matrix([[1,0,0],[0,c1,s1],[0,-s1,c1]]) - R2 = np.matrix([[c2,0,-s2],[0,1,0],[s2,0,c2]]) - R3 = np.matrix([[c3,s3,0],[-s3,c3,0],[0,0,1]]) - - """ Rotation 3-2-1 """ - C = R1 * R2 * R3 - - return RotationMatrix(C) - - -########################################### -def RotationMatrix2AngleAxis(RM): - """ Convert a rotation matrix to a angle axis class """ - - C = RM.C - - """ Compute Angle """ - cos = (C.trace()[0,0] - 1) * 0.5 - - rad = np.arccos(cos) - - sin = np.sin(rad) - - """ Compute axis of rotation """ - ############## - if ( abs(rad - np.pi) > epsilon ) and ( abs(rad + np.pi) > epsilon ): - - ax = ( C[1,2] - C[2,1] ) / (2 * sin) - ay = ( C[2,0] - C[0,2] ) / (2 * sin) - az = ( C[0,1] - C[1,0] ) / (2 * sin) - - ############## - else: - - print('\n RotationMatrix2AngleAxis : bad matrix : to do!!!') - - axis = Vector(ax,ay,az) - - return AngleAxis(rad,axis) - - -########################################### -def AngleAxis2Quaternion(AA): - """ Convert a angle axis class to a Quaternion """ - - """ Vector """ - e = AA.axis * np.sin( AA.rad * 0.5 ) - - """ Scalar """ - n = np.cos( AA.rad * 0.5 ) - - return Quaternion(e,n) - - -########################################### -def AngleAxis2RotationMatrix(AA): - """ Convert a angle axis class to a Rotation Matrix """ - - a = AA.axis - rad = AA.rad - - """ Eye matrix """ - I = np.matrix(np.eye(3,3)) - - cos = np.cos(rad) - sin = np.sin(rad) - - """ Compute Matrix """ - C = cos * I + (1 - cos) * a.col * a.col.T - sin * a.skew - - return RotationMatrix(C) - - -########################################### -def Quaternion2RotationMatrix(Q): - """ Convert a Quaternion to a Rotation Matrix """ - - n = Q.n - e = Q.e - - """ Eye matrix """ - I = np.matrix(np.eye(3,3)) - - C = I * ( n**2 - e.norm**2 ) + 2 * e.col * e.col.T - 2 * n * e.skew - - return RotationMatrix(C) - - -########################################### -def Quaternion2AngleAxis(Q): - """ Convert a Quaternion to a angle axis """ - - n = Q.n - e = Q.e - - """ Axis """ - axis = e * ( 1 / e.norm ) - - """ Angle """ - rad = 2 * np.arccos(n) - - return AngleAxis(rad,axis) - - -########################################### -def RotationMatrix2Quaternion(RM): - """ Convert a Quaternion to a Rotation Matrix """ - - C = RM.C - - trace = C.trace()[0,0] - - if trace < epsilon : - print(' Warning : trace of the matrix is near zero ') - - n = 0.5 * np.sqrt( trace + 1 ) - - ex = C[1,2] - C[2,1] - ey = C[2,0] - C[0,2] - ez = C[0,1] - C[1,0] - - e = Vector(ex,ey,ez) * ( 1 / ( 4 * n ) ) - - return Quaternion(e,n) - - - - -""" -############################################################################## -############# MAIN ########################################### -############################################################################## -""" - - -if __name__ == '__main__': - - V = Vector(10,10,10) - - C1 = euler2RotationMatrix(0,0,90) - C2 = euler2RotationMatrix(90,0,0) - - Q1 = C1.toQuaternion() - Q2 = C2.toQuaternion() - - C21 = C1 * C2 - - Q21 = Q2 * Q1 - - \ No newline at end of file diff --git a/dev/markov/markov.py b/dev/markov/markov.py deleted file mode 100644 index f9dd861a..00000000 --- a/dev/markov/markov.py +++ /dev/null @@ -1,291 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Created on Sun Jan 9 09:07:29 2022 - -@author: alex -""" - -import numpy as np - - -############################################################################### -class MarkovSystem: - """ - Mother class for discrete stochastic dynamical systems - ---------------------------------------------- - n : number of finite states - m : number of control action - p : number of outputs - k : time index - --------------------------------------- - x_k+1 = fk( x_k , u_k , k ) - y_k = hk( x_k , u_k , k ) - - optionnal: - u_k = policy( y_k , k ) : action autonomous decision - - - """ - ########################################################################### - # The two following functions needs to be implemented by child classes - ########################################################################### - - ############################ - def __init__(self, n = 1, m = 1): - """ - The __init__ method of the Mother class can be used to fill-in default - labels, units, bounds, and base values. - """ - - ############################# - # Parameters - ############################# - - p = n - - # Dimensions - self.n = n - self.m = m - self.p = p - - # Labels - self.name = 'ContinuousDynamicSystem' - self.state_label = [] - self.input_label = [] - self.output_label = [] - - # Default Label and units - for i in range(n): - self.state_label.append('State '+str(i)) - for i in range(m): - self.input_label.append('Action '+str(i)) - for i in range(p): - self.output_label.append('Output '+str(i)) - - # Default state and inputs values - self.xbar = 0 - self.ubar = 0 - - ################################ - # Transition Probabilities - ################################ - - self.T_jia = np.zeros((n,n,m)) - - for a in range(m): - - self.T_jia[:,:,a] = np.diag(np.ones((n))) - - ################################ - # Transition cost - ################################ - - self.a_jia = np.zeros((n,n,m)) - - ################################ - # Final cost - ################################ - - self.gN_i = np.zeros((n)) - - - ################################ - # Variables - ################################ - - # Initial value for simulations - self.x0 = np.zeros( n ) - self.x0[0] = 1 - - # Result of last simulation - self.traj = None - - # Cost function for evaluation - # TODO - - - ############################# - def fk( self , x , u , k = 0 ): - """ - compute the evolution of the probability distribution - - """ - - T_ji = self.T_jia[:,:,u] # transition matrix of given action - - x_k1 = np.dot( T_ji , x ) - - return x_k1 - - - ############################# - def check_probability_matrix( self ): - """ - check if transition prob sums to 1 - - """ - - print( self.T_jia.sum( axis = 0 ) ) - - return self.T_jia.sum( axis = 0 ) # should be all ones - - - ########################################################################### - # The following functions can be overloaded when necessary by child classes - ########################################################################### - - ############################# - def h( self , x , k = 0 ): - """ - Output fonction y = h(x,u,t) - - """ - - y = x # default output is state - - return y - - ############################# - def policy( self , y , k ): - """ - - """ - - # Default action - u = self.ubar - - return u - - - ############################# - def simulation_of_density_probability( self , N = 10 , plot = True ): - """ - N = number of steps - """ - - x_k = self.x0 - - for k in range(N): - - y_k = self.h( x_k , k ) - u_k = self.policy( y_k , k ) - x_k1 = self.fk( x_k, u_k , k ) - - x_k = x_k1 - - if plot: - print(x_k1) - - - return x_k1 # return probability distribution at k = N - - - ############################# - def get_valueiteration_algo(self): - - vi = ValueIterationForMarkovProcess(self.T_jia, self.a_jia, self.gN_i) - - return vi - - - -############################################################################### -class ValueIterationForMarkovProcess: - """ - - - - """ - - ############################ - def __init__(self, T_jia , a_jia, gN_i ): - - - self.alpha = 1.0 # discount factor - - - self.T = T_jia - self.a = a_jia - self.g = gN_i - - self.n = T_jia.shape[0] # Number of states - self.m = T_jia.shape[2] # Number of actions - - - # Initialise cost-to-go with final cost - self.J = self.g.copy() - - # Initialise policy map - self.c = np.zeros((self.n)) - - # Initialise Q-values - self.Q = np.zeros((self.n,self.m)) - - - self.print = True - - - ############################### - def compute_backward_step(self): - - # For all states - for i in range(self.n): - - # For all actions - for a in range(self.m): - - Q_j = self.a[:,i,a] + self.alpha * self.J # array of possible cost - - self.Q[i,a] = np.dot( self.T[:,i,a] , Q_j ) # expected value - - - self.J = self.Q.min(1) # Minimum over all possible actions - self.c = self.Q.argmin(1) # Action that minimise Q for all i - - - ############################### - def compute_n_backward_steps(self, n): - - for k in range(n): - - self.compute_backward_step() - - if self.print: - print('Backward step N-',k) - print('J = ',self.J) - print('c = ',self.c) - - - - - - - - - - - - -''' -################################################################# -################## Main ######## -################################################################# -''' - - -if __name__ == "__main__": - """ MAIN TEST """ - - # - m = MarkovSystem(5,3) - - - m.check_probability_matrix() - - vi = m.get_valueiteration_algo() - - vi.alpha = 0.9 - - vi.compute_n_backward_steps(100) - \ No newline at end of file diff --git a/dev/mpc/mpc.py b/dev/mpc/mpc.py deleted file mode 100644 index 04b156f4..00000000 --- a/dev/mpc/mpc.py +++ /dev/null @@ -1,121 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Created on Sun Nov 21 03:14:40 2021 - -@author: alex -""" - -import numpy as np - -from pyro.planning.trajectoryoptimisation import DirectCollocationTrajectoryOptimisation -from pyro.dynamic.statespace import linearize - - -from pyro.control import controller - -########################################################################## -#T -########################################################################### -class NonLinearMPC( controller.StaticController ) : - """ - - """ - ############################ - def __init__(self, sys, dt = 0.1 , n = 10 ): - """ """ - - # Dimensions - self.k = 1 - self.m = sys.m - self.p = sys.p - - super().__init__(self.k, self.m, self.p) - - # Label - self.name = 'MPC Controller' - - # Gains - self.goal = sys.xbar - - self.planner = DirectCollocationTrajectoryOptimisation( sys , dt , n ) - - self.planner.x_goal = self.goal - - - ############################# - def c( self , y , r , t = 0 ): - """ - Feedback static computation u = c(y,r,t) - - INPUTS - y : sensor vector p x 1 - r : reference vector k x 1 - t : time 1 x 1 - - OUPUTS - u : control inputs vector m x 1 - - """ - - self.planner.x_start = y # actual state - - self.planner.compute_optimal_trajectory() - - x,u = self.planner.decisionvariables2xu( self.planner.res.x ) - - #DEBUG - self.planner.show_solution() - print(self.planner.x_start) - - u_next = u[:,0] - - return u_next - - - -''' -################################################################# -################## Main ######## -################################################################# -''' - - -if __name__ == "__main__": - """ MAIN TEST """ - - from pyro.dynamic.massspringdamper import TwoMass - from pyro.analysis.costfunction import QuadraticCostFunction - - sys = TwoMass() - - #Full state feedback (default of class is x2 output only) - sys.C = np.diag([1,1,1,1]) - sys.p = 4 # dim of output vector - sys.output_label = sys.state_label - sys.output_units = sys.state_units - - # Cost function - cf = QuadraticCostFunction.from_sys( sys ) - cf.Q[0,0] = 1 - cf.Q[1,1] = 1 - cf.Q[2,2] = 1 - cf.Q[3,3] = 1 - cf.R[0,0] = 1 - sys.cost_function = cf - - sys.u_ub[0] = +2 - sys.u_lb[0] = 0 - - ctl = NonLinearMPC( sys , 0.1 , 30) - - ctl.planner.goal = np.array([0,0,0,0]) - ctl.planner.maxiter = 10 - - # New cl-dynamic - cl_sys = ctl + sys - - cl_sys.x0 = np.array([0.5,0.5,0,0]) - cl_sys.compute_trajectory(0.3,4,'euler') - cl_sys.plot_trajectory('xu') - cl_sys.animate_simulation() \ No newline at end of file diff --git a/dev/old_to_remove/trajectory_optimisation/Charles_Khazoom_tests/trajopt.py b/dev/old_to_remove/trajectory_optimisation/Charles_Khazoom_tests/trajopt.py deleted file mode 100644 index ee1dfd9f..00000000 --- a/dev/old_to_remove/trajectory_optimisation/Charles_Khazoom_tests/trajopt.py +++ /dev/null @@ -1,279 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Wed Jul 12 12:09:37 2017 - -@author: Charles.Khazoom@hotmail.com -""" - -import numpy as np -import numpy.matlib as mb -import matplotlib.pyplot as plt - -from scipy.optimize import minimize, NonlinearConstraint, rosen, rosen_der,HessianUpdateStrategy,BFGS#, interpolate -from scipy.interpolate import interp1d -from pyro.dynamic import manipulator -from pyro.planning import plan -from pyro.analysis import costfunction -from pyro.analysis import Trajectory -''' -################################################################################ -''' - - -#class TorqueSquaredCostFunction( costfunction.QuadraticCostFunction ): -# def __init__(self): -# costfunction.QuadraticCostFunction.__init__( self ) - -''' -################################################################################ -''' -# create system -sys = manipulator.OneLinkManipulator() - - -''' -Create optization problem -''' - -# set cost function -# minimize torque square is quadraic cost with Q and V set to 0 -#class TorqueSquaredCostFunction( costfunction.QuadraticCostFunction ): should implement this class -sys.cost_function.Q=np.zeros(sys.cost_function.Q.shape) -sys.cost_function.V=np.zeros(sys.cost_function.V.shape) -sys.cost_function.R=np.ones(sys.cost_function.R.shape) # - -global ngrid -ngrid = 30 # number of gridpoint - -######## set bounds ########## -ub_t0 = 0 # bounds on t0 -lb_t0 = 0 - -ub_tf = 2 # bounds on tf -lb_tf = 2 -# should implement an error message if tf is not >t0 and in t0<0 - -#ub_state = np.array([]) -#lb_state -sys.x_ub=[2*np.pi,None] -sys.x_lb=[-2*np.pi,None] - -sys.u_ub = [None] -sys.u_lb = [None] - -ub_x = sys.x_ub # bounds on x -lb_x = sys.x_lb - -ub_u = sys.u_ub # bounds on inputs u -lb_u = sys.u_lb - -ub_x0 = [np.pi,0] # bounds on inital state -lb_x0 = [np.pi,0] - - -ub_xf = [0,0] #sys.x_ub # bounds on final state -lb_xf = [0,0] - -''' -create initial guess -''' - -#x_guess = np.linspace(ub_x0, ub_xf, ngrid) -#u_guess = np.ones([ngrid,sys.m]) -#t_guess = np.linspace(ub_t0, ub_tf, ngrid) -#y_guess= x_guess -#dx_guess=np.zeros(x_guess.shape) -# -#for i in range(ngrid): -# dx_guess[i,] = sys.f(x_guess[i,],u_guess[i,]) # compute f -# -#guess_traj = Trajectory(x_guess, u_guess, t_guess, dx_guess, y_guess) - - - -######## set equality contraints (other than dynamics) ########## -# sys.f contains the dynamics ! - -''' -Convert to non-linear program (direct collocation) - -decision variables are ordered: - - x0[0],x0[1]...,x0[ngrid-1], x1[0],x1[1]...,x1[ngrid-1],...,...xn[0],xn[1]...,xn[ngrid-1] - u0[0],u0[1]...,u0[ngrid-1], u1[0],x1[1]...,u1[ngrid-1],...,...un[0],un[1]...,un[ngrid-1], - t0, tf -''' - -#convert bounds in discete form, for all x(.),u(.),t(.) - -def pack_bounds(lb_x,ub_x,lb_x0,ub_x0,lb_xf,ub_xf,lb_u,ub_u,lb_t0,ub_t0,lb_tf,ub_tf): - - bnds = np.array([]).reshape(0,2) # initialize bounds np.array to append to - - for i in range(sys.n): # convert bounds on x - bnd_arr_to_add = np.concatenate([mb.repmat(lb_x[i], ngrid, 1),mb.repmat(ub_x[i], ngrid, 1)],axis=1) - bnd_arr_to_add[0,:]=np.array([lb_x0[i],ub_x0[i]])#enforce bound on initial value - bnd_arr_to_add[ngrid-1,:]=np.array([lb_xf[i],ub_xf[i]])#enforce bound on final value - bnds = np.append(bnds,bnd_arr_to_add,axis=0) - - for i in range(sys.m): # convert bounds on u - bnd_arr_to_add = np.concatenate([mb.repmat(lb_u[i], ngrid, 1),mb.repmat(ub_u[i], ngrid, 1)],axis=1) - bnds = np.append(bnds,bnd_arr_to_add,axis=0) - - # append bounds on t0 and tF - bnds=np.append(bnds,np.array([lb_t0,lb_t0]).reshape(1,2),axis=0) - bnds=np.append(bnds,np.array([lb_tf,lb_tf]).reshape(1,2),axis=0) - - bnds = tuple(map(tuple, bnds))#convert bnds np.array to tuple for input in NLP solver - - return bnds - - -def traj_2_dec_var(traj): - - dec_vars = np.array([]).reshape(0,1) # initialize dec_vars array - - for i in range(sys.n): # append states x - arr_to_add = traj.x[:,i].reshape(ngrid,1) - dec_vars = np.append(dec_vars,arr_to_add,axis=0) - - for i in range(sys.m): # append inputs u - arr_to_add = traj.u[:,i].reshape(ngrid,1) - dec_vars = np.append(dec_vars,arr_to_add,axis=0) - - # append t0 and tF - dec_vars=np.append(dec_vars,np.array(traj.t[0]).reshape(1,1),axis=0) - dec_vars=np.append(dec_vars,traj.t[-1].reshape(1,1),axis=0) - dec_vars=dec_vars.reshape(ngrid*(sys.n+sys.m)+2,) - return dec_vars - - - - -def unpack_dec_var(decision_variables): - global ngrid - x = np.zeros([ngrid,sys.n]) - u = np.zeros([ngrid,sys.m]) - # unpack decision variables into trajectory - for i in range(0,sys.n): - x[:,i] = decision_variables[i*ngrid:(i+1)*ngrid].reshape(ngrid) - - for i in range(sys.n,sys.n+sys.m): - u[:,i-sys.n] = decision_variables[i*ngrid:(i+1)*ngrid].reshape(ngrid) - - t0 = decision_variables[-2] - tf = decision_variables[-1] - return x,u,t0,tf - -def compute_dx(x,u,sys): - dx = np.zeros(x.shape) - global ngrid - for i in range(ngrid): - dx[i,:]=sys.f(x[i,:],u[i,:]) - return dx - - -def dec_var_2_traj(decision_variables): - global ngrid - - x,u,t0,tf = unpack_dec_var(decision_variables) - - dx = compute_dx(x,u,sys) - - t = np.linspace(t0, tf, ngrid).reshape(ngrid,) - - y=x # find proper fct evaluation later from sys object - - traj = Trajectory(x, u, t, dx, y) - - return traj - -def dynamics_cstr(traj): - - t0 = traj.t[0] - - tf = traj.t[-1] - - h = (tf-t0)/(ngrid-1) #step between each grid - - dyn_constr = np.diff(traj.x,axis=0) - (traj.dx[0:-1]+traj.dx[1:])*h/2 # this must be = 0 - dyn_constr = dyn_constr.reshape((ngrid-1)*sys.n,) - return dyn_constr - -def compute_cost(decision_variables): - traj_opt = dec_var_2_traj(decision_variables) #get trajectorty - traj_opt = sys.cost_function.trajectory_evaluation(traj_opt)#compute cost fcn - cost_function = traj_opt.J[-1]# traj_opt.J is the cumulated cost from integral, we take only the last value - return cost_function - -def interp_traj(traj,ngrid): - - f_x = interp1d(traj.t, traj.x, kind='cubic',axis=0) - f_u = interp1d(traj.t, traj.u, kind='quadratic',axis=0) - - t_interp = np.linspace(traj.t[0],traj.t[-1],ngrid) - x_interp = f_x(t_interp) - u_interp = f_u(t_interp) - dx_interp=compute_dx(x_interp,u_interp,sys) - y_interp = x_interp - traj_interp = Trajectory(x_interp, u_interp, t_interp, dx_interp, y_interp) - return traj_interp - -A = np.load(r'solution_with_slsqp_onelink_200.npy') -ngrid=200 -loaded_traj = dec_var_2_traj(A) -ngrid=250 -bnds = pack_bounds(lb_x,ub_x0,lb_x,ub_x0,lb_xf,ub_xf,lb_u,ub_u,lb_t0,lb_t0,lb_tf,lb_tf) - -cons_slsqp = ({'type': 'eq', 'fun': lambda x: dynamics_cstr(dec_var_2_traj( x )) }) - -cons_trust=NonlinearConstraint(lambda x: dynamics_cstr(dec_var_2_traj( x )), 0, 0)# - - - - - - -guess_traj = interp_traj(loaded_traj,ngrid) -dec_var_guess = traj_2_dec_var(guess_traj) - - - -''' -Solve non-linear program -''' -#method='trust-constr' -res4 = minimize(compute_cost, dec_var_guess,method='SLSQP' , bounds=bnds, constraints=cons_slsqp,tol=1e-6,options={'disp': True,'maxiter':1000}) - - - -#dec_var_guess = np.load('C:\Users\Charles Khazoom\Documents\git\pyro\trajresults\solution_with_trust_const.npy') - -#res4 = minimize(compute_cost, dec_var_guess,method='trust-constr' , bounds=bnds, constraints=cons_trust,tol=1e-6,options={'disp': True,'maxiter':1000},jac='2-point',hess=BFGS()) -# - - - - -''' -Interpolate solution in trajectory object -''' - -result_traj = dec_var_2_traj(res4.x) - -interp_value = 1000 -result_traj_int = interp_traj(result_traj,interp_value) - -sys.traj = result_traj_int -sys.x0=sys.traj.x[0,:] -#sys.plot_trajectory('xu') -ctl = plan.OpenLoopController(result_traj_int) -# - -## New cl-dynamic -cl_sys = ctl + sys -# -cl_sys.compute_trajectory( sys.traj.t[-1] ,interp_value ) -cl_sys.plot_trajectory('xu') -#sys.cost_function.trajectory_evaluation(sys.traj) -#cl_sys.animate_simulation() \ No newline at end of file diff --git a/dev/old_to_remove/trajectory_optimisation/Charles_Khazoom_tests/trajopt_double_pendulum_based_on_rrt_guess.py b/dev/old_to_remove/trajectory_optimisation/Charles_Khazoom_tests/trajopt_double_pendulum_based_on_rrt_guess.py deleted file mode 100755 index 399f5be4..00000000 --- a/dev/old_to_remove/trajectory_optimisation/Charles_Khazoom_tests/trajopt_double_pendulum_based_on_rrt_guess.py +++ /dev/null @@ -1,304 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Wed Jul 12 12:09:37 2017 - -@author: Charles.Khazoom@hotmail.com -""" - -import numpy as np -import numpy.matlib as mb -import matplotlib.pyplot as plt - -from scipy.optimize import minimize, NonlinearConstraint, rosen, rosen_der,HessianUpdateStrategy,BFGS#, interpolate -from scipy.interpolate import interp1d -from pyro.dynamic import manipulator -from pyro.planning import plan -from pyro.analysis import costfunction -from pyro.analysis import Trajectory - -from pyro.control import nonlinear -from pyro.analysis import simulation - -''' -############################################################################## -''' - - -#class TorqueSquaredCostFunction( costfunction.QuadraticCostFunction ): -# def __init__(self): -# costfunction.QuadraticCostFunction.__init__( self ) - -''' -############################################################################## -''' -# create system -#sys = manipulator.OneLinkManipulator() - -from pyro.dynamic import pendulum - -sys = pendulum.DoublePendulum() - -''' -Create optization problem -''' - -# set cost function -# minimize torque square is quadraic cost with Q and V set to 0 -#class TorqueSquaredCostFunction( costfunction.QuadraticCostFunction ): should implement this class -#sys.cost_function.Q=np.zeros(sys.cost_function.Q.shape) -#sys.cost_function.V=np.zeros(sys.cost_function.V.shape) -sys.cost_function.Q[0,0] = 1 -sys.cost_function.Q[1,1] = 1 -sys.cost_function.R[0,0] = 1 -sys.cost_function.R[1,1] = 1 -sys.cost_function.xbar = np.array([0,0,0,0]) - -global ngrid -ngrid = 30 # number of gridpoint - -######## set bounds ########## -ub_t0 = 0 # bounds on t0 -lb_t0 = 0 - -ub_tf = 8 # bounds on tf -lb_tf = 7 -# should implement an error message if tf is not >t0 and in t0<0 - -#ub_state = np.array([]) -#lb_state -sys.x_ub=[+2*np.pi,+2*np.pi,0,0] -sys.x_lb=[-2*np.pi,-2*np.pi,0,0] - -sys.u_ub = [+50.0,+50.0] -sys.u_lb = [-50.0,-50.0] - -ub_x = sys.x_ub # bounds on x -lb_x = sys.x_lb - -ub_u = sys.u_ub # bounds on inputs u -lb_u = sys.u_lb - -ub_x0 = [-3.14,0,0,0] # bounds on inital state -lb_x0 = [-3.14,0,0,0] - - -ub_xf = [0,0,0,0] #sys.x_ub # bounds on final state -lb_xf = [0,0,0,0] - -''' -create initial guess -''' - -#x_guess = np.linspace(ub_x0, ub_xf, ngrid) -#u_guess = np.ones([ngrid,sys.m]) -#t_guess = np.linspace(ub_t0, ub_tf, ngrid) -#y_guess= x_guess -#dx_guess=np.zeros(x_guess.shape) -# -#for i in range(ngrid): -# dx_guess[i,] = sys.f(x_guess[i,],u_guess[i,]) # compute f -# -#guess_traj = Trajectory(x_guess, u_guess, t_guess, dx_guess, y_guess) - - - -######## set equality contraints (other than dynamics) ########## -# sys.f contains the dynamics ! - -''' -Convert to non-linear program (direct collocation) - -decision variables are ordered: - - x0[0],x0[1]...,x0[ngrid-1], x1[0],x1[1]...,x1[ngrid-1],...,...xn[0],xn[1]...,xn[ngrid-1] - u0[0],u0[1]...,u0[ngrid-1], u1[0],x1[1]...,u1[ngrid-1],...,...un[0],un[1]...,un[ngrid-1], - t0, tf -''' - -#convert bounds in discete form, for all x(.),u(.),t(.) - -def pack_bounds(lb_x,ub_x,lb_x0,ub_x0,lb_xf,ub_xf,lb_u,ub_u,lb_t0,ub_t0,lb_tf,ub_tf): - - bnds = np.array([]).reshape(0,2) # initialize bounds np.array to append to - - for i in range(sys.n): # convert bounds on x - bnd_arr_to_add = np.concatenate([mb.repmat(lb_x[i], ngrid, 1),mb.repmat(ub_x[i], ngrid, 1)],axis=1) - bnd_arr_to_add[0,:]=np.array([lb_x0[i],ub_x0[i]])#enforce bound on initial value - bnd_arr_to_add[ngrid-1,:]=np.array([lb_xf[i],ub_xf[i]])#enforce bound on final value - bnds = np.append(bnds,bnd_arr_to_add,axis=0) - - for i in range(sys.m): # convert bounds on u - bnd_arr_to_add = np.concatenate([mb.repmat(lb_u[i], ngrid, 1),mb.repmat(ub_u[i], ngrid, 1)],axis=1) - bnds = np.append(bnds,bnd_arr_to_add,axis=0) - - # append bounds on t0 and tF - bnds=np.append(bnds,np.array([lb_t0,lb_t0]).reshape(1,2),axis=0) - bnds=np.append(bnds,np.array([lb_tf,lb_tf]).reshape(1,2),axis=0) - - bnds = tuple(map(tuple, bnds))#convert bnds np.array to tuple for input in NLP solver - - return bnds - - -def traj_2_dec_var(traj): - - dec_vars = np.array([]).reshape(0,1) # initialize dec_vars array - - for i in range(sys.n): # append states x - arr_to_add = traj.x[:,i].reshape(ngrid,1) - dec_vars = np.append(dec_vars,arr_to_add,axis=0) - - for i in range(sys.m): # append inputs u - arr_to_add = traj.u[:,i].reshape(ngrid,1) - dec_vars = np.append(dec_vars,arr_to_add,axis=0) - - # append t0 and tF - dec_vars=np.append(dec_vars,np.array(traj.t[0]).reshape(1,1),axis=0) - dec_vars=np.append(dec_vars,traj.t[-1].reshape(1,1),axis=0) - dec_vars=dec_vars.reshape(ngrid*(sys.n+sys.m)+2,) - return dec_vars - - - - -def unpack_dec_var(decision_variables): - global ngrid - x = np.zeros([ngrid,sys.n]) - u = np.zeros([ngrid,sys.m]) - # unpack decision variables into trajectory - for i in range(0,sys.n): - x[:,i] = decision_variables[i*ngrid:(i+1)*ngrid].reshape(ngrid) - - for i in range(sys.n,sys.n+sys.m): - u[:,i-sys.n] = decision_variables[i*ngrid:(i+1)*ngrid].reshape(ngrid) - - t0 = decision_variables[-2] - tf = decision_variables[-1] - return x,u,t0,tf - -def compute_dx(x,u,sys): - dx = np.zeros(x.shape) - global ngrid - for i in range(ngrid): - dx[i,:]=sys.f(x[i,:],u[i,:]) - return dx - - -def dec_var_2_traj(decision_variables): - global ngrid - - x,u,t0,tf = unpack_dec_var(decision_variables) - - dx = compute_dx(x,u,sys) - - t = np.linspace(t0, tf, ngrid).reshape(ngrid,) - - y=x # find proper fct evaluation later from sys object - - traj = Trajectory(x, u, t, dx, y) - - return traj - -def dynamics_cstr(traj): - - t0 = traj.t[0] - - tf = traj.t[-1] - - h = (tf-t0)/(ngrid-1) #step between each grid - - dyn_constr = np.diff(traj.x,axis=0) - (traj.dx[0:-1]+traj.dx[1:])*h/2 # this must be = 0 - dyn_constr = dyn_constr.reshape((ngrid-1)*sys.n,) - return dyn_constr - -def compute_cost(decision_variables): - traj_opt = dec_var_2_traj(decision_variables) #get trajectorty - traj_opt = sys.cost_function.trajectory_evaluation(traj_opt)#compute cost fcn - cost = traj_opt.J[-1]# traj_opt.J is the cumulated cost from integral, we take only the last value - return cost - -def interp_traj(traj,ngrid): - - f_x = interp1d(traj.t, traj.x, kind='cubic',axis=0) - f_u = interp1d(traj.t, traj.u, kind='quadratic',axis=0) - - t_interp = np.linspace(traj.t[0],traj.t[-1],ngrid) - x_interp = f_x(t_interp) - u_interp = f_u(t_interp) - dx_interp=compute_dx(x_interp,u_interp,sys) - y_interp = x_interp - traj_interp = Trajectory(x_interp, u_interp, t_interp, dx_interp, y_interp) - return traj_interp - - - - - - -# Guess traj based from RRT solution -loaded_traj = simulation.Trajectory.load('double_pendulum_rrt.npy') -ngrid = loaded_traj.time_steps * 10 - - -# - -bnds = pack_bounds(lb_x,ub_x0,lb_x,ub_x0,lb_xf,ub_xf,lb_u,ub_u,lb_t0,lb_t0,lb_tf,lb_tf) - -cons_slsqp = ({'type': 'eq', 'fun': lambda x: dynamics_cstr(dec_var_2_traj( x )) }) - -cons_trust=NonlinearConstraint(lambda x: dynamics_cstr(dec_var_2_traj( x )), 0, 0)# - - -guess_traj = interp_traj( loaded_traj , ngrid ) -dec_var_guess = traj_2_dec_var( guess_traj ) - - - -''' -Solve non-linear program -''' -#method='trust-constr' -res4 = minimize(compute_cost, dec_var_guess,method='SLSQP' , bounds=bnds, constraints=cons_slsqp,tol=1e-6,options={'disp': True,'maxiter':1000}) - - - -#dec_var_guess = np.load('C:\Users\Charles Khazoom\Documents\git\pyro\trajresults\solution_with_trust_const.npy') - -#res4 = minimize(compute_cost, dec_var_guess,method='trust-constr' , bounds=bnds, constraints=cons_trust,tol=1e-6,options={'disp': True,'maxiter':1000},jac='2-point',hess=BFGS()) -# - - - -''' -Analyze results -''' - - -result_traj = dec_var_2_traj(res4.x) - -result_traj.save('double_pendulum_rrt_optimised.npy') - - -#show solution vs initial guess -sys.traj = loaded_traj -sys.plot_trajectory('xu') -sys.traj = result_traj -sys.plot_trajectory('xu') -#sys.animate_simulation() - -# CLosed-loop controller -ctl = nonlinear.ComputedTorqueController( sys , result_traj ) -ctl.rbar = np.array([0,0]) - -## New cl-dynamic -cl_sys = ctl + sys - -cl_sys.x0 = np.array([-3.14,0,0,0]) -cl_sys.compute_trajectory( 10 ) -cl_sys.plot_trajectory('xu') -cl_sys.animate_simulation() - - -print(sys.cost_function.trajectory_evaluation( guess_traj ).J[-1]) -print(sys.cost_function.trajectory_evaluation( result_traj ).J[-1]) -print(sys.cost_function.trajectory_evaluation( cl_sys.traj ).J[-1]) \ No newline at end of file diff --git a/dev/old_to_remove/trajectory_optimisation/Charles_Khazoom_tests/trajopt_single_pendulum_based_on_rrt_guess.py b/dev/old_to_remove/trajectory_optimisation/Charles_Khazoom_tests/trajopt_single_pendulum_based_on_rrt_guess.py deleted file mode 100755 index 0790842f..00000000 --- a/dev/old_to_remove/trajectory_optimisation/Charles_Khazoom_tests/trajopt_single_pendulum_based_on_rrt_guess.py +++ /dev/null @@ -1,302 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Wed Jul 12 12:09:37 2017 - -@author: Charles.Khazoom@hotmail.com -""" - -import numpy as np -import numpy.matlib as mb -import matplotlib.pyplot as plt - -from scipy.optimize import minimize, NonlinearConstraint, rosen, rosen_der,HessianUpdateStrategy,BFGS#, interpolate -from scipy.interpolate import interp1d -from pyro.dynamic import manipulator -from pyro.planning import plan -from pyro.analysis import costfunction -from pyro.analysis import Trajectory - -from pyro.control import nonlinear -from pyro.analysis import simulation - -''' -################################################################################ -''' - - -#class TorqueSquaredCostFunction( costfunction.QuadraticCostFunction ): -# def __init__(self): -# costfunction.QuadraticCostFunction.__init__( self ) - -''' -################################################################################ -''' -# create system -#sys = manipulator.OneLinkManipulator() - -from pyro.dynamic import pendulum - -sys = pendulum.SinglePendulum() - -''' -Create optization problem -''' - -# set cost function -# minimize torque square is quadraic cost with Q and V set to 0 -#class TorqueSquaredCostFunction( costfunction.QuadraticCostFunction ): should implement this class -#sys.cost_function.Q=np.zeros(sys.cost_function.Q.shape) -#sys.cost_function.V=np.zeros(sys.cost_function.V.shape) -sys.cost_function.Q[0,0] = 0 -sys.cost_function.Q[1,1] = 0 -sys.cost_function.R[0,0] = 10 -sys.cost_function.xbar = np.array([-3.14,0]) - -global ngrid -ngrid = 30 # number of gridpoint - -######## set bounds ########## -ub_t0 = 0 # bounds on t0 -lb_t0 = 0 - -ub_tf = 7 # bounds on tf -lb_tf = 6 -# should implement an error message if tf is not >t0 and in t0<0 - -#ub_state = np.array([]) -#lb_state -sys.x_ub=[2*np.pi,None] -sys.x_lb=[-2*np.pi,None] - -sys.u_ub = [20.0] -sys.u_lb = [-20.0] - -ub_x = sys.x_ub # bounds on x -lb_x = sys.x_lb - -ub_u = sys.u_ub # bounds on inputs u -lb_u = sys.u_lb - -ub_x0 = [0.0,0] # bounds on inital state -lb_x0 = [0.0,0] - - -ub_xf = [-3.14,0] #sys.x_ub # bounds on final state -lb_xf = [-3.14,0] - -''' -create initial guess -''' - -#x_guess = np.linspace(ub_x0, ub_xf, ngrid) -#u_guess = np.ones([ngrid,sys.m]) -#t_guess = np.linspace(ub_t0, ub_tf, ngrid) -#y_guess= x_guess -#dx_guess=np.zeros(x_guess.shape) -# -#for i in range(ngrid): -# dx_guess[i,] = sys.f(x_guess[i,],u_guess[i,]) # compute f -# -#guess_traj = Trajectory(x_guess, u_guess, t_guess, dx_guess, y_guess) - - - -######## set equality contraints (other than dynamics) ########## -# sys.f contains the dynamics ! - -''' -Convert to non-linear program (direct collocation) - -decision variables are ordered: - - x0[0],x0[1]...,x0[ngrid-1], x1[0],x1[1]...,x1[ngrid-1],...,...xn[0],xn[1]...,xn[ngrid-1] - u0[0],u0[1]...,u0[ngrid-1], u1[0],x1[1]...,u1[ngrid-1],...,...un[0],un[1]...,un[ngrid-1], - t0, tf -''' - -#convert bounds in discete form, for all x(.),u(.),t(.) - -def pack_bounds(lb_x,ub_x,lb_x0,ub_x0,lb_xf,ub_xf,lb_u,ub_u,lb_t0,ub_t0,lb_tf,ub_tf): - - bnds = np.array([]).reshape(0,2) # initialize bounds np.array to append to - - for i in range(sys.n): # convert bounds on x - bnd_arr_to_add = np.concatenate([mb.repmat(lb_x[i], ngrid, 1),mb.repmat(ub_x[i], ngrid, 1)],axis=1) - bnd_arr_to_add[0,:]=np.array([lb_x0[i],ub_x0[i]])#enforce bound on initial value - bnd_arr_to_add[ngrid-1,:]=np.array([lb_xf[i],ub_xf[i]])#enforce bound on final value - bnds = np.append(bnds,bnd_arr_to_add,axis=0) - - for i in range(sys.m): # convert bounds on u - bnd_arr_to_add = np.concatenate([mb.repmat(lb_u[i], ngrid, 1),mb.repmat(ub_u[i], ngrid, 1)],axis=1) - bnds = np.append(bnds,bnd_arr_to_add,axis=0) - - # append bounds on t0 and tF - bnds=np.append(bnds,np.array([lb_t0,lb_t0]).reshape(1,2),axis=0) - bnds=np.append(bnds,np.array([lb_tf,lb_tf]).reshape(1,2),axis=0) - - bnds = tuple(map(tuple, bnds))#convert bnds np.array to tuple for input in NLP solver - - return bnds - - -def traj_2_dec_var(traj): - - dec_vars = np.array([]).reshape(0,1) # initialize dec_vars array - - for i in range(sys.n): # append states x - arr_to_add = traj.x[:,i].reshape(ngrid,1) - dec_vars = np.append(dec_vars,arr_to_add,axis=0) - - for i in range(sys.m): # append inputs u - arr_to_add = traj.u[:,i].reshape(ngrid,1) - dec_vars = np.append(dec_vars,arr_to_add,axis=0) - - # append t0 and tF - dec_vars=np.append(dec_vars,np.array(traj.t[0]).reshape(1,1),axis=0) - dec_vars=np.append(dec_vars,traj.t[-1].reshape(1,1),axis=0) - dec_vars=dec_vars.reshape(ngrid*(sys.n+sys.m)+2,) - return dec_vars - - - - -def unpack_dec_var(decision_variables): - global ngrid - x = np.zeros([ngrid,sys.n]) - u = np.zeros([ngrid,sys.m]) - # unpack decision variables into trajectory - for i in range(0,sys.n): - x[:,i] = decision_variables[i*ngrid:(i+1)*ngrid].reshape(ngrid) - - for i in range(sys.n,sys.n+sys.m): - u[:,i-sys.n] = decision_variables[i*ngrid:(i+1)*ngrid].reshape(ngrid) - - t0 = decision_variables[-2] - tf = decision_variables[-1] - return x,u,t0,tf - -def compute_dx(x,u,sys): - dx = np.zeros(x.shape) - global ngrid - for i in range(ngrid): - dx[i,:]=sys.f(x[i,:],u[i,:]) - return dx - - -def dec_var_2_traj(decision_variables): - global ngrid - - x,u,t0,tf = unpack_dec_var(decision_variables) - - dx = compute_dx(x,u,sys) - - t = np.linspace(t0, tf, ngrid).reshape(ngrid,) - - y=x # find proper fct evaluation later from sys object - - traj = Trajectory(x, u, t, dx, y) - - return traj - -def dynamics_cstr(traj): - - t0 = traj.t[0] - - tf = traj.t[-1] - - h = (tf-t0)/(ngrid-1) #step between each grid - - dyn_constr = np.diff(traj.x,axis=0) - (traj.dx[0:-1]+traj.dx[1:])*h/2 # this must be = 0 - dyn_constr = dyn_constr.reshape((ngrid-1)*sys.n,) - return dyn_constr - -def compute_cost(decision_variables): - traj_opt = dec_var_2_traj(decision_variables) #get trajectorty - traj_opt = sys.cost_function.trajectory_evaluation(traj_opt)#compute cost fcn - cost = traj_opt.J[-1]# traj_opt.J is the cumulated cost from integral, we take only the last value - return cost - -def interp_traj(traj,ngrid): - - f_x = interp1d(traj.t, traj.x, kind='cubic',axis=0) - f_u = interp1d(traj.t, traj.u, kind='quadratic',axis=0) - - t_interp = np.linspace(traj.t[0],traj.t[-1],ngrid) - x_interp = f_x(t_interp) - u_interp = f_u(t_interp) - dx_interp=compute_dx(x_interp,u_interp,sys) - y_interp = x_interp - traj_interp = Trajectory(x_interp, u_interp, t_interp, dx_interp, y_interp) - return traj_interp - - - - - - -# Guess traj based from RRT solution -loaded_traj = simulation.Trajectory.load('pendulum_rrt.npy') -ngrid = loaded_traj.time_steps - - -# - -bnds = pack_bounds(lb_x,ub_x0,lb_x,ub_x0,lb_xf,ub_xf,lb_u,ub_u,lb_t0,lb_t0,lb_tf,lb_tf) - -cons_slsqp = ({'type': 'eq', 'fun': lambda x: dynamics_cstr(dec_var_2_traj( x )) }) - -cons_trust=NonlinearConstraint(lambda x: dynamics_cstr(dec_var_2_traj( x )), 0, 0)# - - -guess_traj = interp_traj( loaded_traj , ngrid ) -dec_var_guess = traj_2_dec_var( guess_traj ) - - - -''' -Solve non-linear program -''' -#method='trust-constr' -res4 = minimize(compute_cost, dec_var_guess,method='SLSQP' , bounds=bnds, constraints=cons_slsqp,tol=1e-6,options={'disp': True,'maxiter':1000}) - - - -#dec_var_guess = np.load('C:\Users\Charles Khazoom\Documents\git\pyro\trajresults\solution_with_trust_const.npy') - -#res4 = minimize(compute_cost, dec_var_guess,method='trust-constr' , bounds=bnds, constraints=cons_trust,tol=1e-6,options={'disp': True,'maxiter':1000},jac='2-point',hess=BFGS()) -# - - - -''' -Analyze results -''' - - -result_traj = dec_var_2_traj(res4.x) - -result_traj.save('pendulum_rrt_optimised.npy') - - -#show solution vs initial guess -sys.traj = loaded_traj -sys.plot_trajectory('xu') -sys.traj = result_traj -sys.plot_trajectory('xu') -#sys.animate_simulation() - -# CLosed-loop controller -ctl = nonlinear.ComputedTorqueController( sys , result_traj ) -ctl.rbar = np.array([-3.14]) - -## New cl-dynamic -cl_sys = ctl + sys - -cl_sys.compute_trajectory( 10 ) -cl_sys.plot_trajectory('xu') -cl_sys.animate_simulation() - - -print(sys.cost_function.trajectory_evaluation( guess_traj ).J[-1]) -print(sys.cost_function.trajectory_evaluation( result_traj ).J[-1]) -print(sys.cost_function.trajectory_evaluation( cl_sys.traj ).J[-1]) \ No newline at end of file diff --git a/dev/old_to_remove/trajectory_optimisation/direct_collocation/block_minimal_force.py b/dev/old_to_remove/trajectory_optimisation/direct_collocation/block_minimal_force.py deleted file mode 100644 index 2e7acab2..00000000 --- a/dev/old_to_remove/trajectory_optimisation/direct_collocation/block_minimal_force.py +++ /dev/null @@ -1,128 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Created on Fri Sep 10 11:12:11 2021 - -@author: alexandregirard -""" - -import numpy as np -from scipy.optimize import minimize - -n = 2 -m = 1 -grid = 100 -dt = 0.01 - -mass = 1 -target = 1 - -#dec = np.linspace(0,grid*3,grid*3) - -count = 0 - - -def dec2xu(dec): - - x = np.zeros((n,grid)) - u = np.zeros((m,grid)) - - x[0,:] = dec[0:grid] - x[1,:] = dec[grid:2*grid] - u[0,:] = dec[2*grid:3*grid] - - return x,u - -def cost(dec): - - J = 0 - - x = np.zeros((n,grid)) - u = np.zeros((m,grid)) - - x[0,:] = dec[0:grid] - x[1,:] = dec[grid:2*grid] - u[0,:] = dec[2*grid:3*grid] - - for i in range(grid-1): - - #J = J + 0.5 * dt * ( (x[0,i]-target)**2 + (x[0,i+1]-target)**2 + x[1,i]**2 + x[1,i+1]**2 + u[0,i]**2 + u[0,i+1]**2 ) - - J = J + 0.5 * dt * ( u[0,i]**2 + u[0,i+1]**2 ) - - return J - -def constraints(dec): - - x,u = dec2xu(dec) - - vec=np.zeros(grid*2-2) - - for i in range(grid-1): - - vec[i] = (x[0,i+1] - x[0,i]) - 0.5 * dt * ( x[1,i+1] + x[1,i] ) - vec[i+grid-1] = (x[1,i+1] - x[1,i]) - 0.5 * dt * ( u[0,i+1]/mass + u[0,i]/mass ) - - return vec - - -def compute_bounds(): - bounds = [] - - bounds.append( (0,0.01) ) - - for i in range(1,grid-1): - - bounds.append( (0,10) ) - - bounds.append( (0.99,1) ) - - bounds.append( (0,0.01) ) - - for i in range(1,grid-1): - - bounds.append( (-100,100) ) - - bounds.append( (0,0.01) ) - - for i in range(grid): - - bounds.append( (-100,100) ) - - return bounds - -def display_callback(a): - - - print('One iteration completed') - - return True - - -# Guess -dec = np.zeros(grid*(n+m)) -dec[0:grid] = np.linspace(0,1,grid) -dec[grid:2*grid] = 1 -dec[2*grid:3*grid] = 1 - -bnds = compute_bounds() - -cons = {'type': 'eq', 'fun': constraints } -res = minimize( cost, dec, method='SLSQP', bounds=bnds, constraints=cons, callback=display_callback ) # - -print(res) - -from pyro.dynamic import integrator - -sys = integrator.DoubleIntegrator() - -sys.compute_trajectory(1,100) - - -dec = res.x - -sys.traj.x[:,0] = dec[0:grid] -sys.traj.x[:,1] = dec[grid:2*grid] -sys.traj.u[:,0] = dec[2*grid:3*grid] - -sys.plot_trajectory('xu') diff --git a/dev/old_to_remove/trajectory_optimisation/direct_collocation/double_pendulum_2sec_swing_up.py b/dev/old_to_remove/trajectory_optimisation/direct_collocation/double_pendulum_2sec_swing_up.py deleted file mode 100644 index e905c084..00000000 --- a/dev/old_to_remove/trajectory_optimisation/direct_collocation/double_pendulum_2sec_swing_up.py +++ /dev/null @@ -1,174 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Created on Fri Sep 10 11:12:11 2021 - -@author: alexandregirard -""" - -import numpy as np -from scipy.optimize import minimize - -from pyro.dynamic import pendulum - - - -sys = pendulum.DoublePendulum() - -n = 4 -m = 2 -grid = 20 -dt = 0.1 - - -#dec = np.linspace(0,grid*3,grid*3) - - -def dec2xu(dec): - - x = np.zeros((n,grid)) - u = np.zeros((m,grid)) - - x[0,:] = dec[0:grid] - x[1,:] = dec[grid:2*grid] - x[2,:] = dec[2*grid:3*grid] - x[3,:] = dec[3*grid:4*grid] - u[0,:] = dec[4*grid:5*grid] - u[1,:] = dec[5*grid:6*grid] - - return x,u - -def cost(dec): - - J = 0 - - x = np.zeros((n,grid)) - u = np.zeros((m,grid)) - - x[0,:] = dec[0:grid] - x[1,:] = dec[grid:2*grid] - x[2,:] = dec[2*grid:3*grid] - x[3,:] = dec[3*grid:4*grid] - u[0,:] = dec[4*grid:5*grid] - u[1,:] = dec[5*grid:6*grid] - - for i in range(grid-1): - - #J = J + 0.5 * dt * ( (x[0,i]-target)**2 + (x[0,i+1]-target)**2 + x[1,i]**2 + x[1,i+1]**2 + u[0,i]**2 + u[0,i+1]**2 ) - - J = J + 0.5 * dt * ( x[0,i]**2 + x[0,i+1]**2 + x[1,i]**2 + x[1,i+1]**2 ) - J = J + 0.5 * dt * ( x[2,i]**2 + x[2,i+1]**2 + x[3,i]**2 + x[3,i+1]**2 ) - J = J + 0.5 * dt * ( u[0,i]**2 + u[0,i+1]**2 + u[1,i]**2 + u[1,i+1]**2 ) * 5 - - return J - -def constraints(dec): - - x,u = dec2xu(dec) - - vec=np.zeros(grid*4-4) - - for i in range(grid-1): - - vec[i+grid*0-0] = (x[0,i+1] - x[0,i]) - 0.5 * dt * ( sys.f(x[:,i],u[:,i])[0] + sys.f(x[:,i+1],u[:,i+1])[0] ) - vec[i+grid*1-1] = (x[1,i+1] - x[1,i]) - 0.5 * dt * ( sys.f(x[:,i],u[:,i])[1] + sys.f(x[:,i+1],u[:,i+1])[1] ) - vec[i+grid*2-2] = (x[2,i+1] - x[2,i]) - 0.5 * dt * ( sys.f(x[:,i],u[:,i])[2] + sys.f(x[:,i+1],u[:,i+1])[2] ) - vec[i+grid*3-3] = (x[3,i+1] - x[3,i]) - 0.5 * dt * ( sys.f(x[:,i],u[:,i])[3] + sys.f(x[:,i+1],u[:,i+1])[3] ) - - return vec - - -def compute_bounds(): - - bounds = [] - - #x0 - bounds.append( (-3.14,-3.13) ) - - for i in range(1,grid-1): - - bounds.append( (-4,1) ) - - bounds.append( (0.0,0.01) ) - - #x1 - bounds.append( (0,0.01) ) - - for i in range(1,grid-1): - - bounds.append( (-3,1) ) - - bounds.append( (0,0.01) ) - - #x2 - - bounds.append( (0,0.01) ) - - for i in range(1,grid-1): - - bounds.append( (-2,6) ) - - bounds.append( (0,0.01) ) - - #x3 - - bounds.append( (0,0.01) ) - - for i in range(1,grid-1): - - bounds.append( (-10,10) ) - - bounds.append( (0,0.01) ) - - #u0 - - for i in range(grid): - - bounds.append( (-20,30) ) - - #u1 - - for i in range(grid): - - bounds.append( (-20,30) ) - - return bounds - - -def display_callback(a): - - - print('One iteration completed') - - return True - - - - -# Guess -dec = np.zeros(grid*(n+m)) -#dec[0:grid] = traj.x[:,0] -#dec[grid:2*grid] = traj.x[:,1] -#dec[2*grid:3*grid] = traj.u[:,0] - -bnds = compute_bounds() - -cons = {'type': 'eq', 'fun': constraints } -res = minimize( cost, dec, method='SLSQP', bounds=bnds, constraints=cons, callback=display_callback, options={'disp':True,'maxiter':1000}) # - - -sys.compute_trajectory(grid*dt,grid) - - -dec = res.x - -sys.traj.x[:,0] = dec[0:grid] -sys.traj.x[:,1] = dec[grid:2*grid] -sys.traj.x[:,2] = dec[2*grid:3*grid] -sys.traj.x[:,3] = dec[3*grid:4*grid] -sys.traj.u[:,0] = dec[4*grid:5*grid] -sys.traj.u[:,1] = dec[5*grid:6*grid] - - -sys.plot_trajectory('xu') -sys.animate_simulation() \ No newline at end of file diff --git a/dev/old_to_remove/trajectory_optimisation/direct_collocation/double_pendulum_4sec_swing_up.py b/dev/old_to_remove/trajectory_optimisation/direct_collocation/double_pendulum_4sec_swing_up.py deleted file mode 100644 index 369c56ae..00000000 --- a/dev/old_to_remove/trajectory_optimisation/direct_collocation/double_pendulum_4sec_swing_up.py +++ /dev/null @@ -1,174 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Created on Fri Sep 10 11:12:11 2021 - -@author: alexandregirard -""" - -import numpy as np -from scipy.optimize import minimize - -from pyro.dynamic import pendulum - - - -sys = pendulum.DoublePendulum() - -n = 4 -m = 2 -grid = 20 -dt = 0.2 - - -#dec = np.linspace(0,grid*3,grid*3) - - -def dec2xu(dec): - - x = np.zeros((n,grid)) - u = np.zeros((m,grid)) - - x[0,:] = dec[0:grid] - x[1,:] = dec[grid:2*grid] - x[2,:] = dec[2*grid:3*grid] - x[3,:] = dec[3*grid:4*grid] - u[0,:] = dec[4*grid:5*grid] - u[1,:] = dec[5*grid:6*grid] - - return x,u - -def cost(dec): - - J = 0 - - x = np.zeros((n,grid)) - u = np.zeros((m,grid)) - - x[0,:] = dec[0:grid] - x[1,:] = dec[grid:2*grid] - x[2,:] = dec[2*grid:3*grid] - x[3,:] = dec[3*grid:4*grid] - u[0,:] = dec[4*grid:5*grid] - u[1,:] = dec[5*grid:6*grid] - - for i in range(grid-1): - - #J = J + 0.5 * dt * ( (x[0,i]-target)**2 + (x[0,i+1]-target)**2 + x[1,i]**2 + x[1,i+1]**2 + u[0,i]**2 + u[0,i+1]**2 ) - - J = J + 0.5 * dt * ( x[0,i]**2 + x[0,i+1]**2 + x[1,i]**2 + x[1,i+1]**2 ) - J = J + 0.5 * dt * ( x[2,i]**2 + x[2,i+1]**2 + x[3,i]**2 + x[3,i+1]**2 ) - J = J + 0.5 * dt * ( u[0,i]**2 + u[0,i+1]**2 + u[1,i]**2 + u[1,i+1]**2 ) * 5 - - return J - -def constraints(dec): - - x,u = dec2xu(dec) - - vec=np.zeros(grid*4-4) - - for i in range(grid-1): - - vec[i+grid*0-0] = (x[0,i+1] - x[0,i]) - 0.5 * dt * ( sys.f(x[:,i],u[:,i])[0] + sys.f(x[:,i+1],u[:,i+1])[0] ) - vec[i+grid*1-1] = (x[1,i+1] - x[1,i]) - 0.5 * dt * ( sys.f(x[:,i],u[:,i])[1] + sys.f(x[:,i+1],u[:,i+1])[1] ) - vec[i+grid*2-2] = (x[2,i+1] - x[2,i]) - 0.5 * dt * ( sys.f(x[:,i],u[:,i])[2] + sys.f(x[:,i+1],u[:,i+1])[2] ) - vec[i+grid*3-3] = (x[3,i+1] - x[3,i]) - 0.5 * dt * ( sys.f(x[:,i],u[:,i])[3] + sys.f(x[:,i+1],u[:,i+1])[3] ) - - return vec - - -def compute_bounds(): - - bounds = [] - - #x0 - bounds.append( (-3.14,-3.13) ) - - for i in range(1,grid-1): - - bounds.append( (-5,2) ) - - bounds.append( (0.0,0.01) ) - - #x1 - bounds.append( (0,0.01) ) - - for i in range(1,grid-1): - - bounds.append( (-3,3) ) - - bounds.append( (0,0.01) ) - - #x2 - - bounds.append( (0,0.01) ) - - for i in range(1,grid-1): - - bounds.append( (-3,6) ) - - bounds.append( (0,0.01) ) - - #x3 - - bounds.append( (0,0.01) ) - - for i in range(1,grid-1): - - bounds.append( (-10,10) ) - - bounds.append( (0,0.01) ) - - #u0 - - for i in range(grid): - - bounds.append( (-20,30) ) - - #u1 - - for i in range(grid): - - bounds.append( (-20,30) ) - - return bounds - - -def display_callback(a): - - - print('One iteration completed') - - return True - - - - -# Guess -dec = np.zeros(grid*(n+m)) -#dec[0:grid] = traj.x[:,0] -#dec[grid:2*grid] = traj.x[:,1] -#dec[2*grid:3*grid] = traj.u[:,0] - -bnds = compute_bounds() - -cons = {'type': 'eq', 'fun': constraints } -res = minimize( cost, dec, method='SLSQP', bounds=bnds, constraints=cons, callback=display_callback, options={'disp':True,'maxiter':1000}) # - - -sys.compute_trajectory(grid*dt,grid) - - -dec = res.x - -sys.traj.x[:,0] = dec[0:grid] -sys.traj.x[:,1] = dec[grid:2*grid] -sys.traj.x[:,2] = dec[2*grid:3*grid] -sys.traj.x[:,3] = dec[3*grid:4*grid] -sys.traj.u[:,0] = dec[4*grid:5*grid] -sys.traj.u[:,1] = dec[5*grid:6*grid] - - -sys.plot_trajectory('xu') -sys.animate_simulation() \ No newline at end of file diff --git a/dev/old_to_remove/trajectory_optimisation/direct_collocation/double_pendulum_8sec_swing_up.py b/dev/old_to_remove/trajectory_optimisation/direct_collocation/double_pendulum_8sec_swing_up.py deleted file mode 100644 index e5604ae0..00000000 --- a/dev/old_to_remove/trajectory_optimisation/direct_collocation/double_pendulum_8sec_swing_up.py +++ /dev/null @@ -1,174 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Created on Fri Sep 10 11:12:11 2021 - -@author: alexandregirard -""" - -import numpy as np -from scipy.optimize import minimize - -from pyro.dynamic import pendulum - - - -sys = pendulum.DoublePendulum() - -n = 4 -m = 2 -grid = 40 -dt = 0.2 - - -#dec = np.linspace(0,grid*3,grid*3) - - -def dec2xu(dec): - - x = np.zeros((n,grid)) - u = np.zeros((m,grid)) - - x[0,:] = dec[0:grid] - x[1,:] = dec[grid:2*grid] - x[2,:] = dec[2*grid:3*grid] - x[3,:] = dec[3*grid:4*grid] - u[0,:] = dec[4*grid:5*grid] - u[1,:] = dec[5*grid:6*grid] - - return x,u - -def cost(dec): - - J = 0 - - x = np.zeros((n,grid)) - u = np.zeros((m,grid)) - - x[0,:] = dec[0:grid] - x[1,:] = dec[grid:2*grid] - x[2,:] = dec[2*grid:3*grid] - x[3,:] = dec[3*grid:4*grid] - u[0,:] = dec[4*grid:5*grid] - u[1,:] = dec[5*grid:6*grid] - - for i in range(grid-1): - - #J = J + 0.5 * dt * ( (x[0,i]-target)**2 + (x[0,i+1]-target)**2 + x[1,i]**2 + x[1,i+1]**2 + u[0,i]**2 + u[0,i+1]**2 ) - - J = J + 0.5 * dt * ( x[0,i]**2 + x[0,i+1]**2 + x[1,i]**2 + x[1,i+1]**2 ) - J = J + 0.5 * dt * ( x[2,i]**2 + x[2,i+1]**2 + x[3,i]**2 + x[3,i+1]**2 ) - J = J + 0.5 * dt * ( u[0,i]**2 + u[0,i+1]**2 + u[1,i]**2 + u[1,i+1]**2 ) * 5 - - return J - -def constraints(dec): - - x,u = dec2xu(dec) - - vec=np.zeros(grid*4-4) - - for i in range(grid-1): - - vec[i+grid*0-0] = (x[0,i+1] - x[0,i]) - 0.5 * dt * ( sys.f(x[:,i],u[:,i])[0] + sys.f(x[:,i+1],u[:,i+1])[0] ) - vec[i+grid*1-1] = (x[1,i+1] - x[1,i]) - 0.5 * dt * ( sys.f(x[:,i],u[:,i])[1] + sys.f(x[:,i+1],u[:,i+1])[1] ) - vec[i+grid*2-2] = (x[2,i+1] - x[2,i]) - 0.5 * dt * ( sys.f(x[:,i],u[:,i])[2] + sys.f(x[:,i+1],u[:,i+1])[2] ) - vec[i+grid*3-3] = (x[3,i+1] - x[3,i]) - 0.5 * dt * ( sys.f(x[:,i],u[:,i])[3] + sys.f(x[:,i+1],u[:,i+1])[3] ) - - return vec - - -def compute_bounds(): - - bounds = [] - - #x0 - bounds.append( (-3.14,-3.13) ) - - for i in range(1,grid-1): - - bounds.append( (-5,0.5) ) - - bounds.append( (0.0,0.01) ) - - #x1 - bounds.append( (0,0.01) ) - - for i in range(1,grid-1): - - bounds.append( (-4,4) ) - - bounds.append( (0,0.01) ) - - #x2 - - bounds.append( (0,0.01) ) - - for i in range(1,grid-1): - - bounds.append( (-10,10) ) - - bounds.append( (0,0.01) ) - - #x3 - - bounds.append( (0,0.01) ) - - for i in range(1,grid-1): - - bounds.append( (-10,10) ) - - bounds.append( (0,0.01) ) - - #u0 - - for i in range(grid): - - bounds.append( (-100,100) ) - - #u1 - - for i in range(grid): - - bounds.append( (-100,100) ) - - return bounds - - -def display_callback(a): - - - print('One iteration completed') - - return True - - - - -# Guess -dec = np.zeros(grid*(n+m)) -#dec[0:grid] = traj.x[:,0] -#dec[grid:2*grid] = traj.x[:,1] -#dec[2*grid:3*grid] = traj.u[:,0] - -bnds = compute_bounds() - -cons = {'type': 'eq', 'fun': constraints } -res = minimize( cost, dec, method='SLSQP', bounds=bnds, constraints=cons, callback=display_callback, options={'disp':True,'maxiter':1000}) # - - -sys.compute_trajectory(grid*dt,grid) - - -dec = res.x - -sys.traj.x[:,0] = dec[0:grid] -sys.traj.x[:,1] = dec[grid:2*grid] -sys.traj.x[:,2] = dec[2*grid:3*grid] -sys.traj.x[:,3] = dec[3*grid:4*grid] -sys.traj.u[:,0] = dec[4*grid:5*grid] -sys.traj.u[:,1] = dec[5*grid:6*grid] - -#sys.traj.save('doublependulumswingup8sec') -sys.plot_trajectory('xu') -sys.animate_simulation() \ No newline at end of file diff --git a/dev/old_to_remove/trajectory_optimisation/direct_collocation/double_stage.py b/dev/old_to_remove/trajectory_optimisation/direct_collocation/double_stage.py deleted file mode 100644 index 79459f36..00000000 --- a/dev/old_to_remove/trajectory_optimisation/direct_collocation/double_stage.py +++ /dev/null @@ -1,69 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Fri Nov 16 12:05:08 2018 - -@author: Alexandre -""" -############################################################################### -import numpy as np -############################################################################### -from pyro.dynamic import pendulum -from pyro.control import nonlinear -from pyro.planning import trajectoryoptimisation -from pyro.analysis import simulation -############################################################################### - - -sys = pendulum.DoublePendulum() - -#Max/Min torque -sys.u_ub[0] = +20 -sys.u_ub[1] = +20 -sys.u_lb[0] = -20 -sys.u_lb[1] = -20 - - -tf = 4.0 - -# Coarce traj optimization -n = 20 -dt = tf/n - -planner = trajectoryoptimisation.DirectCollocationTrajectoryOptimisation( sys , dt , n ) - -planner.x_start = np.array([-3.14,0,0,0]) -planner.x_goal = np.array([0,0,0,0]) - -planner.maxiter = 500 -planner.compute_optimal_trajectory() -planner.show_solution() - -# Fine traj optimization -n = 100 -dt = tf/n -planner2 = trajectoryoptimisation.DirectCollocationTrajectoryOptimisation( sys , dt , n ) - -planner2.x_start = np.array([-3.14,0,0,0]) -planner2.x_goal = np.array([0,0,0,0]) - -planner2.set_initial_trajectory_guest( planner.traj ) -planner2.maxiter = 500 -planner2.compute_optimal_trajectory() -planner2.show_solution() - -planner2.save_solution( 'double_pendulum_directcollocation_hires.npy' ) - -# Controller -ctl = nonlinear.ComputedTorqueController( sys , planner2.traj ) - -ctl.rbar = np.array([0,0]) -ctl.w0 = 5 -ctl.zeta = 1 - -# New cl-dynamic -cl_sys = ctl + sys - -# Simultation -cl_sys.x0 = np.array([-3.14,0,0,0]) -cl_sys.plot_trajectory('xu') -cl_sys.animate_simulation() \ No newline at end of file diff --git a/dev/old_to_remove/trajectory_optimisation/direct_collocation/pendulum_from_RRT.py b/dev/old_to_remove/trajectory_optimisation/direct_collocation/pendulum_from_RRT.py deleted file mode 100644 index 07791bf4..00000000 --- a/dev/old_to_remove/trajectory_optimisation/direct_collocation/pendulum_from_RRT.py +++ /dev/null @@ -1,125 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Created on Fri Sep 10 11:12:11 2021 - -@author: alexandregirard -""" - -import numpy as np -from scipy.optimize import minimize -from pyro.dynamic import pendulum - -from pyro.analysis import simulation - - - - -sys = pendulum.SinglePendulum() - -n = 2 -m = 1 -grid = 45 -dt = 0.1 - -mass = 1 -target = 1 - -#dec = np.linspace(0,grid*3,grid*3) - - -def dec2xu(dec): - - x = np.zeros((n,grid)) - u = np.zeros((m,grid)) - - x[0,:] = dec[0:grid] - x[1,:] = dec[grid:2*grid] - u[0,:] = dec[2*grid:3*grid] - - return x,u - -def cost(dec): - - J = 0 - - x = np.zeros((n,grid)) - u = np.zeros((m,grid)) - - x[0,:] = dec[0:grid] - x[1,:] = dec[grid:2*grid] - u[0,:] = dec[2*grid:3*grid] - - for i in range(grid-1): - - #J = J + 0.5 * dt * ( (x[0,i]-target)**2 + (x[0,i+1]-target)**2 + x[1,i]**2 + x[1,i+1]**2 + u[0,i]**2 + u[0,i+1]**2 ) - - J = J + 0.5 * dt * ( u[0,i]**2 + u[0,i+1]**2 ) - - return J - -def constraints(dec): - - x,u = dec2xu(dec) - - vec=np.zeros(grid*2-2) - - for i in range(grid-1): - - vec[i] = (x[0,i+1] - x[0,i]) - 0.5 * dt * ( sys.f(x[:,i],u[:,i])[0] + sys.f(x[:,i+1],u[:,i+1])[0] ) - vec[i+grid-1] = (x[1,i+1] - x[1,i]) - 0.5 * dt * ( sys.f(x[:,i],u[:,i])[1] + sys.f(x[:,i+1],u[:,i+1])[1] ) - return vec - - -def compute_bounds(): - bounds = [] - - bounds.append( (0,0.01) ) - - for i in range(1,grid-1): - - bounds.append( (-4,1) ) - - bounds.append( (-3.14,-3.13) ) - - bounds.append( (0,0.01) ) - - for i in range(1,grid-1): - - bounds.append( (-6,4) ) - - bounds.append( (0,0.01) ) - - for i in range(grid): - - bounds.append( (-10,10) ) - - return bounds - - - -# Guess -traj = simulation.Trajectory.load('pendulum_rrt.npy') -sys.traj = traj -sys.plot_trajectory('xu') - -dec = np.zeros(grid*(n+m)) -dec[0:grid] = traj.x[:,0] -dec[grid:2*grid] = traj.x[:,1] -dec[2*grid:3*grid] = traj.u[:,0] - -bnds = compute_bounds() - -cons = {'type': 'eq', 'fun': constraints } -res = minimize( cost, dec, method='SLSQP', bounds=bnds, constraints=cons, options={'disp':True,'maxiter':1000}) # - - - -dec = res.x - -sys.traj.x[:,0] = dec[0:grid] -sys.traj.x[:,1] = dec[grid:2*grid] -sys.traj.u[:,0] = dec[2*grid:3*grid] - - -sys.plot_trajectory('xu') diff --git a/dev/old_to_remove/trajectory_optimisation/direct_collocation/pendulum_from_sratch.py b/dev/old_to_remove/trajectory_optimisation/direct_collocation/pendulum_from_sratch.py deleted file mode 100644 index 12e1835e..00000000 --- a/dev/old_to_remove/trajectory_optimisation/direct_collocation/pendulum_from_sratch.py +++ /dev/null @@ -1,129 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Created on Fri Sep 10 11:12:11 2021 - -@author: alexandregirard -""" - -import numpy as np -from scipy.optimize import minimize - -from pyro.dynamic import pendulum - - - -sys = pendulum.SinglePendulum() - -n = 2 -m = 1 -grid = 30 -dt = 0.2 - - -#dec = np.linspace(0,grid*3,grid*3) - - -def dec2xu(dec): - - x = np.zeros((n,grid)) - u = np.zeros((m,grid)) - - x[0,:] = dec[0:grid] - x[1,:] = dec[grid:2*grid] - u[0,:] = dec[2*grid:3*grid] - - return x,u - -def cost(dec): - - J = 0 - - x = np.zeros((n,grid)) - u = np.zeros((m,grid)) - - x[0,:] = dec[0:grid] - x[1,:] = dec[grid:2*grid] - u[0,:] = dec[2*grid:3*grid] - - for i in range(grid-1): - - target = -3.14 - J = J + 0.5 * dt * ( (x[0,i]-target)**2 + (x[0,i+1]-target)**2 + x[1,i]**2 + x[1,i+1]**2 + u[0,i]**2 + u[0,i+1]**2 ) - - #J = J + 0.5 * dt * ( u[0,i]**2 + u[0,i+1]**2 ) - - return J - -def constraints(dec): - - x,u = dec2xu(dec) - - vec=np.zeros(grid*2-2) - - for i in range(grid-1): - - vec[i] = (x[0,i+1] - x[0,i]) - 0.5 * dt * ( sys.f(x[:,i],u[:,i])[0] + sys.f(x[:,i+1],u[:,i+1])[0] ) - vec[i+grid-1] = (x[1,i+1] - x[1,i]) - 0.5 * dt * ( sys.f(x[:,i],u[:,i])[1] + sys.f(x[:,i+1],u[:,i+1])[1] ) - return vec - - -def compute_bounds(): - bounds = [] - - bounds.append( (0,0.01) ) - - for i in range(1,grid-1): - - bounds.append( (-4,1) ) - - bounds.append( (-3.14,-3.13) ) - - bounds.append( (0,0.01) ) - - for i in range(1,grid-1): - - bounds.append( (-6,4) ) - - bounds.append( (0,0.01) ) - - for i in range(grid): - - bounds.append( (-10,10) ) - - return bounds - - -def display_callback(a): - - - print('Iteration completed') - - return True - - - -# Guess -dec = np.zeros(grid*(n+m)) -#dec[0:grid] = traj.x[:,0] -#dec[grid:2*grid] = traj.x[:,1] -#dec[2*grid:3*grid] = traj.u[:,0] - -bnds = compute_bounds() - -cons = {'type': 'eq', 'fun': constraints } -res = minimize( cost, dec, method='SLSQP', bounds=bnds, constraints=cons, callback=display_callback, options={'disp':True,'maxiter':1000}) # - - -sys.compute_trajectory(grid*dt,grid) - - -dec = res.x - -sys.traj.x[:,0] = dec[0:grid] -sys.traj.x[:,1] = dec[grid:2*grid] -sys.traj.u[:,0] = dec[2*grid:3*grid] - - -sys.plot_trajectory('xu') -sys.animate_simulation() \ No newline at end of file diff --git a/dev/old_to_remove/trajectory_optimisation/direct_collocation/view_doublependulum_optimized_trajectory.py b/dev/old_to_remove/trajectory_optimisation/direct_collocation/view_doublependulum_optimized_trajectory.py deleted file mode 100644 index 4ddd518f..00000000 --- a/dev/old_to_remove/trajectory_optimisation/direct_collocation/view_doublependulum_optimized_trajectory.py +++ /dev/null @@ -1,17 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Created on Sat Oct 2 16:11:12 2021 - -@author: alex -""" -from pyro.dynamic import pendulum -from pyro.analysis import Trajectory - -sys = pendulum.DoublePendulum() - -#sys.traj = Trajectory.load('doublependulumswingup2sec.npy') -sys.traj = Trajectory.load('doublependulumswingup4sec.npy') - -sys.plot_trajectory('xu') -sys.animate_simulation() \ No newline at end of file diff --git a/dev/old_to_remove/trajectory_optimisation/ipopt_tests/test_ipopt_install.py b/dev/old_to_remove/trajectory_optimisation/ipopt_tests/test_ipopt_install.py deleted file mode 100644 index c153d36e..00000000 --- a/dev/old_to_remove/trajectory_optimisation/ipopt_tests/test_ipopt_install.py +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Created on Fri Sep 10 11:12:11 2021 - -@author: alexandregirard -""" -from scipy.optimize import minimize -from cyipopt import minimize_ipopt - -fun = lambda x: (x[0] - 1)**2 + (x[1] - 2.5)**2 - -cons = ({'type': 'ineq', 'fun': lambda x: x[0] - 2 * x[1] + 2}, {'type': 'ineq', 'fun': lambda x: -x[0] - 2 * x[1] + 6}, {'type': 'ineq', 'fun': lambda x: -x[0] + 2 * x[1] + 2}) - -bnds = ((0, None), (0, None)) - -res = minimize(fun, (2, 0), method='SLSQP', bounds=bnds, constraints=cons) - -print(res) - -res2 = minimize_ipopt(fun, (2, 0), bounds=bnds, constraints=cons) - -print(res2) \ No newline at end of file diff --git a/dev/old_to_remove/trajectory_optimisation/ipopt_tests/test_ipopt_simple.py b/dev/old_to_remove/trajectory_optimisation/ipopt_tests/test_ipopt_simple.py deleted file mode 100644 index bcd501a1..00000000 --- a/dev/old_to_remove/trajectory_optimisation/ipopt_tests/test_ipopt_simple.py +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Created on Fri Sep 10 11:12:11 2021 - -@author: alexandregirard -""" -from scipy.optimize import minimize -from cyipopt import minimize_ipopt - -def cost (x): - - j = x[0]+x[1] - - return -j - -def constraint_circle(x): - - res = x[0]**2+x[1]**2 - 1 - - return -res - - -cons = ({'type': 'ineq', 'fun': constraint_circle}) -#cons = ({'type': 'eq', 'fun': constraint_circle}) - -bnds = ((0, None), (0, None)) - -res = minimize( cost, (0, 0), method='SLSQP', bounds=bnds, constraints=cons) - -print(res) - -res2 = minimize_ipopt( cost, (0, 0), bounds=bnds, constraints=cons) - -print(res2) \ No newline at end of file diff --git a/dev/old_to_remove/value_iteration/3d-vi-test/bicycle_parking_with_valueiteration.py b/dev/old_to_remove/value_iteration/3d-vi-test/bicycle_parking_with_valueiteration.py deleted file mode 100644 index 32961354..00000000 --- a/dev/old_to_remove/value_iteration/3d-vi-test/bicycle_parking_with_valueiteration.py +++ /dev/null @@ -1,59 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Mon Nov 12 20:28:17 2018 - -@author: Alexandre -""" - -import numpy as np - -from pyro.dynamic import vehicle -from pyro.planning import discretizer -from pyro.analysis import costfunction -from pyro.planning import valueiteration -from pyro.control import controller - -sys = vehicle.KinematicBicyleModel() - -sys.x_ub = np.array( [+2,+2,+0.5] ) -sys.x_lb = np.array( [-0,-0,-0.5] ) - -sys.u_ub = np.array( [+1,+1.0] ) -sys.u_lb = np.array( [-1,-1.0] ) - -# Discrete world -grid_sys = discretizer.GridDynamicSystem( sys , (41,41,21) , (3,3) , 0.05 ) -# Cost Function -cf = costfunction.QuadraticCostFunction.from_sys(sys) - - -cf.xbar = np.array( [1,1,0] ) # target -cf.INF = 1E4 -cf.EPS = 0.2 -cf.R = np.array([[0.01,0],[0,0]]) - -# VI algo - -""" -EXPERIMENTAL TEST NOT WORKING -""" - -vi = valueiteration.ValueIteration_ND( grid_sys , cf ) - -vi.uselookuptable = True -vi.initialize() -vi.load_data('parking_vi') -vi.compute_steps(2) -vi.save_data('parking_vi') - -vi.assign_interpol_controller() - - -# -cl_sys = controller.ClosedLoopSystem( sys , vi.ctl ) - - -## Simulation and animation -cl_sys.x0 = np.array([0.2,0.2,0]) -tf = 5 -cl_sys.compute_trajectory( tf , 10001 , 'euler') diff --git a/dev/old_to_remove/value_iteration/bicycle_parking_with_valueiteration.py b/dev/old_to_remove/value_iteration/bicycle_parking_with_valueiteration.py deleted file mode 100644 index 58b09b99..00000000 --- a/dev/old_to_remove/value_iteration/bicycle_parking_with_valueiteration.py +++ /dev/null @@ -1,58 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Mon Nov 12 20:28:17 2018 - -@author: Alexandre -""" - -import numpy as np - -from pyro.dynamic import vehicle -from pyro.planning import discretizer -from pyro.analysis import costfunction -from pyro.planning import valueiteration -from pyro.control import controller - -# initialize system -sys = vehicle.KinematicBicyleModel() - -sys.x_ub = np.array( [+2,+2,+0.5] ) -sys.x_lb = np.array( [-0,-0,-0.5] ) - -sys.u_ub = np.array( [+1,+1.0] ) -sys.u_lb = np.array( [-1,-1.0] ) - -# Discrete world -grid_sys = discretizer.GridDynamicSystem( sys , (41,41,21) , (3,3) , 0.05 ) -# Cost Function -cf = costfunction.QuadraticCostFunction.from_sys( sys ) -cf.xbar = np.array( [1,1,0]) -cf.INF = 1E4 -cf.EPS = 0 -cf.R = np.array([[0.01,0],[0,0]]) - -# VI algo - -vi = valueiteration.ValueIteration_ND( grid_sys , cf ) - -vi.uselookuptable = True -vi.initialize() -vi.load_data('bicycle_parking_vi') -#vi.compute_steps(100, maxJ=args.maxJ, plot=has_dynamic_plot) -#vi.save_data('bicycle_parking_vi') - -vi.assign_interpol_controller() - -vi.plot_cost2go(100) -vi.plot_policy(0) -vi.plot_policy(1) - -cl_sys = controller.ClosedLoopSystem( sys , vi.ctl ) - -## Simulation and animation -tf = 5 - -cl_sys.x0 = np.array( [0,0,0]) -cl_sys.compute_trajectory( tf , 10001 , 'euler') -cl_sys.plot_trajectory('xu') -cl_sys.animate_simulation() diff --git a/dev/old_to_remove/value_iteration/car_with_valueiteration.py b/dev/old_to_remove/value_iteration/car_with_valueiteration.py deleted file mode 100644 index a542f491..00000000 --- a/dev/old_to_remove/value_iteration/car_with_valueiteration.py +++ /dev/null @@ -1,70 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Tue Nov 13 11:05:07 2018 - -@author: Alexandre -""" - -############################################################################### -import numpy as np -import argparse -############################################################################### -from pyro.dynamic import vehicle -from pyro.planning import discretizer -from pyro.analysis import costfunction -from pyro.planning import valueiteration -from pyro.control import controller -############################################################################### - -sys = vehicle.KinematicCarModelwithObstacles() - -############################################################################### - -# Planning - -# Set domain -sys.x_ub = np.array([+35, +3, +3]) -sys.x_lb = np.array([-5, -2, -3]) - -sys.u_ub = np.array([+3, +1]) -sys.u_lb = np.array([-3, -1]) - -# Discrete world -grid_sys = discretizer.GridDynamicSystem(sys, (51, 51, 21), (3, 3), 0.1) - -# Cost Function -cf = costfunction.QuadraticCostFunction.from_sys( sys ) -cf.xbar = np.array( [30, 0, 0] ) # target -cf.INF = 1E8 -cf.EPS = 0.00 -cf.R = np.array([[0.1,0],[0,0]]) - -# VI algo - -vi = valueiteration.ValueIteration_ND( grid_sys , cf ) - -vi.uselookuptable = True -vi.initialize() - -#if load_data: -vi.load_data('car_vi') -# vi.compute_steps(100, plot=True, maxJ=100) -#if save_data: -# vi.save_data('car_vi_racecar_proto_scale_8') - -vi.assign_interpol_controller() - -#vi.plot_cost2go(maxJ=100) -vi.plot_policy(0) -vi.plot_policy(1) - -cl_sys = controller.ClosedLoopSystem( sys , vi.ctl ) -# -## Simulation and animation -x0 = np.array([0, 0, 0]) -tf = 20 - -cl_sys.x0 = x0 -cl_sys.compute_trajectory(tf, 10001, 'euler') -cl_sys.plot_trajectory('xu') -cl_sys.animate_simulation() \ No newline at end of file diff --git a/dev/old_to_remove/value_iteration/car_with_valueiteration_computation.py b/dev/old_to_remove/value_iteration/car_with_valueiteration_computation.py deleted file mode 100644 index 28b920bd..00000000 --- a/dev/old_to_remove/value_iteration/car_with_valueiteration_computation.py +++ /dev/null @@ -1,70 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Tue Nov 13 11:05:07 2018 - -@author: Alexandre -""" - -############################################################################### -import numpy as np -import argparse -############################################################################### -from pyro.dynamic import vehicle -from pyro.planning import discretizer -from pyro.analysis import costfunction -from pyro.planning import valueiteration -from pyro.control import controller -############################################################################### - -sys = vehicle.KinematicCarModelwithObstacles() - -############################################################################### - -# Planning - -# Set domain -sys.x_ub = np.array([+35, +3, +3]) -sys.x_lb = np.array([-5, -2, -3]) - -sys.u_ub = np.array([+3, +1]) -sys.u_lb = np.array([-3, -1]) - -# Discrete world -grid_sys = discretizer.GridDynamicSystem(sys, (51, 51, 21), (3, 3), 0.1) - -# Cost Function -cf = costfunction.QuadraticCostFunction.from_sys( sys ) -cf.xbar = np.array( [30, 0, 0] ) # target -cf.INF = 1E6 -cf.EPS = 0.00 -cf.R = np.array([[0.1,0],[0,0]]) - -# VI algo - -vi = valueiteration.ValueIteration_ND( grid_sys , cf ) - -vi.uselookuptable = True -vi.initialize() - - -vi.compute_steps(30, plot=True, maxJ=100) - -#if save_data: -vi.save_data('car_vi_test_mac_mini') - -vi.assign_interpol_controller() - -#vi.plot_cost2go(maxJ=100) -vi.plot_policy(0) -vi.plot_policy(1) - -cl_sys = controller.ClosedLoopSystem( sys , vi.ctl ) -# -## Simulation and animation -x0 = np.array([0, 0, 0]) -tf = 20 - -cl_sys.x0 = x0 -cl_sys.compute_trajectory(tf, 10001, 'euler') -cl_sys.plot_trajectory('xu') -cl_sys.animate_simulation() \ No newline at end of file diff --git a/dev/old_to_remove/value_iteration/double_pendulum_with_valueiteration_nd.py b/dev/old_to_remove/value_iteration/double_pendulum_with_valueiteration_nd.py deleted file mode 100644 index e4d1881f..00000000 --- a/dev/old_to_remove/value_iteration/double_pendulum_with_valueiteration_nd.py +++ /dev/null @@ -1,63 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Fri Nov 16 12:05:08 2018 - -@author: Alexandre -""" - -#import matplotlib -#matplotlib.use('Qt4Agg') - -############################################################################### -import numpy as np -############################################################################### -from pyro.analysis import costfunction -from pyro.control import controller -from pyro.dynamic import pendulum -############################################################################### - - -import matplotlib.pyplot as plt - -from pyro.planning import discretizer, valueiteration - -# Continuous dynamic system -sys = pendulum.DoublePendulum() - -sys.x_ub = np.array([0.2,0.2,0.2,0.2]) -sys.x_lb = np.array([-0.2,-0.2,-0.2,-0.2]) - -sys.u_ub = np.array([5,2]) -sys.u_lb = np.array([-5,-2]) - -# Discrete world -grid_sys = discretizer.GridDynamicSystem( sys , ( 11, 11, 11, 11) , (3,3) , 0.05) - -# Cost Function -qcf = sys.cost_function - -qcf.xbar = np.array([ 0,0,0,0 ]) # target -qcf.INF = 100 - -# VI algo -vi = valueiteration.ValueIteration_ND( grid_sys , qcf ) - -vi.initialize() -#vi.load_data('bbb') -vi.compute_steps(2,True) -#vi.load_data() -vi.assign_interpol_controller() -vi.plot_policy(0) -vi.plot_cost2go() -vi.save_data('bbb') - - -#asign controller -cl_sys = controller.ClosedLoopSystem( sys , vi.ctl ) - -# Simulation and animation -cl_sys.x0 = np.array([0.1,0.1,0,0]) -cl_sys.compute_trajectory(2,2001,'euler') -cl_sys.plot_trajectory('xu') -cl_sys.animate_simulation() - diff --git a/dev/old_to_remove/value_iteration/holonomic_3d_with_obstacles_valueiteration_nd.py b/dev/old_to_remove/value_iteration/holonomic_3d_with_obstacles_valueiteration_nd.py deleted file mode 100644 index 50f973a6..00000000 --- a/dev/old_to_remove/value_iteration/holonomic_3d_with_obstacles_valueiteration_nd.py +++ /dev/null @@ -1,48 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Mon Nov 12 20:28:17 2018 - -@author: Alexandre -""" - -import numpy as np - -from pyro.dynamic import vehicle -from pyro.planning import discretizer -from pyro.analysis import costfunction -from pyro.planning import valueiteration - -sys = vehicle.Holonomic3DMobileRobotwithObstacles() - -# Discrete world -grid_sys = discretizer.GridDynamicSystem( sys , (41,41,21) , (3,3) ) - -# Cost Function -cf = costfunction.QuadraticCostFunction( - q=np.ones(sys.n), - r=np.ones(sys.m), - v=np.zeros(sys.p) -) - -cf.INF = 1E9 - -# VI algo -vi = valueiteration.ValueIteration_ND( grid_sys , cf ) - -vi.initialize() -vi.load_data('holonomic_3d_obstacles_vi') -# vi.compute_steps(0, maxJ=4000, plot=False) -vi.plot_cost2go(4000) -vi.assign_interpol_controller() -vi.plot_policy(0) -vi.plot_policy(1) -# vi.save_data('holonomic_3d_obstacles_vi') - -# Closed loop -cl_sys = vi.ctl + sys - -# Simulation and animation -cl_sys.x0 = np.array([9,0,0]) -cl_sys.compute_trajectory(tf=20) -cl_sys.plot_trajectory('xu') -cl_sys.animate_simulation() diff --git a/dev/old_to_remove/value_iteration/holonomic_mobile_robot_with_valueiteration_nd.py b/dev/old_to_remove/value_iteration/holonomic_mobile_robot_with_valueiteration_nd.py deleted file mode 100644 index ee3d470b..00000000 --- a/dev/old_to_remove/value_iteration/holonomic_mobile_robot_with_valueiteration_nd.py +++ /dev/null @@ -1,49 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Mon Nov 12 20:28:17 2018 - -@author: Alexandre -""" - -import numpy as np - -from pyro.dynamic import vehicle -from pyro.planning import discretizer -from pyro.analysis import costfunction -from pyro.planning import valueiteration - -sys = vehicle.HolonomicMobileRobotwithObstacles() - -# Discrete world -grid_sys = discretizer.GridDynamicSystem( sys , (51,51) , (3,3) ) - -# Cost Function -cf = costfunction.QuadraticCostFunction( - q=np.ones(sys.n), - r=np.ones(sys.m), - v=np.zeros(sys.p) -) - -cf.INF = 1E9 - -# VI algo -vi = valueiteration.ValueIteration_ND( grid_sys , cf ) - -vi.initialize() -vi.load_data('holonomic_vi') -# vi.compute_steps(500) -vi.plot_cost2go(40000) -vi.assign_interpol_controller() -vi.plot_policy(0) -vi.plot_policy(1) -vi.plot_cost2go() -vi.save_data('holonomic_vi') - -# Closed loop -cl_sys = vi.ctl + sys - -# Simulation and animation -x0 = [9,0] -sim = cl_sys.compute_trajectory(x0 , tf=20) -cl_sys.plot_trajectory(sim, 'xu') -cl_sys.animate_simulation(sim, save=True, file_name='holonomic') diff --git a/dev/old_to_remove/value_iteration/simple_pendulum_with_valueiteration.py b/dev/old_to_remove/value_iteration/simple_pendulum_with_valueiteration.py deleted file mode 100644 index f96c1965..00000000 --- a/dev/old_to_remove/value_iteration/simple_pendulum_with_valueiteration.py +++ /dev/null @@ -1,49 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Mon Nov 12 20:28:17 2018 - -@author: Alexandre -""" - -import numpy as np - -from pyro.dynamic import pendulum -from pyro.planning import discretizer -from pyro.analysis import costfunction -from pyro.planning import valueiteration -from pyro.control import controller - -sys = pendulum.SinglePendulum() - -# Discrete world -grid_sys = discretizer.GridDynamicSystem( sys ) - -# Cost Function -qcf = sys.cost_function - -qcf.xbar = np.array([ -3.14 , 0 ]) # target -qcf.INF = 10000 - -# VI algo -vi = valueiteration.ValueIteration_ND( grid_sys , qcf ) - -vi.initialize() -#vi.load_data('simple_pendulum_vi') -vi.plot_max_J = 250 -vi.compute_steps(100, plot=True) -vi.assign_interpol_controller() -vi.plot_policy(0) -vi.plot_cost2go() -# vi.save_data('simple_pendulum_vi') - -#asign controller -cl_sys = controller.ClosedLoopSystem( sys , vi.ctl ) -cl_sys.cost_function = None - -# Simulation and animation -cl_sys.x0 = np.array([0,0]) -tf = 10 -cl_sys.compute_trajectory(tf) -cl_sys.plot_trajectory('xu') -cl_sys.animate_simulation() - diff --git a/dev/old_to_remove/value_iteration/udes_racecar_with_valueiteration.py b/dev/old_to_remove/value_iteration/udes_racecar_with_valueiteration.py deleted file mode 100644 index 475f4504..00000000 --- a/dev/old_to_remove/value_iteration/udes_racecar_with_valueiteration.py +++ /dev/null @@ -1,68 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Tue Nov 13 11:05:07 2018 - -@author: Alexandre -""" - -############################################################################### -import numpy as np -############################################################################### -from pyro.dynamic import vehicle -from pyro.planning import discretizer -from pyro.analysis import costfunction -from pyro.planning import valueiteration -from pyro.control import controller -############################################################################### - -sys = vehicle.KinematicProtoCarModelwithObstacles() - -############################################################################### - -# Planning - -# Set domain -sys.x_ub = np.array([+3.5, +1, +0.3]) -sys.x_lb = np.array([-2, -1, -0.3]) - -sys.u_ub = np.array([+1, +1]) -sys.u_lb = np.array([-1, -1]) - -# Discrete world -grid_sys = discretizer.GridDynamicSystem(sys, (61, 61, 21), (3, 3), 0.025) -# Cost Function -cf = costfunction.QuadraticCostFunction(sys.n,sys.m,sys.p) - -cf.xbar = np.array( [3, 0, 0] ) # target -cf.INF = 1E4 -cf.EPS = 0.05 -cf.R = np.array([[0.01,0],[0,0]]) - -# VI algo - -vi = valueiteration.ValueIteration_ND( grid_sys , cf ) - -vi.uselookuptable = True -vi.initialize() - -#if load_data: -vi.load_data('udes_racecar') -# vi.compute_steps(50, plot=True, maxJ=100) -#if save_data: -# vi.save_data('udes_racecar') - -vi.assign_interpol_controller() - -vi.plot_cost2go(maxJ=100) -vi.plot_policy(0) -vi.plot_policy(1) - -cl_sys = controller.ClosedLoopSystem( sys , vi.ctl ) -# -## Simulation and animation -cl_sys.x0 = np.array([0, 0, 0]) -tf = 5 - -cl_sys.compute_trajectory(tf, 10001, 'euler') -cl_sys.plot_trajectory('xu') -cl_sys.animate_simulation() \ No newline at end of file diff --git a/dev/plane/plane_controller.py b/dev/plane/plane_controller.py deleted file mode 100644 index dfa247fe..00000000 --- a/dev/plane/plane_controller.py +++ /dev/null @@ -1,86 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Created on Fri Sep 8 10:38:30 2023 - -@author: alex -""" - -import numpy as np - -from pyro.dynamic import plane - -from pyro.control import controller - - - - -############################################################################### - -class PLaneController( controller.StaticController ) : - - ############################ - def __init__( self ): - """ """ - - # Dimensions - self.k = 1 - self.m = 2 - self.p = 6 - - super().__init__(self.k, self.m, self.p) - - # Label - self.name = 'Plane Controller' - - self.v_ref = 40 - self.y_ref = 0.0 - - - ############################# - def c( self , y , r , t = 0 ): - """ - Feedback static computation u = c(y,r,t) - - INPUTS - y : sensor vector p x 1 - r : reference vector k x 1 - t : time 1 x 1 - - OUPUTS - u : control inputs vector m x 1 - - """ - - v = y[3] - theta = y[2] - y = y[1] - - T = 10 * ( self.v_ref - v ) - - theta_ref = 0.1 * ( self.y_ref - y ) - - delta = -0.5 * ( theta_ref - theta ) - - u = np.array([ T , delta ]) - - - return u - - -sys = plane.Plane2D() -sys.l_w = 0.0 - -sys.x0 = np.array([0,0,0.2,15,0,0]) - -ctl = PLaneController() - -cl_sys = ctl + sys - -cl_sys.compute_trajectory( 2 ) - -cl_sys.plot_trajectory() -cl_sys.animate_simulation( time_factor_video = 0.2 ) - - - diff --git a/dev/plane/plane_trajectory_optimisation.py b/dev/plane/plane_trajectory_optimisation.py deleted file mode 100644 index 715865de..00000000 --- a/dev/plane/plane_trajectory_optimisation.py +++ /dev/null @@ -1,48 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Created on Fri Sep 8 10:38:30 2023 - -@author: alex -""" - -import numpy as np - -from pyro.dynamic import plane - -from pyro.planning.trajectoryoptimisation import DirectCollocationTrajectoryOptimisation - -sys = plane.Plane2D() - -sys.x0 = np.array([0,0,0,20,0,0]) - - - -def t2u(t): - - u = np.array([ 20 , -0.1 ]) - - return u - -sys.t2u = t2u - -#sys.gravity = 0 - -sys.compute_trajectory( 2 , 2001 , 'euler' ) -#sys.plot_trajectory('x') -sys.animate_simulation( time_factor_video=0.5 ) - -planner = DirectCollocationTrajectoryOptimisation( sys ) - -planner.x_start = sys.x0 -planner.x_goal = sys.traj.x[-1,:] - -planner.grid = 20 -planner.dt = 0.1 -planner.maxiter = 400 - -planner.set_initial_trajectory_guest( sys.traj ) - -planner.compute_optimal_trajectory() -planner.show_solution() -planner.animate_solution() \ No newline at end of file diff --git a/dev/reinforcement_learning/approximate_dp_tests/approximatedynamicprogramming.py b/dev/reinforcement_learning/approximate_dp_tests/approximatedynamicprogramming.py deleted file mode 100644 index e7591e02..00000000 --- a/dev/reinforcement_learning/approximate_dp_tests/approximatedynamicprogramming.py +++ /dev/null @@ -1,234 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Created on Thu Mar 30 20:27:58 2023 - -@author: alex -""" - -import numpy as np -import matplotlib.pyplot as plt -import matplotlib.animation as animation - -import time - -from pyro.planning import dynamicprogramming - -from functionapproximation import LinearFunctionApproximator - - -############################################################################### -### DP Algo -############################################################################### - -class LinearApproximateDynamicProgramming( dynamicprogramming.DynamicProgrammingWithLookUpTable ): - """ """ - - ############################ - def __init__(self, grid_sys , cost_function , function_approximation , t = 0): - - # Dynamic system - self.grid_sys = grid_sys # Discretized Dynamic system class - self.sys = grid_sys.sys # Base Dynamic system class - - # Function approx - self.fa = function_approximation - - # Cost function - self.cf = cost_function - self.t = t - - # Options - self.alpha = 0.9 # exponential forgetting factor - self.gamma = 0.2 - self.save_time_history = True - - # Memory Variables - self.k = 0 # Number of computed steps - - # Start time (needed to plot elapsed computation time) - self.start_time = time.time() - - # Init params - self.w = np.zeros( self.fa.n ) # Initial params - - self.compute_cost_lookuptable() - - self.compute_kernels() - - self.J = self.P.T @ self.w # Initial J approx on the grid - - # - if self.save_time_history: - - self.w_list = [] - self.J_list = [] - self.t_list = [] - - # Value at t = t_f - self.J_list.append( self.J ) - self.w_list.append( self.w ) - self.t_list.append( self.k ) - - - ############################### - def compute_kernels(self): - """ Compute Kernels """ - - print('\nComputing Funtion Approximation Kernels:') - print('-----------------------------------------') - - self.P = self.fa.compute_all_kernel( self.grid_sys.state_from_node_id ) - - m = self.grid_sys.nodes_n * self.grid_sys.actions_n - n = self.sys.n - - Xnext = self.grid_sys.x_next_table.reshape( ( m , n) ) - self.P_next = self.fa.compute_all_kernel( Xnext ) - - - #### Test - eps = 0.4 #self.cf.EPS - xbar = self.cf.xbar - - on_target = np.full( self.grid_sys.x_next_table[:,:,0].shape , True ) - - for i in range( self.sys.n ): - - on_target = np.logical_and( on_target , (self.grid_sys.x_next_table[:,:,i] - xbar[i])**2 < eps ) - - self.on_target = on_target - - #self.off_target = - #### - - - ############################### - def initialize_backward_step(self): - """ One step of value iteration """ - - # Update values - self.w_old = self.w - self.k = self.k + 1 # index backward in time - - - ############################### - def compute_backward_step(self): - """ One step of value iteration """ - - J_next = self.P_next.T @ self.w - - Q = self.G + self.alpha * J_next.reshape( ( self.grid_sys.nodes_n , self.grid_sys.actions_n ) ) - - Q[ self.on_target ] = 0. # Test - Q[ Q > self.cf.INF ] = self.cf.INF # Test - - J_d = Q.min( axis = 1 ) # New J Samples - - w , J_hat = self.fa.least_square_fit( J_d , self.P ) - - #e = self.J - J_d - #dJ_dw = self.fa.dJ_dw() - - - - #self.J = J_hat - #self.w = w - self.w = self.w + self.gamma * ( w - self.w ) - self.J = self.P.T @ self.w - - - ############################### - def finalize_backward_step(self): - """ One step of value iteration """ - - # Computation time - elapsed_time = time.time() - self.start_time - - # Convergence check - j_max = self.J.max() - delta = self.w - self.w_old - delta_max = delta.max() - delta_min = delta.min() - - print('%d t:%.2f Elasped:%.2f max: %.2f dmax:%.2f dmin:%.2f' % (self.k,self.t,elapsed_time,j_max,delta_max,delta_min) ) - - # List in memory - if self.save_time_history: - self.J_list.append( self.J ) - self.w_list.append( self.w ) - self.t_list.append( self.k ) - - # return largest J change for usage as stoping criteria - return abs(np.array([delta_max,delta_min])).max() - - ################################ - def clean_infeasible_set(self , tol = 1): - """ - Set default policy and cost2go to cf.INF for state for which it is unavoidable - that they will reach unallowable regions - - """ - - pass - - - - -''' -################################################################# -################## Main ######## -################################################################# -''' - - -if __name__ == "__main__": - """ MAIN TEST """ - - - from pyro.dynamic import pendulum - from pyro.planning import discretizer - from pyro.analysis import costfunction - from functionapproximation import QuadraticFunctionApproximator - from functionapproximation import MultipleGaussianFunctionApproximator - - sys = pendulum.SinglePendulum() - - # Discrete world - grid_sys = discretizer.GridDynamicSystem( sys , [101,101] , [3] ) - - # Cost Function - qcf = costfunction.QuadraticCostFunction.from_sys(sys) - - qcf.xbar = np.array([ -3.14 , 0 ]) # target - qcf.INF = 300 - - # Approx - - fa = QuadraticFunctionApproximator( sys.n , x0 = qcf.xbar ) - - # Discrete world - grid_sys_gaussian = discretizer.GridDynamicSystem( sys , [11,11] , [3] , 0.05) - X0 = grid_sys_gaussian.state_from_node_id - - #fa = MultipleGaussianFunctionApproximator( X0 , 3.0 ) + fa - - fa = MultipleGaussianFunctionApproximator( X0 , 1.0 ) - - # DP algo - dp = LinearApproximateDynamicProgramming( grid_sys, qcf, fa ) - - dp.alpha = 0.8 - dp.gamma = 1.0 - - dp.w = dp.w + 20 - - #dp.solve_bellman_equation( tol = 0.1 ) - dp.compute_steps(100) - - #dp.plot_cost2go() - #dp.plot_cost2go_3D() - dp.animate_cost2go() - - - \ No newline at end of file diff --git a/dev/reinforcement_learning/approximate_dp_tests/cost2go_lqr_vs_vi.py b/dev/reinforcement_learning/approximate_dp_tests/cost2go_lqr_vs_vi.py deleted file mode 100644 index cf1d8be7..00000000 --- a/dev/reinforcement_learning/approximate_dp_tests/cost2go_lqr_vs_vi.py +++ /dev/null @@ -1,81 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Created on Sun Oct 16 22:27:47 2022 - -@author: alex -""" - -import numpy as np - -from pyro.dynamic import massspringdamper -from pyro.control import controller -from pyro.analysis import costfunction -from pyro.planning import dynamicprogramming -from pyro.planning import discretizer - -from scipy.linalg import solve_continuous_are - -from functionapproximation import QuadraticFunctionApproximator - -sys = massspringdamper.FloatingSingleMass() - -sys.x_ub[0] = 10.0 -sys.x_lb[0] = -10.0 -sys.x_lb[1] = -5.0 -sys.x_ub[1] = 5.0 -sys.u_ub[0] = 5.0 -sys.u_lb[0] = -5.0 - -# Discrete world -grid_sys = discretizer.GridDynamicSystem( sys , [101,101] , [11] , 0.05) - -# Cost Function -qcf = costfunction.QuadraticCostFunction.from_sys(sys) - -qcf.xbar = np.array([ -0 , 0 ]) # target -qcf.INF = 300 - -qcf.R[0,0] = 10.0 - -qcf.S[0,0] = 10.0 -qcf.S[1,1] = 10.0 - -#################### -# Value iteration solution -#################### - -dp = dynamicprogramming.DynamicProgrammingWithLookUpTable( grid_sys, qcf) - -dp.solve_bellman_equation( tol = 0.5 ) - -ctl = dp.get_lookup_table_controller() - -dp.plot_cost2go() -dp.plot_cost2go_3D() - -#################### -# Quadratic Approx -#################### - -qfa = QuadraticFunctionApproximator( sys.n , x0 = qcf.xbar ) - -Xs = grid_sys.state_from_node_id # All state on the grid - -P = qfa.compute_all_kernel( Xs ) - -w , J_hat = qfa.least_square_fit( dp.J , P ) - -grid_sys.plot_grid_value_3D( J_hat , None , 'Quadratic approx') - -grid_sys.plot_grid_value_3D( dp.J , J_hat , 'J vs. J_hat') - -#################### -# Riccati solution -#################### - -S = solve_continuous_are( sys.A , sys.B , qcf.Q , qcf.R ) - -w_riccati = np.array([0, 0, 0, S[0,0], S[1,1], S[0,1]]) -J_riccati = P.T @ w_riccati -grid_sys.plot_grid_value_3D( J_riccati , J_hat , 'J_riccati vs. J_hat') \ No newline at end of file diff --git a/dev/reinforcement_learning/approximate_dp_tests/fitted_vi_pendulum_21x21_working.py b/dev/reinforcement_learning/approximate_dp_tests/fitted_vi_pendulum_21x21_working.py deleted file mode 100644 index 4e296141..00000000 --- a/dev/reinforcement_learning/approximate_dp_tests/fitted_vi_pendulum_21x21_working.py +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Created on Fri Mar 31 11:35:09 2023 - -@author: alex -""" -import numpy as np - -from pyro.dynamic import pendulum -from pyro.planning import discretizer -from pyro.analysis import costfunction -from approximatedynamicprogramming import LinearApproximateDynamicProgramming -from functionapproximation import QuadraticFunctionApproximator -from functionapproximation import MultipleGaussianFunctionApproximator - -sys = pendulum.SinglePendulum() - -# Discrete world -grid_sys = discretizer.GridDynamicSystem( sys , [101,101] , [3] ) - -# Cost Function -qcf = costfunction.QuadraticCostFunction.from_sys(sys) - -qcf.xbar = np.array([ -3.14 , 0 ]) # target -qcf.INF = 300 - -# Approx - -fa = QuadraticFunctionApproximator( sys.n , x0 = qcf.xbar ) - -# Discrete world -grid_sys_gaussian = discretizer.GridDynamicSystem( sys , [21,21] , [3] , 0.05) -X0 = grid_sys_gaussian.state_from_node_id - -#fa = MultipleGaussianFunctionApproximator( X0 , 3.0 ) + fa - -fa = MultipleGaussianFunctionApproximator( X0 , 0.5 ) - -# DP algo -dp = LinearApproximateDynamicProgramming( grid_sys, qcf, fa ) - -dp.alpha = 0.95 -dp.gamma = 0.2 - -dp.w = dp.w + 20 - -#dp.solve_bellman_equation( tol = 0.1 ) -dp.compute_steps(100) - -#dp.plot_cost2go() -#dp.plot_cost2go_3D() -dp.animate_cost2go() \ No newline at end of file diff --git a/dev/reinforcement_learning/approximate_dp_tests/fitted_vi_pendulum_lowdef_convergence.py b/dev/reinforcement_learning/approximate_dp_tests/fitted_vi_pendulum_lowdef_convergence.py deleted file mode 100644 index 8f9e7a56..00000000 --- a/dev/reinforcement_learning/approximate_dp_tests/fitted_vi_pendulum_lowdef_convergence.py +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Created on Fri Mar 31 11:35:09 2023 - -@author: alex -""" -import numpy as np - -from pyro.dynamic import pendulum -from pyro.planning import discretizer -from pyro.analysis import costfunction -from approximatedynamicprogramming import LinearApproximateDynamicProgramming -from functionapproximation import QuadraticFunctionApproximator -from functionapproximation import MultipleGaussianFunctionApproximator - -sys = pendulum.SinglePendulum() - -# Discrete world -grid_sys = discretizer.GridDynamicSystem( sys , [101,101] , [3] ) - -# Cost Function -qcf = costfunction.QuadraticCostFunction.from_sys(sys) - -qcf.xbar = np.array([ -3.14 , 0 ]) # target -qcf.INF = 300 - -# Approx - -fa = QuadraticFunctionApproximator( sys.n , x0 = qcf.xbar ) - -# Discrete world -grid_sys_gaussian = discretizer.GridDynamicSystem( sys , [11,11] , [3] , 0.05) -X0 = grid_sys_gaussian.state_from_node_id - -#fa = MultipleGaussianFunctionApproximator( X0 , 3.0 ) + fa - -fa = MultipleGaussianFunctionApproximator( X0 , 1.0 ) - -# DP algo -dp = LinearApproximateDynamicProgramming( grid_sys, qcf, fa ) - -dp.alpha = 0.8 -dp.gamma = 1.0 - -dp.w = dp.w + 20 - -#dp.solve_bellman_equation( tol = 0.1 ) -dp.compute_steps(100) - -#dp.plot_cost2go() -#dp.plot_cost2go_3D() -dp.animate_cost2go() \ No newline at end of file diff --git a/dev/reinforcement_learning/approximate_dp_tests/fitted_vi_pendulum_test.py b/dev/reinforcement_learning/approximate_dp_tests/fitted_vi_pendulum_test.py deleted file mode 100644 index 916252da..00000000 --- a/dev/reinforcement_learning/approximate_dp_tests/fitted_vi_pendulum_test.py +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Created on Fri Mar 31 11:35:09 2023 - -@author: alex -""" -import numpy as np - -from pyro.dynamic import pendulum -from pyro.planning import discretizer -from pyro.analysis import costfunction -from approximatedynamicprogramming import LinearApproximateDynamicProgramming -from functionapproximation import QuadraticFunctionApproximator -from functionapproximation import MultipleGaussianFunctionApproximator - -sys = pendulum.SinglePendulum() - -# Discrete world -grid_sys = discretizer.GridDynamicSystem( sys , [101,101] , [3] , 0.1 ) - -# Cost Function -qcf = costfunction.QuadraticCostFunction.from_sys(sys) - -qcf.xbar = np.array([ -3.14 , 0 ]) # target -qcf.INF = 300 - -# Approx - -fa = QuadraticFunctionApproximator( sys.n , x0 = qcf.xbar ) - -# Discrete world -grid_sys_gaussian = discretizer.GridDynamicSystem( sys , [41,41] , [3] , 0.05) -X0 = grid_sys_gaussian.state_from_node_id - -#fa = MultipleGaussianFunctionApproximator( X0 , 3.0 ) + fa - -fa = MultipleGaussianFunctionApproximator( X0 , 0.30 ) - -# DP algo -dp = LinearApproximateDynamicProgramming( grid_sys, qcf, fa ) - -dp.alpha = 0.95 -dp.gamma = 1.0 - -dp.w = dp.w + 20 - -#dp.solve_bellman_equation( tol = 0.1 ) -dp.compute_steps(100) - -#dp.plot_cost2go() -#dp.plot_cost2go_3D() -dp.animate_cost2go() \ No newline at end of file diff --git a/dev/reinforcement_learning/approximate_dp_tests/functionapproximation.py b/dev/reinforcement_learning/approximate_dp_tests/functionapproximation.py deleted file mode 100644 index 383387c2..00000000 --- a/dev/reinforcement_learning/approximate_dp_tests/functionapproximation.py +++ /dev/null @@ -1,335 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Created on Tue Mar 28 15:41:59 2023 - -@author: alex -""" - -import numpy as np - -############################################################################### -### Function approximator -############################################################################### - -class FunctionApproximator(): - - ############################ - def __init__(self, n = 10 ): - - self.n = n # number of parameters - - #self.w = np.zeros( self.n ) - - - ############################ - def J_hat( self , x , w ): - """ Compute J approx given state x and param w """ - - return 0 - - - ############################ - def dJ_dw( self , x ): - """ Compute dJ/dw given state x """ - - return np.zeros( self.n ) - - -############################################################################### -### Linear Function approximator -############################################################################### - -class LinearFunctionApproximator( FunctionApproximator ): - - ############################ - def __init__(self, n = 10 ): - - FunctionApproximator.__init__(self,n) - - - ############################ - def J_hat( self , x , w ): - """ Compute J approx given state x and param w """ - - phi = self.compute_kernel( x ) - - J_hat = phi.T @ w - - return J_hat - - - ############################ - def dJ_dw( self , x ): - """ Compute dJ/dw given state x """ - - phi = self.compute_kernel( x ) - - return phi - - - ############################ - def compute_kernel( self , x ): - """ Compute kernel functions phi given a state x """ - - phi = np.zeros( self.n ) - - return phi - - - ############################ - def compute_all_kernel( self , Xs ): - """ Compute all kernel functions phi given a m state x """ - - m = Xs.shape[0] # number of data point - n = self.n # number of params - - P = np.zeros( ( m , n )) - - for i in range(m): - - P[i,:] = self.compute_kernel( Xs[i,:] ) - - return P.T - - - ############################ - def least_square_fit( self , Js , P ): - """ solve J_d = P w """ - - #P = self.compute_all_kernel( Xs ) - - w = np.linalg.lstsq( P.T , Js , rcond=None)[0] - - J_hat = P.T @ w - - return w , J_hat - - ############################# - def __add__(self, other ): - """ - return new function approx with all kernels - """ - - return CombinedLinearFunctionApproximator( self , other ) - - - -############################################################################### -### Combined Linear Function approximator -############################################################################### - -class CombinedLinearFunctionApproximator( LinearFunctionApproximator ): - - ############################ - def __init__(self, FA1 , FA2 ): - - FunctionApproximator.__init__(self, FA1.n + FA2.n ) - - self.FA1 = FA1 - self.FA2 = FA2 - - - - ############################ - def compute_kernel( self , x ): - """ Compute kernel functions phi given a state x """ - - phi1 = self.FA1.compute_kernel( x ) - phi2 = self.FA2.compute_kernel( x ) - - phi = np.concatenate(( phi1, phi2 )) - - return phi - - - - - - -############################################################################### -### Quadratic Function approximator -############################################################################### - -class QuadraticFunctionApproximator( LinearFunctionApproximator ): - - ############################ - def __init__(self, sys_n = 2 , x0 = None): - """ - J_hat = C + B x + x' A x = w' phi - - """ - - self.sys_n = sys_n - - if x0 is not None: - - self.x0 = x0 - - else: - - self.x0 = np.zeros( sys_n ) - - self.n_2_diag = sys_n - self.n_2_off = int((sys_n**2-sys_n)/2) - self.n_2 = +self.n_2_diag + self.n_2_off # 2nd order number of weight - self.n_1 = sys_n # 1nd order number of weight - self.n_0 = 1 # 0nd order number ofweight - - # Total number of parameters - self.n = int(self.n_2 + self.n_1 + self.n_0) - - - ############################ - def compute_kernel( self , x ): - """ return approx a state x """ - - phi = np.zeros( self.n ) - - x = x - self.x0 - - xxT = np.outer( x , x ) - - #indices - n0 = self.n_0 - n1 = self.n_0 + self.n_1 - n2 = self.n_0 + self.n_1 + self.n_2_diag - n3 = self.n_0 + self.n_1 + self.n_2_diag + self.n_2_off - - phi[0] = 1 - phi[n0:n1] = x - phi[n1:n2] = np.diag( xxT ) - phi[n2:n3] = xxT[np.triu_indices( self.sys_n, k = 1)] - - return phi - - -############################################################################### -### Radial basis function Function approximator -############################################################################### - -class GaussianFunctionApproximator( LinearFunctionApproximator ): - - ############################ - def __init__(self, x0 , sig = 6.0 ): - """ - J_hat = exp( - || x - x0 || / 2 sig^2 ) - - """ - - self.x0 = x0 - self.sys_n = x0.shape[0] - self.n = 1 - self.a = -0.5 / (sig**2) - - - ############################ - def compute_kernel( self , x ): - """ return approx a state x """ - - phi = np.array([0.]) - - e = x - self.x0 - r = e.T @ e - - phi[0] = np.exp( self.a * r ) - - return phi - - -############################################################################### -class MultipleGaussianFunctionApproximator( LinearFunctionApproximator ): - - ############################ - def __init__(self, Xs , sig = 1.0 ): - """ - J_hat = sum exp( - || x - x0 || / 2 sig^2 ) - - """ - - self.Xs = Xs - self.sys_n = Xs.shape[1] - self.n = Xs.shape[0] # number of data point - self.a = -0.5 / (sig**2) - - - ############################ - def compute_kernel( self , x ): - """ return approx a state x """ - - phi = np.zeros(self.n) - - for i in range(self.n): - - e = x - self.Xs[i,:] - r = e.T @ e - - phi[i] = np.exp( self.a * r ) - - return phi - - - -############################################################################### -### Main -############################################################################### - - -if __name__ == "__main__": - """ MAIN TEST """ - - from pyro.dynamic import pendulum - from pyro.dynamic import massspringdamper - from pyro.planning import discretizer - from pyro.analysis import costfunction - from pyro.planning import dynamicprogramming - - sys = pendulum.SinglePendulum() - #sys = - - # Discrete world - grid_sys = discretizer.GridDynamicSystem( sys , [101,101] , [3] ) - - # Cost Function - qcf = costfunction.QuadraticCostFunction.from_sys(sys) - - qcf.xbar = np.array([ -3.14 , 0 ]) # target - qcf.INF = 200 - - # DP algo - dp = dynamicprogramming.DynamicProgrammingWithLookUpTable(grid_sys, qcf) - - dp.solve_bellman_equation( tol = 1.0 ) - dp.plot_cost2go_3D() - - - - # Approx - qfa0 = QuadraticFunctionApproximator( sys.n , x0 = qcf.xbar ) - - sig = 2.0 - - qfa1 = GaussianFunctionApproximator( x0 = np.array([0,0]), sig = sig ) - qfa2 = GaussianFunctionApproximator( x0 = qcf.xbar , sig = sig) - qfa3 = GaussianFunctionApproximator( x0 = qcf.xbar + np.array([1,0]) , sig = sig) - qfa4 = GaussianFunctionApproximator( x0 = qcf.xbar + np.array([1,1]) , sig = sig) - qfa5 = GaussianFunctionApproximator( x0 = qcf.xbar + np.array([0,1]) , sig = sig) - qfa6 = GaussianFunctionApproximator( x0 = qcf.xbar + np.array([-1,0]) , sig = sig) - qfa7 = GaussianFunctionApproximator( x0 = qcf.xbar + np.array([-1,-1]) , sig = sig) - qfa8 = GaussianFunctionApproximator( x0 = qcf.xbar + np.array([0,-1]) , sig = sig) - qfa9 = GaussianFunctionApproximator( x0 = qcf.xbar + np.array([0.5,0]) , sig = sig) - qfa10 = GaussianFunctionApproximator( x0 = qcf.xbar + np.array([0.5,0.5]) , sig = sig) - qfa11 = GaussianFunctionApproximator( x0 = qcf.xbar + np.array([0,0.5]) , sig = sig) - qfa = qfa0 + qfa1 + qfa2 + qfa3 + qfa4 + qfa5 + qfa6 + qfa7 + qfa8 + qfa9 + qfa10 + qfa11 - - Xs = grid_sys.state_from_node_id # All state on the grid - - P = qfa.compute_all_kernel( Xs ) - - w , J_hat = qfa.least_square_fit( dp.J , P ) - - grid_sys.plot_grid_value_3D( J_hat , None , ' J_hat') - grid_sys.plot_grid_value_3D( dp.J , J_hat , 'J vs. J_hat') - - - \ No newline at end of file diff --git a/dev/reinforcement_learning/approximate_dp_tests/pendulum_cost2go_guassian_approx.py b/dev/reinforcement_learning/approximate_dp_tests/pendulum_cost2go_guassian_approx.py deleted file mode 100644 index 5242bd7f..00000000 --- a/dev/reinforcement_learning/approximate_dp_tests/pendulum_cost2go_guassian_approx.py +++ /dev/null @@ -1,91 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Created on Sun Oct 16 22:27:47 2022 - -@author: alex -""" - -import numpy as np - -from pyro.dynamic import pendulum -from pyro.dynamic import massspringdamper -from pyro.planning import discretizer -from pyro.analysis import costfunction -from pyro.planning import dynamicprogramming - -from scipy.linalg import solve_continuous_are - -from functionapproximation import QuadraticFunctionApproximator -from functionapproximation import GaussianFunctionApproximator -from functionapproximation import MultipleGaussianFunctionApproximator - -sys = pendulum.SinglePendulum() - -sys.x_ub[0] = 10.0 -sys.x_lb[0] = -10.0 -sys.x_lb[1] = -5.0 -sys.x_ub[1] = 5.0 -sys.u_ub[0] = 5.0 -sys.u_lb[0] = -5.0 - -# Discrete world -grid_sys = discretizer.GridDynamicSystem( sys , [201,201] , [11] , 0.05) - -# Cost Function -qcf = costfunction.QuadraticCostFunction.from_sys(sys) - -qcf.xbar = np.array([ -3.14 , 0 ]) # target -qcf.INF = 200 - -#################### -# Value iteration solution -#################### - -dp = dynamicprogramming.DynamicProgrammingWithLookUpTable( grid_sys, qcf) - -dp.solve_bellman_equation( tol = 0.5 ) - -#ctl = dp.get_lookup_table_controller() - -#dp.plot_cost2go() -dp.plot_cost2go_3D() - - - -#################### -# Gaussian Approx -#################### - -# ============================================================================= -# qfa1 = GaussianFunctionApproximator( x0 = np.array([0,0]) ) -# qfa2 = GaussianFunctionApproximator( x0 = qcf.xbar ) -# qfa3 = GaussianFunctionApproximator( x0 = qcf.xbar + np.array([1,0]) ) -# qfa4 = GaussianFunctionApproximator( x0 = qcf.xbar + np.array([1,1]) ) -# qfa5 = GaussianFunctionApproximator( x0 = qcf.xbar + np.array([0,1]) ) -# qfa6 = GaussianFunctionApproximator( x0 = qcf.xbar + np.array([-1,0]) ) -# qfa7 = GaussianFunctionApproximator( x0 = qcf.xbar + np.array([-1,-1]) ) -# qfa8 = GaussianFunctionApproximator( x0 = qcf.xbar + np.array([0,-1]) ) -# qfa9 = GaussianFunctionApproximator( x0 = qcf.xbar + np.array([0.5,0]) ) -# qfa10 = GaussianFunctionApproximator( x0 = qcf.xbar + np.array([0.5,0.5]) ) -# qfa11 = GaussianFunctionApproximator( x0 = qcf.xbar + np.array([0,0.5]) ) -# -# qfa = qfa1 + qfa2 + qfa3 + qfa4 + qfa5 + qfa6 + qfa7 + qfa8 + qfa9 + qfa10 + qfa11 -# ============================================================================= - - -# Discrete world -grid_sys_gaussian = discretizer.GridDynamicSystem( sys , [21,21] , [3] , 0.05) -X0 = grid_sys_gaussian.state_from_node_id - -qfa = MultipleGaussianFunctionApproximator( X0 ) + QuadraticFunctionApproximator( sys.n ) - -Xs = grid_sys.state_from_node_id # All state on the grid - -P = qfa.compute_all_kernel( Xs ) - -w , J_hat = qfa.least_square_fit( dp.J , P ) - -grid_sys.plot_grid_value_3D( J_hat , None , 'Gaussian approx') - -grid_sys.plot_grid_value_3D( dp.J , J_hat , 'J vs. J_hat') diff --git a/dev/reinforcement_learning/approximate_dp_tests/pendulum_cost2go_guassian_approx_test.py b/dev/reinforcement_learning/approximate_dp_tests/pendulum_cost2go_guassian_approx_test.py deleted file mode 100644 index cae2a367..00000000 --- a/dev/reinforcement_learning/approximate_dp_tests/pendulum_cost2go_guassian_approx_test.py +++ /dev/null @@ -1,92 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Created on Sun Oct 16 22:27:47 2022 - -@author: alex -""" - -import numpy as np - -from pyro.dynamic import pendulum -from pyro.dynamic import massspringdamper -from pyro.planning import discretizer -from pyro.analysis import costfunction -from pyro.planning import dynamicprogramming - -from scipy.linalg import solve_continuous_are - -from functionapproximation import GaussianFunctionApproximator -from functionapproximation import MultipleGaussianFunctionApproximator - -sys = pendulum.SinglePendulum() - -# ============================================================================= -# sys.x_ub[0] = 10.0 -# sys.x_lb[0] = -10.0 -# sys.x_lb[1] = -5.0 -# sys.x_ub[1] = 5.0 -# sys.u_ub[0] = 5.0 -# sys.u_lb[0] = -5.0 -# ============================================================================= - -# Discrete world -grid_sys = discretizer.GridDynamicSystem( sys , [101,101] , [3] , 0.05) - -# Cost Function -qcf = costfunction.QuadraticCostFunction.from_sys(sys) - -qcf.xbar = np.array([ -3.14 , 0 ]) # target -qcf.INF = 200 - -#################### -# Value iteration solution -#################### - -dp = dynamicprogramming.DynamicProgrammingWithLookUpTable( grid_sys, qcf) - -dp.solve_bellman_equation( tol = 0.5 ) - -#ctl = dp.get_lookup_table_controller() - -#dp.plot_cost2go() -dp.plot_cost2go_3D() - - - -#################### -# Gaussian Approx -#################### - -# ============================================================================= -# qfa1 = GaussianFunctionApproximator( x0 = np.array([0,0]) ) -# qfa2 = GaussianFunctionApproximator( x0 = qcf.xbar ) -# qfa3 = GaussianFunctionApproximator( x0 = qcf.xbar + np.array([1,0]) ) -# qfa4 = GaussianFunctionApproximator( x0 = qcf.xbar + np.array([1,1]) ) -# qfa5 = GaussianFunctionApproximator( x0 = qcf.xbar + np.array([0,1]) ) -# qfa6 = GaussianFunctionApproximator( x0 = qcf.xbar + np.array([-1,0]) ) -# qfa7 = GaussianFunctionApproximator( x0 = qcf.xbar + np.array([-1,-1]) ) -# qfa8 = GaussianFunctionApproximator( x0 = qcf.xbar + np.array([0,-1]) ) -# qfa9 = GaussianFunctionApproximator( x0 = qcf.xbar + np.array([0.5,0]) ) -# qfa10 = GaussianFunctionApproximator( x0 = qcf.xbar + np.array([0.5,0.5]) ) -# qfa11 = GaussianFunctionApproximator( x0 = qcf.xbar + np.array([0,0.5]) ) -# -# qfa = qfa1 + qfa2 + qfa3 + qfa4 + qfa5 + qfa6 + qfa7 + qfa8 + qfa9 + qfa10 + qfa11 -# ============================================================================= - - -# Discrete world -grid_sys_gaussian = discretizer.GridDynamicSystem( sys , [11,11] , [3] , 0.05) -X0 = grid_sys_gaussian.state_from_node_id - -qfa = MultipleGaussianFunctionApproximator( X0 ) - -Xs = grid_sys.state_from_node_id # All state on the grid - -P = qfa.compute_all_kernel( Xs ) - -w , J_hat = qfa.least_square_fit( dp.J , P ) - -grid_sys.plot_grid_value_3D( J_hat , None , 'Gaussian approx') - -grid_sys.plot_grid_value_3D( dp.J , J_hat , 'J vs. J_hat') diff --git a/dev/reinforcement_learning/approximate_dp_tests/pendulum_cost2go_quadratic_approx.py b/dev/reinforcement_learning/approximate_dp_tests/pendulum_cost2go_quadratic_approx.py deleted file mode 100644 index 9452dd71..00000000 --- a/dev/reinforcement_learning/approximate_dp_tests/pendulum_cost2go_quadratic_approx.py +++ /dev/null @@ -1,69 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Created on Sun Oct 16 22:27:47 2022 - -@author: alex -""" - -import numpy as np - -from pyro.dynamic import pendulum -from pyro.dynamic import massspringdamper -from pyro.planning import discretizer -from pyro.analysis import costfunction -from pyro.planning import dynamicprogramming - -from scipy.linalg import solve_continuous_are - -from functionapproximation import QuadraticFunctionApproximator - -sys = pendulum.SinglePendulum() - -# ============================================================================= -# sys.x_ub[0] = 10.0 -# sys.x_lb[0] = -10.0 -# sys.x_lb[1] = -5.0 -# sys.x_ub[1] = 5.0 -# sys.u_ub[0] = 5.0 -# sys.u_lb[0] = -5.0 -# ============================================================================= - -# Discrete world -grid_sys = discretizer.GridDynamicSystem( sys , [101,101] , [3] , 0.05) - -# Cost Function -qcf = costfunction.QuadraticCostFunction.from_sys(sys) - -qcf.xbar = np.array([ -3.14 , 0 ]) # target -qcf.INF = 200 - -#################### -# Value iteration solution -#################### - -dp = dynamicprogramming.DynamicProgrammingWithLookUpTable( grid_sys, qcf) - -dp.solve_bellman_equation( tol = 0.5 ) - -#ctl = dp.get_lookup_table_controller() - -#dp.plot_cost2go() -#dp.plot_cost2go_3D() - -#################### -# Quadratic Approx -#################### - -qfa = QuadraticFunctionApproximator( sys.n , x0 = qcf.xbar ) - -Xs = grid_sys.state_from_node_id # All state on the grid - -P = qfa.compute_all_kernel( Xs ) - -w , J_hat = qfa.least_square_fit( dp.J , P ) - -#grid_sys.plot_grid_value_3D( J_hat , None , 'Quadratic approx') - -grid_sys.plot_grid_value_3D( dp.J , J_hat , 'J vs. J_hat') - diff --git a/dev/reinforcement_learning/dp_tests/dev_test_double_pendulum.py b/dev/reinforcement_learning/dp_tests/dev_test_double_pendulum.py deleted file mode 100644 index 2504db26..00000000 --- a/dev/reinforcement_learning/dp_tests/dev_test_double_pendulum.py +++ /dev/null @@ -1,78 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Created on Sun Oct 16 22:27:47 2022 - -Note: Is this exemple working or a work in progress??? - -@author: alex -""" - -import numpy as np - -from pyro.dynamic import pendulum -from pyro.control import controller -from pyro.analysis import costfunction -from pyro.planning import dynamicprogramming -from pyro.planning import discretizer - -sys = pendulum.DoublePendulum() - -sys.I1 = 5 -sys.I2 = 5 - -dx1 = 4.0 -dx2 = 4.0 -ddx1 = 5.0 -ddx2 = 5.0 - -sys.x_ub = np.array([ dx1 , dx2, ddx1 , ddx2 ]) -sys.x_lb = np.array([ -dx1 , -dx2, -ddx1 , -ddx2 ]) - -sys.u_ub = np.array([ 10.0 , 5.0 ]) -sys.u_lb = np.array([ -10.0 , -5.0 ]) - -# Discrete world -grid_sys = discretizer.GridDynamicSystem( sys , [41,41,21,21] , [3,3] , 0.2 ) - -# Cost Function -qcf = costfunction.QuadraticCostFunction.from_sys(sys) - -qcf.xbar = np.array([ 0 , 0, 0 , 0 ]) # target -qcf.INF = 100000 -qcf.EPS = 0.2 - -qcf.Q[0,0] = 0.1 -qcf.Q[1,1] = 0.1 -qcf.Q[2,2] = 0.1 -qcf.Q[3,3] = 0.1 - -qcf.S[0,0] = 1 -qcf.S[1,1] = 1 -qcf.S[2,2] = 1 -qcf.S[3,3] = 1 - - -# DP algo -dp = dynamicprogramming.DynamicProgrammingWithLookUpTable( grid_sys, qcf ) - -#dp.plot_cost2go() -#dp.compute_steps( 10 , animate_policy = True ) -dp.compute_steps(100) - - -ctl = dp.get_lookup_table_controller() - - - -#asign controller -cl_sys = controller.ClosedLoopSystem( sys , ctl ) - -############################################################################## - -# Simulation and animation -cl_sys.x0 = np.array([0,0.1,0,0.0]) -cl_sys.compute_trajectory( 10, 10001, 'euler') -cl_sys.plot_trajectory('xu') -cl_sys.plot_phase_plane_trajectory() -cl_sys.animate_simulation() diff --git a/dev/reinforcement_learning/dp_tests/test_hidef.py b/dev/reinforcement_learning/dp_tests/test_hidef.py deleted file mode 100644 index 5941e9e9..00000000 --- a/dev/reinforcement_learning/dp_tests/test_hidef.py +++ /dev/null @@ -1,61 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Created on Sun Oct 16 22:27:47 2022 - -@author: alex -""" - -import numpy as np - -from pyro.dynamic import pendulum -from pyro.control import controller -from pyro.analysis import costfunction -from pyro.planning import dynamicprogramming -from pyro.planning import discretizer - -sys = pendulum.SinglePendulum() - -sys.x_ub[0] = +2.1 -sys.x_lb[0] = -3.3 -sys.x_ub[1] = +4.0 -sys.x_lb[1] = -5.0 - -sys.u_ub[0] = +2.5 -sys.u_lb[0] = -2.5 - -# Discrete world -grid_sys = discretizer.GridDynamicSystem( sys , [401,401] , [31] ) - -# Cost Function -qcf = costfunction.QuadraticCostFunction.from_sys(sys) - -qcf.xbar = np.array([ -3.14 , 0 ]) # target -qcf.R[0,0] = 10 -qcf.INF = 300 - -qcf.S[0,0] = 10.0 -qcf.S[1,1] = 10.0 - -# DP algo - -dp = dynamicprogramming.DynamicProgrammingWithLookUpTable( grid_sys, qcf) - -dp.solve_bellman_equation( tol = 0.1 ) - -dp.plot_cost2go() -dp.plot_policy() - - -#asign controller -ctl = dp.get_lookup_table_controller() -cl_sys = controller.ClosedLoopSystem( sys , ctl ) - -############################################################################## - -# Simulation and animation -cl_sys.x0 = np.array([0,0]) -cl_sys.compute_trajectory( 30, 10001, 'euler') -cl_sys.plot_trajectory('xu') -cl_sys.plot_phase_plane_trajectory() -cl_sys.animate_simulation() \ No newline at end of file diff --git a/dev/reinforcement_learning/dp_tests/test_lowdef.py b/dev/reinforcement_learning/dp_tests/test_lowdef.py deleted file mode 100644 index e90e4667..00000000 --- a/dev/reinforcement_learning/dp_tests/test_lowdef.py +++ /dev/null @@ -1,58 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Created on Sun Oct 16 22:27:47 2022 - -@author: alex -""" - -import numpy as np - -from pyro.dynamic import pendulum -from pyro.control import controller -from pyro.analysis import costfunction -from pyro.planning import dynamicprogramming -from pyro.planning import discretizer - -sys = pendulum.SinglePendulum() - -sys.x_ub[0] = +4.0 -sys.x_lb[0] = -4.0 -sys.x_ub[1] = +5.0 -sys.x_lb[1] = -5.0 - -#sys.u_ub[0] = +5.0 -#sys.u_lb[0] = -5.0 - -# Discrete world -grid_sys = discretizer.GridDynamicSystem( sys , [41,21] , [3] , dt = 0.2 ) - -# Cost Function -qcf = costfunction.QuadraticCostFunction.from_sys(sys) - -qcf.xbar = np.array([ -3.14 , 0 ]) # target -qcf.INF = 300 -qcf.EPS = 1.0 - -# DP algo - -dp = dynamicprogramming.DynamicProgrammingWithLookUpTable( grid_sys, qcf) - -dp.solve_bellman_equation( tol = 1.0 ) - -dp.plot_cost2go() -dp.plot_policy() - - -#asign controller -ctl = dp.get_lookup_table_controller() -cl_sys = controller.ClosedLoopSystem( sys , ctl ) - -############################################################################## - -# Simulation and animation -cl_sys.x0 = np.array([0,0]) -cl_sys.compute_trajectory( 30, 10001, 'euler') -cl_sys.plot_trajectory('xu') -cl_sys.plot_phase_plane_trajectory() -cl_sys.animate_simulation() \ No newline at end of file diff --git a/dev/reinforcement_learning/dp_tests/test_policy_evaluator.py b/dev/reinforcement_learning/dp_tests/test_policy_evaluator.py deleted file mode 100644 index 49862bc5..00000000 --- a/dev/reinforcement_learning/dp_tests/test_policy_evaluator.py +++ /dev/null @@ -1,56 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Created on Sun Oct 16 22:27:47 2022 - -@author: alex -""" - -import numpy as np - -from pyro.dynamic import pendulum -from pyro.analysis import costfunction -from pyro.planning import dynamicprogramming -from pyro.planning import discretizer - -sys = pendulum.SinglePendulum() - -sys.x_ub = np.array([+6, +6]) -sys.x_lb = np.array([-9, -6]) - -# Discrete world -grid_sys = discretizer.GridDynamicSystem( sys , [101,101] , [11] ) - -# Cost Function -qcf = costfunction.QuadraticCostFunction.from_sys(sys) - -qcf.xbar = np.array([ -3.14 , 0 ]) # target -qcf.INF = 300 - -qcf.S[0,0] = 10.0 -qcf.S[1,1] = 10.0 - - -# DP algo -dp = dynamicprogramming .DynamicProgrammingWithLookUpTable( grid_sys, qcf) - -dp.solve_bellman_equation( tol = 0.1 ) -dp.plot_cost2go() - -# Get optimal ctl -ctl = dp.get_lookup_table_controller() - - -# Evaluate on same grid -#evaluator = dprog.PolicyEvaluator(ctl, grid_sys, qcf) -#evaluator.solve_bellman_equation() -#evaluator.plot_cost2go() - - -# Evaluate on new grid -grid_sys2 = discretizer.GridDynamicSystem( sys , [151,151] , [11] ) - -#evaluator2 = dynamicprogramming.PolicyEvaluatorWithLookUpTable( ctl, grid_sys2, qcf) -evaluator2 = dynamicprogramming.PolicyEvaluator(ctl, grid_sys2, qcf) -evaluator2.solve_bellman_equation() -evaluator2.plot_cost2go() \ No newline at end of file diff --git a/dev/reinforcement_learning/dp_tests/test_policy_evaluator_with_lookuptable.py b/dev/reinforcement_learning/dp_tests/test_policy_evaluator_with_lookuptable.py deleted file mode 100644 index 45a69709..00000000 --- a/dev/reinforcement_learning/dp_tests/test_policy_evaluator_with_lookuptable.py +++ /dev/null @@ -1,68 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Created on Sun Oct 16 22:27:47 2022 - -@author: alex -""" - -import numpy as np - -from pyro.dynamic import pendulum -from pyro.control import controller -from pyro.analysis import costfunction -from pyro.planning import dynamicprogramming -from pyro.planning import discretizer - -sys = pendulum.SinglePendulum() - -sys.x_ub = np.array([+6, +6]) -sys.x_lb = np.array([-9, -6]) - -# Discrete world -grid_sys = discretizer.GridDynamicSystem( sys , [101,101] , [11] ) - -# Cost Function -qcf = costfunction.QuadraticCostFunction.from_sys(sys) - -qcf.xbar = np.array([ -3.14 , 0 ]) # target -qcf.INF = 300 - -qcf.S[0,0] = 10.0 -qcf.S[1,1] = 10.0 - - -# DP algo -dp = dynamicprogramming.DynamicProgrammingWithLookUpTable( grid_sys, qcf) - -dp.solve_bellman_equation( tol = 0.1 ) -dp.plot_cost2go() - -# Get optimal ctl -ctl = dp.get_lookup_table_controller() - - -# Evaluate on same grid -#evaluator = dprog.PolicyEvaluatorWithLookUpTable(ctl, grid_sys, qcf) -#evaluator.solve_bellman_equation() -#evaluator.plot_cost2go() - -# Evaluate on new grid -grid_sys2 = discretizer.GridDynamicSystem( sys , [301,301] , [11] , ) - -evaluator2 = dynamicprogramming.PolicyEvaluatorWithLookUpTable(ctl, grid_sys2, qcf) -evaluator2.solve_bellman_equation() -evaluator2.plot_cost2go() - - -# Evaluate on new grid - -# Raise the max torque to avoid hitting the min-max boundary with interpolation -sys.u_ub[0] = +10 -sys.u_lb[0] = -10 - -grid_sys2 = discretizer.GridDynamicSystem( sys , [301,301] , [11] ) - -evaluator2 = dynamicprogramming.PolicyEvaluatorWithLookUpTable(ctl, grid_sys2, qcf) -evaluator2.solve_bellman_equation() -evaluator2.plot_cost2go() diff --git a/dev/reinforcement_learning/dp_tests/test_speed.py b/dev/reinforcement_learning/dp_tests/test_speed.py deleted file mode 100644 index 88eca689..00000000 --- a/dev/reinforcement_learning/dp_tests/test_speed.py +++ /dev/null @@ -1,37 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Created on Sun Oct 16 22:27:47 2022 - -@author: alex -""" - -import numpy as np - -from pyro.dynamic import pendulum -from pyro.control import controller -from pyro.planning import dynamicprogramming as dp -from pyro.planning import discretizer -from pyro.analysis import costfunction - -sys = pendulum.SinglePendulum() - -# Discrete world -grid_sys = discretizer.GridDynamicSystem( sys , [51,51] , [11] ) - -# Cost Function -qcf = costfunction.QuadraticCostFunction.from_sys(sys) - -qcf.xbar = np.array([ -3.14 , 0 ]) # target -qcf.INF = 1000000 - - -# DP algo -dp1 = dp.DynamicProgramming( grid_sys, qcf ) -dp1.compute_steps(10) - -dp2 = dp.DynamicProgrammingWithLookUpTable( grid_sys, qcf) -dp2.compute_steps(10) - -dp4 = dp.DynamicProgramming2DRectBivariateSpline(grid_sys, qcf) -dp4.compute_steps(10) \ No newline at end of file diff --git a/dev/reinforcement_learning/dp_tests/validation_double_pendulum_local_controller.py b/dev/reinforcement_learning/dp_tests/validation_double_pendulum_local_controller.py deleted file mode 100644 index 6c1993f5..00000000 --- a/dev/reinforcement_learning/dp_tests/validation_double_pendulum_local_controller.py +++ /dev/null @@ -1,77 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Created on Sun Oct 16 22:27:47 2022 - -Note: Proof of concept with local convergence - -@author: alex -""" - -import numpy as np - -from pyro.dynamic import pendulum -from pyro.control import controller -from pyro.analysis import costfunction -from pyro.planning import dynamicprogramming -from pyro.planning import discretizer - -sys = pendulum.DoublePendulum() - -sys.I1 = 5 -sys.I2 = 5 - -dx1 = 2.0 -dx2 = 2.0 -ddx1 = 3.0 -ddx2 = 6.0 - -sys.x_ub = np.array([ dx1 , dx2, ddx1 , ddx2 ]) -sys.x_lb = np.array([ -dx1 , -dx2, -ddx1 , -ddx2 ]) - -sys.u_ub = np.array([ 10.0 , 5.0 ]) -sys.u_lb = np.array([ -10.0 , -5.0 ]) - -# Discrete world -grid_sys = discretizer.GridDynamicSystem( sys , [31,31,31,31] , [3,3] , 0.05 ) - -# Cost Function -qcf = costfunction.QuadraticCostFunction.from_sys(sys) - -qcf.xbar = np.array([ 0 , 0, 0 , 0 ]) # target -qcf.INF = 1000 -qcf.EPS = 0.2 - -qcf.Q[0,0] = 0.1 -qcf.Q[1,1] = 0.1 -qcf.Q[2,2] = 0.1 -qcf.Q[3,3] = 0.1 - -qcf.S[0,0] = 100 -qcf.S[1,1] = 100 -qcf.S[2,2] = 100 -qcf.S[3,3] = 100 - - -# DP algo -dp = dynamicprogramming.DynamicProgrammingWithLookUpTable( grid_sys, qcf ) - -dp.plot_cost2go() -#dp.compute_steps( 10 , animate_policy = True ) -dp.compute_steps( 200 ) - - -ctl = dp.get_lookup_table_controller() - - - -#asign controller -cl_sys = controller.ClosedLoopSystem( sys , ctl ) - -############################################################################## - -# Simulation and animation -cl_sys.x0 = np.array([-0.5,0.5,0,0.0]) -cl_sys.compute_trajectory( 10, 10001, 'euler') -cl_sys.plot_trajectory('xu') -cl_sys.animate_simulation() diff --git a/dev/reinforcement_learning/dp_tests/validation_dp_4D_with_two_pendulum.py b/dev/reinforcement_learning/dp_tests/validation_dp_4D_with_two_pendulum.py deleted file mode 100644 index 8035957d..00000000 --- a/dev/reinforcement_learning/dp_tests/validation_dp_4D_with_two_pendulum.py +++ /dev/null @@ -1,57 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Created on Sun Oct 16 22:27:47 2022 - -Note: Is this exemple working or a work in progress??? - -@author: alex -""" - -import numpy as np - -from pyro.dynamic import pendulum -from pyro.control import controller -from pyro.analysis import costfunction -from pyro.planning import dynamicprogramming -from pyro.planning import discretizer - -sys = pendulum.TwoIndependentSinglePendulum() - -sys.x_ub = np.array([ 4.0 , 4.0, 5.0 , 5.0 ]) -sys.x_lb = np.array([ -4.0 , -4.0, -5.0 , -5.0 ]) - -# Discrete world -grid_sys = discretizer.GridDynamicSystem( sys , [41,41,21,21] , [3,3] , 0.2 ) - -# Cost Function -qcf = costfunction.QuadraticCostFunction.from_sys(sys) - -qcf.xbar = np.array([ -3.14 , -3.14, 0 , 0 ]) # target -qcf.INF = 1000000 -qcf.EPS = 2.0 - - -# DP algo -dp = dynamicprogramming.DynamicProgrammingWithLookUpTable( grid_sys, qcf) -#dp = dynamicprogramming .DynamicProgrammingFast2DGrid(grid_sys, qcf) - - -#dp.interpol_method = 'nearest' #12 sec -#dp.interpol_method = 'linear' #18 sec -#dp.interpol_method = 'linear' # - -#dp.plot_dynamic_cost2go = False -#dp.compute_steps(1,True) -dp.compute_steps(80) -#dp.save_latest('test4d_2') - -ctl = dp.get_lookup_table_controller() - -cl_sys = ctl + sys - -cl_sys.x0 = np.array([-0.5,0.1,0,0]) -cl_sys.compute_trajectory( 30, 10001, 'euler') -cl_sys.plot_trajectory('xu') -cl_sys.animate_simulation() - diff --git a/dev/reinforcement_learning/pytorch_tests/pytorch_nn_test.py b/dev/reinforcement_learning/pytorch_tests/pytorch_nn_test.py deleted file mode 100644 index 854add62..00000000 --- a/dev/reinforcement_learning/pytorch_tests/pytorch_nn_test.py +++ /dev/null @@ -1,100 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Created on Mon Apr 3 21:52:17 2023 - -@author: alex -""" - -import torch -import math - -import numpy as np -import matplotlib -import matplotlib.pyplot as plt - - -# Get cpu or gpu device for training. -device = "cuda" if torch.cuda.is_available() else "mps" if torch.backends.mps.is_available() else "cpu" -print(f"Using {device} device") - -show = True - - -class NN(torch.nn.Module): - def __init__(self): - """ - In the constructor we instantiate four parameters and assign them as - member parameters. - """ - super().__init__() - self.linear_relu_stack = torch.nn.Sequential( - torch.nn.Linear(1, 20), - torch.nn.ReLU(), - torch.nn.Linear(20, 20), - torch.nn.ReLU(), - torch.nn.Linear(20, 1) - ) - - self.test_layer = torch.nn.Linear(1, 1) - - def forward(self, x): - - return self.linear_relu_stack( x ) - - - -# Create Tensors to hold input and outputs. -x = torch.linspace(-math.pi*2.0, math.pi*2.0, 4000).reshape(-1,1) -y = torch.sin(x).reshape(-1,1) - -fig = plt.figure(figsize=(4,3), dpi=300) -ax = fig.add_subplot(111, autoscale_on=False ) -ax.grid(True) -ax.tick_params( labelsize = 5) -ax.set_xlim( [-4,4] ) -ax.set_ylim( [-1,1] ) -line1 = ax.plot( x, y, '-b' ) - -if show: - fig.show() - plt.ion() - plt.pause( 0.001 ) - - -# Construct our model by instantiating the class defined above -#model = Polynomial3() -model = NN() - -y_hat = model( x ) - -line2 = ax.plot( x, y_hat.detach().numpy() , '-r' ) - -# Construct our loss function and an Optimizer. The call to model.parameters() -# in the SGD constructor will contain the learnable parameters (defined -# with torch.nn.Parameter) which are members of the model. -criterion = torch.nn.MSELoss(reduction='sum') -optimizer = torch.optim.SGD(model.parameters(), lr=1e-5) -for t in range(1000): - # Forward pass: Compute predicted y by passing x to the model - y_hat = model(x) - - # Compute and print loss - loss = criterion(y_hat, y) - - # Zero gradients, perform a backward pass, and update the weights. - optimizer.zero_grad() - loss.backward() - optimizer.step() - - if t % 100 == 99: - print(t, loss.item()) - if show: - line2[0].set_data( x, y_hat.detach().numpy() ) - plt.pause( 0.001 ) - - - - - - diff --git a/dev/reinforcement_learning/pytorch_tests/pytorch_polynomial_test.py b/dev/reinforcement_learning/pytorch_tests/pytorch_polynomial_test.py deleted file mode 100644 index e0011c72..00000000 --- a/dev/reinforcement_learning/pytorch_tests/pytorch_polynomial_test.py +++ /dev/null @@ -1,107 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Created on Mon Apr 3 21:52:17 2023 - -@author: alex -""" - -import torch -import math - -import numpy as np -import matplotlib -import matplotlib.pyplot as plt - - -# Get cpu or gpu device for training. -device = "cuda" if torch.cuda.is_available() else "mps" if torch.backends.mps.is_available() else "cpu" -print(f"Using {device} device") - -show = True - - -class Polynomial3(torch.nn.Module): - def __init__(self): - """ - In the constructor we instantiate four parameters and assign them as - member parameters. - """ - super().__init__() - self.a = torch.nn.Parameter(torch.randn(())) - self.b = torch.nn.Parameter(torch.randn(())) - self.c = torch.nn.Parameter(torch.randn(())) - self.d = torch.nn.Parameter(torch.randn(())) - - def forward(self, x): - """ - In the forward function we accept a Tensor of input data and we must return - a Tensor of output data. We can use Modules defined in the constructor as - well as arbitrary operators on Tensors. - """ - return self.a + self.b * x + self.c * x ** 2 + self.d * x ** 3 - - def string(self): - """ - Just like any class in Python, you can also define custom method on PyTorch modules - """ - return f'y = {self.a.item()} + {self.b.item()} x + {self.c.item()} x^2 + {self.d.item()} x^3' - - - - - -# Create Tensors to hold input and outputs. -x = torch.linspace(-math.pi*1.0, math.pi*1.0, 4000).reshape(-1,1) -y = torch.sin(x).reshape(-1,1) - -fig = plt.figure(figsize=(4,3), dpi=300) -ax = fig.add_subplot(111, autoscale_on=False ) -ax.grid(True) -ax.tick_params( labelsize = 5) -ax.set_xlim( [-4,4] ) -ax.set_ylim( [-1,1] ) -line1 = ax.plot( x, y, '-b' ) - -if show: - fig.show() - plt.ion() - plt.pause( 0.001 ) - - -# Construct our model by instantiating the class defined above -model = Polynomial3() - -y_hat = model( x ) - -line2 = ax.plot( x, y_hat.detach().numpy() , '-r' ) - -criterion = torch.nn.MSELoss(reduction='sum') -optimizer = torch.optim.SGD(model.parameters(), lr=1e-6) - -for t in range(2000): - # Forward pass: Compute predicted y by passing x to the model - y_hat = model(x) - - # Compute and print loss - loss = criterion(y_hat, y) - - # Zero gradients, perform a backward pass, and update the weights. - optimizer.zero_grad() - loss.backward() - optimizer.step() - - if t % 100 == 99: - print(t, loss.item()) - if show: - line2[0].set_data( x, y_hat.detach().numpy() ) - plt.pause( 0.001 ) - - - -print(f'Result: {model.string()}') - - - - - diff --git a/dev/reinforcement_learning/pytorch_tests/pytorch_polynomial_vs_nn_fit.py b/dev/reinforcement_learning/pytorch_tests/pytorch_polynomial_vs_nn_fit.py deleted file mode 100644 index 966f6ed4..00000000 --- a/dev/reinforcement_learning/pytorch_tests/pytorch_polynomial_vs_nn_fit.py +++ /dev/null @@ -1,143 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Created on Mon Apr 3 21:52:17 2023 - -@author: alex -""" - -import torch -import math - -import numpy as np -import matplotlib -import matplotlib.pyplot as plt - - -# Get cpu or gpu device for training. -device = "cuda" if torch.cuda.is_available() else "mps" if torch.backends.mps.is_available() else "cpu" -print(f"Using {device} device") - -show = True - - -class Polynomial3(torch.nn.Module): - def __init__(self): - """ - In the constructor we instantiate four parameters and assign them as - member parameters. - """ - super().__init__() - self.a = torch.nn.Parameter(torch.randn(())) - self.b = torch.nn.Parameter(torch.randn(())) - self.c = torch.nn.Parameter(torch.randn(())) - self.d = torch.nn.Parameter(torch.randn(())) - #self.e = torch.nn.Parameter(torch.randn(())) - #self.f = torch.nn.Parameter(torch.randn(())) - #self.g = torch.nn.Parameter(torch.randn(())) - - def forward(self, x): - """ - In the forward function we accept a Tensor of input data and we must return - a Tensor of output data. We can use Modules defined in the constructor as - well as arbitrary operators on Tensors. - """ - out = ( self.a + self.b * x + self.c * x ** 2 + self.d * x ** 3 ) - #+ self.e * x ** 4 + self.f * x ** 5 + self.g * x ** 6 ) - - return out - - def string(self): - """ - Just like any class in Python, you can also define custom method on PyTorch modules - """ - return f'y = {self.a.item()} + {self.b.item()} x + {self.c.item()} x^2 + {self.d.item()} x^3' - - -class NN(torch.nn.Module): - def __init__(self): - """ - In the constructor we instantiate four parameters and assign them as - member parameters. - """ - super().__init__() - self.linear_relu_stack = torch.nn.Sequential( - torch.nn.Linear(1, 50), - torch.nn.ReLU(), - torch.nn.Linear(50, 20), - torch.nn.ReLU(), - torch.nn.Linear(20, 1) - ) - - def forward(self, x): - - return self.linear_relu_stack( x ) - - - - -# Create Tensors to hold input and outputs. -period = 1 -x = torch.linspace(-math.pi*period, math.pi*period, 4000).reshape(-1,1) -y = torch.sin(x).reshape(-1,1) - -fig = plt.figure(figsize=(4,3), dpi=300) -ax = fig.add_subplot(111, autoscale_on=False ) -ax.grid(True) -ax.tick_params( labelsize = 5) -ax.set_xlim( [-math.pi*period,math.pi*period] ) -ax.set_ylim( [-1,1] ) -line1 = ax.plot( x, y, '-b' ) - -if show: - fig.show() - plt.ion() - plt.pause( 0.001 ) - - -# Construct our model by instantiating the class defined above -model = Polynomial3() -model2 = NN() - -y_hat = model( x ) -y_hat2 = model2( x ) - -line2 = ax.plot( x, y_hat.detach().numpy() , '-r' ) -line3 = ax.plot( x, y_hat2.detach().numpy() , '--g' ) - -criterion = torch.nn.MSELoss(reduction='sum') -criterion2 = torch.nn.MSELoss(reduction='sum') - -optimizer = torch.optim.SGD(model.parameters(), lr=1e-6) -#optimizer2 = torch.optim.SGD(model2.parameters(), lr=1e-6) -optimizer2 = torch.optim.Adam( model2.parameters(), lr=1e-4 ) - -for t in range(5000): - # Forward pass: Compute predicted y by passing x to the model - y_hat = model(x) - y_hat2 = model2(x) - - # Compute and print loss - loss = criterion(y_hat, y) - loss2 = criterion2(y_hat2, y) - - # Zero gradients, perform a backward pass, and update the weights. - optimizer.zero_grad() - loss.backward() - optimizer.step() - - optimizer2.zero_grad() - loss2.backward() - optimizer2.step() - - if t % 100 == 99: - print(t, loss.item()) - if show: - line2[0].set_data( x, y_hat.detach().numpy() ) - line3[0].set_data( x, y_hat2.detach().numpy() ) - plt.pause( 0.001 ) - - - - - diff --git a/dev/reinforcement_learning/pytorch_tests/pytorch_rl_demo.py b/dev/reinforcement_learning/pytorch_tests/pytorch_rl_demo.py deleted file mode 100644 index 2cc18de6..00000000 --- a/dev/reinforcement_learning/pytorch_tests/pytorch_rl_demo.py +++ /dev/null @@ -1,258 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Created on Tue Apr 4 14:14:37 2023 - -@author: alex -""" - -#import gymnasium as gym -import math -import random -import matplotlib -import numpy as np -import matplotlib.pyplot as plt -from collections import namedtuple, deque -from itertools import count - -import torch -import torch.nn as nn -import torch.optim as optim -import torch.nn.functional as F - - -from pyro.dynamic import cartpole - -sys = cartpole.CartPole() -sys.xbar[1] = np.pi # Up-right position -#env = gym.make("CartPole-v1") - -# set up matplotlib -plt.ion() - -# if gpu is to be used -device = "cuda" if torch.cuda.is_available() else "mps" if torch.backends.mps.is_available() else "cpu" - -device = 'cpu' - -Transition = namedtuple('Transition', - ('state', 'action', 'next_state', 'reward')) - - -class ReplayMemory(object): - - def __init__(self, capacity): - self.memory = deque([], maxlen=capacity) - - def push(self, *args): - """Save a transition""" - self.memory.append(Transition(*args)) - - def sample(self, batch_size): - return random.sample(self.memory, batch_size) - - def __len__(self): - return len(self.memory) - - -class DQN(nn.Module): - - def __init__(self, n_observations, n_actions): - super(DQN, self).__init__() - self.layer1 = nn.Linear(n_observations, 128) - self.layer2 = nn.Linear(128, 128) - self.layer3 = nn.Linear(128, n_actions) - - # Called with either one element to determine next action, or a batch - # during optimization. Returns tensor([[left0exp,right0exp]...]). - def forward(self, x): - x = F.relu(self.layer1(x)) - x = F.relu(self.layer2(x)) - return self.layer3(x) - - -BATCH_SIZE = 128 -GAMMA = 0.99 -EPS_START = 0.9 -EPS_END = 0.05 -EPS_DECAY = 1000 -TAU = 0.005 -LR = 1e-4 - -# Get number of actions from gym action space -n_actions = 3 -# Get the number of state observations -n_observations = sys.n - -policy_net = DQN(n_observations, n_actions).to(device) -target_net = DQN(n_observations, n_actions).to(device) -target_net.load_state_dict(policy_net.state_dict()) - -optimizer = optim.AdamW(policy_net.parameters(), lr=LR, amsgrad=True) -memory = ReplayMemory(10000) - - -steps_done = 0 - - -def select_action(state): - global steps_done - sample = random.random() - eps_threshold = EPS_END + (EPS_START - EPS_END) * \ - math.exp(-1. * steps_done / EPS_DECAY) - steps_done += 1 - if sample > eps_threshold: - with torch.no_grad(): - # t.max(1) will return the largest column value of each row. - # second column on max result is index of where max element was - # found, so we pick action with the larger expected reward. - return policy_net(state).max(1)[1].view(1, 1) - else: - return torch.tensor([[ int(np.random.uniform( 0 , 3)) ]], device=device, dtype=torch.long) - - -episode_durations = [] - - -def plot_durations(show_result=False): - plt.figure(1) - durations_t = torch.tensor(episode_durations, dtype=torch.float) - if show_result: - plt.title('Result') - else: - plt.clf() - plt.title('Training...') - plt.xlabel('Episode') - plt.ylabel('Duration') - plt.plot(durations_t.numpy()) - # Take 100 episode averages and plot them too - if len(durations_t) >= 100: - means = durations_t.unfold(0, 100, 1).mean(1).view(-1) - means = torch.cat((torch.zeros(99), means)) - plt.plot(means.numpy()) - - plt.pause(0.001) # pause a bit so that plots are updated - - -def optimize_model(): - if len(memory) < BATCH_SIZE: - return - transitions = memory.sample(BATCH_SIZE) - # Transpose the batch (see https://stackoverflow.com/a/19343/3343043 for - # detailed explanation). This converts batch-array of Transitions - # to Transition of batch-arrays. - batch = Transition(*zip(*transitions)) - - # Compute a mask of non-final states and concatenate the batch elements - # (a final state would've been the one after which simulation ended) - non_final_mask = torch.tensor(tuple(map(lambda s: s is not None, - batch.next_state)), device=device, dtype=torch.bool) - non_final_next_states = torch.cat([s for s in batch.next_state - if s is not None]) - state_batch = torch.cat(batch.state) - action_batch = torch.cat(batch.action) - reward_batch = torch.cat(batch.reward) - - # Compute Q(s_t, a) - the model computes Q(s_t), then we select the - # columns of actions taken. These are the actions which would've been taken - # for each batch state according to policy_net - state_action_values = policy_net(state_batch).gather(1, action_batch) - - # Compute V(s_{t+1}) for all next states. - # Expected values of actions for non_final_next_states are computed based - # on the "older" target_net; selecting their best reward with max(1)[0]. - # This is merged based on the mask, such that we'll have either the expected - # state value or 0 in case the state was final. - next_state_values = torch.zeros(BATCH_SIZE, device=device) - with torch.no_grad(): - next_state_values[non_final_mask] = target_net(non_final_next_states).max(1)[0] - # Compute the expected Q values - expected_state_action_values = (next_state_values * GAMMA) + reward_batch - - # Compute Huber loss - criterion = nn.SmoothL1Loss() - loss = criterion(state_action_values, expected_state_action_values.unsqueeze(1)) - - # Optimize the model - optimizer.zero_grad() - loss.backward() - # In-place gradient clipping - torch.nn.utils.clip_grad_value_(policy_net.parameters(), 100) - optimizer.step() - - - -num_episodes = 600 - -for i_episode in range(num_episodes): - # Initialize the environment and get it's state - - start = sys.x0 - - start[1] = np.random.uniform( 1, -4 ) - - state = start - - state = torch.tensor(state, dtype=torch.float32, device=device).unsqueeze(0) - - x = state.numpy()[0] - - for t in count(): - - action = select_action( state ) - - if action == 0: - - u = np.array([ 20. ]) - - elif action ==1: - - u = np.array([ -20 ]) - - else: - - u = np.array([ 0. ]) - - dx = sys.f( x , u ) - - x_next = dx * 0.1 + x - - reward = -sys.cost_function.g( x , u , 0 ) - observation = x_next - truncated = (not sys.isavalidstate( x_next ) ) or ( t > 199 ) - terminated = np.linalg.norm(sys.xbar - sys.x0) < 0.5 - - reward = torch.tensor([reward], device=device) - done = terminated or truncated - - if terminated: - next_state = None - else: - next_state = torch.tensor(observation, dtype=torch.float32, device=device).unsqueeze(0) - - # Store the transition in memory - memory.push(state, action, next_state, reward) - - # Move to the next state - state = next_state - - # Perform one step of the optimization (on the policy network) - optimize_model() - - # Soft update of the target network's weights - # θ′ ← τ θ + (1 −τ )θ′ - target_net_state_dict = target_net.state_dict() - policy_net_state_dict = policy_net.state_dict() - for key in policy_net_state_dict: - target_net_state_dict[key] = policy_net_state_dict[key]*TAU + target_net_state_dict[key]*(1-TAU) - target_net.load_state_dict(target_net_state_dict) - - if done: - episode_durations.append(t + 1) - plot_durations() - break - -print('Complete') -plot_durations(show_result=True) -plt.ioff() -plt.show() \ No newline at end of file diff --git a/dev/reinforcement_learning/pytorch_tests/pytorch_rl_demo_2.py b/dev/reinforcement_learning/pytorch_tests/pytorch_rl_demo_2.py deleted file mode 100644 index 10d98fa5..00000000 --- a/dev/reinforcement_learning/pytorch_tests/pytorch_rl_demo_2.py +++ /dev/null @@ -1,258 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Created on Tue Apr 4 14:14:37 2023 - -@author: alex -""" - -#import gymnasium as gym -import math -import random -import matplotlib -import numpy as np -import matplotlib.pyplot as plt -from collections import namedtuple, deque -from itertools import count - -import torch -import torch.nn as nn -import torch.optim as optim -import torch.nn.functional as F - - -from pyro.dynamic import pendulum - -sys = pendulum.SinglePendulum() #TODO -sys.xbar[0] = 3.14 -#env = gym.make("CartPole-v1") - -# set up matplotlib -plt.ion() - -# if gpu is to be used -device = "cuda" if torch.cuda.is_available() else "mps" if torch.backends.mps.is_available() else "cpu" - -device = 'cpu' - -Transition = namedtuple('Transition', - ('state', 'action', 'next_state', 'reward')) - - -class ReplayMemory(object): - - def __init__(self, capacity): - self.memory = deque([], maxlen=capacity) - - def push(self, *args): - """Save a transition""" - self.memory.append(Transition(*args)) - - def sample(self, batch_size): - return random.sample(self.memory, batch_size) - - def __len__(self): - return len(self.memory) - - -class DQN(nn.Module): - - def __init__(self, n_observations, n_actions): - super(DQN, self).__init__() - self.layer1 = nn.Linear(n_observations, 128) - self.layer2 = nn.Linear(128, 128) - self.layer3 = nn.Linear(128, n_actions) - - # Called with either one element to determine next action, or a batch - # during optimization. Returns tensor([[left0exp,right0exp]...]). - def forward(self, x): - x = F.relu(self.layer1(x)) - x = F.relu(self.layer2(x)) - return self.layer3(x) - - -BATCH_SIZE = 128 -GAMMA = 0.99 -EPS_START = 0.9 -EPS_END = 0.05 -EPS_DECAY = 1000 -TAU = 0.005 -LR = 1e-4 - -# Get number of actions from gym action space -n_actions = 3 -# Get the number of state observations -n_observations = sys.n - -policy_net = DQN(n_observations, n_actions).to(device) -target_net = DQN(n_observations, n_actions).to(device) -target_net.load_state_dict(policy_net.state_dict()) - -optimizer = optim.AdamW(policy_net.parameters(), lr=LR, amsgrad=True) -memory = ReplayMemory(10000) - - -steps_done = 0 - - -def select_action(state): - global steps_done - sample = random.random() - eps_threshold = EPS_END + (EPS_START - EPS_END) * \ - math.exp(-1. * steps_done / EPS_DECAY) - steps_done += 1 - if sample > eps_threshold: - with torch.no_grad(): - # t.max(1) will return the largest column value of each row. - # second column on max result is index of where max element was - # found, so we pick action with the larger expected reward. - return policy_net(state).max(1)[1].view(1, 1) - else: - return torch.tensor([[ int(np.random.uniform( 0 , 3)) ]], device=device, dtype=torch.long) - - -episode_durations = [] - - -def plot_durations(show_result=False): - plt.figure(1) - durations_t = torch.tensor(episode_durations, dtype=torch.float) - if show_result: - plt.title('Result') - else: - plt.clf() - plt.title('Training...') - plt.xlabel('Episode') - plt.ylabel('Duration') - plt.plot(durations_t.numpy()) - # Take 100 episode averages and plot them too - if len(durations_t) >= 100: - means = durations_t.unfold(0, 100, 1).mean(1).view(-1) - means = torch.cat((torch.zeros(99), means)) - plt.plot(means.numpy()) - - plt.pause(0.001) # pause a bit so that plots are updated - - -def optimize_model(): - if len(memory) < BATCH_SIZE: - return - transitions = memory.sample(BATCH_SIZE) - # Transpose the batch (see https://stackoverflow.com/a/19343/3343043 for - # detailed explanation). This converts batch-array of Transitions - # to Transition of batch-arrays. - batch = Transition(*zip(*transitions)) - - # Compute a mask of non-final states and concatenate the batch elements - # (a final state would've been the one after which simulation ended) - non_final_mask = torch.tensor(tuple(map(lambda s: s is not None, - batch.next_state)), device=device, dtype=torch.bool) - non_final_next_states = torch.cat([s for s in batch.next_state - if s is not None]) - state_batch = torch.cat(batch.state) - action_batch = torch.cat(batch.action) - reward_batch = torch.cat(batch.reward) - - # Compute Q(s_t, a) - the model computes Q(s_t), then we select the - # columns of actions taken. These are the actions which would've been taken - # for each batch state according to policy_net - state_action_values = policy_net(state_batch).gather(1, action_batch) - - # Compute V(s_{t+1}) for all next states. - # Expected values of actions for non_final_next_states are computed based - # on the "older" target_net; selecting their best reward with max(1)[0]. - # This is merged based on the mask, such that we'll have either the expected - # state value or 0 in case the state was final. - next_state_values = torch.zeros(BATCH_SIZE, device=device) - with torch.no_grad(): - next_state_values[non_final_mask] = target_net(non_final_next_states).max(1)[0] - # Compute the expected Q values - expected_state_action_values = (next_state_values * GAMMA) + reward_batch - - # Compute Huber loss - criterion = nn.SmoothL1Loss() - loss = criterion(state_action_values, expected_state_action_values.unsqueeze(1)) - - # Optimize the model - optimizer.zero_grad() - loss.backward() - # In-place gradient clipping - torch.nn.utils.clip_grad_value_(policy_net.parameters(), 100) - optimizer.step() - - - -num_episodes = 600 - -for i_episode in range(num_episodes): - # Initialize the environment and get it's state - - start = sys.x0 - - start[0] = np.random.uniform( 3. , -1. ) - - state = start - - state = torch.tensor(state, dtype=torch.float32, device=device).unsqueeze(0) - - x = state.numpy()[0] - - for t in count(): - - action = select_action( state ) - - if action == 0: - - u = np.array([ 10. ]) - - elif action ==1: - - u = np.array([ -10. ]) - - else: - - u = np.array([ 0. ]) - - dx = sys.f( x , u ) - - x_next = dx * 0.1 + x - - reward = 10 - sys.cost_function.g( x , u , 0 ) - observation = x_next - truncated = (not sys.isavalidstate( x_next ) ) or ( t > 99 ) - terminated = np.linalg.norm(sys.xbar - sys.x0) < 0.5 - - reward = torch.tensor([reward], device=device) - done = terminated or truncated - - if terminated: - next_state = None - else: - next_state = torch.tensor(observation, dtype=torch.float32, device=device).unsqueeze(0) - - # Store the transition in memory - memory.push(state, action, next_state, reward) - - # Move to the next state - state = next_state - - # Perform one step of the optimization (on the policy network) - optimize_model() - - # Soft update of the target network's weights - # θ′ ← τ θ + (1 −τ )θ′ - target_net_state_dict = target_net.state_dict() - policy_net_state_dict = policy_net.state_dict() - for key in policy_net_state_dict: - target_net_state_dict[key] = policy_net_state_dict[key]*TAU + target_net_state_dict[key]*(1-TAU) - target_net.load_state_dict(target_net_state_dict) - - if done: - episode_durations.append(t + 1) - plot_durations() - break - -print('Complete') -plot_durations(show_result=True) -plt.ioff() -plt.show() \ No newline at end of file diff --git a/dev/reinforcement_learning/rl_tests/pendulum_test_rl.py b/dev/reinforcement_learning/rl_tests/pendulum_test_rl.py deleted file mode 100644 index a60ee8e2..00000000 --- a/dev/reinforcement_learning/rl_tests/pendulum_test_rl.py +++ /dev/null @@ -1,50 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Created on Sun Oct 16 22:27:47 2022 - -@author: alex -""" - -import numpy as np - -from pyro.dynamic import pendulum -from pyro.control import controller -from pyro.analysis import costfunction -from pyro.planning import dynamicprogramming -from pyro.planning import discretizer - -from reinforcementlearning import QLearning - -sys = pendulum.SinglePendulum() - -sys.x_ub[0] = 4.0 -sys.x_lb[0] = -7.0 -sys.x_lb[1] = -6.0 -sys.x_ub[1] = 6.0 - -# Discrete world -grid_sys = discretizer.GridDynamicSystem( sys , [101,101] , [3] , 0.1) - -# Cost Function -qcf = costfunction.QuadraticCostFunction.from_sys(sys) - -qcf.xbar = np.array([ -3.14 , 0 ]) # target -qcf.INF = 300 - -qcf.R[0,0] = 1.0 - -qcf.S[0,0] = 10.0 -qcf.S[1,1] = 10.0 - - -# DP algo -dp = QLearning( grid_sys , qcf ) - -dp.compute_J_from_Q() - -dp.compute_policy_from_Q() - -dp.compute_episodes(1000) - -dp.plot_cost2go() \ No newline at end of file diff --git a/dev/reinforcement_learning/rl_tests/reinforcementlearning.py b/dev/reinforcement_learning/rl_tests/reinforcementlearning.py deleted file mode 100644 index 3dcfd188..00000000 --- a/dev/reinforcement_learning/rl_tests/reinforcementlearning.py +++ /dev/null @@ -1 +0,0 @@ -#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ Created on Wed Oct 12 20:13:17 2022 @author: alex """ import numpy as np import matplotlib.pyplot as plt from scipy.interpolate import RectBivariateSpline as interpol2D from scipy.interpolate import RegularGridInterpolator as rgi from pyro.analysis import costfunction from pyro.planning import dynamicprogramming from pyro.planning import discretizer ################################################################## class EpsilonGreedyController( dynamicprogramming.LookUpTableController ): ############################ def __init__(self, grid_sys , pi_star , epsilon = 0.7 ): """ Pyro controller based on a discretized lookpup table of optimal control inputs where the optimal action is taken with probability espsilon, else a random action is taken. Parameters ---------- grid_sys : pyro GridDynamicSystem class A discretized dynamic system pi_star : numpy array, dim = self.grid_sys.nodes_n , dtype = int A list of optimal action index for each node id """ super().__init__( grid_sys , pi_star ) self.name = 'Epsilon Greedy Controller' self.epsilon = epsilon ############################# def c( self , y , r , t = 0 ): """ State feedback (y=x) - no reference - time independent """ x = y if np.random.uniform(0,1) < self.epsilon: # greedy behavior u = self.lookup_table_selection( x ) else: # Random exploration random_index = int(np.random.uniform( 0 , self.grid_sys.actions_n )) u = self.grid_sys.input_from_action_id[ random_index ] # TODO add domain check for random actions? return u ############################################################################### ### RL algo ############################################################################### class QLearning( dynamicprogramming.DynamicProgramming ): """ """ ############################ def __init__(self, grid_sys , cost_function ): # Dynamic system self.grid_sys = grid_sys # Discretized Dynamic system class self.sys = grid_sys.sys # Base Dynamic system class # Cost function self.cf = cost_function self.dt = self.grid_sys.dt self.t = 0 # Q Learning Parameters self.alpha = 0.99 # Discount factor self.eta = 0.8 # Learning rate self.interpol_method ='linear' # "linear”, “nearest”, “slinear”, “cubic”, and “quintic” self.initialize() # Trainer self.trajectories = [] # COntroller self.eps = 0.7 ############################## def initialize(self): """ initialize Q values """ self.J = np.zeros( self.grid_sys.nodes_n , dtype = float ) self.pi = np.zeros( self.grid_sys.nodes_n , dtype = int ) self.Q = np.zeros( ( self.grid_sys.nodes_n , self.grid_sys.actions_n ) , dtype = float ) # Initial cost-to-go evaluation for s in range( self.grid_sys.nodes_n ): x = self.grid_sys.state_from_node_id[ s , : ] # Final Cost of all states self.J[ s ] = self.cf.h( x , self.t ) # For all control actions for a in range( self.grid_sys.actions_n ): # Set all Q values to the terminal cost self.Q[ s , a ] = self.J[ s ] ############################## def compute_J_from_Q(self): """ update the J table from Q values """ for s in range( self.grid_sys.nodes_n ): self.J[ s ] = self.Q[s,:].min() # Create interpol function self.J_interpol = self.grid_sys.compute_interpolation_function( self.J , self.interpol_method , bounds_error = False , fill_value = 0 ) ############################## def compute_policy_from_Q(self): """ update the J table from Q values """ for s in range( self.grid_sys.nodes_n ): self.pi[ s ] = self.Q[s,:].argmin() ############################################### def Q_update(self, x , u , x_next ): """ """ s = self.grid_sys.get_nearest_node_id_from_state( x ) a = self.grid_sys.get_nearest_action_id_from_input( u ) if self.sys.isavalidstate( x_next ): J_next = self.J_interpol( x_next ) Q_sample = self.cf.g( x , u, self.t ) * self.dt + self.alpha * J_next else: Q_sample = self.cf.INF # Q update error = Q_sample - self.Q[ s , a ] self.Q[ s , a ] = self.Q[ s , a ] + self.eta * error ############################################### def learn_from_traj( self, traj , pass_number = 1 ): steps = traj.x.shape[0] - 1 for p in range(pass_number): self.compute_J_from_Q() for i in range(steps-1,-1,-1): x = traj.x[i,:] u = traj.u[i,:] x_next = traj.x[i+1,:] self.Q_update( x , u , x_next ) ############################################### def generate_traj( self ): self.compute_policy_from_Q() ctl = EpsilonGreedyController( self.grid_sys, self.pi , self.eps ) cl_sys = ctl + self.sys tf = 10 n = int( tf / self.dt ) cl_sys.x0 = np.random.uniform(-1,1, self.sys.n ) cl_sys.compute_trajectory( tf , n , 'euler') return cl_sys.traj ############################################### def compute_episodes( self, n = 1 ): for i in range(n): print('Episode:',i) new_traj = self.generate_traj() self.learn_from_traj( new_traj ) #self.trajectories.append(new_traj) #for traj in self.trajectories: # self.learn_from_traj( traj , 10 ) ############################################### def show_episode( self ): self.compute_policy_from_Q() ctl = EpsilonGreedyController( self.grid_sys, self.pi , 1.0 ) cl_sys = ctl + self.sys tf = 10 cl_sys.x0 = np.random.uniform(-1,1, self.sys.n ) cl_sys.compute_trajectory( tf , 10001 ) cl_sys.plot_trajectory('xu') cl_sys.animate_simulation() \ No newline at end of file diff --git a/dev/rigidbody/rocket2.py b/dev/rigidbody/rocket2.py deleted file mode 100644 index 7ca335e2..00000000 --- a/dev/rigidbody/rocket2.py +++ /dev/null @@ -1,252 +0,0 @@ -# -*- coding: utf-8 -*- - -############################################################################### -import numpy as np -############################################################################### -from pyro.dynamic import system -from pyro.kinematic import geometry -from pyro.kinematic import drawing -############################################################################### - -from rigidbody import RigidBody2D - -############################################################################## - -############################################################################### - -class Rocket2D( RigidBody2D ): - """ - - """ - - ############################ - def __init__(self): - """ """ - - RigidBody2D.__init__( self , force_inputs = 1, other_inputs = 1) - - self.input_label = ['T','delta'] - self.input_units = ['[N]','[rad]'] - - # State working range - self.x_ub = np.array([+50,+100,+2,10,10,10]) - self.x_lb = np.array([-50,-0,-2,-10,-10,-10]) - - self.u_ub = np.array([+5000,+1]) - self.u_lb = np.array([+100,-1]) - - # Dynamic properties - self.mass = 1000.0 - self.inertia = 1000.0 - self.l_t = 1.0 # Distance between CG and Thrust - - self.gravity = 9.8 - - self.damping_coef = np.array([ [ 1.0, 1.0, 1.0 ] , - [ 1.0, 1.0, 1.0 ] ]) - - # Kinematic param - self.width = 0.2 - self.height = 2.0 - - # rocket drawing - - # Graphic output parameters - self.dynamic_domain = True - self.dynamic_range = 10 - - pts = np.zeros(( 10 , 3 )) - l = self.height - w = self.width - - pts[0,:] = np.array([ -l, 0,0]) - pts[1,:] = np.array([-l, -w,0]) - pts[2,:] = np.array([+l, -w,0]) - pts[3,:] = np.array([l+w,0,0]) - pts[4,:] = np.array([+l, +w,0]) - pts[5,:] = np.array([-l, +w,0]) - pts[6,:] = pts[0,:] - pts[7,:] = pts[0,:] + np.array([-w,-w,0]) - pts[8,:] = pts[0,:] + np.array([-w,+w,0]) - pts[9,:] = pts[0,:] - - self.drawing_body_pts = pts - - ########################################################################### - def B(self, q , u ): - """ - Actuator Matrix : dof x m - """ - - B = np.zeros((3,1)) - - delta = u[1] - - B[0,0] = np.cos( delta ) - B[1,0] = np.sin( delta ) - B[2,0] = - self.l_t * np.sin( delta ) - - return B - - - ########################################################################### - def g(self, q ): - """ - Gravitationnal forces vector : dof x 1 - """ - - # Gravity in inertial frame - g_a = np.zeros( self.dof ) - g_a[1] = self.mass * self.gravity - - # Gravity in body frame - g_body = self.N( q ).T @ g_a - - return g_body - - - ########################################################################### - def d(self, q , v , u ): - """ - State-dependent dissipative forces : dof x 1 - """ - - d = np.zeros(self.dof ) - - C = self.damping_coef - - # quadratic + linear damping based on 6 coefficients - d = v*np.abs(v) * C[0,:] + v * C[1,:] - - return d - - ########################################################################### - def forward_kinematic_domain(self, q ): - - l = self.height * 3 - - x = q[0] - y = q[1] - z = 0 - - if self.dynamic_domain: - - domain = [ ( -l + x , l + x ) , - ( -l + y , l + y ) , - ( -l + z , l + z ) ]# - else: - - domain = [ ( -l , l ) , - ( -l , l ) , - ( -l , l ) ]# - - return domain - - - ########################################################################### - def forward_kinematic_lines(self, q ): - - lines_pts = [] # list of array (n_pts x 3) for each lines - lines_style = [] - lines_color = [] - - ############################### - # ground line - ############################### - - pts = np.zeros(( 2 , 3 )) - pts[0,:] = np.array([-10,0,0]) - pts[1,:] = np.array([+10,0,0]) - - lines_pts.append( pts ) - lines_style.append( '--') - lines_color.append( 'k' ) - - ########################### - # body - ########################### - - x = q[0] - y = q[1] - theta = q[2] - - W_T_B = geometry.transformation_matrix_2D( theta , x , y ) - - pts_B = self.drawing_body_pts - pts_W = drawing.transform_points_2D( W_T_B , pts_B ) - - lines_pts.append( pts_W ) - lines_style.append( '-') - lines_color.append( 'b' ) - - ########################### - # C.G. - ########################### - - pts = np.zeros(( 1 , 3 )) - pts[0,:] = np.array([x,y,0]) - - lines_pts.append( pts ) - lines_style.append( 'o') - lines_color.append( 'b' ) - - return lines_pts , lines_style , lines_color - - ########################################################################### - def forward_kinematic_lines_plus(self, x , u , t ): - """ - show trust vectors - - - """ - - lines_pts = [] # list of array (n_pts x 3) for each lines - lines_style = [] - lines_color = [] - - ########################### - # trust force vector - ########################### - - length = u[0] / self.u_ub[0] * self.height - delta = u[1] - offset = -self.height - self.width - - pts_body = drawing.arrow_from_length_angle( length, delta, x = offset, origin = 'tip' ) - W_T_B = geometry.transformation_matrix_2D( x[2], x[0] , x[1] ) - pts_W = drawing.transform_points_2D( W_T_B , pts_body ) - - lines_pts.append( pts_W ) - lines_style.append( '-') - lines_color.append( 'r' ) - - return lines_pts , lines_style , lines_color - - -''' -################################################################# -################## Main ######## -################################################################# -''' - - -if __name__ == "__main__": - """ MAIN TEST """ - - sys = Rocket2D() - - sys.gravity = 9.81 - - sys.x0[0] = 0 - sys.x0[1] = 0 - sys.x0[2] = np.pi/2 - - sys.x0[3] = 0.0 - sys.x0[4] = 0.0 - sys.x0[5] = 0.0 - - sys.ubar[0] = sys.gravity * sys.mass * 1.1 - sys.ubar[1] = -0.01 - - sys.plot_trajectory('xu') - sys.animate_simulation() \ No newline at end of file diff --git a/dev/tests/old_tests/saveloadtest.py b/dev/tests/old_tests/saveloadtest.py deleted file mode 100755 index 281469ba..00000000 --- a/dev/tests/old_tests/saveloadtest.py +++ /dev/null @@ -1,18 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Created on Sat Mar 21 14:25:38 2020 - -@author: alex -""" - -import numpy as np -from pyro.dynamic import pendulum -from pyro.control import nonlinear -from pyro.analysis import simulation - -sys = pendulum.DoublePendulum() - -sys.x0 = np.array([0.0,1.,1.,0.]) -sys.compute_trajectory() - diff --git a/dev/tests/old_tests/test_all_examples.py b/dev/tests/old_tests/test_all_examples.py deleted file mode 100644 index 580f674f..00000000 --- a/dev/tests/old_tests/test_all_examples.py +++ /dev/null @@ -1,128 +0,0 @@ -""" -Run all examples - -Does not check for correct results, only that there are no exceptions. - -Running this: - -* Using pytest: `pytest -ra examples/` (replace examples with path to the - examples folder where this script lives). Pytest will produce a report - of failed/succeeded tests - -* As a script: `python ./examples/test_all_examples.py`. Will stop at the - exception. - -TODO: Add actual tests that check for correct results - -""" - -from pathlib import Path - -from importlib.util import spec_from_file_location, module_from_spec - -import sys - -import inspect - -from matplotlib import pyplot as plt - -_all_examples = [ - "./bicycle/bicycle.py", - "./bicycle/bicycle_exploration_with_rrt.py", - "./bicycle/bicycle_parallel_parking_with_rrt.py", - "./cartpole/cartpole_natural_behavior.py", - "./cartpole/cartpole_with_computed_torque.py", - "./cartpole/cartpole_with_rrt_and_computed_torque.py", - "./cartpole/underactuated_cartpole_swingup.py", - "./cartpole/underactuated_cartpole_with_partialfeedbacklinearization.py", - "./cartpole/underactuated_cartpole_with_rrt.py", - "./double_pendulum/double_pendulum.py", - "./double_pendulum/double_pendulum_with_computed_torque.py", - "./double_pendulum/double_pendulum_with_rrt.py", - "./double_pendulum/double_pendulum_with_rrt_and_computed_torque.py", - "./double_pendulum/double_pendulum_with_sliding_mode.py", - "./double_pendulum/double_pendulum_with_trajectory_following_computed_torque.py", - "./double_pendulum/double_pendulum_with_trajectory_following_open_loop_controller.py", - "./double_pendulum/double_pendulum_with_trajectory_following_sliding_mode_controller.py", - "./holonomic_mobile_robot/holonomic_mobile_robot_exploration_with_obstacles_with_rrt.py", - "./holonomic_mobile_robot/holonomic_mobile_robot_exploration_with_rrt.py", - "./holonomic_mobile_robot/holonomic_mobile_robot_with_valueiteration.py", - "./integrators/double_integrator.py", - "./integrators/double_integrator_optimal_controller.py", - "./integrators/integrators_with_closed_loops.py", - "./integrators/simple_integrator.py", - "./integrators/triple_integrator.py", - "./robot_arms/fivelinkrobot_kinematic_controller.py", - "./robot_arms/threelinkrobot_computed_torque_controller.py", - "./robot_arms/threelinkrobot_kinematic_controller.py", - "./robot_arms/twolinkrobot_computed_torque_controller.py", - "./robot_arms/twolinkrobot_effector_impedance_controller.py", - "./robot_arms/twolinkrobot_joint_impedance_controller.py", - "./robot_arms/twolinkrobot_kinematic_controller.py", - "./robot_arms/twolinkrobot_kinematic_vs_dynamic_openloop.py", - "./robot_arms/twolinkrobot_quasi_static_controllers.py", - "./simple_pendulum/custom_simple_pendulum.py", - "./simple_pendulum/simple_pendulum_with_computed_torque.py", - "./simple_pendulum/simple_pendulum_with_open_loop_controller.py", - "./simple_pendulum/simple_pendulum_with_pid.py", - "./simple_pendulum/simple_pendulum_with_rrt.py", - "./simple_pendulum/simple_pendulum_with_sliding_mode_controller.py", - "./simple_pendulum/simple_pendulum_with_trajectory_following_computed_torque.py", - "./simple_pendulum/simple_pendulum_with_trajectory_following_sliding_mode_controller.py", - "./simple_pendulum/simple_pendulum_with_valueiteration.py", -] - -this_script_dir = Path(__file__).parent -this_module = sys.modules[__name__] -#print(_all_examples) - -def import_from_file(modulename, filepath): - """import a file as a module and return the module object - - Everything will be executed, except if it's conditional to - __name__ == "__main__" - - """ - - spec = spec_from_file_location(modulename, filepath) - mod = module_from_spec(spec) - spec.loader.exec_module(mod) - - return mod - -def gettestfunc(modname, fullpath): - """Create a function that imports a file and runs the main function - - """ - - def run_example_main(): - ex_module = import_from_file(modname, fullpath) - - # Call function `main()` if it exists - if hasattr(ex_module, "main"): - ex_main_fun = getattr(ex_module, "main") - ex_main_fun() - - # Close all figs to reclaim memory - plt.close('all') - - return run_example_main - -_all_test_funs = [] - -for example_file in _all_examples: - relpath = Path(example_file) - fullpath = this_script_dir.joinpath(relpath) - modname = relpath.stem # file name without extension - - # Define a new function with a name starting with test_ so it is ran - # by pytest. - setattr(this_module, "test_" + modname, - gettestfunc(modname, fullpath)) - -def main(): - for fun in _all_test_funs: - fun() - -if __name__ == "__main__": - main() \ No newline at end of file diff --git a/dev/tests/old_tests/test_all_pendulums.py b/dev/tests/old_tests/test_all_pendulums.py deleted file mode 100644 index 5a585e2b..00000000 --- a/dev/tests/old_tests/test_all_pendulums.py +++ /dev/null @@ -1,94 +0,0 @@ -""" -Run all examples - -Does not check for correct results, only that there are no exceptions. - -Running this: - -* Using pytest: `pytest -ra examples/` (replace examples with path to the - examples folder where this script lives). Pytest will produce a report - of failed/succeeded tests - -* As a script: `python ./examples/test_all_examples.py`. Will stop at the - exception. - -TODO: Add actual tests that check for correct results - -""" - -from pathlib import Path - -from importlib.util import spec_from_file_location, module_from_spec - -import sys - -import inspect - -from matplotlib import pyplot as plt - -_all_examples = [ - "./simple_pendulum/custom_simple_pendulum.py", - "./simple_pendulum/simple_pendulum_with_computed_torque.py", - "./simple_pendulum/simple_pendulum_with_open_loop_controller.py", - "./simple_pendulum/simple_pendulum_with_pid.py", - "./simple_pendulum/simple_pendulum_with_rrt.py", - "./simple_pendulum/simple_pendulum_with_sliding_mode_controller.py", - "./simple_pendulum/simple_pendulum_with_trajectory_following_computed_torque.py", - "./simple_pendulum/simple_pendulum_with_trajectory_following_sliding_mode_controller.py", - "./simple_pendulum/simple_pendulum_with_valueiteration.py" -] - -this_script_dir = Path(__file__).parent -this_module = sys.modules[__name__] -#print(_all_examples) - -def import_from_file(modulename, filepath): - """import a file as a module and return the module object - - Everything will be executed, except if it's conditional to - __name__ == "__main__" - - """ - - spec = spec_from_file_location(modulename, filepath) - mod = module_from_spec(spec) - spec.loader.exec_module(mod) - - return mod - -def gettestfunc(modname, fullpath): - """Create a function that imports a file and runs the main function - - """ - - def run_example_main(): - ex_module = import_from_file(modname, fullpath) - - # Call function `main()` if it exists - if hasattr(ex_module, "main"): - ex_main_fun = getattr(ex_module, "main") - ex_main_fun() - - # Close all figs to reclaim memory - plt.close('all') - - return run_example_main - -_all_test_funs = [] - -for example_file in _all_examples: - relpath = Path(example_file) - fullpath = this_script_dir.joinpath(relpath) - modname = relpath.stem # file name without extension - - # Define a new function with a name starting with test_ so it is ran - # by pytest. - setattr(this_module, "test_" + modname, - gettestfunc(modname, fullpath)) - -def main(): - for fun in _all_test_funs: - fun() - -if __name__ == "__main__": - main() \ No newline at end of file diff --git a/dev/tests/old_tests/test_integrators.py b/dev/tests/old_tests/test_integrators.py deleted file mode 100644 index 545c720e..00000000 --- a/dev/tests/old_tests/test_integrators.py +++ /dev/null @@ -1,121 +0,0 @@ - -from collections import namedtuple - -import numpy as np - -import pytest - -from pyro.dynamic import integrator - - -SystemTestCase = namedtuple('SystemTestCase', ['sut', 'x0', 'get_ref_sol']) -SysSolution = namedtuple('SysSolution', ['t', 'x', 'y']) - -# Fixtures - -@pytest.fixture -def double_integ(): - """Generate a DoubleIntegrator CDS and accompanying solution - computed from analytical solution. - """ - - # System under test - sut = integrator.DoubleIntegrator() - sut.ubar = np.array([4.83,]) - - # Initial conditions - x0 = np.array([4.37, -3.74]) - - # Calculate analytical solution - def get_ref_sol(t): - npts = t.shape[0] - x_ref = np.empty((npts, 2)) - x_ref[:, 0] = (0.5 * sut.ubar * t**2) + (x0[1] * t) + x0[0] - x_ref[:, 1] = x0[1] + sut.ubar * t - - y_ref = x_ref[:, 0].reshape((npts, 1)) - - return SysSolution(t, x_ref, y_ref) - - return SystemTestCase(sut, x0, get_ref_sol) - -def test_simple_integrator_constant(): - # Setup simple integrator with constant input - I = integrator.SimpleIntegrator() - I.ubar = np.array([4.13,]) - - # Solution params - tf = 10 - x0 = 15.2 - npts = 100 - - # Reference solution - t_ref = np.linspace(0, tf, npts) - x_ref = (x0 + t_ref * I.ubar).reshape((npts, 1)) - - sim = I.compute_trajectory(x0, tf=tf, n=npts) - - assert(np.allclose(t_ref, sim.t)) - assert(np.allclose(x_ref, sim.x)) - assert(np.allclose(x_ref, sim.y)) - -def test_simple_integrator_ramp(): - """Simple integrator with ramp input""" - I = integrator.SimpleIntegrator() - - # Solution params - tf = 10 - x0 = 39.4 - npts = 100 - - # Ramp input - ramp_cst = np.pi - def u(t): - return np.asarray(t * ramp_cst) - - # Reference solution - t_ref = np.linspace(0, tf, npts) - x_ref = (x0 + 0.5 * ramp_cst * t_ref**2).reshape((npts, 1)) - - sim = I.compute_trajectory(x0, tf=tf, n=npts, u=u) - - assert(np.allclose(t_ref, sim.t)) - assert(np.allclose(x_ref, sim.x)) - assert(np.allclose(x_ref, sim.y)) - -def test_double_integrator_constant_ode(double_integ): - # Solution params - tf = 10 - npts = 10 - t_ref = np.linspace(0, tf, npts) - - # Reference solution - ref_sol = double_integ.get_ref_sol(t_ref) - - # Solution computed by SUT - I = double_integ.sut - sim = I.compute_trajectory(double_integ.x0, tf=tf, n=npts, solver='ode') - - assert(np.allclose(t_ref, sim.t)) - assert(np.allclose(ref_sol.x, sim.x)) - assert(np.allclose(ref_sol.y, sim.y)) - -def test_double_integrator_constant_euler(double_integ): - # Solution params - tf = 10 - npts = 100 - t_ref = np.linspace(0, tf, npts) - - # Reference solution - ref_sol = double_integ.get_ref_sol(t_ref) - - # Solution computed by SUT - I = double_integ.sut - sim = I.compute_trajectory(double_integ.x0, tf=tf, n=npts, solver='euler') - - # Euler's method has low-order convergence, so we tolerate 1% error - atol, rtol = 1, 0.01 - - assert(np.allclose(t_ref, sim.t)) - assert(np.allclose(ref_sol.x, sim.x, atol=atol, rtol=rtol)) - assert(np.allclose(ref_sol.y, sim.y, atol=atol, rtol=rtol)) \ No newline at end of file diff --git a/dev/tests/old_tests/test_pidcontrol.py b/dev/tests/old_tests/test_pidcontrol.py deleted file mode 100644 index 3dc2b948..00000000 --- a/dev/tests/old_tests/test_pidcontrol.py +++ /dev/null @@ -1,187 +0,0 @@ - -import numpy as np - -from scipy import integrate, signal - -import pytest - -from pyro.control.linear import PIDController, ProportionalController - -from pyro.dynamic.system import ContinuousDynamicSystem - -from pyro.dynamic.manipulator import TwoLinkManipulator - -class FirstOrder(ContinuousDynamicSystem): - def __init__(self, tau): - super().__init__(1, 1, 1) - self.tau = tau - - def f(self, x, u, t): - return (u - x) / self.tau - -def step(a, delay=0): - def u(t): - if t < delay: - return 0 - else: - return a - return u - -def filtered_deriv(y, x, tau=0): - """Numerical derivative with optional lowpass filter. - - tau is the filter time constant expressed in same units as x (eg seconds if x is - time). - """ - dy = np.empty(y.shape) - dy[0] = (y[1] - y[0]) / (x[1] - x[0]) - for i in range(1, (dy.shape[0] - 1)): - dy[i] = (y[i+1] - y[i-1]) / (x[i+1] - x[i-1]) - dy[-1] = (y[-1] - y[-2]) / (x[-1] - x[-2]) - - # No filter - if tau <= 0: - return dy - - # sample freq - fs = x.shape[0] / (x[-1] - x[0]) - nyqfreq = fs / 2 - w0 = 1 / (tau*2*np.pi*nyqfreq) - - lowpass = signal.iirfilter(1, w0, btype='lowpass', analog=False) - - # Create initial conditions so y=0 at t=0 - zi = signal.lfiltic(*lowpass, y=[0], x=dy[:1]) - - filtered, _ = signal.lfilter(*lowpass, dy, zi=-zi) - return filtered - -def test_sdof_prop(): - """Test single DOF system with proportional control""" - tau_p = 2 - tf = 1.5 - kp = 20 - - sys = FirstOrder(tau_p) - ctl = ProportionalController(20) - clsys = ctl + sys - - sim = clsys.compute_trajectory(x0=[0], r=step(1), tf=tf, n=100) - - # analytic solution - kcl = kp / (kp + 1) # steady state asymptote value - tau_cl = tau_p / (kp + 1) # Closed loop time constant - x_ref = kcl * (1-np.exp(-sim.t/tau_cl)) - - assert np.allclose(sim.x[:, 0], x_ref) - -def test_sdof_pid(): - """Check PID controller outputs""" - tf = 2 # simulation time - npts = 500 # simulation samples - tau = 1E-2 # time constant of derivative filter - - # Create system/controller and run simulation - sys = FirstOrder(1) - ctl = PIDController([[3]], [[2]], [[0.6]], dv_tau=tau) - clsys = ctl + sys - sim = clsys.compute_trajectory(0, r=step(1), tf=tf, n=npts) - - # error - e = (sim.r - sim.y)[:, 0] - - # integral of error - e_int = integrate.cumtrapz(e, sim.t, initial=0) - - # filtered derivative of error - e_fd = filtered_deriv(e, sim.t, tau) - - # Check controller output - ctl_ref = (e * ctl.KP + e_int * ctl.KI + e_fd * ctl.KD).reshape((sim.n,)) - - assert np.allclose(sim.u[:, 0], ctl_ref, atol=0, rtol=1E-2) - -def test_nd_pid(): - tf = 2 - npts = 500 - tau = 1E-2 - - class DummyManipulator(TwoLinkManipulator): - """Manipulator with null response to inputs""" - def __init__(self): - super().__init__() - self.p = 2 - - def B(self, q): - return np.zeros(self.dof) - - def h(self, x, u, t): - """Read joint positions""" - return x[:2] - - sys = DummyManipulator() - ctl = PIDController( - KP = np.ones((2,2)), - KI = np.ones((2,2)), - KD = np.ones((2,2)), - dv_tau=tau - ) - clsys = ctl + sys - - x0 = [np.pi-0.5, np.pi/2, 0, 0] - sim = clsys.compute_trajectory(x0=x0, tf=tf, n=npts) - - errors = sim.r - sim.y - - e_int = integrate.cumtrapz(errors, sim.t, axis=0, initial=0) - - e_der = np.empty(errors.shape) - for j in range(errors.shape[1]): - e_der[:, j] = filtered_deriv(errors[:, j], sim.t, tau=tau) - - ctl_ref = errors.dot(ctl.KP.T) + e_int.dot(ctl.KI.T) + e_der.dot(ctl.KD.T) - - assert np.allclose(sim.u, ctl_ref, atol=0.05) - -def test_check_and_normalize(): - # Check normalization of scalars to 2D array - ctl = PIDController(1, 1, 1) - for arr in [ctl.KP, ctl.KI, ctl.KD]: - assert arr.ndim == 2 - assert arr.shape == (1, 1) - assert arr[0, 0] == 1 - - # Check normalization of 1D array to 2D array - vals = [1, 2, 3] - ctl = PIDController(vals, vals, vals) - for arr in [ctl.KP, ctl.KI, ctl.KD]: - assert arr.ndim == 2 - assert arr.shape == (1, 3) - assert np.all(vals == arr) - - # Check that 2D arrays should be unchanged - vals = np.ones((3, 4)) * 2.3 - ctl = PIDController(vals, vals, vals) - for arr in [ctl.KP, ctl.KI, ctl.KD]: - assert arr.ndim == 2 - assert arr.shape == vals.shape - assert np.all(vals == arr) - - # Check default zero values for KI and KD - for testval in [1, [1, 2, 3], np.ones((8, 10))]: - ctl = PIDController(testval) - for arr in [ctl.KI, ctl.KD]: - assert arr.shape == ctl.KP.shape - assert np.all(arr == np.zeros(ctl.KP.shape)) - - with pytest.raises(ValueError): - ctl = PIDController(1, KI=np.ones(2)) - with pytest.raises(ValueError): - ctl = PIDController(1, KD=np.ones(2)) - with pytest.raises(ValueError): - ctl = PIDController(np.ones((2, 3)), KI=np.ones((3, 2))) - with pytest.raises(ValueError): - ctl = PIDController(np.ones((2, 3)), KD=np.ones((3, 2))) - -if __name__ == "__main__": - test_check_and_normalize() \ No newline at end of file diff --git a/dev/tests/old_tests/test_state_space.py b/dev/tests/old_tests/test_state_space.py deleted file mode 100644 index 917540c4..00000000 --- a/dev/tests/old_tests/test_state_space.py +++ /dev/null @@ -1,204 +0,0 @@ - -import numpy as np - -import pytest - -from pyro.dynamic import StateSpaceSystem, linearize - -from pyro.dynamic.pendulum import SinglePendulum - -from pyro.dynamic.manipulator import TwoLinkManipulator - -class SdofOscillator(StateSpaceSystem): - """Single DOF mass-spring-damper system - - Defined using state-space matrices - - """ - - def __init__(self, m, k, b): - """ - m: mass - k: spring constant - b: damping constant - """ - - self.mass = m - self.k = k - self.b = b - - A = np.array([[0, 1], [-k/m, -b/m]]) - B = np.array([0, 1/m]).reshape((2, 1)) - - C = np.identity(2) - D = np.zeros((2, 1)) - - super().__init__(A, B, C, D) - - def solve_analytical_free(self, x0, dx0, t): - """ - Free vibration equations from: - Rao (2019) Vibrations of Continuous Systems (2 ed) - eq 2.16 - 2.21 - - x0: initial position - dx0: initial velocity - """ - - # undamped natural frequency - wn = np.sqrt(self.k / self.mass) - - # damping ratio - xi = self.b / 2 / self.mass / wn - - if xi < 1: - wd = np.sqrt(1 - xi**2) * wn - return np.exp(-xi * wn * t) *\ - ((x0 * np.cos(wd * t)) + (((dx0 + xi*wn*x0) / wd) * np.sin(wd*t))) - else: - raise NotImplementedError("Only underdamped implemented") - - def solve_analytical_forced(self, x0, dx0, f0, w0, t): - """ - Solution of *undamped* oscillator with harmonic excitation of the form - - F = f0*cos(w0*t) - - ref: Rao (2019) Vibrations of Continuous Systems (2 ed) - eq 2.27 - """ - - if self.b != 0: - raise NotImplementedError("Only implemented for undamped system") - - wn = np.sqrt(self.k / self.mass) - return (x0 - (f0 / (self.k - self.mass * w0**2))) * np.cos(wn*t) \ - + dx0 / wn * np.sin(wn*t) \ - + (f0 / (self.k - self.mass * w0**2)) * np.cos(w0*t) - -def test_1dof_oscillator_free(): - sys = SdofOscillator(2.0, 100.0, 10.0) - x0 = np.array([0.2, 0]) - - # Simulate time solution - sim = sys.compute_trajectory(x0) - - # Reference analytical time solution - ref = sys.solve_analytical_free(x0[0], x0[1], sim.t) - - assert np.allclose(sim.x[:, 0], ref) - assert np.allclose(sim.y[:, 0], ref) - -def test_1dof_oscillator_forced(): - sys = SdofOscillator(m=2.0, k=100.0, b=0.0) - x0 = np.array([0.2, 0]) - - # External force - w0 = 2 - f0 = 4.5 - def u(t): - t = np.asarray(t) - return (np.cos(w0*t) * f0).reshape((t.size, 1)) - - # Simulate time solution - sim = sys.compute_trajectory(x0, u=u, n=1000) - - # Reference analytical time solution - ref = sys.solve_analytical_forced(x0[0], x0[1], f0, w0, sim.t) - - assert np.allclose(sim.x[:, 0], ref, atol=1e-5, rtol=0) - assert np.allclose(sim.y[:, 0], ref, atol=1e-5, rtol=0) - assert np.allclose(sim.u, u(sim.t)) - -def test_dimension_checks(): - # Valid dimensions for system with 5 states, 2 inputs and 4 outputs - A = np.zeros((5, 5)) - B = np.zeros((5, 2)) - C = np.zeros((4, 5)) - D = np.zeros((4, 2)) - - sys = StateSpaceSystem(A, B, C, D) - assert sys.n == 5 - assert sys.m == 2 - assert sys.p == 4 - - # Unsquare A matrix - with pytest.raises(ValueError): - StateSpaceSystem(A[:4, :], B, C, D) - - # B does not match number of states - with pytest.raises(ValueError): - StateSpaceSystem(A, B[:4, :], C, D) - - # C does not match number of states - with pytest.raises(ValueError): - StateSpaceSystem(A, B, C[:, :4], D) - - # mismatch number of inputs between B and D - with pytest.raises(ValueError): - StateSpaceSystem(A, B[:, :1], C, D) - - # mismatch number of outputs between C and D - with pytest.raises(ValueError): - StateSpaceSystem(A, B, C, D[:2, :]) - -def test_linearize_identity(): - """Linearization of linear system should be identical""" - - # ensure repeatable random matrices - np.random.seed(0) - - A = np.random.rand(5, 5) - B = np.random.rand(5, 2) - C = np.random.rand(4, 5) - D = np.random.rand(4, 2) - - sys = StateSpaceSystem(A, B, C, D) - - x0 = np.random.rand(5, 1) - u0 = np.random.rand(2, 1) - - linearized = linearize(sys, x0, u0, 1e-3) - - assert np.allclose(sys.A, linearized.A) - assert np.allclose(sys.B, linearized.B) - assert np.allclose(sys.C, linearized.C) - assert np.allclose(sys.D, linearized.D) - -def test_linearize_pendulum(): - class Cartesian1Pendulum(SinglePendulum): - """Single pendulum with cartesian coordinate ouput (y)""" - def h(self, x, u, t): - return np.array([np.sin(x[0]), -np.cos(x[0])]) * self.l1 - - nlsys = Cartesian1Pendulum() - nlsys.lc1 = 0.3 - nlsys.l1 = nlsys.lc1 - nlsys.m1 = 1.2 - nlsys.d1 = 0.1 - - # Linearization point - x0 = np.zeros((2, 1)) - u0 = np.zeros((1,)) - y0 = nlsys.h(x0, 0, 0).reshape((2,)) - - # linearize with epsilon = 0.01 rad (~1 deg) - linsys = linearize(nlsys, x0, u0, epsilon_x=0.01) - - # Simulate with 5 degree initial position and zero velocity - x_init = np.array([0.087, 0]) - - nlsim = nlsys.compute_trajectory(x_init, tf=10) - linsim = linsys.compute_trajectory(x_init, tf=10) - - rtol = 0.0 - atol = x_init[0] * 0.02 # 2% error tolerance - - assert np.allclose(nlsim.t, linsim.t) - assert np.allclose(nlsim.x, linsim.x, rtol, atol) - assert np.allclose(nlsim.dx, linsim.dx, rtol, atol) - assert np.allclose(nlsim.u, linsim.u) - assert np.allclose(nlsim.y, (linsim.y + y0), rtol, atol) - -if __name__ == "__main__": - pass \ No newline at end of file diff --git a/dev/tests/old_tests/test_stateobserver.py b/dev/tests/old_tests/test_stateobserver.py deleted file mode 100644 index a7a7e1a6..00000000 --- a/dev/tests/old_tests/test_stateobserver.py +++ /dev/null @@ -1,200 +0,0 @@ - -import pytest - -import numpy as np - -from pyro.dynamic.statespace import StateSpaceSystem, StateObserver - -from io import StringIO - -class TwoDofSs(StateSpaceSystem): - def __init__(self): - A = np.array([ - [-0.00054584, 0.0002618 ], - [ 0.0002618 , -0.00061508] - ]) - - B = np.array([ - [0.00078177, 0.00028403], - [0., 0.00041951]] - ) - - C = np.array([[0., 1.]]) - - D = np.array([[0., 0.]]) - - super().__init__(A, B, C, D) - -class Test_Obs_TwoDofSs(): - """ - Test data (expected Kalman gains, sim results, etc) were generated with Matlab - script "observer_twodofss.m". - """ - def test_obs_dimcheck(self): - sys = TwoDofSs() - with pytest.raises(ValueError): - L = np.empty([2, 2]) - StateObserver.from_ss(sys, L) - - with pytest.raises(ValueError): - L = np.empty([4, 1]) - obs = StateObserver.from_ss(sys, L) - - L = np.empty([2, 1]) - obs = StateObserver.from_ss(sys, L) - - def test_kalman_gain_from_ss(self): - sys = TwoDofSs() - Q = np.array([[0.6324, 0.1880], [0.1880, 0.5469]]); - R = 0.9575; - kf = StateObserver.kalman_from_ss(sys, Q, R) - - # Kalman gain - L_matlab = np.array([0.241879988531013, 0.163053708572278]).reshape(2, 1) * 1E-3 - - # Oberserver covariance matrix - P_matlab = np.array([ - [0.530701428966605, 0.231600089018445], - [0.231600089018445, 0.156123925957956] - ]) * 1E-3 - np.testing.assert_array_almost_equal(kf.L, L_matlab) - np.testing.assert_array_almost_equal(kf.P, P_matlab) - - def test_kalman_gain_from_ABCD(self): - sys = TwoDofSs() - Q = np.array([[0.6324, 0.1880], [0.1880, 0.5469]]); - R = 0.9575; - kf = StateObserver.kalman(sys.A, sys.B, sys.C, sys.D, Q, R) - - # Kalman gain - L_matlab = np.array([0.241879988531013, 0.163053708572278]).reshape(2, 1) * 1E-3 - - # Oberserver covariance matrix - P_matlab = np.array([ - [0.530701428966605, 0.231600089018445], - [0.231600089018445, 0.156123925957956] - ]) * 1E-3 - - np.testing.assert_array_almost_equal(kf.L, L_matlab) - np.testing.assert_array_almost_equal(kf.P, P_matlab) - - def test_kalman_check_dims(self): - sys = TwoDofSs() - - with pytest.raises(ValueError): - Q = np.empty([3, 3]) - R = np.empty(1) - kf = StateObserver.kalman_from_ss(sys, Q, R) - - with pytest.raises(ValueError): - Q = np.empty([2, 3]) - R = np.empty(1) - kf = StateObserver.kalman_from_ss(sys, Q, R) - - with pytest.raises(ValueError): - Q = np.empty([3, 2]) - R = np.empty(1) - kf = StateObserver.kalman_from_ss(sys, Q, R) - - with pytest.raises(ValueError): - Q = np.empty([2, 2]) - R = np.empty([2, 1]) - kf = StateObserver.kalman_from_ss(sys, Q, R) - - Q = np.empty([2, 2]) - R = np.empty(1) - kf = StateObserver.kalman_from_ss(sys, Q, R) - - - def test_sim_observed_sys(self): - """Simulate ObservedSystem and compare result against matlab""" - sys = TwoDofSs() - Q = np.array([[0.6324, 0.1880], [0.1880, 0.5469]]); - R = 0.9575; - kf = StateObserver.kalman_from_ss(sys, Q, R) - - tt = np.linspace(0, 10_000, 50) - - def t2u(t): - if (t % 2000) < 1000: return [100, 20] - else: return [150, 20] - - sys.t2u = t2u - sys.x0 = np.array([50, 20]) - kf.x0 = np.array([0, 0]) - - osys = kf + sys - tf = 10_000 - traj = osys.compute_trajectory(tf=tf, n=50, method="DOP853", rtol=1E-4, atol=1E-3) - - # Matlab generated data - expected_x_est_txt = """ - 0,0 - 17.1816933112253,2.67356729335786 - 32.6690586962738,5.82859520370164 - 46.6546237710204,9.32442222148156 - 59.3067122681008,13.0471771432147 - 71.5276536671465,16.9072478692853 - 89.4100863270359,21.0605927899578 - 105.581133740505,25.5464167418339 - 120.235163744847,30.2450926073082 - 133.54083561347,35.0605541856489 - 144.145342049008,39.908533059689 - 147.782605315062,44.4764402774289 - 151.218496987128,48.6570058723352 - 154.457796350395,52.4874078787767 - 157.506432493375,56.0004668309786 - 162.611520978705,59.2429648674815 - 172.620730276353,62.5084875703597 - 181.714021581887,65.8486940494296 - 189.992743238983,69.2127999649183 - 197.544222204844,72.5606793538811 - 201.474865866373,75.8310112090093 - 200.549189229051,78.7251242885965 - 199.838307324667,81.2244129101249 - 199.302580326945,83.3864237545624 - 198.90932687086,85.2596270743574 - 202.325759488412,86.9320759701282 - 209.310514157894,88.7129350344944 - 215.639957552738,90.6092996685361 - 221.389332577766,92.5772594675247 - 226.623132747702,94.5816224562129 - 226.987428251076,96.5278400916074 - 224.255676111288,98.1279656932221 - 221.876950861141,99.406279861416 - 219.799635130726,100.417827326237 - 217.980439792433,101.208381543566 - 221.501471929412,101.90611387318 - 227.117466778374,102.793385710491 - 232.185808843378,103.840649870868 - 236.771941112517,105.003439669918 - 240.931870147046,106.245677667186 - 238.893939424458,107.421823001517 - 235.388555299305,108.281100091856 - 232.286339967808,108.871642671038 - 229.532653928748,109.244248870842 - 227.08108262064,109.441075730228 - 231.406190089678,109.643118035093 - 236.327062319301,110.07665108912 - 240.75426253915,110.692120331065 - 244.748083523848,111.444937451339 - 248.360465903124,112.298568351518 - """.strip() - - expected_x_est = np.loadtxt(StringIO(expected_x_est_txt), delimiter=",") - - # plots for debugging - # - # from matplotlib import pyplot as plt - # plt.figure() - # plt.plot(traj.t, expected_x_est, "-o"); - # plt.plot(traj.t, traj.y, "-x"); - # plt.plot(traj.t, traj.x[:, :2], color="k"); - # plt.show() - - # Compare against matlab with 0.1 % error - np.testing.assert_allclose(expected_x_est, traj.y, rtol=1E-3, atol=0.01) - -if __name__ == "__main__": - import scipy.io - Test_Obs_TwoDofSs().test_sim_observed_sys() diff --git a/dev/tests/old_tests/test_sys b/dev/tests/old_tests/test_sys deleted file mode 100644 index 4e674040..00000000 --- a/dev/tests/old_tests/test_sys +++ /dev/null @@ -1,31 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Created on Thu Jan 23 12:08:36 2020 - -@author: alex -""" - -import numpy as np -from pyro.dynamic import integrator - - -################################### -# Simple integrator -sys = integrator.SimpleIntegrator() - -# Default input signal -sys.ubar = np.array([1]) - -# Phase plane behavior -sys.plot_phase_plane(0,0) # only one state for two axis! - -# Initial conditions -sys.x0 = np.array([2]) - -# Simulation -traj = sys.compute_trajectory() - -# Plot output -sys.plot_trajectory() -sys.plot_phase_plane_trajectory(0,0) \ No newline at end of file diff --git a/dev/tests/old_tests/test_utils.py b/dev/tests/old_tests/test_utils.py deleted file mode 100644 index 02d4d29c..00000000 --- a/dev/tests/old_tests/test_utils.py +++ /dev/null @@ -1,13 +0,0 @@ -import matplotlib.pyplot as plt - -def compare_signals(x1, y1, x2, y2): - fig = plt.figure() - - num_subfigs = y1.shape[1] - for j in range(num_subfigs): - ax = fig.add_subplot(num_subfigs, 1, j+1) - ax.plot(x1, y1[:, j], label="y1") - ax.plot(x2, y2[:, j], label="y2") - ax.legend() - - return fig diff --git a/dev/tests/old_tests/test_value_iteration.py b/dev/tests/old_tests/test_value_iteration.py deleted file mode 100644 index bf62d768..00000000 --- a/dev/tests/old_tests/test_value_iteration.py +++ /dev/null @@ -1,87 +0,0 @@ -""" -Run all examples - -Does not check for correct results, only that there are no exceptions. - -Running this: - -* Using pytest: `pytest -ra examples/` (replace examples with path to the - examples folder where this script lives). Pytest will produce a report - of failed/succeeded tests - -* As a script: `python ./examples/test_all_examples.py`. Will stop at the - exception. - -TODO: Add actual tests that check for correct results - -""" - -from pathlib import Path - -from importlib.util import spec_from_file_location, module_from_spec - -import sys - -import inspect - -from matplotlib import pyplot as plt - -_all_examples = [ - "./simple_pendulum/simple_pendulum_with_valueiteration_nd.py", - "./holonomic_mobile_robot/holonomic_mobile_robot_with_valueiteration_nd.py", -] - -this_script_dir = Path(__file__).parent -this_module = sys.modules[__name__] -#print(_all_examples) - -def import_from_file(modulename, filepath): - """import a file as a module and return the module object - - Everything will be executed, except if it's conditional to - __name__ == "__main__" - - """ - - spec = spec_from_file_location(modulename, filepath) - mod = module_from_spec(spec) - spec.loader.exec_module(mod) - - return mod - -def gettestfunc(modname, fullpath): - """Create a function that imports a file and runs the main function - - """ - - def run_example_main(): - ex_module = import_from_file(modname, fullpath) - - # Call function `main()` if it exists - if hasattr(ex_module, "main"): - ex_main_fun = getattr(ex_module, "main") - ex_main_fun() - - # Close all figs to reclaim memory - plt.close('all') - - return run_example_main - -_all_test_funs = [] - -for example_file in _all_examples: - relpath = Path(example_file) - fullpath = this_script_dir.joinpath(relpath) - modname = relpath.stem # file name without extension - - # Define a new function with a name starting with test_ so it is ran - # by pytest. - setattr(this_module, "test_" + modname, - gettestfunc(modname, fullpath)) - -def main(): - for fun in _all_test_funs: - fun() - -if __name__ == "__main__": - main() \ No newline at end of file diff --git a/dev/tests/old_tests/traj.npz b/dev/tests/old_tests/traj.npz deleted file mode 100644 index 49c75ddb69eb9acd0b35aa8f3b37c027a0a2fca8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1362186 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

b((MGR0 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 zpa!S`YJeJ`2B-mQfEu6%r~zt#8lVQK0cwC6pa!S`YJeJ`2B-mQfEu6%r~zt#8lVQK z0cwC6pa!S`YJeJ`2B-mQfEu6%r~zt#8lVQK0cwC6pa!S`YJeJ`2B-mQfEu6%r~zt# z8lVQK0cwC6pa!S`YJeJ`2B-mQfEu6%r~zt#8lVQK0cwC6pa!S`YJeJ`2B-mQfEu6% zr~zt#8lVQK0cwC6pa!S`YJeJ`2B-mQfEu6%r~zt#8lVQK0cwC6pa!S`YJeJ`2B-mQ zfEu6%r~zt#8lVQK0cwC6pa!S`YJeJ`2B-mQfEu6%r~zt#8lVQK0cwC6pa!S`YJeJ` z2B-mQfEu6%r~zt#8lVQK0cwC6pa!S`YJeJ`2B-mQfEu6%r~zt#8lVQK0cwC6pa!S` zYJeJ`2B-mQfEu6%r~zt#8lVQK0cwC6pa!S`YJeJ`2B-mQfEu6%r~zt#8lVQK0cwC6 zpa!S`YJeJ`2B-mQfEu6%r~zt#8lVQK0cwC6pa!S`YJeJ`2B-mQfEu6%r~zt#8lVQK z0cwC6pa!S`YJeJ`2B-mQfEu6%r~zt#8lVQK0cwC6pa!S`YJeJ`2B-mQfEu6%r~zt# z8lVQK0cwC6pa!S`YJeJ`2B-mQfEu6%r~zt#8lVQK0cwC6pa!S`YJeJ`2B-mQfEu6% zr~zt#8lVQK0cwC6pa!S`YJeJ`2B-mQfEu6%r~zt#8lVQK0cwC6pa!S`YJeJ`2B-mQ zfEu6%r~zt#8lVQK0cwC6pa!S`YJeJ`2B-mQfEu6%r~zt#8lVQK0cwC6pa!S`YJeJ` z2B-mQfEu6%r~zt#8lVQK0cwC6pa!S`YJeJ`2B-mQfEu6%r~zt#8lVQK0cwC6pa!S` zYJeJ`2B-mQfEu6%r~zt#8lVQK0cwC6pa!S`YJeJ`2B-mQfEu6%r~zt#8lVQK0cwC6 zpa!S`YJeJ`2B-mQfEu6%r~zt#8lVQK0cwC6pa!S`YJeJ`2B-mQfEu6%r~zt#8lVQK z0cwC6pa!S`YJeJ`2B-mQfEu6%r~zt#8lVQK0cwC6pa!S`YJeJ`2B-mQfEu6%r~zt# z8lVQK0cwC6pa!S`YJeJ`2B-mQfEu6%r~zt#8lVQK0cwC6pa!S`YJeJ`2B-mQfEu6% zr~zt#8lVQK0cwC6pa!S`YJeJ`2B-mQfEu6%r~zt#8lVQK0cwC6pa!S`YJeJ`2B-mQ zfEu6%r~zt#8lVQK0cwC6pa!S`YJeJ`2B-mQfEu6%r~zt#8lVQK0cwC6pa!S`YJeJ` z2L2xgRxMN4(7~@WP?^*G+_UE_Rh795r@4d{R|Kv+FN9|Eo^W`RC4x^~+an zQdK#oa(axNz4I=oF=ivinD3rB#%RQt-G`ixI@vmIKjdU*-}(2Jwhqqr@b}JpZ4cYS zzmJ_VdGh2bM&pb|oE`E1{L{PZLcssD2Pra^?_9m7}Nhe0v7@dB(hS<+TTn7x2GxXTB}zt2V*&68`sIj9F;>v7gVc;MaLG z;rbp`tHZD3*WYBG#*FyrwD%T%pW94fOVi;m`MFtV}P@+adPF%i+h! zR(D=PKfF9In5zyIZ9$3`c)9$U37&)DNBHCAd&wmEEexys`w}l_0CNEB4b=$5%Nxk_ z&h>>c9Ttq&Cz$EHbZLff@Or&r#%;L~ z)iCxgUca|Y0@&qFdWYBZ9kcIN^%-XRd%V8y8Te+P>;qo!56n7{1IqFGe`H?5rOWmI zh#o#N^?$QYRE`coA0doImw*uT62jaa+9eeId}99h?LMKWPt3HgKcCUpXXYX=5e$7s zZ=ab=xb|=xpubS&zpfiR6g`GA&U-trz!&uSg^6w$3)lDydJSW=|Mv~iZy3|{-QvU0 z^H=7RPwlJNWna8=~_@_Dp`G&XCcc$~!v$6Y*x7T;Z?cM?~_#JPzA56Mo*A>Rw z?+0VhIsMf?@OF%3j&$A}ha>U!jAVXw>Z^;y+w~_i@P7l0x9?A;>*BTi#M_xLUEl5u z!P}cK|LdHGw>yt{9lH!Bo`<(Tk68uvYF*^v9PpUt&U#{aI1fC=m)jZOFPw{C%;K&q z{0ryf7b6GTrk8)=ocv-Qc7C_SUpOzn7#3tL=^KS}6U9iNri|rLI6qNL=QP6&jB^yl zbmf>9h4U1}bgdVyXq>BPrYpa-(Kuhx%>ULyG|pKx)0JCpG|pQzbFOop4UNIMi($re z-9Rxoe=*FTu5}TEa~Q)M@64$<2Inz`8Q!_B^nc@A{$}Dj_3ikL^ZA=O)OmINe&d|} zW`=Yvpx-#Ju?*?_b|YhPZey7}yF35H;{3)koy!R(7UwvY>72$Bm9aR_aZIN@wFz-J z*KrK&LKvtxobNcMb35z2kHa~SW4g9oZ5+;fJoB$J=PB_x_wh_%XZ=p`IREj?ivIlJnPKKGy%IvU@W^XU;_4$z|?f*kbs>eF!wvhy)6NINo3}BEx<(V zCXp$Jnp#{Fv7bcdf8&>k9VK?Q1y?u;drD%uw*P`8>?(=rx;Z?Ou&*TMfBKTJvm_?F z^Xm3Y#@>>dvt8dY8M{knMs*2H#{QC-l;o~I*kLl`0^55-{}k*oh0*Ie;G|%eDa`M# z!&M6QnZg|GJUnEiV5cd}pspJv6?;u(B0CQU8&a{`RK~vR@Rf@FrZQdYKR*>aPG!1| zPTW7(^B<;b-EaAWUH@VJcR2fleg9#))_dt6>^u!`_cZK14R8N6>^=?WAPxIZ!+A)< zJ&=ZTk%s#q4d){b_d*)ZNgD2lG@O?-+!N_IH|e-9(s6#$ac`vK9Hry_NXL0f$32pc zbCr(!Bpv4~9rsE)&RII{mvo%Bblfu;ICmMiZ!&QHGH~x?;2dV){>flEw|k3g2JWE@ z=6|OL8Mu!!a6U6|FJ<7IX5fCxz6X!P*_f{s(aVGAsOq}OT++&$I z*O|D_GI72$aj#|KoM+;G%fxxl!abLTbDxF#E(_;B3-?|Yc94bpFAICf!abOUU1Z@t z%)&mha4%+ICt0{3v#^(J+>_bZO*Zb!Z0si+_hvSBl#Tl{8+*#eJ(`VOW#c~0#=f#~ zuV!Os*|=YGu(urCvpLvZ4({6=>@NrRZVq;sgZnp!`QPbe4({O`>@o-UaSryGgL^p# zJI%rUoQu8Y;-1dMZgX*8=VHIPxVLk$<6PX|x!7|q?(tmgIv4kOF7};^dp#FB&%@61 zu=70ZJP$k1!_M=t^E~W44?EAp&hxPIJnTFVJI}+;^RV+g>^u)U&&SU5vGaWFJRdvH z$IkPy^L*?)A3M*-&hxSJeC#|QJI}|?^Re@M>^vVkFTl^8!5X z6fift&NmA1_)~zL7hvZF*m(hVUVxn!VCRL{c_DUQh@BT==Y`mLA$DGfofl%~h1hu^ zc3y~`7h>my*m)s#UWlC+V&_HJc@cJAgq;^*=SA3g5q4gLofl!}Mc8=}c3y;?7h&f` z*m)6lUWA<&Vdurzc`zofl*0#n^ceDAtHT7t*j5@tZx2}22XUV@#MVCN;+c?ou2f}NLQ z=cU+rDRy3potI+grPz5Xc3z5|mtyCo*m)^-UW%QUV&|pUc`0^YhPQhe_Fjg!e;IaP zhI3Gc{g>f9l;IvI!?`HKeNcw;QHFb=4CkZ__d^-ZOBwEoGMt+-+!tjyKV`T#%5aX# zaDSBHJeA=dDZ{xc!+lbQ^Hqj>r3~k+4EIYJ&RZGo89vS(ANLI(=Z}wjhmUi}$Nj^{ zdF10B;^SQMaUbzf%0z3~A;Bira=R*QKJ__)>NPx#l0q#u!b|k?4DZritxJL!pl>qmt z0Q(Z)UKL`jP!R*2mRao-BDKOyd2A$BOl{VT*Cg}8@>*rgEnu@L(d;$9YF zr$XG%LhMzDds>Ly3UOZxv0owXZ6S6n#QiPAo`tx_h1j(a_qh=J7UEtPV&@|4T!fvA zuyYZ1F2c@5*trNh7h&fj>|BJMi?DMMb}qusMcBCrI~QT+BJ5m*or|z@5q2)Z&PCX{ z2s;;H=OXM}gq@49a}jnf!p=q5xd=NKVdrA(T#TKIv2!tYF2>Hq*tr-x7h~sQ>|BhU zi?MStb}q)w#n`zRI~QZ;V(eUuor|$^F?KG-&c)cd7&{kZ=VI(!jGc?Ib1`-<#?Hmq zxfnYy$Ii>K^K$IG96K+^&dag$a_qbuJ1@u1%dzuv?7SR1FUQWy@pxE{=RxJzc{z4o zj-8id=jGUWId)!-otI|BDKOR#eZb}qrrCD^$H zJC|VR66{=polCHD33e{Q&L!Bn1Ur{t=MwB(f}KmSa|w1X!OkVvxdc0xVCNF-T!Nif zV&|3Ec_nsUiJezs=atxbC3aqkomXP#mDqVDc3z2{S7PUt*m)&(UWuJoV&|3Ec_nsU ziJezs=atxbC3aqkomXP#mDqVDc3z2{S7PUt*m)&(F2&BJ*trxtmtyBq>|BbSOR;k) zb}q%vrP#R?JC|bTQtVucolCKEDW3mI@qUjK&x56S|3`|QOR;k)b}q%vrP#R?JC|bT zQtVucolCKEDRwT!&a1HVD(t)pJFmjdtFZGb?7Rv)ufoo&u=6VHyb3$7!p^I(^D6AT z3OldD&a1HVD(t)pJFmjdtFZGb?7Rv)ufoo&u=6VHyb3$7!p^I(^D6AT3Oko!=Q8YE zhMmi>a~XCn!_H;cxePm(VdpaJT!x*?uyYx9F2l}c*trZlmtp5J>|BPO%dm49b}qxt zW!Sk4JC|YSGVEN2oy)Lu8Fnti&SluS3_GvJ&a1KWYV5okJFmvhtFiNH?7SK~ug1=+ zvGZ!|yc#>N#?Gs;^J?t88auDX&a1KWYV5okJFmvhtFiNH?7SK~ug1=+vGZ!|yc#>N z#?Gs;^J?t820O38&TFvq8tl9VJFmgcYq0Yg?7Rj$uffi1u=5)1yaqe3!Om;2^BU~D z20O38&TFvq8ob|I!-%?`{;R>xYq0Yg?7Rj$uffi1u=5)1yaqe3!Om;2^IGh@7CW!S z&TFyrTI{?QJFmsgYq9fM?7S8`uf@)5vGZE&ycRpJ#m;N7^IGh@7CW!S&TFyrTI{?Q zJFmsgYq9fM?7S8`uf@)5vGZE&ycRpJ#m?n;yUVe6Io|$q>|Ty@AjkgYI1h5%19F@T zIqm~F&W9ZLf*j{Wj{8B5^CHJRA;-CqmYDIqn%bo=3`Y-^lTNQjU8^j^~wf+&^+Wzm(%1lH>75 zj{8WC$00fHB{|Ni9QTtP=T(k-N{(|Y$9*No`IY0|lH(l9aev8ip5?g5`{SxSb<$Ca33qMPX+E}1$L^y{j9)V6}YDr*sTKh zwF3K9;NDhX#|qrv3hY^ddt8BCD{!AHux|zKbp>{=#Lku2xe_~9V&_WiT#214v2!JM zuEfrj*trrrS7PT%>|BYRE3tDWcCN(EmDsrwJ6B@oO6**Tohz|(C3ddF|BYRE3tDWcCN(E zmDsrwJ6B@oO6**Tohz|(C3ddF&Xw4?5<6F7=Su8ciJdF4bLIcBbeD0rC2(E0cXxM} z3i{YB*0bquq*GczR2mhT?(Xhly1Tp6>F%z(+ckS#y!pT3GYm8PTHpPfbJl%0|C;k9 z&X+h};(UqoCC-;PU*dd;^CixgIA7v?iSs4SmpEVIe2McV&X+h};(UqoCC-;PU*dd; z^CixgIA7v?iSs4SmpEVIe2McV&X+h};(UqoCC-;PU*dd;^CixgIA7v?iSs4SmpEVI ze2McV&X+h};(UqoCC-;PU*dd;^CixgIA7v?iSs4SmpEVMe3|oQ&X+k~=6sp+WzLs5 zU*>$7^JUJLIbY^{ne%1NmpNbNe3|oQ&X+k~=6sp+WzLs5U*>$7^JUJLIbZg@ahdaF z&X+k~=6sp+WzLs5U*>$7^JUJLIbY^{ne%1NmpNbNe3|oQ&X+k~=6sp+WzLs5U*>$7 z^JUJLIbY^{ne%1NmpNbNe3|oQ&Q~~J;e3Vj70y>UU*UX(^A*llIA7s>h4U58S2$nc ze1-EB&Q~~J;e3Vj70y>UU*UX(^A*llIA7s>h4U58S2$nce1-EB&Q~~J;e3Vj70y>U zU*UX(^A*llIA7s>h4U58S2$nce1-EB&Q~~J;e3Vj70y>UU*UX(^A*llIA7s>h4U58 zS2$nce3kQ6&R02K<$RU%RnAvAU*&w2^Ht7QIbY>`mGf23S2`mGf23SM9`A&R02K<$RU%RnAvAU*&w2^Ht7QIbY>`mGf23 zS2`mGf23S2bbbb-{5?M^9{~7 zIN#uWgYyl}H#pzme1r21&Nn#U;CzGg4bC?>-{5?M^9{~7IN#uWgYyl}H#pzme1r21 z&Nn#U;CzGg4bC?>-{5?M^9{~7IN#uWgYyl}H#pzme1r21&Nn#U;CzGg4bC?>-{5?c z^G(h-Ip5@blk-i^H#y(re3SD{&Nn&VaP8e2eof&bK(<;(UwqEzY+%-{O3W^DWM|IN#!Yi}Nkcw>aP8 ze2eof&bK(<;(UwqEzY+%-{O3W^DWM|IN#!Yi}Nkcw>aP8e2eof&bK(<;(UwqEzY+% z-{O3W^DWM|IN#!Yi}Nkcw>aP8e2epK&bK+==6sv;ZO*qj-{yRq^KH(zIp5}doAYhX zw>jVDe4F!a&bK+==6sv;ZO*qj-{yRq^KH(zIp5}doAYhXw>jVDe4F!a&bK+==6sv; zZO*qj-{yRq^KHKlw9WZ8=i8ibbH2^_Hs{-%Z*#uQ`8Ma?{L1u`3~nhobPbH!}$*9JDl%uzQg$r=R2J5aK6L&4(B_Z?{L1u z`3~nhobPbH!}$*9JDl%uzQg$r=R2J5aK6L&4(B_Z?{L1u`3~nhobPbH!}$*9JDl%u zzQg$r=R2J5aK6L&4(B_Z?{L1u`3~nhobPbH!}$*9JDl%uzQg$r=R2J5aK6j=F6X

F->Z(y$BP#TBlGY*=*T>LzdAAx-_wlD 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

-{|1>CGauIM&jsWs+}@{DD8Pt2_iXY!2}IN} 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 z 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

QjQ-f%EsVU5^hh*@^u=KTIk{ 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 W=a_Z>{S-X~Jw+PM#=2>3mi`O)G9Rq~ diff --git a/dev/tests/test_graphical_settings.py b/dev/tests/test_graphical_settings.py deleted file mode 100644 index f044a6c4..00000000 --- a/dev/tests/test_graphical_settings.py +++ /dev/null @@ -1,35 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Fri Nov 16 12:05:08 2018 - -@author: Alexandre -""" - - -import matplotlib -import matplotlib.pyplot as plt -import sys as python_system - -############################################################################### -import numpy as np -############################################################################### -from pyro.dynamic import pendulum -############################################################################### - -#matplotlib.use('Qt5Agg') -#plt.ion() - -print('The current matplotlib backend is:', matplotlib.get_backend() ) -print('Matplotlib interactive mode is activated: ', plt.isinteractive() ) -print('The python script is running interactive', hasattr(python_system, 'ps1') ) - -sys = pendulum.DoublePendulum() - -# Simultation -sys.x0 = np.array([-0.1,0,0,0]) -sys.plot_trajectory() -sys.plot_phase_plane_trajectory(0, 2) -sys.animate_simulation() -#sys.animate_simulation( is_3d = True ) -sys.show3( [1,1] ) - diff --git a/dev/trajectory_optim_test/rocket_landing_with_trajectory_optimization.py b/dev/trajectory_optim_test/rocket_landing_with_trajectory_optimization.py deleted file mode 100644 index fde41570..00000000 --- a/dev/trajectory_optim_test/rocket_landing_with_trajectory_optimization.py +++ /dev/null @@ -1,62 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Created on Sun Oct 3 08:27:06 2021 - -@author: alex -""" - -import numpy as np - -from pyro.dynamic.rocket import Rocket -from pyro.analysis.costfunction import QuadraticCostFunction -from pyro.dynamic.statespace import linearize -from pyro.control.lqr import synthesize_lqr_controller -from pyro.planning import trajectoryoptimisation -from pyro.analysis import costfunction - -# Non-linear model -sys = Rocket() - -sys.inertia = 400 - -sys.xbar = np.array([0,2.2,0,0,0,0]) -sys.ubar = np.array([1,0]) * sys.mass * sys.gravity # Nominal trust = weight - -# Linear model -ss = linearize( sys , 0.01 ) - -# Cost function -cf = QuadraticCostFunction.from_sys( sys ) -cf.Q[0,0] = 1 -cf.Q[1,1] = 10000 -cf.Q[2,2] = 0.1 -cf.Q[3,3] = 0 -cf.Q[4,4] = 10000 -cf.Q[5,5] = 0 -cf.R[0,0] = 0.01 -cf.R[1,1] = 10.0 - -# LQR controller -ctl = synthesize_lqr_controller( ss , cf , sys.xbar , sys.ubar ) - -ctl.ubar - -# Simulation Closed-Loop Non-linear with LQR controller -cl_sys = ctl + sys -cl_sys.x0 = np.array([1,10,-0.8,0,0,0]) -traj = cl_sys.compute_trajectory(10) - -cl_sys.plot_trajectory('xu') - -planner = trajectoryoptimisation.DirectCollocationTrajectoryOptimisation( sys , 0.1, 40 ) - -planner.x_start = np.array([1.,10.,-0.8,0.,0.,0.]) -planner.x_goal = np.array([0.,0.,0.,0.,0.,0.]) - -planner.init_dynamic_plot() -planner.set_initial_trajectory_guest( traj ) -# planner.compute_optimal_trajectory() -# planner.show_solution() -# planner.animate_solution() - diff --git a/examples/courses/udes_gmc714/demo_simple_pendulum_multiple_controller_options.py b/examples/courses/udes_gmc714/demo_simple_pendulum_multiple_controller_options.py index 887c8aff..dce7dc22 100644 --- a/examples/courses/udes_gmc714/demo_simple_pendulum_multiple_controller_options.py +++ b/examples/courses/udes_gmc714/demo_simple_pendulum_multiple_controller_options.py @@ -98,7 +98,7 @@ cl_sys.x0[0] = q0 cl_sys.compute_trajectory(tf,10001,'euler') cl_sys.plot_trajectory('xu') +sys.plot_phase_plane() +cl_sys.plot_phase_plane_trajectory_closed_loop() cl_sys.animate_simulation() -sys.plot_phase_plane() -cl_sys.plot_phase_plane_trajectory_closed_loop() \ No newline at end of file diff --git a/examples/courses/udes_gro640/prob/demo_crash_commande_en_position.py b/examples/courses/udes_gro640/prob/demo_crash_commande_en_position.py index f9906466..11eb23ba 100755 --- a/examples/courses/udes_gro640/prob/demo_crash_commande_en_position.py +++ b/examples/courses/udes_gro640/prob/demo_crash_commande_en_position.py @@ -26,7 +26,7 @@ # Configurations de départs clsys.x0 = np.array([0,0.5,0]) # crash -#clsys.x0 = np.array([0,0.7,0]) # fonctionne +# clsys.x0 = np.array([0,0.7,0]) # fonctionne # Simulation clsys.compute_trajectory( solver = 'odeint' ) diff --git a/examples/demos_by_system/car_propulsion/longitudinal_car_braking_value_iteration.py b/examples/demos_by_system/car_propulsion/longitudinal_car_braking_value_iteration.py index ecaf9265..0defc280 100644 --- a/examples/demos_by_system/car_propulsion/longitudinal_car_braking_value_iteration.py +++ b/examples/demos_by_system/car_propulsion/longitudinal_car_braking_value_iteration.py @@ -9,14 +9,14 @@ ############################################################################### import numpy as np ############################################################################### -from pyro.dynamic import longitudinal_vehicule +from pyro.dynamic import vehicle_propulsion from pyro.planning import discretizer from pyro.analysis import costfunction from pyro.planning import dynamicprogramming from pyro.control import controller ############################################################################### -sys = longitudinal_vehicule.LongitudinalFrontWheelDriveCarWithWheelSlipInput() +sys = vehicle_propulsion.LongitudinalFrontWheelDriveCarWithWheelSlipInput() ############################################################################### diff --git a/examples/demos_by_system/car_propulsion/longitudinal_car_with_torque_input.py b/examples/demos_by_system/car_propulsion/longitudinal_car_with_torque_input.py index 4a57dc15..95f5c198 100644 --- a/examples/demos_by_system/car_propulsion/longitudinal_car_with_torque_input.py +++ b/examples/demos_by_system/car_propulsion/longitudinal_car_with_torque_input.py @@ -8,10 +8,10 @@ ############################################################################### import numpy as np ############################################################################### -from pyro.dynamic import longitudinal_vehicule +from pyro.dynamic import vehicle_propulsion #################################### -sys = longitudinal_vehicule.LongitudinalFrontWheelDriveCarWithTorqueInput() +sys = vehicle_propulsion.LongitudinalFrontWheelDriveCarWithTorqueInput() sys.x0 = np.array([0,0.1,0,0]) diff --git a/examples/demos_by_system/car_steering/bicycle.py b/examples/demos_by_system/car_steering/bicycle.py index 6f804f71..83458308 100644 --- a/examples/demos_by_system/car_steering/bicycle.py +++ b/examples/demos_by_system/car_steering/bicycle.py @@ -8,11 +8,11 @@ ############################################################################### import numpy as np ############################################################################### -from pyro.dynamic import vehicle +from pyro.dynamic import vehicle_steering ############################################################################### # Vehicule dynamical system -sys = vehicle.KinematicBicyleModel() +sys = vehicle_steering.KinematicBicyleModel() # Set default wheel velocity and steering angle sys.ubar = np.array([2,0.1]) diff --git a/examples/demos_by_system/car_steering/bicycle_exploration_with_rrt.py b/examples/demos_by_system/car_steering/bicycle_exploration_with_rrt.py index b2ed2604..42131f18 100644 --- a/examples/demos_by_system/car_steering/bicycle_exploration_with_rrt.py +++ b/examples/demos_by_system/car_steering/bicycle_exploration_with_rrt.py @@ -8,11 +8,11 @@ ############################################################################### import numpy as np ############################################################################### -from pyro.dynamic import vehicle +from pyro.dynamic import vehicle_steering from pyro.planning import randomtree ############################################################################### -sys = vehicle.KinematicBicyleModel() +sys = vehicle_steering.KinematicBicyleModel() ############################################################################### diff --git a/examples/demos_by_system/car_steering/bicycle_parallel_parking_with_rrt.py b/examples/demos_by_system/car_steering/bicycle_parallel_parking_with_rrt.py index f81744f9..24a5aa24 100644 --- a/examples/demos_by_system/car_steering/bicycle_parallel_parking_with_rrt.py +++ b/examples/demos_by_system/car_steering/bicycle_parallel_parking_with_rrt.py @@ -7,11 +7,11 @@ ############################################################################### import numpy as np ############################################################################### -from pyro.dynamic import vehicle +from pyro.dynamic import vehicle_steering from pyro.planning import randomtree ############################################################################### -sys = vehicle.KinematicBicyleModel() +sys = vehicle_steering.KinematicBicyleModel() sys.dynamic_domain = False ############################################################################### diff --git a/examples/demos_by_system/car_steering/car.py b/examples/demos_by_system/car_steering/car.py index 25c61a02..00d41ba1 100644 --- a/examples/demos_by_system/car_steering/car.py +++ b/examples/demos_by_system/car_steering/car.py @@ -9,12 +9,12 @@ ############################################################################### import numpy as np ############################################################################### -from pyro.dynamic import vehicle +from pyro.dynamic import vehicle_steering ############################################################################### # Vehicule dynamical system #sys = vehicle.KinematicCarModel() -sys = vehicle.KinematicCarModelwithObstacles() +sys = vehicle_steering.KinematicCarModelwithObstacles() # Set default wheel velocity and steering angle sys.ubar = np.array([2,0.2]) diff --git a/examples/demos_by_system/car_steering/car_trajectory_optimisation.py b/examples/demos_by_system/car_steering/car_trajectory_optimisation.py index fed1da49..e4e08112 100644 --- a/examples/demos_by_system/car_steering/car_trajectory_optimisation.py +++ b/examples/demos_by_system/car_steering/car_trajectory_optimisation.py @@ -8,7 +8,7 @@ import numpy as np -from pyro.dynamic.vehicle import KinematicCarModel +from pyro.dynamic.vehicle_steering import KinematicCarModel from pyro.planning.trajectoryoptimisation import DirectCollocationTrajectoryOptimisation diff --git a/examples/demos_by_system/car_steering/car_trajectory_with_rrt.py b/examples/demos_by_system/car_steering/car_trajectory_with_rrt.py index 6980b562..50c8870d 100644 --- a/examples/demos_by_system/car_steering/car_trajectory_with_rrt.py +++ b/examples/demos_by_system/car_steering/car_trajectory_with_rrt.py @@ -8,11 +8,11 @@ ############################################################################### import numpy as np ############################################################################### -from pyro.dynamic import vehicle +from pyro.dynamic import vehicle_steering from pyro.planning import randomtree ############################################################################### -sys = vehicle.KinematicCarModelwithObstacles() +sys = vehicle_steering.KinematicCarModelwithObstacles() ############################################################################### diff --git a/examples/demos_by_system/car_steering/car_with_custom_lateral_controller.py b/examples/demos_by_system/car_steering/car_with_custom_lateral_controller.py index deb3f72f..81d5532d 100644 --- a/examples/demos_by_system/car_steering/car_with_custom_lateral_controller.py +++ b/examples/demos_by_system/car_steering/car_with_custom_lateral_controller.py @@ -9,12 +9,12 @@ ############################################################################### import numpy as np ############################################################################### -from pyro.dynamic import vehicle +from pyro.dynamic import vehicle_steering from pyro.control import controller ############################################################################### # Vehicule dynamical system -sys = vehicle.ConstantSpeedKinematicCarModel() +sys = vehicle_steering.ConstantSpeedKinematicCarModel() class CarController( controller.StaticController ) : diff --git a/examples/demos_by_system/car_steering/car_with_valueiteration_minimum_time.py b/examples/demos_by_system/car_steering/car_with_valueiteration_minimum_time.py index d3447744..83932915 100755 --- a/examples/demos_by_system/car_steering/car_with_valueiteration_minimum_time.py +++ b/examples/demos_by_system/car_steering/car_with_valueiteration_minimum_time.py @@ -12,10 +12,10 @@ from pyro.analysis import costfunction from pyro.planning import dynamicprogramming from pyro.planning import discretizer -from pyro.dynamic import vehicle +from pyro.dynamic import vehicle_steering ############################################################################### -sys = vehicle.KinematicCarModelwithObstacles() +sys = vehicle_steering.KinematicCarModelwithObstacles() # Set domain sys.x_ub = np.array([+35, +3, +3]) diff --git a/examples/demos_by_system/car_steering/car_with_valueiteration_quadratic_cost.py b/examples/demos_by_system/car_steering/car_with_valueiteration_quadratic_cost.py index ee0fd70d..e7992e25 100755 --- a/examples/demos_by_system/car_steering/car_with_valueiteration_quadratic_cost.py +++ b/examples/demos_by_system/car_steering/car_with_valueiteration_quadratic_cost.py @@ -11,10 +11,10 @@ from pyro.analysis import costfunction from pyro.planning import dynamicprogramming from pyro.planning import discretizer -from pyro.dynamic import vehicle +from pyro.dynamic import vehicle_steering ############################################################################### -sys = vehicle.KinematicCarModelwithObstacles() +sys = vehicle_steering.KinematicCarModelwithObstacles() # Set domain sys.x_ub = np.array([+35, +3, +3]) diff --git a/examples/demos_by_system/holonomic_mobile_robot/holonomic_mobile_robot_exploration_with_obstacles_with_rrt.py b/examples/demos_by_system/holonomic_mobile_robot/holonomic_mobile_robot_exploration_with_obstacles_with_rrt.py index 2e398ec0..8b403799 100644 --- a/examples/demos_by_system/holonomic_mobile_robot/holonomic_mobile_robot_exploration_with_obstacles_with_rrt.py +++ b/examples/demos_by_system/holonomic_mobile_robot/holonomic_mobile_robot_exploration_with_obstacles_with_rrt.py @@ -7,10 +7,10 @@ import numpy as np -from pyro.dynamic import vehicle +from pyro.dynamic import vehicle_steering from pyro.planning import randomtree -sys = vehicle.HolonomicMobileRobotwithObstacles() +sys = vehicle_steering.HolonomicMobileRobotwithObstacles() sys.obstacles = [ [ (2,2),(4,10)], diff --git a/examples/demos_by_system/holonomic_mobile_robot/holonomic_mobile_robot_exploration_with_rrt.py b/examples/demos_by_system/holonomic_mobile_robot/holonomic_mobile_robot_exploration_with_rrt.py index af8efabb..e8be27ef 100644 --- a/examples/demos_by_system/holonomic_mobile_robot/holonomic_mobile_robot_exploration_with_rrt.py +++ b/examples/demos_by_system/holonomic_mobile_robot/holonomic_mobile_robot_exploration_with_rrt.py @@ -7,10 +7,10 @@ import numpy as np -from pyro.dynamic import vehicle +from pyro.dynamic import vehicle_steering from pyro.planning import randomtree -sys = vehicle.HolonomicMobileRobot() +sys = vehicle_steering.HolonomicMobileRobot() x_start = np.array([0,0]) x_goal = np.array([8,8]) diff --git a/examples/demos_by_system/holonomic_mobile_robot/holonomic_mobile_robot_with_valueiteration.py b/examples/demos_by_system/holonomic_mobile_robot/holonomic_mobile_robot_with_valueiteration.py index 4d7c4c07..6ad0d221 100644 --- a/examples/demos_by_system/holonomic_mobile_robot/holonomic_mobile_robot_with_valueiteration.py +++ b/examples/demos_by_system/holonomic_mobile_robot/holonomic_mobile_robot_with_valueiteration.py @@ -7,12 +7,12 @@ import numpy as np -from pyro.dynamic import vehicle +from pyro.dynamic import vehicle_steering from pyro.planning import discretizer from pyro.analysis import costfunction from pyro.planning import dynamicprogramming -sys = vehicle.HolonomicMobileRobotwithObstacles() +sys = vehicle_steering.HolonomicMobileRobotwithObstacles() # Discrete world grid_sys = discretizer.GridDynamicSystem( sys , (51,51) , (3,3) ) diff --git a/examples/demos_by_system/suspension/suspension.py b/examples/demos_by_system/suspension/suspension.py index 5efab0c3..8fe9ee27 100644 --- a/examples/demos_by_system/suspension/suspension.py +++ b/examples/demos_by_system/suspension/suspension.py @@ -9,9 +9,6 @@ import numpy as np from pyro.dynamic import suspension -from pyro.analysis import costfunction -from pyro.planning import dynamicprogramming -from pyro.planning import discretizer sys = suspension.QuarterCarOnRoughTerrain() diff --git a/examples/demos_by_tool/dynamicprogramming/2D_navigation.py b/examples/demos_by_tool/dynamicprogramming/2D_navigation.py index 9caadf92..5492248a 100644 --- a/examples/demos_by_tool/dynamicprogramming/2D_navigation.py +++ b/examples/demos_by_tool/dynamicprogramming/2D_navigation.py @@ -8,12 +8,12 @@ import numpy as np -from pyro.dynamic import vehicle +from pyro.dynamic import vehicle_steering from pyro.analysis import costfunction from pyro.planning import dynamicprogramming from pyro.planning import discretizer -sys = vehicle.HolonomicMobileRobotwithObstacles() +sys = vehicle_steering.HolonomicMobileRobotwithObstacles() #sys.obstacles[1][0] = (5,5) diff --git a/examples/demos_by_tool/dynamicprogramming/braking_reachability.py b/examples/demos_by_tool/dynamicprogramming/braking_reachability.py index c4a752b8..291b14ab 100644 --- a/examples/demos_by_tool/dynamicprogramming/braking_reachability.py +++ b/examples/demos_by_tool/dynamicprogramming/braking_reachability.py @@ -8,12 +8,12 @@ import numpy as np -from pyro.dynamic import longitudinal_vehicule +from pyro.dynamic import vehicle_propulsion from pyro.analysis import costfunction from pyro.planning import dynamicprogramming from pyro.planning import discretizer -sys = longitudinal_vehicule.LongitudinalFrontWheelDriveCarWithWheelSlipInput() +sys = vehicle_propulsion.LongitudinalFrontWheelDriveCarWithWheelSlipInput() sys.x_ub[0] = 60 sys.x_lb[0] = 0 diff --git a/examples/demos_by_tool/dynamicprogramming/car_braking.py b/examples/demos_by_tool/dynamicprogramming/car_braking.py index 6abd3e28..82b52c3c 100644 --- a/examples/demos_by_tool/dynamicprogramming/car_braking.py +++ b/examples/demos_by_tool/dynamicprogramming/car_braking.py @@ -8,12 +8,12 @@ import numpy as np -from pyro.dynamic import longitudinal_vehicule +from pyro.dynamic import vehicle_propulsion from pyro.analysis import costfunction from pyro.planning import dynamicprogramming from pyro.planning import discretizer -sys = longitudinal_vehicule.LongitudinalFrontWheelDriveCarWithWheelSlipInput() +sys = vehicle_propulsion.LongitudinalFrontWheelDriveCarWithWheelSlipInput() sys.x_ub[1] = 15 sys.x_lb[1] = 0 diff --git a/examples/demos_by_tool/dynamicprogramming/car_parking.py b/examples/demos_by_tool/dynamicprogramming/car_parking.py index e5d80437..0bc8c9a0 100644 --- a/examples/demos_by_tool/dynamicprogramming/car_parking.py +++ b/examples/demos_by_tool/dynamicprogramming/car_parking.py @@ -11,9 +11,9 @@ from pyro.analysis import costfunction from pyro.planning import dynamicprogramming from pyro.planning import discretizer -from pyro.dynamic import vehicle +from pyro.dynamic import vehicle_steering -sys = vehicle.KinematicCarModelwithObstacles() +sys = vehicle_steering.KinematicCarModelwithObstacles() # Set domain sys.x_ub = np.array([+35, +3, +3]) diff --git a/examples/demos_by_tool/trajectory_planning/double_pendulum_with_trajectory_optimization.py b/examples/demos_by_tool/trajectory_planning/double_pendulum_with_trajectory_optimization.py new file mode 100644 index 00000000..6de3dcd5 --- /dev/null +++ b/examples/demos_by_tool/trajectory_planning/double_pendulum_with_trajectory_optimization.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Created on Sat Oct 30 14:04:51 2021 + +@author: alex +""" + +import numpy as np + +from pyro.dynamic.pendulum import DoublePendulum +from pyro.planning.trajectoryoptimisation import DirectCollocationTrajectoryOptimisation + + +sys = DoublePendulum() + +#Max/Min torque +sys.u_ub[0] = +20 +sys.u_ub[1] = +20 +sys.u_lb[0] = -20 +sys.u_lb[1] = -20 + +planner = DirectCollocationTrajectoryOptimisation( sys , 0.2 , 20 ) + +planner.x_start = np.array([3.14,0,0,0]) +planner.x_goal = np.array([0,0,0,0]) + +planner.maxiter = 500 +planner.set_linear_initial_guest(True) +planner.init_dynamic_plot() +planner.compute_optimal_trajectory() +# planner.show_solution() +planner.animate_solution() \ No newline at end of file diff --git a/examples/demos_by_tool/trajectory_planning/double_pendulum_with_trajectory_optimization_CUSTOM.py b/examples/demos_by_tool/trajectory_planning/double_pendulum_with_trajectory_optimization_CUSTOM.py deleted file mode 100644 index 369c56ae..00000000 --- a/examples/demos_by_tool/trajectory_planning/double_pendulum_with_trajectory_optimization_CUSTOM.py +++ /dev/null @@ -1,174 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Created on Fri Sep 10 11:12:11 2021 - -@author: alexandregirard -""" - -import numpy as np -from scipy.optimize import minimize - -from pyro.dynamic import pendulum - - - -sys = pendulum.DoublePendulum() - -n = 4 -m = 2 -grid = 20 -dt = 0.2 - - -#dec = np.linspace(0,grid*3,grid*3) - - -def dec2xu(dec): - - x = np.zeros((n,grid)) - u = np.zeros((m,grid)) - - x[0,:] = dec[0:grid] - x[1,:] = dec[grid:2*grid] - x[2,:] = dec[2*grid:3*grid] - x[3,:] = dec[3*grid:4*grid] - u[0,:] = dec[4*grid:5*grid] - u[1,:] = dec[5*grid:6*grid] - - return x,u - -def cost(dec): - - J = 0 - - x = np.zeros((n,grid)) - u = np.zeros((m,grid)) - - x[0,:] = dec[0:grid] - x[1,:] = dec[grid:2*grid] - x[2,:] = dec[2*grid:3*grid] - x[3,:] = dec[3*grid:4*grid] - u[0,:] = dec[4*grid:5*grid] - u[1,:] = dec[5*grid:6*grid] - - for i in range(grid-1): - - #J = J + 0.5 * dt * ( (x[0,i]-target)**2 + (x[0,i+1]-target)**2 + x[1,i]**2 + x[1,i+1]**2 + u[0,i]**2 + u[0,i+1]**2 ) - - J = J + 0.5 * dt * ( x[0,i]**2 + x[0,i+1]**2 + x[1,i]**2 + x[1,i+1]**2 ) - J = J + 0.5 * dt * ( x[2,i]**2 + x[2,i+1]**2 + x[3,i]**2 + x[3,i+1]**2 ) - J = J + 0.5 * dt * ( u[0,i]**2 + u[0,i+1]**2 + u[1,i]**2 + u[1,i+1]**2 ) * 5 - - return J - -def constraints(dec): - - x,u = dec2xu(dec) - - vec=np.zeros(grid*4-4) - - for i in range(grid-1): - - vec[i+grid*0-0] = (x[0,i+1] - x[0,i]) - 0.5 * dt * ( sys.f(x[:,i],u[:,i])[0] + sys.f(x[:,i+1],u[:,i+1])[0] ) - vec[i+grid*1-1] = (x[1,i+1] - x[1,i]) - 0.5 * dt * ( sys.f(x[:,i],u[:,i])[1] + sys.f(x[:,i+1],u[:,i+1])[1] ) - vec[i+grid*2-2] = (x[2,i+1] - x[2,i]) - 0.5 * dt * ( sys.f(x[:,i],u[:,i])[2] + sys.f(x[:,i+1],u[:,i+1])[2] ) - vec[i+grid*3-3] = (x[3,i+1] - x[3,i]) - 0.5 * dt * ( sys.f(x[:,i],u[:,i])[3] + sys.f(x[:,i+1],u[:,i+1])[3] ) - - return vec - - -def compute_bounds(): - - bounds = [] - - #x0 - bounds.append( (-3.14,-3.13) ) - - for i in range(1,grid-1): - - bounds.append( (-5,2) ) - - bounds.append( (0.0,0.01) ) - - #x1 - bounds.append( (0,0.01) ) - - for i in range(1,grid-1): - - bounds.append( (-3,3) ) - - bounds.append( (0,0.01) ) - - #x2 - - bounds.append( (0,0.01) ) - - for i in range(1,grid-1): - - bounds.append( (-3,6) ) - - bounds.append( (0,0.01) ) - - #x3 - - bounds.append( (0,0.01) ) - - for i in range(1,grid-1): - - bounds.append( (-10,10) ) - - bounds.append( (0,0.01) ) - - #u0 - - for i in range(grid): - - bounds.append( (-20,30) ) - - #u1 - - for i in range(grid): - - bounds.append( (-20,30) ) - - return bounds - - -def display_callback(a): - - - print('One iteration completed') - - return True - - - - -# Guess -dec = np.zeros(grid*(n+m)) -#dec[0:grid] = traj.x[:,0] -#dec[grid:2*grid] = traj.x[:,1] -#dec[2*grid:3*grid] = traj.u[:,0] - -bnds = compute_bounds() - -cons = {'type': 'eq', 'fun': constraints } -res = minimize( cost, dec, method='SLSQP', bounds=bnds, constraints=cons, callback=display_callback, options={'disp':True,'maxiter':1000}) # - - -sys.compute_trajectory(grid*dt,grid) - - -dec = res.x - -sys.traj.x[:,0] = dec[0:grid] -sys.traj.x[:,1] = dec[grid:2*grid] -sys.traj.x[:,2] = dec[2*grid:3*grid] -sys.traj.x[:,3] = dec[3*grid:4*grid] -sys.traj.u[:,0] = dec[4*grid:5*grid] -sys.traj.u[:,1] = dec[5*grid:6*grid] - - -sys.plot_trajectory('xu') -sys.animate_simulation() \ No newline at end of file diff --git a/examples/demos_by_tool/trajectory_planning/linear_trajectory_optimisation.py b/examples/demos_by_tool/trajectory_planning/linear_trajectory_optimisation.py deleted file mode 100644 index 01748825..00000000 --- a/examples/demos_by_tool/trajectory_planning/linear_trajectory_optimisation.py +++ /dev/null @@ -1,30 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Created on Sun Oct 3 08:27:06 2021 - -@author: alex -""" - -import numpy as np - -from pyro.dynamic.massspringdamper import TwoMass -from pyro.planning.trajectoryoptimisation import DirectCollocationTrajectoryOptimisation - - -sys = TwoMass() - -sys.u_ub[0] = +2 -sys.u_lb[0] = 0 - -planner = DirectCollocationTrajectoryOptimisation( sys , 0.1, 40 ) - -planner.x_start = np.array([0.5,0.5,0,0]) -planner.x_goal = np.array([0,0,0,0]) - -planner.init_dynamic_plot() -planner.compute_optimal_trajectory() -# planner.show_solution() -planner.animate_solution() - - diff --git a/examples/demos_by_tool/trajectory_planning/linear_trajectory_optimisation_vectorized.py b/examples/demos_by_tool/trajectory_planning/linear_trajectory_optimisation_vectorized.py deleted file mode 100644 index 2f908370..00000000 --- a/examples/demos_by_tool/trajectory_planning/linear_trajectory_optimisation_vectorized.py +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Created on Sun Oct 3 08:27:06 2021 - -@author: alex -""" - -import numpy as np - -from pyro.dynamic.massspringdamper import TwoMass -from pyro.planning.trajectoryoptimisation import DirectCollocationTrajectoryOptimisation -from pyro.analysis.costfunction import QuadraticCostFunctionVectorized - - -sys = TwoMass() - -sys.u_ub[0] = +2 -sys.u_lb[0] = 0 - -cf = QuadraticCostFunctionVectorized( 4, 1) -# cf = sys.cost_function - -planner = DirectCollocationTrajectoryOptimisation( sys , 0.1, 40 , cost_function = cf) - -planner.x_start = np.array([0.5,0.5,0,0]) -planner.x_goal = np.array([0,0,0,0]) - -planner.init_dynamic_plot() -planner.compute_optimal_trajectory() -# planner.show_solution() -# planner.animate_solution() - - diff --git a/examples/demos_by_tool/trajectory_planning/simple_pendulum_trajectory_optimization_CUSTOM.py b/examples/demos_by_tool/trajectory_planning/simple_pendulum_trajectory_optimization_CUSTOM.py deleted file mode 100644 index afa64a63..00000000 --- a/examples/demos_by_tool/trajectory_planning/simple_pendulum_trajectory_optimization_CUSTOM.py +++ /dev/null @@ -1,120 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Created on Fri Sep 10 11:12:11 2021 - -@author: alexandregirard -""" - -import numpy as np -from scipy.optimize import minimize - -from pyro.dynamic import pendulum - - - -sys = pendulum.SinglePendulum() - -n = 2 -m = 1 -grid = 100 -dt = 0.1 - - -#dec = np.linspace(0,grid*3,grid*3) - - -def dec2xu(dec): - - x = np.zeros((n,grid)) - u = np.zeros((m,grid)) - - x[0,:] = dec[0:grid] - x[1,:] = dec[grid:2*grid] - u[0,:] = dec[2*grid:3*grid] - - return x,u - -def cost(dec): - - J = 0 - - x = np.zeros((n,grid)) - u = np.zeros((m,grid)) - - x[0,:] = dec[0:grid] - x[1,:] = dec[grid:2*grid] - u[0,:] = dec[2*grid:3*grid] - - for i in range(grid-1): - - #J = J + 0.5 * dt * ( (x[0,i]-target)**2 + (x[0,i+1]-target)**2 + x[1,i]**2 + x[1,i+1]**2 + u[0,i]**2 + u[0,i+1]**2 ) - - J = J + 0.5 * dt * ( u[0,i]**2 + u[0,i+1]**2 ) - - return J - -def constraints(dec): - - x,u = dec2xu(dec) - - vec=np.zeros(grid*2-2) - - for i in range(grid-1): - - vec[i] = (x[0,i+1] - x[0,i]) - 0.5 * dt * ( sys.f(x[:,i],u[:,i])[0] + sys.f(x[:,i+1],u[:,i+1])[0] ) - vec[i+grid-1] = (x[1,i+1] - x[1,i]) - 0.5 * dt * ( sys.f(x[:,i],u[:,i])[1] + sys.f(x[:,i+1],u[:,i+1])[1] ) - return vec - - -def compute_bounds(): - bounds = [] - - bounds.append( (0,0.01) ) - - for i in range(1,grid-1): - - bounds.append( (-4,1) ) - - bounds.append( (-3.14,-3.13) ) - - bounds.append( (0,0.01) ) - - for i in range(1,grid-1): - - bounds.append( (-6,4) ) - - bounds.append( (0,0.01) ) - - for i in range(grid): - - bounds.append( (-10,10) ) - - return bounds - - - -# Guess -dec = np.zeros(grid*(n+m)) -#dec[0:grid] = traj.x[:,0] -#dec[grid:2*grid] = traj.x[:,1] -#dec[2*grid:3*grid] = traj.u[:,0] - -bnds = compute_bounds() - -cons = {'type': 'eq', 'fun': constraints } -res = minimize( cost, dec, method='SLSQP', bounds=bnds, constraints=cons, options={'disp':True,'maxiter':1000}) # - - -sys.compute_trajectory(grid*dt,grid) - - -dec = res.x - -sys.traj.x[:,0] = dec[0:grid] -sys.traj.x[:,1] = dec[grid:2*grid] -sys.traj.u[:,0] = dec[2*grid:3*grid] - - -sys.plot_trajectory('xu') -sys.animate_simulation() \ No newline at end of file diff --git a/examples/projects/vehicle_modeling/advanced_vehicles.py b/examples/projects/vehicle_modeling/advanced_vehicles.py index e45264fc..034162ed 100644 --- a/examples/projects/vehicle_modeling/advanced_vehicles.py +++ b/examples/projects/vehicle_modeling/advanced_vehicles.py @@ -11,7 +11,7 @@ import numpy as np ############################################################################## from pyro.dynamic import system -from pyro.dynamic import vehicle +from pyro.dynamic import vehicle_steering ############################################################################### @@ -20,7 +20,7 @@ ############################################################################### -class LateralDynamicBicycleModelwithSpeedInput( vehicle.KinematicCarModel ): +class LateralDynamicBicycleModelwithSpeedInput( vehicle_steering.KinematicCarModel ): """ Equations of Motion ------------------------- diff --git a/examples/projects/vehicle_modeling/kinematic_VI_bicycle.py b/examples/projects/vehicle_modeling/kinematic_VI_bicycle.py index 2672ec3d..f9081c1a 100644 --- a/examples/projects/vehicle_modeling/kinematic_VI_bicycle.py +++ b/examples/projects/vehicle_modeling/kinematic_VI_bicycle.py @@ -9,7 +9,7 @@ import numpy as np import matplotlib.pyplot as plt ############################################################################### -from pyro.dynamic import vehicle +from pyro.dynamic import vehicle_steering ############################################################################### import advanced_vehicles import test_vehicle_controllers @@ -18,7 +18,7 @@ # "Fake" controller - Varying inputs (delta, T_f, T_r) throughout time (change in linear.py) ctl = test_vehicle_controllers.kinematicInputs() # Vehicule dynamical system -sys = vehicle.KinematicBicyleModel() +sys = vehicle_steering.KinematicBicyleModel() # Set default wheel velocity and steering angle cl_sys = ctl+sys diff --git a/pyro/analysis/costfunction.py b/pyro/analysis/costfunction.py index 12bd3d02..ebef4a0f 100644 --- a/pyro/analysis/costfunction.py +++ b/pyro/analysis/costfunction.py @@ -97,9 +97,6 @@ def trajectory_evaluation(self, traj): ############################################################################### # Basic cost functions ############################################################################### - - -############################################################################# class QuadraticCostFunction( CostFunction ): """ diff --git a/pyro/analysis/costfunction_legacy.py b/pyro/analysis/costfunction_legacy.py deleted file mode 100644 index ef418dcf..00000000 --- a/pyro/analysis/costfunction_legacy.py +++ /dev/null @@ -1,261 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Fri Aug 07 11:51:55 2015 - -@author: agirard -""" - -############################################################################### -import numpy as np -from copy import copy -from scipy.integrate import cumtrapz -############################################################################### - - -############################################################################### -# Mother cost function class -############################################################################### - -class CostFunction(): - """ - Mother class for cost functions of continuous dynamical systems - ---------------------------------------------- - n : number of states - m : number of control inputs - p : number of outputs - --------------------------------------- - J = int( g(x,u,y,t) * dt ) + h( x(T) , y(T) , T ) - - """ - - ############################ - def __init__(self): - self.INF = 1E3 - self.EPS = 1E-3 - - ########################################################################### - # The two following functions needs to be implemented by child classes - ########################################################################### - - ############################# - def h(self, x, t = 0): - """ Final cost function """ - - raise NotImplementedError - - ############################# - def g(self, x, u, y, t): - """ step cost function """ - - raise NotImplementedError - - ########################################################################### - # Method using h and g - ########################################################################### - - ############################# - def trajectory_evaluation(self, traj): - """ - - Compute cost and add it to simulation data - - Parameters - ---------- - traj : instance of `pyro.analysis.Trajectory` - - Returns - ------- - new_traj : A new instance of the input trajectory, with updated `J` and - `dJ` fields - - J : array of size ``traj.time_steps`` (number of timesteps in - trajectory) - Cumulative value of cost integral at each time step. The total - cost is - therefore ``J[-1]``. - - dJ : array of size ``traj.time_steps`` (number of timesteps in - trajectory) - Value of cost function evaluated at each point of the tracjectory. - """ - - dJ = np.empty(traj.time_steps) - for i in range(traj.time_steps): - x = traj.x[i, :] - u = traj.u[i, :] - y = traj.y[i, :] - t = traj.t[i] - dJ[i] = self.g( x, u, y, t) - - J = cumtrapz(y=dJ, x=traj.t, initial=0) - - new_traj = copy(traj) - new_traj.J = J - new_traj.dJ = dJ - - return new_traj - - -############################################################################### -# Basic cost functions -############################################################################### - - -############################################################################# - -class QuadraticCostFunction( CostFunction ): - """ - Quadratic cost functions of continuous dynamical systems - ---------------------------------------------- - n : number of states - m : number of control inputs - p : number of outputs - --------------------------------------- - J = int( g(x,u,y,t) * dt ) + h( x(T) , y(T) , T ) - - g = xQx + uRu + yVy - h = 0 - - """ - - ############################ - def __init__(self, n, m, p): - - CostFunction.__init__(self) - - self.n = n - self.m = m - self.p = p - - self.xbar = np.zeros(self.n) - self.ubar = np.zeros(self.m) - self.ybar = np.zeros(self.p) - - # Quadratic cost weights - self.Q = np.diag( np.ones(n) ) - self.R = np.diag( np.ones(m) ) - self.V = np.diag( np.zeros(p) ) - - # Optionnal zone of zero cost if ||dx|| < EPS - self.ontarget_check = True - - ############################ - @classmethod - def from_sys(cls, sys): - """ From ContinuousDynamicSystem instance """ - - instance = cls( sys.n , sys.m , sys.p ) - - instance.xbar = sys.xbar - instance.ubar = sys.ubar - instance.ybar = np.zeros( sys.p ) - - return instance - - - ############################# - def h(self, x , t = 0): - """ Final cost function with zero value """ - - return 0 - - - ############################# - def g(self, x, u, y, t): - """ Quadratic additive cost """ - - # Check dimensions - if not x.shape[0] == self.Q.shape[0]: - raise ValueError( - "Array x of shape %s does not match weights Q with %d components" \ - % (x.shape, self.Q.shape[0]) - ) - if not u.shape[0] == self.R.shape[0]: - raise ValueError( - "Array u of shape %s does not match weights R with %d components" \ - % (u.shape, self.R.shape[0]) - ) - if not y.shape[0] == self.V.shape[0]: - raise ValueError( - "Array y of shape %s does not match weights V with %d components" \ - % (y.shape, self.V.shape[0]) - ) - - # Delta values with respect to bar values - dx = x - self.xbar - du = u - self.ubar - dy = y - self.ybar - - dJ = ( np.dot( dx.T , np.dot( self.Q , dx ) ) + - np.dot( du.T , np.dot( self.R , du ) ) + - np.dot( dy.T , np.dot( self.V , dy ) ) ) - - # Set cost to zero if on target - if self.ontarget_check: - if ( np.linalg.norm( dx ) < self.EPS ): - dJ = 0 - - return dJ - - -############################################################################## - -class TimeCostFunction( CostFunction ): - """ - Mother class for cost functions of continuous dynamical systems - ---------------------------------------------- - n : number of states - m : number of control inputs - p : number of outputs - --------------------------------------- - J = int( g(x,u,y,t) * dt ) + h( x(T) , y(T) , T ) = T - - g = 1 - h = 0 - - """ - - ############################ - def __init__(self, xbar ): - - CostFunction.__init__(self) - - self.xbar = xbar - - self.ontarget_check = True - - ############################# - def h(self, x , t = 0): - """ Final cost function with zero value """ - - return 0 - - - ############################# - def g(self, x , u , y, t = 0 ): - """ Unity """ - - if (x.shape[0] != self.xbar.shape[0]): - raise ValueError("Got x with %d values, but xbar has %d values" % - (x.shape[1], self.xbar.shape[0])) - - dJ = 1 - - if self.ontarget_check: - dx = x - self.xbar - if ( np.linalg.norm( dx ) < self.EPS ): - dJ = 0 - - return dJ - -''' -################################################################# -################## Main ######## -################################################################# -''' - - -if __name__ == "__main__": - """ MAIN TEST """ - - pass \ No newline at end of file diff --git a/pyro/analysis/graphical.py b/pyro/analysis/graphical.py index 223c933f..e42ef4d7 100644 --- a/pyro/analysis/graphical.py +++ b/pyro/analysis/graphical.py @@ -911,7 +911,7 @@ def __animate__(self,i): """ MAIN TEST """ from pyro.dynamic import pendulum - from pyro.dynamic import vehicle + from pyro.dynamic import vehicle_steering sys = pendulum.DoublePendulum() sys.x0 = np.array([0.1,0.1,0,0]) @@ -938,7 +938,7 @@ def __animate__(self,i): a = Animator(sys) a.animate_simulation( sys.traj, 1, is_3d) - sys = vehicle.KinematicBicyleModel() + sys = vehicle_steering.KinematicBicyleModel() sys.ubar = np.array([1,0.01]) sys.x0 = np.array([0,0,0]) diff --git a/pyro/dynamic/drone.py b/pyro/dynamic/drone.py index 4db89a23..80859462 100644 --- a/pyro/dynamic/drone.py +++ b/pyro/dynamic/drone.py @@ -796,7 +796,7 @@ def forward_kinematic_lines_plus(self, x , u , t ): #sys = Drone2D() - if False: + if True: sys = Drone2D() @@ -808,7 +808,7 @@ def forward_kinematic_lines_plus(self, x , u , t ): sys.plot_trajectory() sys.animate_simulation() - if True: + if False: sys = SpeedControlledDrone2D() diff --git a/pyro/dynamic/manipulator.py b/pyro/dynamic/manipulator.py index 6bf9edf8..7ab34738 100644 --- a/pyro/dynamic/manipulator.py +++ b/pyro/dynamic/manipulator.py @@ -1484,6 +1484,11 @@ def forward_kinematic_lines(self, q ): lines_color.append('b') return lines_pts , lines_style , lines_color + + ########################################################################### + def forward_kinematic_lines_plus(self, x , u , t ): + + return [],[],[] @@ -1513,6 +1518,8 @@ def __init__(self): # params self.setparams() + + self.lines_plus = False ############################# @@ -1669,7 +1676,11 @@ def forward_kinematic_lines(self, q ): lines_color.append('b') return lines_pts , lines_style , lines_color - + + ########################################################################### + def forward_kinematic_lines_plus(self, x , u , t ): + + return [],[],[] ############################################################################## @@ -1781,8 +1792,6 @@ def forward_kinematic_lines(self, q ): return lines_pts , lines_style , lines_color - ########################################################################### - ############################################################################### # Five Planar Link Manipulator with obstacles @@ -1877,8 +1886,6 @@ def forward_kinematic_lines(self, q ): lines_color.append('k') return lines_pts , lines_style , lines_color - - ########################################################################### @@ -1898,9 +1905,10 @@ def forward_kinematic_lines(self, q ): sys.x0[0] = 0.1 sys.ubar[0] = 4 sys.ubar[1] = 4 - #sys.animate_simulation() - #sys.plot_trajectory() - #sys.plot_end_effector_trajectory() + + sys.plot_trajectory() + sys.animate_simulation() + sys.plot_end_effector_trajectory() #Ellispoid validation diff --git a/pyro/dynamic/longitudinal_vehicule.py b/pyro/dynamic/vehicle_propulsion.py similarity index 99% rename from pyro/dynamic/longitudinal_vehicule.py rename to pyro/dynamic/vehicle_propulsion.py index d29e7358..82443e5e 100644 --- a/pyro/dynamic/longitudinal_vehicule.py +++ b/pyro/dynamic/vehicle_propulsion.py @@ -12,7 +12,6 @@ from pyro.dynamic import system ############################################################################## -import matplotlib import matplotlib.pyplot as plt diff --git a/pyro/dynamic/vehicle.py b/pyro/dynamic/vehicle_steering.py similarity index 98% rename from pyro/dynamic/vehicle.py rename to pyro/dynamic/vehicle_steering.py index 388f592e..3f6b58fc 100644 --- a/pyro/dynamic/vehicle.py +++ b/pyro/dynamic/vehicle_steering.py @@ -1206,29 +1206,29 @@ def xut2q( self, x , u , t ): if __name__ == "__main__": """ MAIN TEST """ - sys = KinematicBicyleModel() + # sys = KinematicBicyleModel() - sys.ubar = np.array([2,-0.5]) - sys.plot_trajectory() - #sys.animate_simulation() + # sys.ubar = np.array([2,-0.5]) + # sys.plot_trajectory() + # sys.animate_simulation() sys = KinematicCarModelwithObstacles() sys.ubar = np.array([2,-0.5]) sys.plot_trajectory() - #sys.animate_simulation() + sys.animate_simulation() - sys = UdeSRacecar() + # sys = UdeSRacecar() - sys.ubar = np.array([2,-0.5]) - sys.plot_trajectory() - sys.animate_simulation() + # sys.ubar = np.array([2,-0.5]) + # sys.plot_trajectory() + # sys.animate_simulation() - sys = HolonomicMobileRobotwithObstacles() + # sys = HolonomicMobileRobotwithObstacles() - sys.ubar = np.array([1,1]) - sys.plot_trajectory() - #sys.animate_simulation() + # sys.ubar = np.array([1,1]) + # sys.plot_trajectory() + # sys.animate_simulation() \ No newline at end of file diff --git a/pyro/planning/trajectorygeneration.py b/pyro/planning/trajectorygeneration.py index 697ae9a2..d8477f02 100644 --- a/pyro/planning/trajectorygeneration.py +++ b/pyro/planning/trajectorygeneration.py @@ -728,15 +728,15 @@ def solve(self, show=True): ### Optimization on polynomial parameters ########################################### - # ge.poly_N = 12 - # ge.x0_N = 3 - # ge.xf_N = 3 - # ge.Rs = 0.0 * np.ones(ge.poly_N + 1) - # # ge.Ws = np.array([0, 0.0, 10.0, 1.0, 1.0, 1.0, 1.0]) - # ge.Ws = np.array([0, 1.0, 0.0, 0, 0.0, 0, 0]) - # # ge.Ws = np.array([0.01, 10.0, 0.01, 0.01, 0.01, 0.01, 0.01]) + ge.poly_N = 12 + ge.x0_N = 3 + ge.xf_N = 3 + ge.Rs = 0.0 * np.ones(ge.poly_N + 1) + # ge.Ws = np.array([0, 0.0, 10.0, 1.0, 1.0, 1.0, 1.0]) + ge.Ws = np.array([0, 1.0, 0.0, 0, 0.0, 0, 0]) + # ge.Ws = np.array([0.01, 10.0, 0.01, 0.01, 0.01, 0.01, 0.01]) - # p, X, t = ge.solve() # order 12 with optimization on polynomial parameters + p, X, t = ge.solve() # order 12 with optimization on polynomial parameters ############################# ### Fully constrained order 7 @@ -755,14 +755,14 @@ def solve(self, show=True): ### Fully constrained order 9 ############################# - # ge = SingleAxisPolynomialTrajectoryGenerator( - # x0=x0, xf=xf, tf=10, poly_N=9, diff_N=7, dt=0.01 - # ) + ge = SingleAxisPolynomialTrajectoryGenerator( + x0=x0, xf=xf, tf=10, poly_N=9, diff_N=7, dt=0.01 + ) - # ge.x0_N = 5 - # ge.xf_N = 5 + ge.x0_N = 5 + ge.xf_N = 5 - # ge.solve() + ge.solve() ############################# ### Overconstrained order 3

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 L$9G*%mGXZ8yBJ-` literal 0 HcmV?d00001 diff --git a/dev/dimensionless/car1_policy.pdf b/dev/dimensionless/car1_policy.pdf new file mode 100644 index 0000000000000000000000000000000000000000..8a7a8c0a3f32c3b0b5694c6842cd926f59a8caa7 GIT binary patch literal 54698 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 IJQ4N$2hlx~$p8QV literal 0 HcmV?d00001 diff --git a/dev/dimensionless/car1_traj.pdf b/dev/dimensionless/car1_traj.pdf new file mode 100644 index 0000000000000000000000000000000000000000..4e6e6f316d4467307c17125d95eed0fc692d5a80 GIT binary patch literal 15380 zcmcJWbyyrp*XR?16Cgo?!{Cqr!!Wo8cXxLUFv#E#2ofx~1rHD)xVyUqx8M-mg9HhB zhivxS?CyK-`~7o=hi9r!pRPKm`czM!`c+XYh=?(Qm{?J%3+Dj^^{5~KGXP>_h04nd z0IImznF4^qhEPLmh&ceLU}$dY2w;IJC;|BSQB7@4?iD$IyFd(L3k86GDgZU)jjT+K zp#Y9Q4nu7%s40N;4}pRq6l&^V3t+oHLoppd1E1N0EDePquCNxFVSg-~oB$ROJ3to|C=7E7cBLbL>kmyqTU!Y12z0OZdq>=#{P3cOKk07pj+J)Ck-b#Fe zLdeyJbb1UabFGY-_JLI69SR;wMv)MyJrI4F5|OSSwYQzNFgZ|Zy)UF8dDTi^PFfIh z+FN&Xm3()5eK}a8?*+QqZ|K;+oZCG|61Zs}mXAq({dKdZPhS7%WW4QYZcbQXeMx_= zr48-QadpX?1Vez-R=|AAL(J2OQzD5ikoAYorbbMRbvn=#LwoDXLxcn~vKW83O3&}` z*4T~7q)F)6sG-P?GNL)L8__!P%jLu4-itCz@^@*r0+`js%wA!@3%1>_9Jld1p1A6) zU)Si-sRw7W8P2C#+G$_$c)DDex!1_7et^h4dJ}2bYZ}VGsfnkCq0buX?Nbw2_Rew9 zoy`2T@0al|o5O`$J;M_&zMe9r25rAQb^S~dbXHfL!4eTFRgxNPaj=< z0xd3cM?Vi43Hf$*ftozLT^7rgo2htEWZQM_;W@JbcK1wqJJ~5QA9ot7nX$|3H4%To zwfHDG;`QbJVR*~J_c6m4{W|4BJ_($g?@5G>3&(eB0)Yn@HTsu*4ciAj&hHj)P7bZy zkG{0bUl5(oeD8pspME8C3528tuxGPMQjn_6s#(z5Xlskl$`$f`20*TNZFZH9IvQ(56PnPG%Bt?pS=CnjM z{PK=@ABZX?j{6GGr|xI!+%9LBRXbyTIwa$P)D?Y%m|`LU%AG#sMcSxj%42RS5G<;9 z|F-d=+1;!wxSw$DaJe^)u5W<(l>^hO4rOhV)^{w74T{noXdbH1+0nmHc};DZ503)) z?2Yr9Q?ZHh9nrgZ_EAV!kR&>001qS3ONxOsD724mC8dc zqX>+}NN!)->C}y@RT)bwFg>GlA|{>cBP*S1GHXOFQFs}cFeF=V!#CYJH@UB(9HC9M z)Gze;CVp+4y8)MTuFB@9=>QgS1SE@F$ualwhXD(>cNmbh!M+vt3)?{8oF{)^&CbS0 zc$>o}4E>*)#C)`&bIT1R&DJMBi1l(u33jSJ4x%{vJG0klYxiuNbipD!^)mYbCpRH0!eKAyrcIDopp zX8mfncP6+B%>BHZYb0(gviq3F6z9%z4u@m!bIOP4Gd;AJL1lPQX}|2wdE8KrmX!C1pcKSL&2#2-M!$Mo`i1JD z=nxypN#N8nrqg|Q5%W;>OQv-SUoCTz0>;-kChOehx~5c_KAZ4RNho~2_cX&1vRsv9!2@MGA}=sknKq(!Q`vJr2fxb5g1r?z>5{KkUHCXhI-)v%4a9d24ufvQK*P|>t)@lY~PkE)Us zR)+)fnSu*~`gUHOmg(Hie8~RBPGg;;j%6a>plo6GH1-m%P9L^om7?b>8{M)d9bIOZ zw=Y};y;Q;%r%91YmZj2@3Qk$|Qnkw#bW%B7!e7)|mrO~Bl$m$EsC6IVhhPy-d18!l zGwOVJD)4IOpd#2-x={MH{R!A>FF85*^_dXxD)?;8Vo~H}W}=3NA7#1xkToNt)kf!3 z$G!pgw^44ISF?l7p-Dq|1kj3x#2(xxibhV|?bZM(e^}qlYm3PH5 z_`wU)lxf4w8BXPC^#Ym$cpl-;O-~w|D6V>EwX}M<%bea8Vwsm!z#joN7puua3&S+B z$0hg6h32O}qW?JCs*UO`IP|*vbcp`?lPfz5rCDZ6WmL)SG~B2tleA&DBSlRqKQOMU zYNO8Th#Egc>*H*@=Ti!D5vmuTvJlnAHTTfReMi3!O{g(47?t~s)Ju+afK6w#=;4F&-Y1qH}Yqp>G&ysyeVr#TvR&rp=?fswh-suRIzmnJ3hj zih(Z~D=w$8Sl7?qK8`4jBuwf(zx97794_?~o%%^-1ny_u$3#1s{?py<0#D;79O94T z=Mq>2_X)mm(#HJI@Q^QZrc8FQHU?`iEczB8sO9gemhG0|*LUD<#*8BEbe*0Xju3jH zYXJN^6JE3VJ(fS?wYG?=%(Q;C%lbacFR9mOOFW6W9%j?y6LSSQeNc*TdsEKLl*n6$xM><~t*L%V z$<2m5px6865pLN@S%_pvysnUC#XdML_QSE7aw6pfLNWhz4TLU2)i}g*-r22!1pRGe z%B~N+lXY*bliH5YH+8cv*L*I*l+jOGhyzdZ{oB%1nCx4P3}eu{d%iY)Q463$uNw$Z zE7SLVDDI0E+G=FS)#Ump+~l@4yP`hmWDwaa|6AJw;vhO-tZ6fXT{GzrWYqx!qZjCv z<-3Ji4CP8!@nK%x6i*n|o|P)>U-`H8xnQ?C7vw)u%N(9XdRN=tP9&2}O%qIMu%b{8 zZjBdMR0y$ku>TNC&eCfz#vTE*eu5rHAWgK7A}(M{>?NlrZ^JsN5+B;D@%+uAf$5iB zpZ!a`FFr@o*^j65r7W_rdHAfcm7rJj-$&)H#NaCGcL=G2(>w_ZV;aK?y?c@&GS0psGf!`#-u z>j36up*V~|d(@PEIQ6O#mWivfhcc_4E+y0kBy{^42k)>*>|J|-vl!TZ(tB0D#dDhe zZ0rCgjL&Cr6zm0wZ(9x9Z7a|urw|=$cUh*38+5gaXO-tvKQ1uvT&>7iG0$+b2gF3` zupYlp<|W|y>^5xXbc~$tN<7#`vTb5r*)<8L4p(sIi9&JnmPB+tY}|xdoQL+nhmviw zik@7^la#s1pqLV`X{`%6Uq)BD{s~H%*ej?;WrL;PxWfdYj^x0Ibp=u$Ug+s72}JA) zFGyxp!Sq2_yG-CiyjfNyY>ii+>HUh54L|$hQn(8VG_T;;CpMNK_Lyug_&geoCM2}a z!cakuR=m9Q!N4}<%EqY34U6tAduLqScD9hG_bhV~!p0_6OEH^BW;IAAP{lw<(BqiKL-|mO0y!Y$AXmNFgfGpM z!D>KQgCk?A2*Co$^hgEK&#{sN+nOll(Y{vJ?7Yz(y(wahN>!Mec~9BoGfVO2>RU5M zr_t~F+tmUOzInHCE=KV>S9p6i)}Ft#gA8Pw`wvE*#*mes4t~AEhKokMPt<;88oyGu zpJ~Z`ssaYFbNrdd{mf_Xh1#&B=TF7^Um`3CgMK(SBuQpWj+a!)d$w4;p^XwB1Y;FM zN_i(#m2h4uMm=B1R5rGcy~Ix~yB?)j)!^Y5BgrM%nkFC{m*JZGW=mn5$gO;Ln8FpY zpsAhnnZW2PfSiFgclOgG{%-zWH#FmW-1rxQO>{Q zRMAfVb}%*6EOdeTHRp^}Uw}76^nn!p*MqYt@=Z$Gjzs^C&TWh{m+zjzj8FD({9Po@#EQ=9cxpop^) z<%3^#{eo@v)5o_0dxat(pRj4%YY3v7G4wHI9zQb-RZ5;k9^e@u7r33QOg7~VdCdA1T?$fkIt$IHWlQrRn zs@Jv>9>roADGs#tHc3%t5l07D$c%y;q3f9;#p-CeVV!k&2))_-3je~)zVN*oFwcJH{WPuI_@&uc5!H)t zZ8s+4rK|`p&l_U7k=v73DE2={ZiV)@_$*a4*%6Swt%;MmAUrdAM4ZEo;z!(Rn&YS9 zupIj}`1wnsDunkhn8bJS>}dCV5jnHv^6dANSa)b=UO}~P#X$P)gSDxwa;=ut5xFPh zjaV4NouNf`8Z)11++I~4Wcu&sNzD1H=l4W7lQ?NuxFa`mB@GZg_a zb%+)vZ4QfMhZ&Q5VJh%QH}K&Je$anCp~_B)<97QeBB&omu&BAuTZj0 zrsBL4?vCNOxFIdZSB%t}O$j-O6rjG(QjX?l{wO%JeBDD*L7wezj-wHPu4*$zv0Q3j zW!Ms(CKxCt6SbnFlT6BTxVe(mvGtQHhaR#O#+Q7a-=2<4HIgjn25A~cS|?`(f49Ek zVKZ#XpxLvSR>7`jvp#M{-i|{1oLtiAxZWP1AueQZf5d2T_x-cCw_6ue%Ow(u5lNRc zoy4GR17Wg4q3G2H>qei0Z_N7e=ZIms!=$GAFDq5<%8hyP*1b7=8Uy(mNo>MkaMeq+ z@na45ByJ!RIe%Psshd47AEV=FIgZ*g({r7qlC`q-YyyM>SB!1d2o4gjok_0QkUZVv zis2-EvOeU-X8XHwvH@MEE!$@&KXS}}yQZj=?@rhp)z&F$x{aruG+48t>-r7=uZhC#an_zd-CV*^|?F*FV;vg=&E zY_g1_Y(upu&S;_>$*jiu?+ZU@g{^54Wx5rh%!M(rCkamWE|bv%YhK>=%P*i*pn!EJ z_l&9&ix-2E$A=O*{hkf;z^UZeldovsfrAI`F8RZhq-rKs>el>a?ce6x$)SCH)>ckF29?b6I&j)+XJ)dCM zCRQB3kO*sbIm}(C#%v*J`LU%XC{wp>@I6+i<=gh&c`w2P&{gw~Eq!2jtY7-#`L}0r zL@q&ww~wp*Duo>YZ(22g4+P#^N<@<|;W6KQZc=Jo8h=pq8sG2XmM_LJKfE0G z`94&gs|{WH)3cGnFnl=;;S}=vwzokf56Hf^8bzxK0*_8CstN1!xVMWgrlt&V5G)=? z4k4euHrlxh{F1q=N7Orx{9{&%yQ!0XN8s+G3y;-Tbfc4Ho9gL@;OMS_ z`w%Bb$VfIG?=f!GLgkBFb;T?XsSN#k6!wQQ^d7uXOHz*;4moUjiQ3I=E(Six@~(F4 zOY^R_=i0c=2FM2-_E~xw5z0$mtD^8T9VOU$?wK^XGWgR%bQ$HQR#>xF)wwnBJ%eVGrhiQEe-URrGq^AyC&^ zvYlsdI&*bH7GzY>DGRENVr73@I`ay9P;;y5>;CxkgsUE_q_wrh)3D7ldrZhDm*BV0 ztPc;}@fK<9g>}=W0cA{>ujqqjIdvdy+VuI_>Z{y@Q<8;w;kax}h2dS#gX3_^GL?)( z8RDfGVloAUj!wPbaXvb^IEy6VD?Yp!!ges!l8RFAk>+pwvMt*jr zqgaWEM$Htbq2PO9sbkdT8C-{jCq0m{o}ztd#$%SHYm#fMhRW1m87%sGpfV$LyM4X; z@vF>U{2r(x#Ta$YQz@PZwBQyyer{#Pb}poCvOa`iD)}md5CUah&o?A7!D+1V?jg>Nmsp^T=2(rF7PfZw1L>VFZ_%u(6};V{J-QMj>b;8}}Lx zaB4BC8On-;;yMM>Xt4w+P}e_K98nL#TJv{h%1m8U(WMLSg_@9J^!4Vv_g?a1*z93E zq@26UFm3aGP{2xV`yXCA@8|aa{x%7KO|k#iw?|_G{HHz$Lq-S>9#TH|Pp`B8Vdk9$ z%<SC8>R^7daEi;3BdoB3ab)nAT9daD-JT~7qa4cOtA$n0d-2UNS_#h`KyrT2 z)U$d~*8rUoS^lzY67jUM?D_IuJm4uJahPcjYHZXIXkA~f9W1x6#`_Y42QXE8M@oBHLE4b6pa{*D> z)1YtYAZI>p^qCr7Dg_dkl@r;$;w<;gQz_d833=A_<$t&v_Z;TWyYUC<`H63U%1%bm zKfsTq$i3wL9PEW($k5UB4+U{ybqx`<7b2!shH6gAhPIB3@IM@kJK5P;o7&tH z8s>X60=#cZ#MIH)!O{*2aR7jRQ6+T%=oheoHKhU(mlTmTw7ZX@zq1(7JyrUDI(P3C zEOYr^c_nHKBR2OI1OK#`Q33$Gw+@yk8QMbcZFPi!BhVjcOBaT#AgZ>OKQRyJ4{G!m zFZ=1qzfJqi*Pp)28QQ?0)&I4tf3hTV7?WcEqpO7eKv#?)W>{Y^g4sZ@K4E8uK`M4m z7#;%(-kS;qaItXxX*U$W$O489K4C*U2~$gR3s}FS0yX|D!aRjRKTBglTXSnuSXTm- zVfR$+zHsj=EZu`e1n3vplQnczw={v#E;cTf-$C4;U=Y?Xu-l;xU}FKnOaZYl-;mOeF{Ack%lKN}g2b5nA+l9Pk zakuCNMIL|M2#{8b?Q*a7mf}2O9@bFV0fKODb4US~dOKv*L4*v$TO3%6Qm(U^$Fm*@ z>dAQ6i;@>!($7P0G*>I;9$cKqGsNkmamGbNojFvHV1&K2?DjqqdmI zNFSPBcUmGWGwCTd!CwLknRFlDBwW0DVIX3o*(LN|q;U)HF`Q0;Br9Qwp+wgyC-w=D zsi@ee*1`MK$S&}tYdtihMC^qn*SLcgGjr?UA?a4oqbzPeUa5DL_Li|v;YBN{K1lfQ zeoEcp4xDm`@75tPMYQr`#V;CsB0Kwbi;-22`W1e$eT84ApdPiDRkmuy;5j||r5~P{ zFD6YptHBuJI@UT{tn`>%<~LzeaGbLNgJc5A;})uSk~ee-8ou3JAG6+rr7H%IS7@GM z2udFcH^+SYI19R^xIUycsqqPu(soTXDLU&&|G3`1!>!N@6yB{PIrA0P9*(=Ux`VSg zR!>lWIqqw9r#qsuSMn*tQ_+%b-KdI8OG8W2$!3sDYpn#3B$|!($+WDc9aC2NI?gsK z2FR;&P@)SjiyJB`y)NX<#i|M{07wmrCt2a4+}?F67xxc_gnlmYKl`HA2!BA`S2Ej+ zVXuYN9GhL#Y!RA6+(-DVf4_Y;NkBSDR*00!@>+;d=EN5H5!#6vYp7sH7Q{lhXpppe zj_fO1Z{Voei1$Z=ns61mOfM%Is^(AncMTyGqF*ly$%=*}@TR0Z6YkET%s`PB{jW}U zUFS#mzi~HSUe&Y0EjNheM>MmurZ)0wog~$kdKEp%vjhgxN4f_M)xJB{$du=>&Ai3W ze`CLm4n-lN*{e1sqK6_?eTt{2XP~lj4>X!~*GDrd!vN|ibNTBD4Cs~u$FEYC=kyK}Shmpmn z{=nrWIgT*>Ji@wy-z84NM!GHjW8phh??au#rsYHf^Fup3?^4DKBRhzlyq(M8%wlgu zoOd}kuCqUv=8Uqg7YC!^%>Mo>X~k@LFRJC=K zzS9b?P%acC6tQXeij_js?_`InZLur+#@Y(u!pG>lsoDdiAaN$gQk|x7xbM&(KD@%V z2?7Z`2DLhrF1?}-=%2eu`mIel1eu!UULVm=3G!U|ukrfo8?iStL?jd)qBt9%T?d;z zW#>T%tK2A%c!pHq&o=hl60s^ph1&ovTWaTk}mqMm)z7 zw6qwaJ~A07w#2FEHk>o^dyYAdZhYOS;4O#lo{{SWNx0MNb*59HXVOvD`FwM)YVwo> zfYT}xvQO(O4R@<;zAshp^O&uvH(!P88;Vhsgk1olLCxivgOI6mUMB5;yFUro=IX02TgPOPB6rdPXsmONHbAzGX`!Jl^}g9ZX%M&(9!Gff}t z;t}CRfS8hn@UTUrPA1H4A~fN`2U$c>6)35mecj2*WD?v@oZVq~KTD`<*E69e-bO-& zf~ZdfIu|kj@~UKJug=i27+iCV`^qV=n!k=Jd6-8!t!QAXBv$?T=hZJ=JM`h5{q}1L z#abH0+RtDUR=fFrm(%sN^%vYxy%s1ZqZsu0oTk&pu{JeNClX&=Fu`C_dH9x*+hFTkfHaXY9NH7Va`9VZQExpw)w^Z3x(jBo__>$H3zfZP^)cx zH_wl*A3b=39yUjzkfzg>{o!YH=2{oSl$1XLTCT&q_np%Tqn+$_r~E9v`ewtZyeg`P z#|*-Oj3Ecdr)J0^yKu!@x>l|8k>xX0qsDTedL!&Co%bm?-!j6)NuBiO+&4Xj3h+d* z;4X=w^<@}&k9gLU`{_xx7vkw~%Z1`I9l0}w%UL!J;*%)8s){Nt7$wtqWM!xBrn{N( zJp{{nxX(i5#lx+%uj!R9vWH!PEv?6z*Oj^U6Hz3?qz5WQGax` z+$P?M&eft#7mL`VO~2H#8L5;Ckip+P>5P=SfxhV!e0a6d8FrKQIRun@>cTay6D1Zx zjiW#jvsy8chOL_*54GH}FwB<@W!wpvpD)~1J$y_vEdTjT3@t(4L@qETDLNhu)tFF) z)55eA9xv9Fm2Gf2(sfEEzc`}xJb5JG9j;&J5THN9WqIP~6v3}`zHGLA%D6ja<5vHA zA%5ndy6S4l&HQeb-KC!>jLh?>3n>i3>}{i~%lJ@txZcv+Q&KM{keb+*!=3g$F67d@q15w{eeqd3R8Y>V>FYCW}H>wfT zp4HpoblkOwmg^3TOoyf`A_h%=>Azzd%)t9VD)xh7ov)tV60_0dLl`HqK$=8W*rwjPUn{sfS>gBdyf+-Rwpm2j%1JC zxCYFtI*uN?W4dT*ZqbpGbq(i2J}=#M6J4Fu$hJ7C8v?x}(2eRfdDc>z9wDaeZgFxL zyp+}=Y)I5hObwHv{wn$04PA2m!<qys4Ww1P&UBWDErTunJCdV1sIW(Sc(Gz9k2j-wq19X#bRD)wD7s7IyHnw({!hr!L@Q zqtwGUjW;s2h3y{!dtmEkL|RP;iv*$4fc@$Rx>ktch9*w)L`{2RCBRd}wxSFuZ~P9@~4@0CAd+?ofiq~Ghq zI!I*~gQhGv1`AzGk_-f^s5|n*8mNiz(6?=xS^n4b?LyrsyW! zYTBKd?KCbWrLIPP@LVK^Pp`%cvcl#~xf=omJ@TTdBhia9G4lyVt$X9#!A63OACsnQx+CaC&*8SoD)Kb2#>A|` zooowVmqteQoNX_RF7l_(t9sJ;V}2Cu*WC89C*nuemc)si9KvR;R1S6R`!2M*F?PkM zMZCY?=IK@EnpdG>e`Cznll?$`e;>reN4jIqc8Pf8fIC)ADo_l$$f3b`L&91vNJNh~ ztAT#?sYYVS&h*m$=;2qfMI781n=zWS=Lqf|kFUbL$LZ9ZV#Lan?94lLXGQkZUn{$W zT}jzkSL4fgHHq#~?koy9oZ2cDHp*~GQ$o1lTPR7S{iAissST|P;@~R7;J%mih4b&v zMH2H{eFBRS3;GAn())xu8}=+RLRyMa_I*b1s4NeZz0UU;FD%!#O33>@@NFzLExF%X zxve*)Sx(h;W1_I>cwCKhdU45r*yL_G);r&ysuQ@>illO}_+}Mp@$e%$VM_Kn8=aQ{ zNn0-bAUrkv910-{d+(<2!h854c-nOqjpr_HO7wW)+wyHs-5#ifaKHS9vGDN1hqSMV z!?#j0cA{mSn~OM78q&qi?tk;ni+V6BTyn{Xd#Kf>TyH2gITj?9S1vb`)A)&4QI4MM zx&HH|9Trxb=+Xy>trW~L-wF<6NA#SfMpZ)2>qs|(ITEd@j7=<;ctuhLSq(F|o93rG z?MO;qC6pArc+!y*xXbOnozTYVKBv}(`YGqdu~1Uev-wDjuJ0kZ`tId)rph)C6>G^O zEUdOowWTMrmjfN}C5mD)e7BI4Z5S9b*4`3)ahR79ltic<(e_rHN*d zv(B2}8le89DzZb~_|^p+o;$n1YRk?*8Df-q`f6UP>gj7)N)G$H4~-!f&z>wb1P#fq zzOZy|nK~Ed2eUD;Q9VsT*zu{rSZFDc+_mM*=BTHI3fZe7 z$MunO5XG>=SOMo-Tka!fV%w?a40&J6NO_$lb1WT`UXvVVl5y}O@J@p2d|X^An@;TF zUYT%vHiwQ`;OC65EVMcp;P8^dQ#`x`wF$UN=~rHD-#DFl4z)3FJ3`fyj)-2o9z0^{ zxjS55D!QmKYg}s!jd-WZ>Fc4RVZH0|ABp|xNk_a- z)6#=C`9G@+Ijx!}UeNalI9zvzlJOj7nO#S&YVzD(SzRyF-M(4%5V>8n;vIJjbueRd zUi8iNCM!wVxVUolOjzY1z6)E0S;M+Imx~ow!<8o0S<`qy)5AepO%JI(Jhk9$JL2qF zvg7SOVhb;E@C;Zj;c6Z@qU)h#=hgY9h~a2009CQvbloz@aM+?F)p|j89VgDEv4yrG zn11~NlXSVZg;exkE-)?@; zK7qu=!P7QS)om!0r|`lW1u=+f_QVa`J1KE0eBi zn0Fv?7O&U7%>ugK?M(LH5UZDTUrE`izERd(Z;K;Vw`=?oa{k`l4W4OLV=jzsL1Q6I zx}hw@n7RiwTsqnw)YD+&*1rHgVhRscTNM)O=Ztq?_^L5r|4{r+f?=oohZ)0~D^iae z@#avl!iMu|3i7oiwD1@kIJChTYL*rkh#;5Jr($aMc*t&fUpX^&j%y?NkUe}Hq8hhN z3{kZw?8%z|$F0Wp<+D(u($Z?g_C1#?UuGnxWnc9-BObOVCfQ!C2YsUpPYvM@H|AXQ zjM1%|dBYJFu3}rhx~2Hcv=WFPoW;NyZOqj3w1cES$d;I2M)fU^okqAML5@*4N+69< zHA0iHOi04&6Kpfnq*w+tvn0DiKo*Nem!KH&R@cKDzJ4F9y>vtMOAqFb@a-F3sY{K$ z&9-LJm@y74J{p&W+l|MN%IRp;ceIPF4BX)e?DmlfCe7i z{0D3Q1Jwfmujo^>e5fDuldf;CL%5tVyYh>w0qtW=uG0e6F7ji3NoB~d9*>e>CkoFH_!MVo4B9NoC`d{{ppclq_rx=h0dIte@T*`}$T zBGUIlc#HAvd*>(RY-IvHUozhtsf%+e@t~%uER+|ohxvJ$s|mW9t355nxjW0^Gml@*7L14d+<>GGVEMTf3Wj8UBU?Ym!tc9`SnjtJ{W&=Pqf`GP zTmQkE|1Ht_*@a5?1nDoL#qv+s_S1uZoAjTF*1wE_-4ts>7|8;&{F7wC8fRo>2g5)U z7b}b#fx-87{Qn?XKk<_iY!BFIPMz}Exxi=G5{*A72aNO(MU&+YQu#{|L9gcJcv*1z~LgjST^ypUhT}<^QS%dwTy;`>kVtgS3C~ z(cd1Ye+hqEhx(WB-y`9V9g09{OOu~C_I~{T3|?V~6O4ZTH1gNVR+ist1Pa5p;oU=K zI$1-goizk%ZD|B>1~YLmu`mEEpinzUZXnR+e@-z$9L(uafeH=~6DMQXuD;)|vNJIQ z7#SMBg0=Fi2AJm0>MbF*BKNx==|s3$m|4Ie78Y1UvoN!Q7@1jVnVD(nf9n@l2U9cD z`#p%L%>VNNu(Pp&*#KsM|B>B8@B0tH_LmIA%69*__=oI1TK^?uhjq278ZY(v4g??Yy$-5gbm!k>w&;*;D5_FSYe~+ zZ}ov#xLE(y1_u~6%>SXsdOxiHA%jKDzxCKSfAa(CU_co z08q%l*}&S)3;>ijFf(xius{S90sQ<(Cbq^;f}Fn;5Vf;)2C)7V0IJIwTACO+12}%a z6mfSJQ*t(NHUY5xo*{4G>}=v_3jjY|A_0}m4U8>p%>e9w+;y}wQZjJ{XhE747KNx{ z;_eIpO4vYD5d7m?=#Os+fX*-Kfc|a3lip9-JDNbW`(1sYqKT88i=z?5o+tek0YDiO zV+#X8J9kKn%#a@zPEG&|h#jDV1Qdc81u5wS;QC!sz}D6das_%4`?VvUZvWYH(k8ZM z&gKBnAFYX6SVL?D07b1K9UyFCWM^#hq_UHx`mz3@t-?lZ4d}Ar z`=TV5s%?Ot%`q%X`s8|6)mhN&kwxA5S1m8yEho*zNp0{Y^;Naj={_Sr@Eo5pzAH;x zvb7GH<9ZrL4{*-KIK%DcD6#X41V68+i)~3AxmBD-<{*Q&a{Oh(GW1dp=Y6f`Xq$n@ z%gDGvqN-QRU)kW@3+J1@?Orr3CAc*$l!+f3G~V_P?kv4_eJu@qxXVv=$I_L3E$g;& z@Zef^d#Pb=<&YPD9u`;ovN5{0q!`*pY=UG=l{=>gaMIK1l#mhPunA)~I;sJzs>NZy zAA)L#t_aV8Hqg>Q{~_s=x#C|+vOPl)mB5ygv%gI3*~ZFMCY?3aFfDmKQ+sVL#D}>R zO`aw&a;R2`;Nx(Dq7>e1z#h~HA(px=pHA|UMpybK;vU-q63aLU?^-(i!+2(5% zOngyhhg}>W8bQEFF!p#XUZE2mpGkmGija8tcOtDs!j4?-2vv>H`7_>aWx=Zp%q@9> z=r7@o%2lDb*vKGiEg)W$2a$U^6*hPYaoAtOR9G>Mm|Y8smpjdVL-3OWO}%+9adf%&t>bNly`=XHk}7rXXYwn`rH%HnpdaLd z-PP0U1#fo;?s7e;(Dau4`E`i9R|G=f=Z{mRIRxr!^r7u)njBj_v<=lT=LHAvsW;CF znuy=lHrkw84Y$16VkKx!bdQK#SXCp0I345)|F5fa{N_}*2*okDwwYCAWB%;+KEpI3;A-Ya>B`dDIs-V z;OA9$BkZL!JJZ(P57Gq^k($VRAyQl@FNj9sh>)t z_4W5^+UF$(m_C)#_8sLnXX@>)zL!XtNzWf<8@d&+dJMz$z0OA0EQ^S54LQ#bGrp#e zd1J(12W?H+)bhjrd~53>c>LgT78UoS=>p?u@!&i1@jhijJg+W;(r8u80O2s>-(EDL z53{$vl)&esctDY83`d}x=whRS78SPOubQ3{Z6Qa4ZbYeyB+6z-hdReLO)xPx3ZvWU z=|9j9xiY1*)xaN2B4y^_t{q~ zvxewfMVq%@m$jnGnNo_-Zc4{gGxtS5iv{L#Cp@_K*P?zR=V6C$qRW1dNS^MRca>;9 zFD(H-$v#^noQmP*|4Pa{@rxO&X_HCQJNmblr87`gVB7A}t-b1cC2fUtC%ZVJnFF7r z=cQ*#PBkN@MGo|?V72`2)b-CwK}q7Hbo0Z@u-dy=PBr-ZGmDLeUmaaU7ZFPXRl}jO zqhHrq}}hY~cZpA%ZRD~ey_ z6f`Bo%#QOE=`P+$m)&fcW|{F#mibVp6LnoB#z#`y9-^Z4xv&nZPWTveMPGa&4mPd3 zLLv?8m#kaCj|{$w>2#=1{6RM=oDkhb5$?(}Rzv=Eye6(Xe_wp2vkFG~^W2UhuW8({FVh?>qebGCYmm7c z&0Nha3-S9_U-A1vX}*Kk`z+~LV^Cf}ZqjrNT{0g!@P?iVndCTg%HdG-#*EZV2W`+} z&?e)>q$LNN*h>w0$7and*ra4)ns@Vo8Xw?>L#yRP%sxTeIH~*kdu+13AYOE!jE`N&;R4HR9kXCJ|_QGU&=R z>6Eir@a#F*NvWQfCK<$O{!#@Ry@-~$o*%QY@SwW!I&sRS=yXsH)mcg6 zx9}*!X4{3NWpJ5mY;k+LWl^ec6h!MfqsFT6lBg`Cp{JJ*Ffxc+FReUPnxvV81um}i zYkR@frxTV$R&$LKb395QNLLdCfwO|I6S1wTG5DcNN=#&8OoirxHehH>_OfLq5r(9Dcv}_s!NbeREbFc;G8j-WQEgDu|qNn45WRprFNOJ*)WWGaen(s^X~iukku3iWt*V@CN{)xKK4 zp!b(=m1zdc`d-AnyEyilwaQ^A@kXEkZ<3h$5)X}*Ch=Nq*4;iqSj&aL(`+tPUzZXj(wb(agn$`*!B zVL>M%s+%aUwRMc~szYdX0Rhfg1Ts=58$5-wwtW z9=c!hgb$6tCzkY$X`b5Btus$mY~tdz2@?wvNTbbh{#G2@W4R|?f-Fs8^mbGg%a2>U zhAo+~{ga!OE}DxU&t5dlzPp<-sCA*oM2?F{a-#A}oMU#U!3n26&x#Fup7LNKB{eSN zU{FOm!sj($1&4KqkCI|nfEESI0+p)MID>92GxI|v23w6KAy03FS2DX8;YHTIzbO%w z5vbdj2r(2^mk2amTA&MaXaZ2g$M4)Iy{Ev>IX!FZ*cW`S@ZLPQ_?o@L(VF-?`7I-JqPlgHmTpFjL$xOLEAO0?-}Wz=cZK0(%t=j74C1CW z-%4p2=WF7`9=#{&UXzt2@`#j4Zs9j?_^4A4(^wPidN>ZZNHq<6q&&5*QGXfVgB7zo ze^|q)A1#7cbcpR&oTUAsVQ~;{sm~f0wOvgsXa>$Yf3vHtlRa{mB(K5B;S0=UA9=qn zu8CG7b$o5lf`M!H8=ejQDCXr|$v`?4pyzykrj$>W0rOn%KvrBt^q~BFQmbVu>Spgv zpa|+2B~SXj1@mFI`ys>lo`ucynq^@HqrF^U-dAQ!BTeH;@OZ6C&@P9TB=VwEwI@M< zls2Z&I&_I2_mG|zq4B~ADX1N(SW-L9{9Cp9=G6P}Eq}QJ8)_bN=Sg@uFWrf$ARx zZgfZrx6H~6v*B3|qA<0r*e8(B?1{LSog}DixihoR7{(W2T`20pED6ov%6zY@iUoJw zNhCo-K_I5tH=~5TLYAYL^kpl#-Gkpt(+ix*OMu-bWLZzC86#}=hRf*_T;z4eIgIRv z{Fr_o0#ebeJ);Q)v4~Z1zWx`!kSx3hr3hI9UITe1ArOPnlYfF!4{pr*QZ0 zQ6LDw%E8J40R9mZvVg(NkVx>qM1(b}p7zSq_>Uv^BMoj>N2YTOWaxgL$;s;pLr;Vh*hU5dpwoXT41$44_Gt%BD`Yk8L>darpyM4m~~tsPxP zk!@4ZbS4INcI~2Fx_$R*y+|*MVeNcqM_bWPHla-LxN1A$$d(+wk@$8I`VF` z4BWE!Y)p3CLxBddST2+U;W5r$)NFLDVB=UGQJ)+kc**;apB*VC5DRg^!z=r~UIvUc z!m@sRaRGA{GK2Plnz(y;g@Y6O2XYGZSW9)<*Viw0Ub`wwP%&rTsD7eDB7?z* z!9&$9B`PdHq0#|2cm++*fJ61G_eS#nD3b3d>>hPmm(e%)z zQBbZhooHtl1ibSN9)J5_y0Omg8rTXY?8k{F!qAOJVRFp`^KzilPyr)K3!DEI^{bRl z`H)<+0G`T*F;ViA$1F|B7u_Ex$r{f`YQOCyK*wSlDh##vwMbHA6Ggu_mmUW>vN^7} zyk>nn(wJrwz&aUbE^0>DJu;!eFWtT{_3+GWRc2Gh9VK3&1y$D+K2}@vE#_sXr=Z{V z8VtRuL-8k7V4=g_r&$`=$s5y&5=#2x_Iu;WayD4ES52|p2p!4m z?D0u{tBaGoCb%?&Cd%VR3?S+<$qP_+T#a1|e)Wd12KEy@lh{74Jv7wnv3?!;Q$i(dBuhn)q;qa!BuSzz8(4 zfFCzWe$S|`kyEamEy$Bn>u zk`*C$(dE};3led})5&9Y%k{nU4kg`YDlI(c?i`Ja8_{I6Vx-b&NyvjE2lWq1I+?`SwKRTR zuklc7E{wPC%i~iY`ka{r7Gh%hy$=rmr5t-8weN;|3S zTSZ4MKJ1Y@+OEn+4r1@UX|DN@LY=d!(Ih?6euU;$hsR0MAsv?;Tb%PBd1gO8S7~S* zh9ADycEJ9AJK`N1e|k2NmAN(Zvg3%LX`|tF{#ZqDvy9j-xg9L>!eQYzE`Qed1`%1z zQfMa1qoDmEJcdToi6JiP7-~mlnN6-funglkc%&ZD6kx_{M;;7n%ERxwMO+TZi{y8&5<%C5cPt{z-qb3(INj=+Kv1R^n z3jm`bk7)P?W*W^&9$iyZ|5Nyh0jT(BOpF2E{&ky_bir*+dg9gol_p8x3r{@K^W?zQoSxD}gT4XQZ_Rj+h&jf3!1x7+LPCLuWOvc2ubXoU!6?JXv+26tc$l-#!pOBmMrx zFj`drczSMLOVC)zy<2iUGoz0UYmO2*f^gwuxc3RkNzPZ8&MtGBQlKT{i|G@Qwmu0}kHTXbS;DzP^0b)mw|z~(M%V1rn}Rrjg49hr z8jDp=&JjX&nQ;Vo#4C}OM!J#bw0FQmI~OOrv0Pl`>Bl zp1e^jk|<3l9JaiK9cDJyLm4u>8$Ehbyc-?)HtzH9<=&t4TX-1~$VuF(Ao4SvCfIr% z7`M1H1k%{)Fv`wsWZQaCqR9oG;Bw1K@*aH&>OIXo$BVvx1|q@hy`>`I(Ikrq-xx>Y zb#!m#BtJdzx@+jVr6h@VmtSvzSL|xRM9*2}vt0x_kds_VHfycxU|xwgE3MlGZ-ALV zN4@Eii!VcYdPhQ2F?&U|x@O-JEp&>x)pwaAM|fPL;YNrPp{+%d!rO3`FHynW)wisW z)i#x{_<3WiuCQHtkYjpMx=JnLeAH@^>{5O08uhUh^FZ4UBQZD1;0z$ z4?9XJSEC<-uf*%+M;sHJM%3|xteLK#%J$4iF-wTDfqC2y@sug5oM1zTQ_e7@t6_5I zgA68GaD_3L9q|xzTf907Rtv$X&tQmClTp<`MmY4POE9%2%X@jM##ahsY9BDa1-dh3 zr7kP$&<6K88irL_f`ygizgMgyUt!tF{9@_iB}bl+{5smHJ_?lvZ#Aj| zPaImMGmF0_W+m_Su~W1nt_Ohl>Zql6#FfbjOvP4s|WD_3^<~2lU}6Q zs8i6Uo@@uJ?7B}Xg>33{1Q_*%kZ~JNb+(0;V-*Yf*#ikSoRQe9ciQdqi_ojroSEVF z`ZmZ&;JFs0M`~)AwC-Ek@#QWTxysi)y?Yp+c}b-xcd8Gyf{{$Ii_6Z7>{)k}J@!(}b%RHk zcYpIAdhLl^{W%)Hv$3DR3#jB`==?h>lMsH&dAbLA;1@J-GWlIVOh`>#Se0Jb#L__3 zMajU{iBZnb+QPxb1VWc2gq7YpIh)u>*qYh_o~WCmiJ1i?`gjYV6@UOX6FLA;&e7P! z@d=L6N*F^(l!fzKNT!mDy}h-G%@fmNeu7EBr>2BWoQxbT?49i#0iZuPoEiZ12jYS> zrEDiAAuMBH|Kvq~r&pjS4)^~w?#U`h;Fh8XsqB?)YnJmBh;_s0q2*AnA32E<-I1>y8bN=@U zJN%|t=Qm6W7@VAz0(kFXYRGCq1c)5$|?CoCMQQAo8w-i*_le4Yj%L>>z;(73B(#$&=#Z-@Wdfc)A3{TZQniALSG74~!1s~|Z{ADVWax}lV~y3-}S zt>=J?DvY`a#!s>3zRL^*+Je_%3#O(X35&O-$Zst2Ppd))?^QPVN#o!$lwd!uZ`Rn> z)BE-8C%hOL{EQP1ByPO3eMYf%(jn+8^KyqyKp16h>%Ej}Y_~_PuO#Ow^QgM=9uS26 zkVgWz(cL4h{XoDly2F9FEa^UcsBR0VVi=u(MV=BiW;U(uo71mKGLqf>XV=}*9IVs3(Cr6y#vj)hEE<6QL_BoYu& z+9<;$?r9U${d>4_vOlp(RShAmQ)8eBNSz3^#vJF&gC5B5PH2ql-i1qQxu+VJTz01C zYL;@ZBO{VZ8pRCX}QL(H()fr)3M9JDS z?9AXy%#|RPe%Xmc=MQii(J;LgFs(&`TM06>WJ9TjU%Bc7Y{nC+oT@JZ(qYPmiJymT z%*!t0>pI~|)TIpw&+m#SHCQ~NW%H>$b9+OEEkw5nyD1-VgWa^1Zi|N^^r+%{qJ7e` zny7DfVsGzT&UkHTZ)Y!O?{+e`-1jlgw-U?l?!BSCCtj7M)B87le4oA$)^5uC8^DOZ z7$<``GAHz@ffxG}QAfRh3=v_0E;sMz^ zeuf}pevw_g>MyFb$;=h4QiW82n)c4}FwKZ6rD6dBVVkBk%oOSY7keZv^L-gVYfISc zcZT0hRG-0r5My#G*KUb;{@wYi$AiC-!-vyguu=Bu%S8o-GdMf+t>Cz}2KEbL%7LO70!& zFU(m)A0c?Y82J)ID;1;9!z1L8>TkCyzz{hT`7o!5-|rjk?lN}GC{Hn|YliV97ivm$ zVp1EqnimS5&aB!Qab|1?wp{6Jsnd><1&%?*(gElG1RoLdwiEaQt?4E$vbO2U zjwKS5tM8s|o6;YhD_kcal?uUBFvKLJ{Xc0e<){P_Tpp!Y3L&X!JhkK>e-Y#YM6V-J=2W z%;S(6`(R&|AnuYt45;xZTw)du^uHs4oj4|z<#skPol=oyGo zrMsQfA#$6qTeHWSz`LWCZST9IE_A7u$63d4cJFM_jZ)ZDAoZ#4(cTm`WjC6zVGhGfG`U(jt~6_{O4h_Gx;r}6 z8=T~csw$Dv#3}y5TWMqv06i){vWIE*Xdjml_alfYSr8XXB?|gM!^HVLhEMYZI`+L&s$%WLl!$P8grF;7v#(ZVa|aCu7Nx9pXD_W>3Tycr zD3eEdq|!=;X3ApKUJY)1?cSq{=o)bNCSR(lUaExyd11BxIpAitvA*$|`|J6MS98e= zB)%Ns5oSCe)fCz7C<}??FNKUAjktf1o7~j65 z_I_h<{Vpnz;!7@TpJ~y}+LS7mh0QoNJMw!TvGkp@`M}m~9x=DKhYe)!H#sDBIo^N1 zmvLT8fpMa9UJ=4|^kyS`<~{d}s3)&#VW>{Sw2GxRtfVU_@x3xzrSVX0l+l8sO+Qz` z2IdCW-B2Qj=Qe#>n$zrf%snr9dDcd??Lxnjl=vCUr(>zOjqkM{U^1o*H<&57Q0#Z` z41^avShw}+FpU7gX{yhB(hRfH7mDw%4SuBsiK(2oEzh8&$;m?8-8KQG!3Sn&+F+03Gq}QatU;O2POn zC+;kvN|tT?_$2Z*6%oZH!(?jD?A+A-^tYybP^_|^9`kl`ViA^FcXUeExufpDwl8NI zch&h0Q&GgDBz!jq5fl?4m({Iz$9L;;NI$yU9ugl#7V42_ONAYfXWwYrj8#j%m&V&Z z?~0VYclPTNfV$o43cpVq3<2d|xN%KtM~Q||Vat=pY*bC9Vd*5uIa}m2&E zZ-ich<9oe1E{pR3myi6KSF5JG7mWKeHg6k!mg47*YHMy+-kLqmv%3uthLd`ocEg9; zG5gx+=rBU*j5b>MdP(T!1<~;r$$F%$)160&{lE^GmneFZ=G!osEK>SXfqDBYcBA(^ zc=64$2_e5>DG(`4B7;Rma-v8yy<*@xY+U_AM|NMQ%USm_a=r&JGTk{{0q(=>*MUc- z;Y{2N644*zn|zJz7U<2!8R490D29)T@7w%Oz803aI&RSpCkk*njV|a`rhXXdFj2A~ zq^hgjhU|RnDmz_B6J7U-u2-{MtnzwwWMC7rind{#N=YazA4(bjxknMxDeR$q|CN*| zAiTm8hAPh*dH|H^@X{SNzFiTB(G-f04&hc>pM#%-rZTU=s4S5g9#@$%F_G9LPO53fj>|f3Yt(b|+gRr94Z1OfI!vhH#(X(7!vsBhtAeZcm?=nfPX>7428Yx;G2IovswYt7b*4|m}Q1S0#? z{(BX!H~k_ju+9;{_hrPHJn`=1`CoYT^ir4bun-$zS|*#B+QSb|fxVwS%0&oJ8oiRq zSXN#deMtT!$$obE{k!w~k^1rMQN0w0UjXUsU!i1r;67Ro|YhflBCATi>BxF+Sc@E%W3aj@o*g zm>qLl+(zW`QHY@#eMUnkqGcPX|SvAX3&sfjlo0}l^_MN($Rr{9kHJ% zdHQJxjm283$V9Jmg1lJxKpnBzWkL9-a7D|j;)#(=g7Rix-zw5`a9n+k@nK9+F33x@ zm?_)&K4bM`F_wL&S3R?A@#Pvz!%#p!EL;*?k{G_2NEk~vOY-8XsfD74aATpbavsIx z+-q(=5FcGS3Mk&i#lzK8g57EIvsU z)J8UGK(`Z<$Vgr>F(vouma`tr;ETAFl!iLF;Va=hKHWNRyLGTH z#eN771no`TK&%^QZ2B%3slm^+mn(gqEr)UCS5Diyk*)`vu-SC;> zG1}Xg>G?lOQApA?JYaRB7hbl@DDX6~#l&npKi?I)E02uoz1&?IU*=C=RPmw>M9&cz z(Af2MAmm5TlE99f9>HR(Rtk0R|1P+{HF3+RNpyJF?&aO!URb5s3biJ>jQujkW`a8H z6|9FR%58-2B(17TjA(_Ty;+yeyzqgVkCI#Xt)z{0EuOS@i^u`R-m;+Mg{?wyvox0! zg&h}68wIgcV6+Yym4RhZ-1F-2=ikfvBlr&&B8m7d3s^;o1OkKR>E4C98uTtR+O?IW z9KIXFrL;Iw^1eD`yteqZQ%2UG!MC;2vf}Yz`F67<&0?mZ2OSZt?Rh)N>CGjVvCZ9f zrh9cb)A0I6Gm_HH{Ma(m94ZHuASL$-OzW*r+@23J3_}I8fJlJI-nZ?)^a-W}hGvsR z{gqq0A{}nTu3S6D+h@ul+;5K2mY}ZRk@S~v_*YBBPPJ`va}h;K*>$tC2i}KyQw>K& zNURugk9@JI)E$XUjs-~;R?5!hH5U*m$kLI%(tEYC$HHb4UH%O23psPlanVWan69hj zxN^u<1Ibn}N1`>Qk+H=JuW+gWn?WXb%i?U8J#m>;LRk_0i_W~DeQuB4gmzAk1=V(> zf;{>&!K4d8KuTke`@NloRh207x1m&9cH@4zeBFfe3( z`wjoA`?R?_U0g1kDc2^IF#hT5PcE}H8p!5(n`{a0@6}$^M0Uy4euyjW@aFe0--Z{gZDb0x&j3T6URVx+d0f70wO1`BHy zI-E@`&P6(P78Dr?t|XGVw_Vzt_O?+Xcvt1QL%V(;A9b84;{45)`;?i;cBVB`&fg+Z zPJ6`+Q`@-DIFFflk`SHWu=ENNVZq>Q64rdD~ApdspmvdrrX-WsY9&H_EtLhfZmGDcE_nj}_3I ztY14TTWq`U=w~|a(2{7k5H+NU!Bxa-rQAR%vx8%xTfr-Nn#wpB1&ew=0x>q#wYch`pg?2@lo@1 zd#S!*x1ci7NXaO3!WlbxW+43aIc@m+*_{~Qt#%nA4qLop>u>c2%>06u{46+xGs&@V z@TDAU+?MKU{ujoD9_akmok5)Z>SjtL-_*@I;Wwn7zx7K~5vkcXe+{|%9K58y6fV_N5n@EuixeRh?Eva+vUxkO#CpmU5vsZ& zC^*0w@5r#GKI8x;_9)J<*Ym@a;hQ^r?^~knkzn~P*Nqf}I|=9FGc4f97H6nwT3isU zY)Zefi7Cp6{pz7oR_p@TR`LmZ#H5`{+%A!wiUUD!;S_7!Mr{9Q7Ahnf8ui%zSF)9> zj6^i-8-b=oqYgyGyBm$5W7>$+5dH`w&SkF{orXC-j_?R&+scg{1ss!VAYO1b181}m zQ!hp*@xTXLB7SL=-+1iRBP8(i3?mSOs10jjTZE)T5;k67nVKZUG9a5K*`ENiS=74) zM2U8~q3-zx-eDf38>roQGIvJo-t$V{s2^;%x01w6aA5LLOK*G{08wGaNQ@fuG)gfS z5@Jcb@4}QW&j@AMS;t!mcX*2@CCDVQ$obJW!~K}EN z9B*?l>hQ~vwiqyfBzJyzXlCGyZ;Z`{s*eX`oE%2(Q=^y%rCl+eL}cg(YrsF z)B-vtTHI$}Tf50k1SC};SfPxQ++8Mr>t?ckLwY{wDbz>Fn*hu`FhhF%eOA{!uQWmM zoNd`#~uXJJn5Xa%hXNf4`*VRA@_t_jrVg!FoNi*5fs`>3JyH60-9LD@`aSuQ+I z&2-!HG}}o%U#uvrHe*#6P*Ju6y0|Q@!LRqUw4HG6Y5CS`rJP+! zcYFVQyubP9p0&SkbA#}Pzx?Ql_xw&To?z2|AzXqo3JQNCT>l@*7KDfVJhG(*VEK7? z41#2SAzMG^!msDDSe_1H{W&@Qqf!4NTmQkD|1Ht_*@cQv1nDoL#qv+s_S1rYtMs3V z*1wd249ZWE#me$ek_Bm;k&T@d0-CtkAl!(R^+}KaA0+E1eo};-n){PvadEKyLb862 z%aeUt0Lacq$R7xT)B&)u|B-x}?CcPf_UEq-gmgh}{dxKH4as_v{yoXb2C0Z0al82mH+MA29{o*D-+b3rP3QWKK?g|2aMJjpzj z{?%vONq|0)Jqz5fvVrDK1Aw14r@UzXFqGJn~J`d8+^XTtAiI)PFa#y@fF)BOMG zyh3&^5c>I3$v<|sviwpbPzZ8x@d+~1${0A?TiZEXTNnadS(!MPSQr52&d&Bu+(4ks z|J-7-b2Otv0?Iqu8M_!k&O!cK%HG%%U}#`u1!?7v8X%HCtGBST6@EG|N-NCG!py=7 zVqt-JGz&8uDE!^nUsPZ2!mu zv2jB9?mzR`pS=4Ya_sC7^Z${@!T#_1I5__;2f?!cC=02G9Ws=EtB;wPl@+qE{XLJB zlk1=QF*CFLyFOM$IR0q|L_6?5?OxG|DMOj$@z;N&W;8UHt+cJyt9&phY7@oAX_l(?4F$e=YN@>`!aqy|IfMZql12909$N&HU literal 0 HcmV?d00001 diff --git a/examples/demos_by_system/pendulum_double/double_pendulum_with_trajectory_optimization.py b/examples/demos_by_system/pendulum_double/double_pendulum_with_trajectory_optimization.py index 955f3e41..6de3dcd5 100644 --- a/examples/demos_by_system/pendulum_double/double_pendulum_with_trajectory_optimization.py +++ b/examples/demos_by_system/pendulum_double/double_pendulum_with_trajectory_optimization.py @@ -26,6 +26,8 @@ planner.x_goal = np.array([0,0,0,0]) planner.maxiter = 500 +planner.set_linear_initial_guest(True) +planner.init_dynamic_plot() planner.compute_optimal_trajectory() -planner.show_solution() +# planner.show_solution() planner.animate_solution() \ No newline at end of file diff --git a/examples/demos_by_tool/trajectory_planning/mountain_car_trajectory_optimization.py b/examples/demos_by_tool/trajectory_planning/mountain_car_trajectory_optimization.py index 1908bc45..19fdf808 100644 --- a/examples/demos_by_tool/trajectory_planning/mountain_car_trajectory_optimization.py +++ b/examples/demos_by_tool/trajectory_planning/mountain_car_trajectory_optimization.py @@ -40,6 +40,7 @@ planner.x_goal = np.array([+0.0,+0.0]) planner.init_dynamic_plot() +planner.set_linear_initial_guest() planner.compute_optimal_trajectory() # planner.show_solution() planner.animate_solution() diff --git a/pyro/planning/trajectoryoptimisation.py b/pyro/planning/trajectoryoptimisation.py index dc39fa8a..5ae5e88c 100644 --- a/pyro/planning/trajectoryoptimisation.py +++ b/pyro/planning/trajectoryoptimisation.py @@ -87,7 +87,41 @@ def init_dynamic_plot(self): def set_initial_trajectory_guest(self, traj): new_traj = traj.re_sample( self.grid ) - self.dec_init = self.traj2decisionvariables( new_traj )[:,0] + self.dec_init = self.traj2decisionvariables( new_traj ) + + if self.dynamic_plot: + + traj = self.decisionvariables2traj( self.dec_init ) + self.plotter.update_plot( traj, 'xu' ) + plt.pause( 0.001 ) + + ############################ + def set_linear_initial_guest(self, derivatives = False ): + + xs = np.linspace( self.x_start, self.x_goal, self.grid ) + us = np.linspace( self.sys.ubar, self.sys.ubar, self.grid ) + + # For second order mechanical system + if derivatives: + dof = int(self.sys.n/2) + tf = self.grid * self.dt + dx = ( self.x_goal[:dof] - self.x_start[:dof] ) / tf + dxs = np.linspace( dx, dx, self.grid ) + xs[:,dof:] = dxs + + + dec = np.array([]).reshape(0,1) # initialize dec_vars array + + for i in range(self.sys.n): # append states x + arr_to_add = xs[:,i].reshape(self.grid,1) + dec = np.append(dec,arr_to_add,axis=0) + + for i in range(self.sys.m): # append inputs u + arr_to_add = us[:,i].reshape(self.grid,1) + dec = np.append(dec,arr_to_add,axis=0) + + self.dec_init = dec[:,0] + if self.dynamic_plot: @@ -170,7 +204,7 @@ def traj2decisionvariables(self, traj): arr_to_add = traj.u[:,i].reshape(self.grid,1) dec = np.append(dec,arr_to_add,axis=0) - return dec + return dec[:,0] ############################ From 110f969a3567414c197879812251c338a725ec94 Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Wed, 24 Jan 2024 16:18:04 -0500 Subject: [PATCH 40/93] added target box --- dev/boat/boat.py | 8 ++++---- dev/boat/boat_parking_with_lqr.py | 11 ++++++++--- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/dev/boat/boat.py b/dev/boat/boat.py index 925996ea..8ff33c7c 100644 --- a/dev/boat/boat.py +++ b/dev/boat/boat.py @@ -210,9 +210,9 @@ def forward_kinematic_lines(self, q ): lines_pts = [] # list of array (n_pts x 3) for each lines lines_style = [] lines_color = [] - + ############################### - # ground line + # trailler line ############################### pts = np.zeros(( 2 , 3 )) @@ -302,7 +302,7 @@ def forward_kinematic_lines_plus(self, x , u , t ): sys.ubar[0] = 00 sys.ubar[1] = 0 - + sys.compute_trajectory( tf = 100 ) - sys.plot_trajectory() + sys.plot_trajectory('x') sys.animate_simulation() \ No newline at end of file diff --git a/dev/boat/boat_parking_with_lqr.py b/dev/boat/boat_parking_with_lqr.py index 6ef0474f..02b83b0d 100644 --- a/dev/boat/boat_parking_with_lqr.py +++ b/dev/boat/boat_parking_with_lqr.py @@ -27,10 +27,11 @@ cf = QuadraticCostFunction.from_sys( sys ) cf.Q[0,0] = 10000 cf.Q[1,1] = 0 -cf.Q[2,2] = 10000 +cf.Q[2,2] = 100 cf.Q[3,3] = 0 cf.Q[4,4] = 0 cf.Q[5,5] = 0 + cf.R[0,0] = 1 cf.R[1,1] = 10000 @@ -42,7 +43,11 @@ # Simulation Closed-Loop Non-linear with LQR controller cl_sys = ctl + sys -cl_sys.x0 = np.array([1,-2,-0.2,0,0,0]) +cl_sys.x0 = np.array([1,-10,-0.2,0,0,0]) +cl_sys.x0 = np.array([-2.0,-10,-0.5,0,0,0]) cl_sys.compute_trajectory(20) cl_sys.plot_trajectory('xu') -cl_sys.animate_simulation() \ No newline at end of file +ani = cl_sys.animate_simulation( time_factor_video = 1.0 ) +# cl_sys.animate_simulation( save = True , file_name = 'boat_parking_with_lqr.gif' ) +# ani.save( 'test.mp4', writer='imagemagick', fps=30) +# ani.save( 'test.gif') \ No newline at end of file From 89410fe535c7132178bc83546d220ad91779f927 Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Wed, 24 Jan 2024 16:25:01 -0500 Subject: [PATCH 41/93] fixed boat bug --- dev/boat/boat.py | 18 ++++++++++++------ dev/boat/boat_parking_with_lqr.py | 8 ++++---- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/dev/boat/boat.py b/dev/boat/boat.py index 8ff33c7c..c0038123 100644 --- a/dev/boat/boat.py +++ b/dev/boat/boat.py @@ -215,9 +215,15 @@ def forward_kinematic_lines(self, q ): # trailler line ############################### - pts = np.zeros(( 2 , 3 )) - pts[0,:] = np.array([-10,0,0]) - pts[1,:] = np.array([+10,0,0]) + w = 1.2 + l = 2.0 + + pts = np.zeros(( 5 , 3 )) + pts[0,:] = np.array([-w,-l,0]) + pts[1,:] = np.array([+w,-l,0]) + pts[2,:] = np.array([+w,+l,0]) + pts[3,:] = np.array([-w,+l,0]) + pts[4,:] = np.array([-w,-l,0]) lines_pts.append( pts ) lines_style.append( '--') @@ -298,10 +304,10 @@ def forward_kinematic_lines_plus(self, x , u , t ): sys = Boat() - sys.x0 = np.array([0,-10,0,0,2,0]) + sys.x0 = np.array([0,-5,0,0,2,0]) - sys.ubar[0] = 00 - sys.ubar[1] = 0 + sys.ubar[0] = 100 + sys.ubar[1] = 0.1 sys.compute_trajectory( tf = 100 ) sys.plot_trajectory('x') diff --git a/dev/boat/boat_parking_with_lqr.py b/dev/boat/boat_parking_with_lqr.py index 02b83b0d..c8ab27ef 100644 --- a/dev/boat/boat_parking_with_lqr.py +++ b/dev/boat/boat_parking_with_lqr.py @@ -26,11 +26,11 @@ # Cost function cf = QuadraticCostFunction.from_sys( sys ) cf.Q[0,0] = 10000 -cf.Q[1,1] = 0 +cf.Q[1,1] = 1 cf.Q[2,2] = 100 -cf.Q[3,3] = 0 -cf.Q[4,4] = 0 -cf.Q[5,5] = 0 +cf.Q[3,3] = 1 +cf.Q[4,4] = 1 +cf.Q[5,5] = 1 cf.R[0,0] = 1 cf.R[1,1] = 10000 From 16f1de36da7f654ba996a71a25cbbe1ab1a5fef8 Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Thu, 25 Jan 2024 12:08:43 -0500 Subject: [PATCH 42/93] started a more generic mechanical system class --- dev/rigidbody/rigidbody.py | 504 +++++++++++++++++++++++++++++++++++++ 1 file changed, 504 insertions(+) create mode 100644 dev/rigidbody/rigidbody.py diff --git a/dev/rigidbody/rigidbody.py b/dev/rigidbody/rigidbody.py new file mode 100644 index 00000000..13eb11d8 --- /dev/null +++ b/dev/rigidbody/rigidbody.py @@ -0,0 +1,504 @@ +# -*- coding: utf-8 -*- +""" +Created on Tue Oct 23 20:45:37 2018 + +@author: Alexandre +""" + + +############################################################################### +import numpy as np +############################################################################### +from pyro.dynamic import system +############################################################################### + +############################################################################### + +class GeneralizedMechanicalSystem( system.GeneralizedMechanicalSystem ): + """ + Generalized Mechanical system with position inputs + + ------------------------------------------------------- + M(q) dv + C(q,v) v + d(q,v,u) + g(q) = B(q,u) e(u) + dq = N(q) v + ------------------------------------------------------- + + numper of inputs are: + m = m_f + m_o + --------------------------------------------------- + m : integer : number of inputs + m_f : integer : number of force inputs + m_o : integer : number of other inputs + u : dim = ( m , 1) : vector of all input variables + + v : dim = (dof, 1) : velocity variables + q : dim = (pos, 1) : position variables + dq : dim = (pos, 1) : derivatives of position variables + dv : dim = (dof, 1) : acceleration variables + e : dim = (m_f, 1) : force input variables + d(q,v,u) : dim = (dof, 1) : state-dependent dissipative forces + g(q) : dim = (dof, 1) : state-dependent conservatives forces + M(q) : dim = (dof, dof) : inertia matrix + C(q,v) : dim = (dof, dof) : corriolis matrix + B(q,u) : dim = (dof, m_f) : actuator matrix + N(q) : dim = (pos, dof) : transformation matrix + + """ + + ############################ + def __init__(self, dof = 1 , force_inputs = 1, other_inputs = 1, pos = None): + """ """ + + # Degree of Freedom + self.dof = dof + self.pos = pos + + self.m_f = force_inputs + self.m_o = other_inputs + + # Nb of configurations + if pos == None: # If not specifyied + pos = dof + + # Dimensions + n = dof + pos + m = self.m_f + self.m_o + p = n + + # initialize standard params + system.ContinuousDynamicSystem.__init__(self, n, m, p) + + # Name + self.name = str(dof) + 'DoF Generalized Mechanical System' + + # Labels, bounds and units + for i in range(pos): + # positions states + self.x_ub[i] = + 10 + self.x_lb[i] = - 10 + self.state_label[i] = 'Position '+ str(i) + self.state_units[i] = '[m]' + for i in range(dof): + # generalized velocity states + j = i + pos + self.x_ub[j] = + 10 + self.x_lb[j] = - 10 + self.state_label[j] = 'Velocity ' + str(i) + self.state_units[j] = '[m/sec]' + for i in range(self.m_f): + self.u_ub[i] = + 5 + self.u_lb[i] = - 5 + self.input_label[i] = 'Force input ' + str(i) + self.input_units[i] ='[N]' + self.output_label = self.state_label + self.output_units = self.state_units + + ########################################################################### + # The following functions needs to be overloaded by child classes + # to represent the dynamic of the system + ########################################################################### + + ############################# + def u2e( self, u ): + """ + extract force inputs from all inputs + """ + + e = u[ 0 : self.m_f ] + + return e + + ########################################################################### + def B(self, q , u ): + """ + Actuator Matrix : dof x m + """ + + B = np.zeros( ( self.dof , self.m ) ) + + for i in range(min(self.m,self.dof)): + B[i,i] = 1 # Diag matrix for the first m rows + + return B + + + ########################################################################### + def d(self, q , v , u ): + """ + State-dependent dissipative forces : dof x 1 + """ + + d = np.zeros(self.dof ) # Default is zero vector + + return d + + + ########################################################################### + # No need to overwrite the following functions for custom system + ########################################################################### + + ############################## + def generalized_forces(self, q , v , dv , t = 0 ): + + raise NotImplementedError + + + ############################## + def actuator_forces(self, q , v , dv , t = 0 ): + """ Computed actuator forces given a trajectory (inverse dynamic) """ + + raise NotImplementedError + + + ############################## + def accelerations(self, q , v , u , t = 0 ): + """ + Compute accelerations vector (foward dynamic) + given : + - actuator forces + - actual position and velocities + """ + + M = self.M( q ) + C = self.C( q , v ) + g = self.g( q ) + d = self.d( q , v, u ) + B = self.B( q , u ) + + e = self.u2e( u ) + + dv = np.linalg.inv( M ) @ ( B @ e - C @ v - g - d ) + + return dv + + + + +############################################################################### + +class GeneralizedMechanicalSystemWithPositionInputs( system.ContinuousDynamicSystem ): + """ + Mechanical system where the generalized velocities are not the time + derivative of the generalized coordinates. + ------------------------------------------------------- + M(q) dv + C(q,v) v + d(q,v) + g(q) = B(q) u + dq = N(q) v + ------------------------------------------------------- + v : dim = (dof, 1) : velocity variables + q : dim = (pos, 1) : position variables + dq : dim = (pos, 1) : derivatives of position variables + dv : dim = (dof, 1) : acceleration variables + u : dim = (m, 1) : force input variables + d(q,v) : dim = (dof, 1) : state-dependent dissipative forces + g(q) : dim = (dof, 1) : state-dependent conservatives forces + M(q) : dim = (dof, dof) : inertia matrix + C(q,v) : dim = (dof, dof) : corriolis matrix + B(q) : dim = (dof, m) : actuator matrix + N(q) : dim = (pos, dof) : actuator matrix + + """ + + ############################ + def __init__(self, dof = 1 , pos = None, actuators = None): + """ """ + + # Degree of Freedom + self.dof = dof + self.pos = pos + + # Nb of configurations + if pos == None: # If not specifyied + pos = dof + + # Nb of actuators + if actuators == None: # If not specifyied the sys is fully actuated + actuators = dof + + # Dimensions + n = dof + pos + m = actuators + p = n + + # initialize standard params + system.ContinuousDynamicSystem.__init__(self, n, m, p) + + # Name + self.name = str(dof) + 'DoF Generalized Mechanical System' + + # Labels, bounds and units + for i in range(pos): + # positions states + self.x_ub[i] = + 10 + self.x_lb[i] = - 10 + self.state_label[i] = 'Position '+ str(i) + self.state_units[i] = '[m]' + for i in range(dof): + # generalized velocity states + j = i + pos + self.x_ub[j] = + 10 + self.x_lb[j] = - 10 + self.state_label[j] = 'Velocity ' + str(i) + self.state_units[j] = '[m/sec]' + for i in range(actuators): + self.u_ub[i] = + 5 + self.u_lb[i] = - 5 + self.input_label[i] = 'Force input ' + str(i) + self.input_units[i] ='[N]' + self.output_label = self.state_label + self.output_units = self.state_units + + ########################################################################### + # The following functions needs to be overloaded by child classes + # to represent the dynamic of the system + ########################################################################### + + ########################################################################### + def M(self, q ): + """ + Inertia matrix + ---------------------------------- + dim( H ) = ( dof , dof ) + + such that --> Kinetic Energy = 0.5 * v^T * H(q) * v + + """ + + M = np.diag( np.ones( self.dof ) ) # Default is identity matrix + + return M + + + ########################################################################### + def C(self, q , v ): + """ + Corriolis and Centrifugal Matrix + ------------------------------------ + dim( C ) = ( dof , dof ) + + such that --> d H / dt = C + C^T + + + """ + + C = np.zeros( ( self.dof , self.dof ) ) # Default is zeros matrix + + return C + + ########################################################################### + def N(self, q ): + """ + Transformation matrix from generalized velocities to derivatives of + configuration variables + ------------------------------------ + dim( N ) = ( pos , dof ) + + dq = N(q) v + ------------------------------------ + """ + + N = np.zeros( ( self.pos , self.dof ) ) + + for i in range(min( self.pos, self.dof) ): + N[i,i] = 1 # Diag matrix for the first m rows + + return N + + + ########################################################################### + def B(self, q ): + """ + Actuator Matrix : dof x m + """ + + B = np.zeros( ( self.dof , self.m ) ) + + for i in range(min(self.m,self.dof)): + B[i,i] = 1 # Diag matrix for the first m rows + + return B + + + ########################################################################### + def g(self, q ): + """ + Gravitationnal forces vector : dof x 1 + """ + + g = np.zeros( self.dof ) # Default is zero vector + + return g + + + ########################################################################### + def d(self, q , v ): + """ + State-dependent dissipative forces : dof x 1 + """ + + d = np.zeros(self.dof ) # Default is zero vector + + return d + + + ########################################################################### + # No need to overwrite the following functions for custom system + ########################################################################### + + ############################# + def x2q( self, x ): + """ from state vector (x) to angle and speeds (q,dq) """ + + q = x[ 0 : self.pos ] + v = x[ self.pos : self.n ] + + return [ q , v ] + + + ############################# + def q2x( self, q , v ): + """ from angle and speeds (q,dq) to state vector (x) """ + + x = np.zeros( self.n ) + + x[ 0 : self.pos ] = q + x[ self.pos : self.n ] = v + + return x + + + ############################# + def xut2q( self, x , u , t ): + """ compute configuration variables """ + + return self.x2q(x)[0] + + + ############################## + def generalized_forces(self, q , v , dv , t = 0 ): + """ Computed generalized forces given a trajectory """ + + M = self.M( q ) + C = self.C( q , v ) + g = self.g( q ) + d = self.d( q , v ) + + # Generalized forces + forces = H @ dv + C @ v + g + d + + return forces + + + ############################## + def actuator_forces(self, q , v , dv , t = 0 ): + """ Computed actuator forces given a trajectory (inverse dynamic) """ + + if self.dof == self.m: + + B = self.B( q ) + + # Generalized forces + forces = self.generalized_forces( q , v , dv , t ) + + # Actuator forces + u = np.linalg.inv( B ) @ forces + + return u + + else: + + raise NotImplementedError + + + ############################## + def accelerations(self, q , v , u , t = 0 ): + """ + Compute accelerations vector (foward dynamic) + given : + - actuator forces + - actual position and velocities + """ + + M = self.M( q ) + C = self.C( q , v ) + g = self.g( q ) + d = self.d( q , v) + B = self.B( q ) + + dv = np.linalg.inv( M ) @ ( B @ u - C @ v - g - d ) + + return dv + + + ########################################################################### + def f(self, x , u , t = 0 ): + """ + Continuous time foward dynamics evaluation + + dx = f(x,u,t) + + INPUTS + x : state vector n x 1 + u : control inputs vector m x 1 + t : time 1 x 1 + + OUPUTS + dx : state derivative vectror n x 1 + + """ + + # from state vector (x) to position and velocities + [ q , v ] = self.x2q( x ) + + # compute accelerations + dv = self.accelerations( q , v , u , t ) + + # compute derivative of position varibles + dq = self.N( q ) @ v + + # convert to state vector diff (dx) + dx = self.q2x( dq , dv ) + + return dx + + + ########################################################################### + def kinetic_energy(self, q , v ): + """ Compute kinetic energy """ + + e = 0.5 * v.T @ self.M( q ) @ v + + return e + + + + + + +# class RigidBody( system.ContinuousDynamicSystem ): +# """ +# Mechanical system with Equations of Motion written in +# a body-fixed frame of reference + + + +''' +################################################################# +################## Main ######## +################################################################# +''' + + +if __name__ == "__main__": + """ MAIN TEST """ + + sys = GeneralizedMechanicalSystem( dof = 2 , pos = 2 , actuators = 2 ) + + sys.show( q = np.array([ 1.0, 2.0]) ) + sys.show3( q = np.array([-0.5, 1.5]) ) + + sys.ubar = np.array([1,2]) + sys.x0 = np.array([0,0,0,0]) + + sys.plot_trajectory() + sys.animate_simulation() + \ No newline at end of file From 8b83c91f6a2fe0131b78220fa355064e31927f26 Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Thu, 25 Jan 2024 15:27:31 -0500 Subject: [PATCH 43/93] new rocket in body frame coord --- dev/rigidbody/rigidbody.py | 447 +++++++++++++++++++++++-------------- dev/rigidbody/vehicle2.py | 249 +++++++++++++++++++++ 2 files changed, 527 insertions(+), 169 deletions(-) create mode 100644 dev/rigidbody/vehicle2.py diff --git a/dev/rigidbody/rigidbody.py b/dev/rigidbody/rigidbody.py index 13eb11d8..d2954836 100644 --- a/dev/rigidbody/rigidbody.py +++ b/dev/rigidbody/rigidbody.py @@ -13,170 +13,7 @@ ############################################################################### ############################################################################### - -class GeneralizedMechanicalSystem( system.GeneralizedMechanicalSystem ): - """ - Generalized Mechanical system with position inputs - - ------------------------------------------------------- - M(q) dv + C(q,v) v + d(q,v,u) + g(q) = B(q,u) e(u) - dq = N(q) v - ------------------------------------------------------- - - numper of inputs are: - m = m_f + m_o - --------------------------------------------------- - m : integer : number of inputs - m_f : integer : number of force inputs - m_o : integer : number of other inputs - u : dim = ( m , 1) : vector of all input variables - - v : dim = (dof, 1) : velocity variables - q : dim = (pos, 1) : position variables - dq : dim = (pos, 1) : derivatives of position variables - dv : dim = (dof, 1) : acceleration variables - e : dim = (m_f, 1) : force input variables - d(q,v,u) : dim = (dof, 1) : state-dependent dissipative forces - g(q) : dim = (dof, 1) : state-dependent conservatives forces - M(q) : dim = (dof, dof) : inertia matrix - C(q,v) : dim = (dof, dof) : corriolis matrix - B(q,u) : dim = (dof, m_f) : actuator matrix - N(q) : dim = (pos, dof) : transformation matrix - - """ - - ############################ - def __init__(self, dof = 1 , force_inputs = 1, other_inputs = 1, pos = None): - """ """ - - # Degree of Freedom - self.dof = dof - self.pos = pos - - self.m_f = force_inputs - self.m_o = other_inputs - - # Nb of configurations - if pos == None: # If not specifyied - pos = dof - - # Dimensions - n = dof + pos - m = self.m_f + self.m_o - p = n - - # initialize standard params - system.ContinuousDynamicSystem.__init__(self, n, m, p) - - # Name - self.name = str(dof) + 'DoF Generalized Mechanical System' - - # Labels, bounds and units - for i in range(pos): - # positions states - self.x_ub[i] = + 10 - self.x_lb[i] = - 10 - self.state_label[i] = 'Position '+ str(i) - self.state_units[i] = '[m]' - for i in range(dof): - # generalized velocity states - j = i + pos - self.x_ub[j] = + 10 - self.x_lb[j] = - 10 - self.state_label[j] = 'Velocity ' + str(i) - self.state_units[j] = '[m/sec]' - for i in range(self.m_f): - self.u_ub[i] = + 5 - self.u_lb[i] = - 5 - self.input_label[i] = 'Force input ' + str(i) - self.input_units[i] ='[N]' - self.output_label = self.state_label - self.output_units = self.state_units - - ########################################################################### - # The following functions needs to be overloaded by child classes - # to represent the dynamic of the system - ########################################################################### - - ############################# - def u2e( self, u ): - """ - extract force inputs from all inputs - """ - - e = u[ 0 : self.m_f ] - - return e - - ########################################################################### - def B(self, q , u ): - """ - Actuator Matrix : dof x m - """ - - B = np.zeros( ( self.dof , self.m ) ) - - for i in range(min(self.m,self.dof)): - B[i,i] = 1 # Diag matrix for the first m rows - - return B - - - ########################################################################### - def d(self, q , v , u ): - """ - State-dependent dissipative forces : dof x 1 - """ - - d = np.zeros(self.dof ) # Default is zero vector - - return d - - - ########################################################################### - # No need to overwrite the following functions for custom system - ########################################################################### - - ############################## - def generalized_forces(self, q , v , dv , t = 0 ): - - raise NotImplementedError - - - ############################## - def actuator_forces(self, q , v , dv , t = 0 ): - """ Computed actuator forces given a trajectory (inverse dynamic) """ - - raise NotImplementedError - - - ############################## - def accelerations(self, q , v , u , t = 0 ): - """ - Compute accelerations vector (foward dynamic) - given : - - actuator forces - - actual position and velocities - """ - - M = self.M( q ) - C = self.C( q , v ) - g = self.g( q ) - d = self.d( q , v, u ) - B = self.B( q , u ) - - e = self.u2e( u ) - - dv = np.linalg.inv( M ) @ ( B @ e - C @ v - g - d ) - - return dv - - - - -############################################################################### - -class GeneralizedMechanicalSystemWithPositionInputs( system.ContinuousDynamicSystem ): +class GeneralizedMechanicalSystem( system.ContinuousDynamicSystem ): """ Mechanical system where the generalized velocities are not the time derivative of the generalized coordinates. @@ -204,11 +41,11 @@ def __init__(self, dof = 1 , pos = None, actuators = None): # Degree of Freedom self.dof = dof - self.pos = pos # Nb of configurations if pos == None: # If not specifyied pos = dof + self.pos = pos # Nb of actuators if actuators == None: # If not specifyied the sys is fully actuated @@ -473,14 +310,285 @@ def kinetic_energy(self, q , v ): +############################################################################### + +class GeneralizedMechanicalSystemWithPositionInputs( GeneralizedMechanicalSystem ): + """ + Generalized Mechanical system with position inputs + + ------------------------------------------------------- + M(q) dv + C(q,v) v + d(q,v,u) + g(q) = B(q,u) e(u) + dq = N(q) v + ------------------------------------------------------- + + numper of inputs are: + m = m_f + m_o + --------------------------------------------------- + m : integer : number of inputs + m_f : integer : number of force inputs + m_o : integer : number of other inputs + u : dim = ( m , 1) : vector of all input variables + + v : dim = (dof, 1) : velocity variables + q : dim = (pos, 1) : position variables + dq : dim = (pos, 1) : derivatives of position variables + dv : dim = (dof, 1) : acceleration variables + e : dim = (m_f, 1) : force input variables + d(q,v,u) : dim = (dof, 1) : state-dependent dissipative forces + g(q) : dim = (dof, 1) : state-dependent conservatives forces + M(q) : dim = (dof, dof) : inertia matrix + C(q,v) : dim = (dof, dof) : corriolis matrix + B(q,u) : dim = (dof, m_f) : actuator matrix + N(q) : dim = (pos, dof) : transformation matrix + + """ + + ############################ + def __init__(self, dof = 1 , force_inputs = 1, other_inputs = 1, pos = None): + """ """ + + # Degree of Freedom + self.dof = dof + + self.m_f = force_inputs + self.m_o = other_inputs + + # Nb of configurations + if pos == None: # If not specifyied + pos = dof + self.pos = pos + + # Dimensions + n = dof + pos + m = self.m_f + self.m_o + p = n + + # initialize standard params + system.ContinuousDynamicSystem.__init__(self, n, m, p) + + # Name + self.name = str(dof) + 'DoF Generalized Mechanical System' + + # Labels, bounds and units + for i in range(pos): + # positions states + self.x_ub[i] = + 10 + self.x_lb[i] = - 10 + self.state_label[i] = 'Position '+ str(i) + self.state_units[i] = '[m]' + for i in range(dof): + # generalized velocity states + j = i + pos + self.x_ub[j] = + 10 + self.x_lb[j] = - 10 + self.state_label[j] = 'Velocity ' + str(i) + self.state_units[j] = '[m/sec]' + for i in range(self.m_f): + self.u_ub[i] = + 5 + self.u_lb[i] = - 5 + self.input_label[i] = 'Force input ' + str(i) + self.input_units[i] ='[N]' + self.output_label = self.state_label + self.output_units = self.state_units + + ########################################################################### + # The following functions needs to be overloaded by child classes + # to represent the dynamic of the system + ########################################################################### + + ############################# + def u2e( self, u ): + """ + extract force inputs from all inputs + """ + + e = u[ 0 : self.m_f ] + + return e + + ########################################################################### + def B(self, q , u ): + """ + Actuator Matrix : dof x m + """ + + B = np.zeros( ( self.dof , self.m_f ) ) + + for i in range(min( self.m_f , self.dof )): + B[i,i] = 1 # Diag matrix for the first m rows + + return B + + + ########################################################################### + def d(self, q , v , u ): + """ + State-dependent dissipative forces : dof x 1 + """ + + d = np.zeros(self.dof ) # Default is zero vector + + return d + + + ########################################################################### + # No need to overwrite the following functions for custom system + ########################################################################### + + ############################## + def generalized_forces(self, q , v , dv , t = 0 ): + + raise NotImplementedError + + + ############################## + def actuator_forces(self, q , v , dv , t = 0 ): + """ Computed actuator forces given a trajectory (inverse dynamic) """ + + raise NotImplementedError + + + ############################## + def accelerations(self, q , v , u , t = 0 ): + """ + Compute accelerations vector (foward dynamic) + given : + - actuator forces + - actual position and velocities + """ + + M = self.M( q ) + C = self.C( q , v ) + g = self.g( q ) + d = self.d( q , v, u ) + B = self.B( q , u ) + + e = self.u2e( u ) + + dv = np.linalg.inv( M ) @ ( B @ e - C @ v - g - d ) + + return dv + + + +############################################################################### + +class RigidBody2D( GeneralizedMechanicalSystemWithPositionInputs ): + """ + + Mechanical system with Equations of Motion written in + a body-fixed frame of reference + + """ + + ############################ + def __init__(self, force_inputs = 1, other_inputs = 1): + """ """ + + # Degree of Freedom + self.dof = 3 + self.pos = 3 + + self.m_f = force_inputs + self.m_o = other_inputs + + # Dimensions + n = 6 + m = self.m_f + self.m_o + p = n + + # initialize standard params + system.ContinuousDynamicSystem.__init__(self, n, m, p) + + # Name + self.name = 'Planar Rigid Body' + self.state_label = ['x','y','theta','v1','v2','w'] + self.state_units = ['[m]','[m]','[rad]','[m/sec]','[m/sec]','[rad/sec]'] + self.input_label = ['T','delta'] + self.input_units = ['[N]','[rad]'] + self.output_label = self.state_label + self.output_units = self.state_units + + # Dynamic properties + self.mass = 1.0 + self.inertia = 1.0 + self.l_t = 1.0 # Distance between CG and Thrust + + ########################################################################### + def M(self, q ): + + M = np.diag( np.array([ self.mass , self.mass, self.inertia ]) ) + + return M + + + ########################################################################### + def C(self, q , v ): + + C = np.zeros( ( self.dof , self.dof ) ) + + return C + + ########################################################################### + def N(self, q ): + """ + Transformation matrix from generalized velocities to derivatives of + configuration variables + ------------------------------------ + dim( N ) = ( pos , dof ) + + dq = N(q) v + ------------------------------------ + """ -# class RigidBody( system.ContinuousDynamicSystem ): -# """ -# Mechanical system with Equations of Motion written in -# a body-fixed frame of reference + theta = q[2] + N = np.array( [ [ np.cos(theta) , -np.sin(theta) , 0 ] , + [ np.sin(theta) , +np.cos(theta) , 0 ] , + [ 0 , 0 , 1 ] ] ) + + return N + ########################################################################### + def B(self, q ): + """ + Actuator Matrix : dof x m + """ + + B = np.zeros((3,1)) + + delta = u[1] + + B[0,0] = np.cos( delta ) + B[1,0] = np.sin( delta ) + B[2,0] = - self.l_t * np.sin( delta ) + + return B + + + ########################################################################### + def g(self, q ): + """ + Gravitationnal forces vector : dof x 1 + """ + + g = np.zeros( self.dof ) # Default is zero vector + + return g + + + ########################################################################### + def d(self, q , v ): + """ + State-dependent dissipative forces : dof x 1 + """ + + d = np.zeros(self.dof ) # Default is zero vector + + return d + + ''' ################################################################# ################## Main ######## @@ -492,6 +600,7 @@ def kinetic_energy(self, q , v ): """ MAIN TEST """ sys = GeneralizedMechanicalSystem( dof = 2 , pos = 2 , actuators = 2 ) + sys = GeneralizedMechanicalSystemWithPositionInputs( dof = 3 , pos = 1 , force_inputs= 1 , other_inputs=1 ) sys.show( q = np.array([ 1.0, 2.0]) ) sys.show3( q = np.array([-0.5, 1.5]) ) diff --git a/dev/rigidbody/vehicle2.py b/dev/rigidbody/vehicle2.py new file mode 100644 index 00000000..631d7442 --- /dev/null +++ b/dev/rigidbody/vehicle2.py @@ -0,0 +1,249 @@ +# -*- coding: utf-8 -*- + +############################################################################### +import numpy as np +############################################################################### +from pyro.dynamic import system +from pyro.kinematic import geometry +from pyro.kinematic import drawing +############################################################################### + +from rigidbody import RigidBody2D + +############################################################################## + +############################################################################### + +class Rocket2D( RigidBody2D ): + """ + + """ + + ############################ + def __init__(self): + """ """ + + RigidBody2D.__init__( self , force_inputs = 1, other_inputs = 1) + + # State working range + self.x_ub = np.array([+50,+100,+2,10,10,10]) + self.x_lb = np.array([-50,-0,-2,-10,-10,-10]) + + self.u_ub = np.array([+5000,+1]) + self.u_lb = np.array([+100,-1]) + + # Dynamic properties + self.mass = 1000.0 + self.inertia = 1000.0 + self.l_t = 1.0 # Distance between CG and Thrust + + self.gravity = 9.8 + + self.damping_coef = np.array([ [ 1.0, 1.0, 1.0 ] , + [ 1.0, 1.0, 1.0 ] ]) + + # Kinematic param + self.width = 0.2 + self.height = 2.0 + + # rocket drawing + + # Graphic output parameters + self.dynamic_domain = True + self.dynamic_range = 10 + + pts = np.zeros(( 10 , 3 )) + l = self.height + w = self.width + + pts[0,:] = np.array([ -l, 0,0]) + pts[1,:] = np.array([-l, -w,0]) + pts[2,:] = np.array([+l, -w,0]) + pts[3,:] = np.array([l+w,0,0]) + pts[4,:] = np.array([+l, +w,0]) + pts[5,:] = np.array([-l, +w,0]) + pts[6,:] = pts[0,:] + pts[7,:] = pts[0,:] + np.array([-w,-w,0]) + pts[8,:] = pts[0,:] + np.array([-w,+w,0]) + pts[9,:] = pts[0,:] + + self.drawing_body_pts = pts + + ########################################################################### + def B(self, q , u ): + """ + Actuator Matrix : dof x m + """ + + B = np.zeros((3,1)) + + delta = u[1] + + B[0,0] = np.cos( delta ) + B[1,0] = np.sin( delta ) + B[2,0] = - self.l_t * np.sin( delta ) + + return B + + + ########################################################################### + def g(self, q ): + """ + Gravitationnal forces vector : dof x 1 + """ + + # Gravity in inertial frame + g_a = np.zeros( self.dof ) + g_a[1] = self.mass * self.gravity + + # Gravity in body frame + g_body = self.N( q ).T @ g_a + + return g_body + + + ########################################################################### + def d(self, q , v , u ): + """ + State-dependent dissipative forces : dof x 1 + """ + + d = np.zeros(self.dof ) + + C = self.damping_coef + + # quadratic + linear damping based on 6 coefficients + d = v*np.abs(v) * C[0,:] + v * C[1,:] + + return d + + ########################################################################### + def forward_kinematic_domain(self, q ): + + l = self.height * 3 + + x = q[0] + y = q[1] + z = 0 + + if self.dynamic_domain: + + domain = [ ( -l + x , l + x ) , + ( -l + y , l + y ) , + ( -l + z , l + z ) ]# + else: + + domain = [ ( -l , l ) , + ( -l , l ) , + ( -l , l ) ]# + + return domain + + + ########################################################################### + def forward_kinematic_lines(self, q ): + + lines_pts = [] # list of array (n_pts x 3) for each lines + lines_style = [] + lines_color = [] + + ############################### + # ground line + ############################### + + pts = np.zeros(( 2 , 3 )) + pts[0,:] = np.array([-10,0,0]) + pts[1,:] = np.array([+10,0,0]) + + lines_pts.append( pts ) + lines_style.append( '--') + lines_color.append( 'k' ) + + ########################### + # body + ########################### + + x = q[0] + y = q[1] + theta = q[2] + + W_T_B = geometry.transformation_matrix_2D( theta , x , y ) + + pts_B = self.drawing_body_pts + pts_W = drawing.transform_points_2D( W_T_B , pts_B ) + + lines_pts.append( pts_W ) + lines_style.append( '-') + lines_color.append( 'b' ) + + ########################### + # C.G. + ########################### + + pts = np.zeros(( 1 , 3 )) + pts[0,:] = np.array([x,y,0]) + + lines_pts.append( pts ) + lines_style.append( 'o') + lines_color.append( 'b' ) + + return lines_pts , lines_style , lines_color + + ########################################################################### + def forward_kinematic_lines_plus(self, x , u , t ): + """ + show trust vectors + + + """ + + lines_pts = [] # list of array (n_pts x 3) for each lines + lines_style = [] + lines_color = [] + + ########################### + # trust force vector + ########################### + + length = u[0] / self.u_ub[0] * self.height + delta = u[1] + offset = -self.height - self.width + + pts_body = drawing.arrow_from_length_angle( length, delta, x = offset, origin = 'tip' ) + W_T_B = geometry.transformation_matrix_2D( x[2], x[0] , x[1] ) + pts_W = drawing.transform_points_2D( W_T_B , pts_body ) + + lines_pts.append( pts_W ) + lines_style.append( '-') + lines_color.append( 'r' ) + + return lines_pts , lines_style , lines_color + + +''' +################################################################# +################## Main ######## +################################################################# +''' + + +if __name__ == "__main__": + """ MAIN TEST """ + + sys = Rocket2D() + + sys.gravity = 9.81 + + sys.x0[0] = 0 + sys.x0[1] = 0 + sys.x0[2] = np.pi/2 + + sys.x0[3] = 0.0 + sys.x0[4] = 0.0 + sys.x0[5] = 0.0 + + sys.ubar[0] = sys.gravity * sys.mass * 1.1 + sys.ubar[1] = -0.01 + + sys.plot_trajectory('xu') + sys.animate_simulation() \ No newline at end of file From e42bb54486d67810fd2ecaa0e75b70629946c892 Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Thu, 25 Jan 2024 15:53:28 -0500 Subject: [PATCH 44/93] working on rigid body examples --- dev/rigidbody/boat2.py | 228 ++++++++++++++++++++++ dev/rigidbody/boat_parking_with_lqr2.py | 46 +++++ dev/rigidbody/rigidbody.py | 22 +-- dev/rigidbody/{vehicle2.py => rocket2.py} | 3 + 4 files changed, 279 insertions(+), 20 deletions(-) create mode 100644 dev/rigidbody/boat2.py create mode 100644 dev/rigidbody/boat_parking_with_lqr2.py rename dev/rigidbody/{vehicle2.py => rocket2.py} (98%) diff --git a/dev/rigidbody/boat2.py b/dev/rigidbody/boat2.py new file mode 100644 index 00000000..ab9120a7 --- /dev/null +++ b/dev/rigidbody/boat2.py @@ -0,0 +1,228 @@ +# -*- coding: utf-8 -*- + +############################################################################### +import numpy as np +############################################################################### +from pyro.dynamic import system +from pyro.kinematic import geometry +from pyro.kinematic import drawing +############################################################################### + +from rigidbody import RigidBody2D + +############################################################################## + +############################################################################### + +class Boat2D( RigidBody2D ): + """ + + """ + + ############################ + def __init__(self): + """ """ + + RigidBody2D.__init__( self , force_inputs = 2, other_inputs = 0) + + self.input_label = ['Tx','Ty'] + self.input_units = ['[N]','[N]'] + + # State working range + self.x_ub = np.array([+50,+100,+2,10,10,10]) + self.x_lb = np.array([-50,-0,-2,-10,-10,-10]) + + self.u_ub = np.array([+100,+100]) + self.u_lb = np.array([+100,-100]) + + # Dynamic properties + self.mass = 1000.0 + self.inertia = 1000.0 + self.l_t = 2.0 # Distance between CG and Thrust vector + + self.gravity = 9.8 + + self.damping_coef = np.array([ [ 1.0, 1.0, 1.0 ] , + [ 1.0, 1.0, 1.0 ] ]) + + # Kinematic param + self.width = 1.0 + self.height = self.l_t + + # rocket drawing + + # Graphic output parameters + self.dynamic_domain = True + self.dynamic_range = 10 + + pts = np.zeros(( 6 , 3 )) + l = self.height + w = self.width + + pts[0,:] = np.array([-l, +w,0]) + pts[1,:] = np.array([-l, -w,0]) + pts[2,:] = np.array([+l, -w,0]) + pts[3,:] = np.array([l+w,0,0]) + pts[4,:] = np.array([+l, +w,0]) + pts[5,:] = np.array([-l, +w,0]) + + self.drawing_body_pts = pts + + ########################################################################### + def B(self, q , u ): + """ + Actuator Matrix : dof x m + """ + + B = np.zeros((3,2)) + + B[0,0] = 1 + B[1,1] = 1 + B[2,1] = - self.l_t + + return B + + + ########################################################################### + def d(self, q , v , u ): + """ + State-dependent dissipative forces : dof x 1 + """ + + d = np.zeros(self.dof ) + + C = self.damping_coef + + # quadratic + linear damping based on 6 coefficients + d = v*np.abs(v) * C[0,:] + v * C[1,:] + + return d + + ########################################################################### + def forward_kinematic_domain(self, q ): + + l = self.height * 3 + + x = q[0] + y = q[1] + z = 0 + + if self.dynamic_domain: + + domain = [ ( -l + x , l + x ) , + ( -l + y , l + y ) , + ( -l + z , l + z ) ]# + else: + + domain = [ ( -l , l ) , + ( -l , l ) , + ( -l , l ) ]# + + return domain + + + ########################################################################### + def forward_kinematic_lines(self, q ): + + lines_pts = [] # list of array (n_pts x 3) for each lines + lines_style = [] + lines_color = [] + + ############################### + # ground line + ############################### + + pts = np.zeros(( 2 , 3 )) + pts[0,:] = np.array([-10,0,0]) + pts[1,:] = np.array([+10,0,0]) + + lines_pts.append( pts ) + lines_style.append( '--') + lines_color.append( 'k' ) + + ########################### + # body + ########################### + + x = q[0] + y = q[1] + theta = q[2] + + W_T_B = geometry.transformation_matrix_2D( theta , x , y ) + + pts_B = self.drawing_body_pts + pts_W = drawing.transform_points_2D( W_T_B , pts_B ) + + lines_pts.append( pts_W ) + lines_style.append( '-') + lines_color.append( 'b' ) + + ########################### + # C.G. + ########################### + + pts = np.zeros(( 1 , 3 )) + pts[0,:] = np.array([x,y,0]) + + lines_pts.append( pts ) + lines_style.append( 'o') + lines_color.append( 'b' ) + + return lines_pts , lines_style , lines_color + + ########################################################################### + def forward_kinematic_lines_plus(self, x , u , t ): + """ + show trust vectors + + + """ + + lines_pts = [] # list of array (n_pts x 3) for each lines + lines_style = [] + lines_color = [] + + ########################### + # trust force vector + ########################### + + vx = u[0] / self.u_ub[0] * self.height + vy = u[1] / self.u_ub[1] * self.height + offset = -self.height + + pts_body = drawing.arrow_from_components( vx , vy , x = offset, origin = 'tip' ) + W_T_B = geometry.transformation_matrix_2D( x[2], x[0] , x[1] ) + pts_W = drawing.transform_points_2D( W_T_B , pts_body ) + + lines_pts.append( pts_W ) + lines_style.append( '-') + lines_color.append( 'r' ) + + return lines_pts , lines_style , lines_color + + +''' +################################################################# +################## Main ######## +################################################################# +''' + + +if __name__ == "__main__": + """ MAIN TEST """ + + sys = Boat2D() + + sys.x0[0] = 0 + sys.x0[1] = 0 + sys.x0[2] = 0 + + sys.x0[3] = 1.0 + sys.x0[4] = 0.0 + sys.x0[5] = 0.0 + + sys.ubar[0] = 100 + sys.ubar[1] = 100 + + sys.plot_trajectory('xu') + sys.animate_simulation() \ No newline at end of file diff --git a/dev/rigidbody/boat_parking_with_lqr2.py b/dev/rigidbody/boat_parking_with_lqr2.py new file mode 100644 index 00000000..28cca4fa --- /dev/null +++ b/dev/rigidbody/boat_parking_with_lqr2.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Created on Sun Oct 3 08:27:06 2021 + +@author: alex +""" + +import numpy as np + +from boat2 import Boat2D + +from pyro.analysis.costfunction import QuadraticCostFunction +from pyro.dynamic.statespace import linearize +from pyro.control.lqr import synthesize_lqr_controller + + +# Non-linear model +sys = Boat2D() + +# Linear model +ss = linearize( sys , 0.01 ) + +# Cost function +cf = QuadraticCostFunction.from_sys( sys ) +cf.Q[0,0] = 1000 +cf.Q[1,1] = 1000 +cf.Q[2,2] = 1000 +cf.Q[3,3] = 1 +cf.Q[4,4] = 1 +cf.Q[5,5] = 1 + +cf.R[0,0] = 1 +cf.R[1,1] = 1 + +# LQR controller +ctl = synthesize_lqr_controller( ss , cf , sys.xbar , sys.ubar ) + +print(ctl.K) + +# Simulation Closed-Loop Non-linear with LQR controller +cl_sys = ctl + sys +cl_sys.x0 = np.array([1,-10,-0.2,0,0,0]) +cl_sys.compute_trajectory(20) +cl_sys.plot_trajectory('xu') +cl_sys.animate_simulation( time_factor_video = 1.0 ) \ No newline at end of file diff --git a/dev/rigidbody/rigidbody.py b/dev/rigidbody/rigidbody.py index d2954836..dc6ac267 100644 --- a/dev/rigidbody/rigidbody.py +++ b/dev/rigidbody/rigidbody.py @@ -504,8 +504,8 @@ def __init__(self, force_inputs = 1, other_inputs = 1): self.name = 'Planar Rigid Body' self.state_label = ['x','y','theta','v1','v2','w'] self.state_units = ['[m]','[m]','[rad]','[m/sec]','[m/sec]','[rad/sec]'] - self.input_label = ['T','delta'] - self.input_units = ['[N]','[rad]'] + self.input_label = ['u1','u2'] + self.input_units = ['[]','[]'] self.output_label = self.state_label self.output_units = self.state_units @@ -549,24 +549,6 @@ def N(self, q ): return N - - ########################################################################### - def B(self, q ): - """ - Actuator Matrix : dof x m - """ - - B = np.zeros((3,1)) - - delta = u[1] - - B[0,0] = np.cos( delta ) - B[1,0] = np.sin( delta ) - B[2,0] = - self.l_t * np.sin( delta ) - - return B - - ########################################################################### def g(self, q ): """ diff --git a/dev/rigidbody/vehicle2.py b/dev/rigidbody/rocket2.py similarity index 98% rename from dev/rigidbody/vehicle2.py rename to dev/rigidbody/rocket2.py index 631d7442..7ca335e2 100644 --- a/dev/rigidbody/vehicle2.py +++ b/dev/rigidbody/rocket2.py @@ -25,6 +25,9 @@ def __init__(self): RigidBody2D.__init__( self , force_inputs = 1, other_inputs = 1) + self.input_label = ['T','delta'] + self.input_units = ['[N]','[rad]'] + # State working range self.x_ub = np.array([+50,+100,+2,10,10,10]) self.x_lb = np.array([-50,-0,-2,-10,-10,-10]) From cf23f2f342006b8f5e1b90b5db648177f45af163 Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Thu, 25 Jan 2024 16:06:15 -0500 Subject: [PATCH 45/93] added input labels --- dev/rigidbody/boat2.py | 8 ++++---- dev/rigidbody/boat_parking_with_lqr2.py | 26 +++++++++++++++++++++---- 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/dev/rigidbody/boat2.py b/dev/rigidbody/boat2.py index ab9120a7..f4a13047 100644 --- a/dev/rigidbody/boat2.py +++ b/dev/rigidbody/boat2.py @@ -29,11 +29,11 @@ def __init__(self): self.input_units = ['[N]','[N]'] # State working range - self.x_ub = np.array([+50,+100,+2,10,10,10]) - self.x_lb = np.array([-50,-0,-2,-10,-10,-10]) + self.x_ub = np.array([+10,+10,+2,10,10,10]) + self.x_lb = np.array([-10,-10,-2,-10,-10,-10]) - self.u_ub = np.array([+100,+100]) - self.u_lb = np.array([+100,-100]) + self.u_ub = np.array([+1000,+100]) + self.u_lb = np.array([-1000,-100]) # Dynamic properties self.mass = 1000.0 diff --git a/dev/rigidbody/boat_parking_with_lqr2.py b/dev/rigidbody/boat_parking_with_lqr2.py index 28cca4fa..bfa2654d 100644 --- a/dev/rigidbody/boat_parking_with_lqr2.py +++ b/dev/rigidbody/boat_parking_with_lqr2.py @@ -17,21 +17,39 @@ # Non-linear model sys = Boat2D() + + +# from pyro.planning.trajectoryoptimisation import DirectCollocationTrajectoryOptimisation + +# planner = DirectCollocationTrajectoryOptimisation( sys , 0.1 , 20 ) + + +# planner.init_dynamic_plot() + +# planner.x_start = np.array([-5,0,0,2,0,0]) +# planner.x_goal = np.array([0,0,0,0,0,0]) + +# planner.set_linear_initial_guest() + +# planner.maxiter = 500 + +# planner.compute_optimal_trajectory() + # Linear model ss = linearize( sys , 0.01 ) # Cost function cf = QuadraticCostFunction.from_sys( sys ) -cf.Q[0,0] = 1000 +cf.Q[0,0] = 1 cf.Q[1,1] = 1000 cf.Q[2,2] = 1000 cf.Q[3,3] = 1 cf.Q[4,4] = 1 cf.Q[5,5] = 1 -cf.R[0,0] = 1 -cf.R[1,1] = 1 +cf.R[0,0] = 0.1 +cf.R[1,1] = 1.0 # LQR controller ctl = synthesize_lqr_controller( ss , cf , sys.xbar , sys.ubar ) @@ -40,7 +58,7 @@ # Simulation Closed-Loop Non-linear with LQR controller cl_sys = ctl + sys -cl_sys.x0 = np.array([1,-10,-0.2,0,0,0]) +cl_sys.x0 = np.array([+0.2,-0.2,-0.1,0,0,0]) cl_sys.compute_trajectory(20) cl_sys.plot_trajectory('xu') cl_sys.animate_simulation( time_factor_video = 1.0 ) \ No newline at end of file From 41f6edfd5c2030842c21c8bf47ea7c051f3dbbb7 Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Wed, 31 Jan 2024 16:17:09 -0500 Subject: [PATCH 46/93] added coriolis matrix for rigid body 2d class --- dev/rigidbody/boat2.py | 6 +++--- dev/rigidbody/boat_parking_with_lqr2.py | 15 +++++++++------ dev/rigidbody/rigidbody.py | 5 +++++ 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/dev/rigidbody/boat2.py b/dev/rigidbody/boat2.py index f4a13047..428798dd 100644 --- a/dev/rigidbody/boat2.py +++ b/dev/rigidbody/boat2.py @@ -37,13 +37,13 @@ def __init__(self): # Dynamic properties self.mass = 1000.0 - self.inertia = 1000.0 + self.inertia = 10000.0 self.l_t = 2.0 # Distance between CG and Thrust vector self.gravity = 9.8 - self.damping_coef = np.array([ [ 1.0, 1.0, 1.0 ] , - [ 1.0, 1.0, 1.0 ] ]) + self.damping_coef = np.array([ [ 1.0, 100.0, 1.0 ] , + [ 1.0, 100.0, 1.0 ] ]) # Kinematic param self.width = 1.0 diff --git a/dev/rigidbody/boat_parking_with_lqr2.py b/dev/rigidbody/boat_parking_with_lqr2.py index bfa2654d..1bfa23f8 100644 --- a/dev/rigidbody/boat_parking_with_lqr2.py +++ b/dev/rigidbody/boat_parking_with_lqr2.py @@ -35,21 +35,24 @@ # planner.compute_optimal_trajectory() +sys.xbar[3] = 2.0 +sys.ubar[0] = 100.0 + # Linear model ss = linearize( sys , 0.01 ) # Cost function cf = QuadraticCostFunction.from_sys( sys ) -cf.Q[0,0] = 1 -cf.Q[1,1] = 1000 -cf.Q[2,2] = 1000 +cf.Q[0,0] = 0 +cf.Q[1,1] = 10 +cf.Q[2,2] = 10 cf.Q[3,3] = 1 cf.Q[4,4] = 1 cf.Q[5,5] = 1 -cf.R[0,0] = 0.1 -cf.R[1,1] = 1.0 +cf.R[0,0] = 0.001 +cf.R[1,1] = 0.001 # LQR controller ctl = synthesize_lqr_controller( ss , cf , sys.xbar , sys.ubar ) @@ -58,7 +61,7 @@ # Simulation Closed-Loop Non-linear with LQR controller cl_sys = ctl + sys -cl_sys.x0 = np.array([+0.2,-0.2,-0.1,0,0,0]) +cl_sys.x0 = np.array([+1.2,-1.2,-0.2,0,0,0]) cl_sys.compute_trajectory(20) cl_sys.plot_trajectory('xu') cl_sys.animate_simulation( time_factor_video = 1.0 ) \ No newline at end of file diff --git a/dev/rigidbody/rigidbody.py b/dev/rigidbody/rigidbody.py index dc6ac267..71d21d2d 100644 --- a/dev/rigidbody/rigidbody.py +++ b/dev/rigidbody/rigidbody.py @@ -527,6 +527,11 @@ def C(self, q , v ): C = np.zeros( ( self.dof , self.dof ) ) + w = v[2] + + C[1,0] = + self.mass * w + C[0,1] = - self.mass * w + return C ########################################################################### From b1f018171dc42fe08e132dca0938efe417885b0d Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Thu, 1 Feb 2024 00:30:23 -0500 Subject: [PATCH 47/93] tested cascade controller --- dev/plane/plane_controller.py | 6 +- dev/rigidbody/boat2.py | 16 +-- dev/rigidbody/boat_parking_with_lqr2.py | 10 +- dev/rigidbody/boat_parking_with_test_alex.py | 130 +++++++++++++++++++ 4 files changed, 146 insertions(+), 16 deletions(-) create mode 100644 dev/rigidbody/boat_parking_with_test_alex.py diff --git a/dev/plane/plane_controller.py b/dev/plane/plane_controller.py index 49774193..dfa247fe 100644 --- a/dev/plane/plane_controller.py +++ b/dev/plane/plane_controller.py @@ -43,9 +43,9 @@ def c( self , y , r , t = 0 ): Feedback static computation u = c(y,r,t) INPUTS - y : sensor signal_proc vector p x 1 - r : reference signal_proc vector k x 1 - t : time 1 x 1 + y : sensor vector p x 1 + r : reference vector k x 1 + t : time 1 x 1 OUPUTS u : control inputs vector m x 1 diff --git a/dev/rigidbody/boat2.py b/dev/rigidbody/boat2.py index 428798dd..4ff5d0f6 100644 --- a/dev/rigidbody/boat2.py +++ b/dev/rigidbody/boat2.py @@ -37,13 +37,13 @@ def __init__(self): # Dynamic properties self.mass = 1000.0 - self.inertia = 10000.0 - self.l_t = 2.0 # Distance between CG and Thrust vector + self.inertia = 2000.0 + self.l_t = 3.0 # Distance between CG and Thrust vector self.gravity = 9.8 - self.damping_coef = np.array([ [ 1.0, 100.0, 1.0 ] , - [ 1.0, 100.0, 1.0 ] ]) + self.damping_coef = np.array([ [ 10.0, 1000.0, 10.0 ] , + [ 100.0, 1000.0, 100.0 ] ]) # Kinematic param self.width = 1.0 @@ -187,7 +187,7 @@ def forward_kinematic_lines_plus(self, x , u , t ): ########################### vx = u[0] / self.u_ub[0] * self.height - vy = u[1] / self.u_ub[1] * self.height + vy = u[1] / self.u_ub[0] * self.height offset = -self.height pts_body = drawing.arrow_from_components( vx , vy , x = offset, origin = 'tip' ) @@ -217,11 +217,11 @@ def forward_kinematic_lines_plus(self, x , u , t ): sys.x0[1] = 0 sys.x0[2] = 0 - sys.x0[3] = 1.0 - sys.x0[4] = 0.0 + sys.x0[3] = 0.0 + sys.x0[4] = 0 sys.x0[5] = 0.0 - sys.ubar[0] = 100 + sys.ubar[0] = 1000 sys.ubar[1] = 100 sys.plot_trajectory('xu') diff --git a/dev/rigidbody/boat_parking_with_lqr2.py b/dev/rigidbody/boat_parking_with_lqr2.py index 1bfa23f8..908a142b 100644 --- a/dev/rigidbody/boat_parking_with_lqr2.py +++ b/dev/rigidbody/boat_parking_with_lqr2.py @@ -35,8 +35,8 @@ # planner.compute_optimal_trajectory() -sys.xbar[3] = 2.0 -sys.ubar[0] = 100.0 +sys.xbar[3] = 0.0 +sys.ubar[0] = 0.0 # Linear model @@ -44,9 +44,9 @@ # Cost function cf = QuadraticCostFunction.from_sys( sys ) -cf.Q[0,0] = 0 -cf.Q[1,1] = 10 -cf.Q[2,2] = 10 +cf.Q[0,0] = 1 +cf.Q[1,1] = 1 +cf.Q[2,2] = 1 cf.Q[3,3] = 1 cf.Q[4,4] = 1 cf.Q[5,5] = 1 diff --git a/dev/rigidbody/boat_parking_with_test_alex.py b/dev/rigidbody/boat_parking_with_test_alex.py new file mode 100644 index 00000000..1bce4b76 --- /dev/null +++ b/dev/rigidbody/boat_parking_with_test_alex.py @@ -0,0 +1,130 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Created on Sun Oct 3 08:27:06 2021 + +@author: alex +""" + +import numpy as np + +from scipy.linalg import solve_continuous_are + +from boat2 import Boat2D + +from pyro.analysis.costfunction import QuadraticCostFunction +from pyro.dynamic.statespace import linearize +from pyro.control.lqr import synthesize_lqr_controller + + +from pyro.control import controller + + + + +############################################################################### + +class BoatController( controller.StaticController ) : + + ############################ + def __init__( self , sys ): + """ """ + + # Dimensions + self.k = 3 + self.m = 2 + self.p = 6 + + super().__init__(self.k, self.m, self.p) + + # Label + self.name = 'Boat Controller' + + + self.sys = sys + + ss = linearize( sys , 0.01 ) + + # Velocity only + self.A = ss.A[3:,3:] + self.B = ss.B[3:,:] + + # Cost function + cf = QuadraticCostFunction(3,2) + cf.Q[0,0] = 1000 + cf.Q[1,1] = 1000 + cf.Q[2,2] = 10000 + + cf.R[0,0] = 0.001 + cf.R[1,1] = 0.001 + + S = solve_continuous_are( self.A , self.B , cf.Q , cf.R ) + + # Matrix gain + BTS = np.dot( self.B.T , S ) + R_inv = np.linalg.inv( cf.R ) + self.K = np.dot( R_inv , BTS ) + + self.v_d = np.array([1,0,-0.5]) + + self.q_d = np.array([0,0,0.0]) + + self.KP = np.array([[ 0.5 , 0 , 0], + [ 0 , 0.5 , 0], + [ 0 , 0 , 2]]) + + self.t_max = np.array([10000,1000]) + self.t_min = np.array([-1000,-1000]) + self.v_max = np.array([5.0,1.0,1.0]) + self.v_min = np.array([-1.0,-1.0,-1.0]) + + + ############################# + def c( self , y , r , t = 0 ): + + q = y[0:3] + v = y[3:] + + q_e = self.q_d - q + + if q_e[0]**2>2.0 or q_e[1]**2>2.0: + q_e[2] = np.arctan2(q_e[1],q_e[0]) - q[2] + + # v_d = self.v_d + v_d = self.sys.N( q ).T @ self.KP @ q_e + + v_d = np.clip( v_d , self.v_min , self.v_max ) + + v_e = v_d - v + + u = self.K @ v_e + + u = np.clip( u , self.t_min , self.t_max ) + + + return u + + +# Non-linear model +sys = Boat2D() + + + +ctl = BoatController( sys ) + +print(ctl.K) + +# Simulation Closed-Loop Non-linear with LQR controller +cl_sys = ctl + sys + +cl_sys.x0 = np.array([3,3,1.0,5,0,0]) +cl_sys.compute_trajectory(20) + +# cl_sys.x0 = np.array([-20,10,-2.5,0,0,0]) +# cl_sys.compute_trajectory(20) + +# cl_sys.x0 = np.array([50,50,0.0,0,0,0]) +# cl_sys.compute_trajectory(50) + +cl_sys.plot_trajectory('xu') +cl_sys.animate_simulation( time_factor_video = 1.0 ) \ No newline at end of file From 09ff9d126f6732d17a6f9a43507bd49020f51109 Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Thu, 1 Feb 2024 16:57:30 -0500 Subject: [PATCH 48/93] boat with current coef --- dev/rigidbody/boat2.py | 166 ++++++++++++++++--- dev/rigidbody/boat_parking_with_test_alex.py | 16 +- 2 files changed, 151 insertions(+), 31 deletions(-) diff --git a/dev/rigidbody/boat2.py b/dev/rigidbody/boat2.py index 4ff5d0f6..590452a1 100644 --- a/dev/rigidbody/boat2.py +++ b/dev/rigidbody/boat2.py @@ -2,10 +2,12 @@ ############################################################################### import numpy as np +import matplotlib.pyplot as plt ############################################################################### from pyro.dynamic import system from pyro.kinematic import geometry from pyro.kinematic import drawing +from pyro.analysis import graphical ############################################################################### from rigidbody import RigidBody2D @@ -38,26 +40,38 @@ def __init__(self): # Dynamic properties self.mass = 1000.0 self.inertia = 2000.0 - self.l_t = 3.0 # Distance between CG and Thrust vector + self.l_t = 2.0 # Distance between CG and Thrust vector - self.gravity = 9.8 + self.gravity = 9.8 # gravity + + # Hydrodynamic coefficients + + # linear damping + self.damping_coef = np.array([ 200.0, 1000.0, 1000.0 ]) + + # quadratic damping + self.Cx_max = 0.5 + self.Cy_max = 0.6 + self.Cm_max = 0.1 + + self.rho = 1000.0 # water density + self.Afc = 0.5 # frontal area + self.Alc = 1.0 # lateral area + self.loa = self.l_t * 2 # length over all - self.damping_coef = np.array([ [ 10.0, 1000.0, 10.0 ] , - [ 100.0, 1000.0, 100.0 ] ]) + # self.v_current = np.array([0,0,0]) # current velocity in world frame # Kinematic param - self.width = 1.0 - self.height = self.l_t - - # rocket drawing + self.width = 2.0 + self.height = self.l_t * 2 # Graphic output parameters self.dynamic_domain = True self.dynamic_range = 10 pts = np.zeros(( 6 , 3 )) - l = self.height - w = self.width + l = self.height * 0.5 + w = self.width * 0.5 pts[0,:] = np.array([-l, +w,0]) pts[1,:] = np.array([-l, -w,0]) @@ -82,21 +96,44 @@ def B(self, q , u ): return B + ########################################################################### + def CurrentCoef(self, alpha ): + + # Cl = np.sin( 2 * alpha ) # flat plate approx + # Cd = 1 - np.cos( 2 * alpha ) # flat plate approx + # Cm = 0.0 + + # Fig. 7.6 from Fossen + Cx = -0.5 * np.cos( alpha ) * np.abs( np.cos( alpha ) ) + Cy = +0.6 * np.sin( alpha ) * np.abs( np.sin( alpha ) ) + Cm = +0.1 * np.sin( 2 * alpha ) + + return np.array([ Cx , Cy , Cm ]) ########################################################################### def d(self, q , v , u ): """ State-dependent dissipative forces : dof x 1 """ - - d = np.zeros(self.dof ) - C = self.damping_coef + # linear damping + d_lin = v * self.damping_coef + + V2 = v[0]**2 + v[1]**2 + alpha = -np.arctan2( v[1] , v[0] ) + + Cx = self.CurrentCoef( alpha )[0] + Cy = self.CurrentCoef( alpha )[1] + Cm = self.CurrentCoef( alpha )[2] + + # hydrodynamic forces + fx = -0.5 * self.rho * self.Afc * Cx * V2 + fy = -0.5 * self.rho * self.Alc * Cy * V2 + mz = -0.5 * self.rho * self.Alc * self.loa * Cm * V2 - # quadratic + linear damping based on 6 coefficients - d = v*np.abs(v) * C[0,:] + v * C[1,:] + d_quad = np.array([ fx , fy , mz ]) - return d + return d_quad + d_lin ########################################################################### def forward_kinematic_domain(self, q ): @@ -129,12 +166,19 @@ def forward_kinematic_lines(self, q ): lines_color = [] ############################### - # ground line + # parking pose ############################### - pts = np.zeros(( 2 , 3 )) - pts[0,:] = np.array([-10,0,0]) - pts[1,:] = np.array([+10,0,0]) + pts = np.zeros(( 6 , 3 )) + l = self.height * 0.5 + w = self.width * 0.5 + + pts[0,:] = np.array([-l, +w,0]) + pts[1,:] = np.array([-l, -w,0]) + pts[2,:] = np.array([+l, -w,0]) + pts[3,:] = np.array([l+w,0,0]) + pts[4,:] = np.array([+l, +w,0]) + pts[5,:] = np.array([-l, +w,0]) lines_pts.append( pts ) lines_style.append( '--') @@ -181,14 +225,17 @@ def forward_kinematic_lines_plus(self, x , u , t ): lines_pts = [] # list of array (n_pts x 3) for each lines lines_style = [] lines_color = [] + + # M per Newton of force + f2r = 1.0 / self.u_ub[0] * self.height * 0.5 ########################### # trust force vector ########################### - vx = u[0] / self.u_ub[0] * self.height - vy = u[1] / self.u_ub[0] * self.height - offset = -self.height + vx = u[0] * f2r + vy = u[1] * f2r + offset = -self.l_t pts_body = drawing.arrow_from_components( vx , vy , x = offset, origin = 'tip' ) W_T_B = geometry.transformation_matrix_2D( x[2], x[0] , x[1] ) @@ -197,8 +244,71 @@ def forward_kinematic_lines_plus(self, x , u , t ): lines_pts.append( pts_W ) lines_style.append( '-') lines_color.append( 'r' ) + + + ########################### + # hydro forces + ########################### + + q , v = self.x2q( x ) + + f = -self.d( q , v , u) + + pts_body = drawing.arrow_from_components( f[0] * f2r , f[1] * f2r ) + + pts_W = drawing.transform_points_2D( W_T_B , pts_body ) + + lines_pts.append( pts_W ) + lines_style.append( '--') + lines_color.append( 'k' ) + + return lines_pts , lines_style , lines_color + + + ########################################################################### + def plot_alpha2Coefs(self, alpha_min = -3.15, alpha_max = 3.15 ): + + alphas = np.arange( alpha_min, alpha_max, 0.05 ) + + n = alphas.shape[0] + Cxs = np.zeros((n,1)) + Cys = np.zeros((n,1)) + Cms = np.zeros((n,1)) + + for i in range(n): + Cxs[i] = self.CurrentCoef( alphas[i] )[0] + Cys[i] = self.CurrentCoef( alphas[i] )[1] + Cms[i] = self.CurrentCoef( alphas[i] )[2] + + fig , ax = plt.subplots(3, figsize=graphical.default_figsize, + dpi= graphical.default_dpi, frameon=True) + + fig.canvas.manager.set_window_title('Aero curve') + + ax[0].plot( alphas , Cxs , 'b') + ax[0].set_ylabel('Cx', fontsize=graphical.default_fontsize) + ax[0].set_xlabel('alpha', fontsize=graphical.default_fontsize ) + ax[0].tick_params( labelsize = graphical.default_fontsize ) + ax[0].grid(True) + + ax[1].plot( alphas , Cys , 'b') + ax[1].set_ylabel('Cy', fontsize=graphical.default_fontsize) + ax[1].set_xlabel('alpha', fontsize=graphical.default_fontsize ) + ax[1].tick_params( labelsize = graphical.default_fontsize ) + ax[1].grid(True) + + ax[2].plot( alphas , Cms , 'b') + ax[2].set_ylabel('Cm', fontsize=graphical.default_fontsize) + ax[2].set_xlabel('alpha', fontsize=graphical.default_fontsize ) + ax[2].tick_params( labelsize = graphical.default_fontsize ) + ax[2].grid(True) + + fig.tight_layout() + fig.canvas.draw() + + plt.show() ''' @@ -218,11 +328,13 @@ def forward_kinematic_lines_plus(self, x , u , t ): sys.x0[2] = 0 sys.x0[3] = 0.0 - sys.x0[4] = 0 + sys.x0[4] = 0.0 sys.x0[5] = 0.0 - sys.ubar[0] = 1000 - sys.ubar[1] = 100 - + sys.ubar[0] = 10000 + sys.ubar[1] = 10 + + sys.plot_alpha2Coefs() + sys.plot_trajectory('xu') sys.animate_simulation() \ No newline at end of file diff --git a/dev/rigidbody/boat_parking_with_test_alex.py b/dev/rigidbody/boat_parking_with_test_alex.py index 1bce4b76..a37b0125 100644 --- a/dev/rigidbody/boat_parking_with_test_alex.py +++ b/dev/rigidbody/boat_parking_with_test_alex.py @@ -44,6 +44,7 @@ def __init__( self , sys ): self.sys = sys ss = linearize( sys , 0.01 ) + print(ss.A) # Velocity only self.A = ss.A[3:,3:] @@ -64,6 +65,7 @@ def __init__( self , sys ): BTS = np.dot( self.B.T , S ) R_inv = np.linalg.inv( cf.R ) self.K = np.dot( R_inv , BTS ) + print(self.K) self.v_d = np.array([1,0,-0.5]) @@ -120,11 +122,17 @@ def c( self , y , r , t = 0 ): cl_sys.x0 = np.array([3,3,1.0,5,0,0]) cl_sys.compute_trajectory(20) -# cl_sys.x0 = np.array([-20,10,-2.5,0,0,0]) -# cl_sys.compute_trajectory(20) +cl_sys.plot_trajectory('xu') +cl_sys.animate_simulation( time_factor_video = 1.0 ) + +cl_sys.x0 = np.array([-20,10,-2.5,0,0,0]) +cl_sys.compute_trajectory(20) + +cl_sys.plot_trajectory('xu') +cl_sys.animate_simulation( time_factor_video = 1.0 ) -# cl_sys.x0 = np.array([50,50,0.0,0,0,0]) -# cl_sys.compute_trajectory(50) +cl_sys.x0 = np.array([50,50,0.0,0,0,0]) +cl_sys.compute_trajectory(50) cl_sys.plot_trajectory('xu') cl_sys.animate_simulation( time_factor_video = 1.0 ) \ No newline at end of file From 4038b3184c15b01b79acc9c080de47bc63981b36 Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Fri, 2 Feb 2024 16:17:07 -0500 Subject: [PATCH 49/93] traj tracking --- dev/rigidbody/boat2.py | 28 +++-- dev/rigidbody/boat_parking_with_test_alex.py | 111 ++++++++++++++++--- 2 files changed, 114 insertions(+), 25 deletions(-) diff --git a/dev/rigidbody/boat2.py b/dev/rigidbody/boat2.py index 590452a1..3dfd8e4a 100644 --- a/dev/rigidbody/boat2.py +++ b/dev/rigidbody/boat2.py @@ -250,17 +250,31 @@ def forward_kinematic_lines_plus(self, x , u , t ): # hydro forces ########################### - q , v = self.x2q( x ) + # q , v = self.x2q( x ) - f = -self.d( q , v , u) + # f = -self.d( q , v , u) - pts_body = drawing.arrow_from_components( f[0] * f2r , f[1] * f2r ) + # pts_body = drawing.arrow_from_components( f[0] * f2r , f[1] * f2r ) - pts_W = drawing.transform_points_2D( W_T_B , pts_body ) + # pts_W = drawing.transform_points_2D( W_T_B , pts_body ) - lines_pts.append( pts_W ) - lines_style.append( '--') - lines_color.append( 'k' ) + # lines_pts.append( pts_W ) + # lines_style.append( '--') + # lines_color.append( 'k' ) + + ########################### + # Target + ########################### + + a = 10. + w = 0.3 + + pts = np.zeros(( 1 , 3 )) + pts[0,:] = np.array([ a * np.cos(w*t) , a * np.sin(w*t), 0.0 ]) + + lines_pts.append( pts ) + lines_style.append( 'o') + lines_color.append( 'r' ) diff --git a/dev/rigidbody/boat_parking_with_test_alex.py b/dev/rigidbody/boat_parking_with_test_alex.py index a37b0125..d1fd1b62 100644 --- a/dev/rigidbody/boat_parking_with_test_alex.py +++ b/dev/rigidbody/boat_parking_with_test_alex.py @@ -19,7 +19,15 @@ from pyro.control import controller - +############################################################################### +def na( theta ): + """ + Normalize angle to [-pi,pi] + """ + + theta = ( theta + np.pi ) % (2*np.pi) - np.pi + + return theta ############################################################################### @@ -40,7 +48,6 @@ def __init__( self , sys ): # Label self.name = 'Boat Controller' - self.sys = sys ss = linearize( sys , 0.01 ) @@ -54,10 +61,10 @@ def __init__( self , sys ): cf = QuadraticCostFunction(3,2) cf.Q[0,0] = 1000 cf.Q[1,1] = 1000 - cf.Q[2,2] = 10000 + cf.Q[2,2] = 5000 - cf.R[0,0] = 0.001 - cf.R[1,1] = 0.001 + cf.R[0,0] = 0.0001 + cf.R[1,1] = 0.0001 S = solve_continuous_are( self.A , self.B , cf.Q , cf.R ) @@ -67,10 +74,6 @@ def __init__( self , sys ): self.K = np.dot( R_inv , BTS ) print(self.K) - self.v_d = np.array([1,0,-0.5]) - - self.q_d = np.array([0,0,0.0]) - self.KP = np.array([[ 0.5 , 0 , 0], [ 0 , 0.5 , 0], [ 0 , 0 , 2]]) @@ -79,6 +82,47 @@ def __init__( self , sys ): self.t_min = np.array([-1000,-1000]) self.v_max = np.array([5.0,1.0,1.0]) self.v_min = np.array([-1.0,-1.0,-1.0]) + + self.trajectory_following = False + self.d_max = 2.0 + + + ############################# + def q_d( self , t = 0 ): + """ Return the desired position """ + + if self.trajectory_following: + + # vx = 5.0 + a = 10. + w = 0.3 + + q_d = np.array([ a * np.cos(w*t) , a * np.sin(w*t), 0.0 ]) + + q_d[2] = np.arctan2( a * w * np.cos(w * t ), a * w * - np.sin(w*t)) + + else: + + q_d = np.array([0,0,0.0]) + + return q_d + + ############################# + def dq_d( self , t = 0 ): + """ Return the desired position """ + + if self.trajectory_following: + + a = 10.0 + w = 0.3 + + dq_d = np.array([ a * w * - np.sin(w*t) , a * w * np.cos(w * t ), 0 ]) + + else: + + dq_d = np.array([0,0,0.0]) + + return dq_d ############################# @@ -86,24 +130,44 @@ def c( self , y , r , t = 0 ): q = y[0:3] v = y[3:] + + q_d = self.q_d(t) + dq_d = self.dq_d(t) - q_e = self.q_d - q - - if q_e[0]**2>2.0 or q_e[1]**2>2.0: - q_e[2] = np.arctan2(q_e[1],q_e[0]) - q[2] - - # v_d = self.v_d - v_d = self.sys.N( q ).T @ self.KP @ q_e + # Configuration error + q_e = q_d - q + q_e[2] = na( na(q_d[2]) - na(q[2]) ) # withtout cyclic fuck-up + d_e = np.linalg.norm(q_e[0:2]) # distance to target + + # Dynamic heading ref + # If far from target, head to target + # If close to target, head to ref orientation + if d_e > self.d_max: + actual = na( q[2] ) + desired = na( np.arctan2(q_e[1],q_e[0]) ) + q_e[2] = na( desired - actual ) + + # Position outter loop in inertial frame + dq_r = self.KP @ q_e + dq_d * 0.9 + + # Desired velocity in body frame + v_d = self.sys.N( q ).T @ dq_r + + # Direct Velocity control for debugging + # v_d = np.array([1,0,-0.5]) + # Velocity setpoint limits v_d = np.clip( v_d , self.v_min , self.v_max ) + # Velocity error v_e = v_d - v + # Velocity inner loop u = self.K @ v_e + # Max/min thrust u = np.clip( u , self.t_min , self.t_max ) - return u @@ -135,4 +199,15 @@ def c( self , y , r , t = 0 ): cl_sys.compute_trajectory(50) cl_sys.plot_trajectory('xu') -cl_sys.animate_simulation( time_factor_video = 1.0 ) \ No newline at end of file +cl_sys.animate_simulation( time_factor_video = 1.0 ) +# cl_sys.animate_simulation( time_factor_video = 1.0 , save = True , file_name = 'boat2' , show = False) + + +cl_sys.x0 = np.array([0,0,0,0,0,0]) +ctl.trajectory_following = True +# ctl.d_max = 0.0 +cl_sys.compute_trajectory(40) + +cl_sys.plot_trajectory('xu') +cl_sys.animate_simulation( time_factor_video = 1.0 ) +# cl_sys.animate_simulation( time_factor_video = 1.0 , save = True , file_name = 'boat1' , show = False) From e6183e4255b45adefd37edc74569f4915f91e1c1 Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Fri, 9 Feb 2024 13:38:46 -0500 Subject: [PATCH 50/93] boat parking update --- dev/rigidbody/boat_parking_with_test_alex.py | 24 ++++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/dev/rigidbody/boat_parking_with_test_alex.py b/dev/rigidbody/boat_parking_with_test_alex.py index d1fd1b62..8f93f14b 100644 --- a/dev/rigidbody/boat_parking_with_test_alex.py +++ b/dev/rigidbody/boat_parking_with_test_alex.py @@ -183,23 +183,23 @@ def c( self , y , r , t = 0 ): # Simulation Closed-Loop Non-linear with LQR controller cl_sys = ctl + sys -cl_sys.x0 = np.array([3,3,1.0,5,0,0]) -cl_sys.compute_trajectory(20) +# cl_sys.x0 = np.array([3,3,1.0,5,0,0]) +# cl_sys.compute_trajectory(20) -cl_sys.plot_trajectory('xu') -cl_sys.animate_simulation( time_factor_video = 1.0 ) +# # cl_sys.plot_trajectory('xu') +# cl_sys.animate_simulation( time_factor_video = 1.0 ) -cl_sys.x0 = np.array([-20,10,-2.5,0,0,0]) -cl_sys.compute_trajectory(20) +# cl_sys.x0 = np.array([-20,10,-2.5,0,0,0]) +# cl_sys.compute_trajectory(20) -cl_sys.plot_trajectory('xu') -cl_sys.animate_simulation( time_factor_video = 1.0 ) +# # cl_sys.plot_trajectory('xu') +# cl_sys.animate_simulation( time_factor_video = 1.0 ) -cl_sys.x0 = np.array([50,50,0.0,0,0,0]) -cl_sys.compute_trajectory(50) +# cl_sys.x0 = np.array([50,50,0.0,0,0,0]) +# cl_sys.compute_trajectory(50) -cl_sys.plot_trajectory('xu') -cl_sys.animate_simulation( time_factor_video = 1.0 ) +# # cl_sys.plot_trajectory('xu') +# # cl_sys.animate_simulation( time_factor_video = 1.0 ) # cl_sys.animate_simulation( time_factor_video = 1.0 , save = True , file_name = 'boat2' , show = False) From 30a16ce51fdf8e93c7b2c6811937ae96afadf9d7 Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Fri, 16 Feb 2024 11:23:36 -0500 Subject: [PATCH 51/93] lines from controller object --- dev/rigidbody/boat2.py | 51 ++--- dev/rigidbody/boat_parking_with_test_alex.py | 177 +++++++++++------- dev/rigidbody/rigidbody.py | 148 ++++++++++++++- .../acrobot/acrobot_with_lqr.py | 4 +- pyro/control/controller.py | 115 +++++++++++- 5 files changed, 374 insertions(+), 121 deletions(-) diff --git a/dev/rigidbody/boat2.py b/dev/rigidbody/boat2.py index 3dfd8e4a..b690e1ae 100644 --- a/dev/rigidbody/boat2.py +++ b/dev/rigidbody/boat2.py @@ -4,7 +4,7 @@ import numpy as np import matplotlib.pyplot as plt ############################################################################### -from pyro.dynamic import system +from pyro.dynamic import system from pyro.kinematic import geometry from pyro.kinematic import drawing from pyro.analysis import graphical @@ -85,7 +85,11 @@ def __init__(self): ########################################################################### def B(self, q , u ): """ - Actuator Matrix : dof x m + Actuator Matrix + ------------------ + Here u is a 2D point force [ F_x , F_y ] + applied at a point located at a distance l_t behind the CG + hence also creating a yaw moment """ B = np.zeros((3,2)) @@ -113,7 +117,11 @@ def CurrentCoef(self, alpha ): ########################################################################### def d(self, q , v , u ): """ - State-dependent dissipative forces : dof x 1 + Hydrodynamic dissipative forces + ----------------------------------- + + The model is a combination of linear and quadratic damping + """ # linear damping @@ -165,25 +173,6 @@ def forward_kinematic_lines(self, q ): lines_style = [] lines_color = [] - ############################### - # parking pose - ############################### - - pts = np.zeros(( 6 , 3 )) - l = self.height * 0.5 - w = self.width * 0.5 - - pts[0,:] = np.array([-l, +w,0]) - pts[1,:] = np.array([-l, -w,0]) - pts[2,:] = np.array([+l, -w,0]) - pts[3,:] = np.array([l+w,0,0]) - pts[4,:] = np.array([+l, +w,0]) - pts[5,:] = np.array([-l, +w,0]) - - lines_pts.append( pts ) - lines_style.append( '--') - lines_color.append( 'k' ) - ########################### # body ########################### @@ -217,9 +206,7 @@ def forward_kinematic_lines(self, q ): ########################################################################### def forward_kinematic_lines_plus(self, x , u , t ): """ - show trust vectors - - + Graphical output showing trust vectors and hydrodynamic forces """ lines_pts = [] # list of array (n_pts x 3) for each lines @@ -262,20 +249,6 @@ def forward_kinematic_lines_plus(self, x , u , t ): # lines_style.append( '--') # lines_color.append( 'k' ) - ########################### - # Target - ########################### - - a = 10. - w = 0.3 - - pts = np.zeros(( 1 , 3 )) - pts[0,:] = np.array([ a * np.cos(w*t) , a * np.sin(w*t), 0.0 ]) - - lines_pts.append( pts ) - lines_style.append( 'o') - lines_color.append( 'r' ) - return lines_pts , lines_style , lines_color diff --git a/dev/rigidbody/boat_parking_with_test_alex.py b/dev/rigidbody/boat_parking_with_test_alex.py index 8f93f14b..04da5187 100644 --- a/dev/rigidbody/boat_parking_with_test_alex.py +++ b/dev/rigidbody/boat_parking_with_test_alex.py @@ -37,8 +37,7 @@ class BoatController( controller.StaticController ) : ############################ def __init__( self , sys ): """ """ - - # Dimensions + # Dimensions of signals self.k = 3 self.m = 2 self.p = 6 @@ -48,54 +47,60 @@ def __init__( self , sys ): # Label self.name = 'Boat Controller' + # Dynamic model available to the controller self.sys = sys - ss = linearize( sys , 0.01 ) - print(ss.A) - - # Velocity only + # Velocity inner loop parameters + self.reference_velocity = np.array([ 5.0, 0.0, 0.0]) + + # Linearized model + ss = linearize( sys , 0.01 ) self.A = ss.A[3:,3:] self.B = ss.B[3:,:] + print('Velocity linearized dynamic') + print('----------------------------') + print('A =\n', self.A) + print('B =\n', self.B) # Cost function cf = QuadraticCostFunction(3,2) cf.Q[0,0] = 1000 cf.Q[1,1] = 1000 cf.Q[2,2] = 5000 - cf.R[0,0] = 0.0001 cf.R[1,1] = 0.0001 - S = solve_continuous_are( self.A , self.B , cf.Q , cf.R ) - - # Matrix gain - BTS = np.dot( self.B.T , S ) - R_inv = np.linalg.inv( cf.R ) - self.K = np.dot( R_inv , BTS ) - print(self.K) + # LQR solution + self.S = solve_continuous_are( self.A , self.B , cf.Q , cf.R ) + self.K = np.linalg.inv( cf.R ) @ self.B.T @ self.S + print('Velocity inner-loop LQR gain matrix =\n', self.K) + # Outer position loop parameters + self.position_control_isactivated = True self.KP = np.array([[ 0.5 , 0 , 0], [ 0 , 0.5 , 0], [ 0 , 0 , 2]]) - - self.t_max = np.array([10000,1000]) - self.t_min = np.array([-1000,-1000]) - self.v_max = np.array([5.0,1.0,1.0]) - self.v_min = np.array([-1.0,-1.0,-1.0]) - - self.trajectory_following = False self.d_max = 2.0 + # Trajectory following parameters + self.trajectory_following_isactivated = False + self.traj_amplitude = 10.0 + self.traj_frequency = 0.3 + + # Saturations + self.f_max = np.array([10000,1000]) + self.f_min = np.array([-1000,-1000]) + self.v_max = np.array([5.0,1.0,1.0]) + self.v_min = np.array([-1.0,-1.0,-1.0]) ############################# def q_d( self , t = 0 ): """ Return the desired position """ - if self.trajectory_following: + if self.trajectory_following_isactivated: - # vx = 5.0 - a = 10. - w = 0.3 + a = self.traj_amplitude + w = self.traj_frequency q_d = np.array([ a * np.cos(w*t) , a * np.sin(w*t), 0.0 ]) @@ -109,15 +114,17 @@ def q_d( self , t = 0 ): ############################# def dq_d( self , t = 0 ): - """ Return the desired position """ + """ Return the time derivative of the desired position """ - if self.trajectory_following: + if self.trajectory_following_isactivated: - a = 10.0 - w = 0.3 + a = self.traj_amplitude + w = self.traj_frequency dq_d = np.array([ a * w * - np.sin(w*t) , a * w * np.cos(w * t ), 0 ]) + # TODO: dq_d[2] = ... + else: dq_d = np.array([0,0,0.0]) @@ -127,37 +134,49 @@ def dq_d( self , t = 0 ): ############################# def c( self , y , r , t = 0 ): + """ + Control law + """ - q = y[0:3] - v = y[3:] + # Avilable observations + q = y[0:3] # position feedback + v = y[3:] # velocity feedback - q_d = self.q_d(t) - dq_d = self.dq_d(t) - - # Configuration error - q_e = q_d - q - q_e[2] = na( na(q_d[2]) - na(q[2]) ) # withtout cyclic fuck-up - d_e = np.linalg.norm(q_e[0:2]) # distance to target - - # Dynamic heading ref - # If far from target, head to target - # If close to target, head to ref orientation - if d_e > self.d_max: - actual = na( q[2] ) - desired = na( np.arctan2(q_e[1],q_e[0]) ) - q_e[2] = na( desired - actual ) - - # Position outter loop in inertial frame - dq_r = self.KP @ q_e + dq_d * 0.9 - - # Desired velocity in body frame - v_d = self.sys.N( q ).T @ dq_r - - # Direct Velocity control for debugging - # v_d = np.array([1,0,-0.5]) - - # Velocity setpoint limits - v_d = np.clip( v_d , self.v_min , self.v_max ) + if self.position_control_isactivated: + + # Desired position and velocity + q_d = self.q_d(t) + dq_d = self.dq_d(t) + + # Configuration error + q_e = q_d - q + d_e = np.linalg.norm(q_e[0:2]) # absolute distance to target + + # Angular position error withtout cyclic fuck-up + q_e[2] = na( na(q_d[2]) - na(q[2]) ) + + # Dynamic heading ref + # If far from target, reference orientation is overided to + # instead making the boat head toward the desired position + if d_e > self.d_max: + actual = na( q[2] ) + desired = na( np.arctan2( q_e[1] , q_e[0] ) ) + q_e[2] = na( desired - actual ) + + # Position outter loop in inertial frame + dq_r = self.KP @ q_e + dq_d * 1.0 + # TODO 0.9 is a quick n dirty fix for the boat to always lag behind the target + + # Desired velocity in body frame + v_d = self.sys.N( q ).T @ dq_r + + # Velocity setpoint limits + v_d = np.clip( v_d , self.v_min , self.v_max ) + + else: + + # Direct Velocity control for debugging + v_d = self.reference_velocity # Velocity error v_e = v_d - v @@ -166,28 +185,46 @@ def c( self , y , r , t = 0 ): u = self.K @ v_e # Max/min thrust - u = np.clip( u , self.t_min , self.t_max ) + u = np.clip( u , self.f_min , self.f_max ) return u + + ######################################################################### + def forward_kinematic_lines_plus( self, x , u , t ): + """ + Graphical output for the controller + ----------------------------------- + plot the desired boat pose + x,u,t are the state, input and time of the global closed-loop system -# Non-linear model -sys = Boat2D() + """ + # desired boat pose, from model forward kinematics + pts, style, color = self.sys.forward_kinematic_lines( self.q_d(t) ) + # Change the line style and color + style[0] = '--' + color[0] = 'c' + color[1] = 'c' -ctl = BoatController( sys ) + return pts, style, color -print(ctl.K) + +# Non-linear model +sys = Boat2D() + +# Cascade controller +ctl = BoatController( sys ) # Simulation Closed-Loop Non-linear with LQR controller cl_sys = ctl + sys -# cl_sys.x0 = np.array([3,3,1.0,5,0,0]) -# cl_sys.compute_trajectory(20) +cl_sys.x0 = np.array([3,3,1.0,5,0,0]) +cl_sys.compute_trajectory(10) -# # cl_sys.plot_trajectory('xu') -# cl_sys.animate_simulation( time_factor_video = 1.0 ) +cl_sys.plot_trajectory('xu') +cl_sys.animate_simulation( time_factor_video = 1.0 ) # cl_sys.x0 = np.array([-20,10,-2.5,0,0,0]) # cl_sys.compute_trajectory(20) @@ -203,9 +240,9 @@ def c( self , y , r , t = 0 ): # cl_sys.animate_simulation( time_factor_video = 1.0 , save = True , file_name = 'boat2' , show = False) -cl_sys.x0 = np.array([0,0,0,0,0,0]) -ctl.trajectory_following = True -# ctl.d_max = 0.0 +# cl_sys.x0 = np.array([0,0,0,0,0,0]) +ctl.trajectory_following_isactivated = True +# # ctl.d_max = 0.0 cl_sys.compute_trajectory(40) cl_sys.plot_trajectory('xu') diff --git a/dev/rigidbody/rigidbody.py b/dev/rigidbody/rigidbody.py index 71d21d2d..be486ca7 100644 --- a/dev/rigidbody/rigidbody.py +++ b/dev/rigidbody/rigidbody.py @@ -10,6 +10,8 @@ import numpy as np ############################################################################### from pyro.dynamic import system +from pyro.kinematic import geometry +from pyro.kinematic import drawing ############################################################################### ############################################################################### @@ -220,7 +222,7 @@ def generalized_forces(self, q , v , dv , t = 0 ): d = self.d( q , v ) # Generalized forces - forces = H @ dv + C @ v + g + d + forces = M @ dv + C @ v + g + d return forces @@ -482,7 +484,7 @@ class RigidBody2D( GeneralizedMechanicalSystemWithPositionInputs ): """ ############################ - def __init__(self, force_inputs = 1, other_inputs = 1): + def __init__(self, force_inputs = 2, other_inputs = 0): """ """ # Degree of Freedom @@ -514,6 +516,10 @@ def __init__(self, force_inputs = 1, other_inputs = 1): self.inertia = 1.0 self.l_t = 1.0 # Distance between CG and Thrust + # Default graphic output parameters + self.dynamic_domain = True + + ########################################################################### def M(self, q ): @@ -566,7 +572,7 @@ def g(self, q ): ########################################################################### - def d(self, q , v ): + def d(self, q , v , u): """ State-dependent dissipative forces : dof x 1 """ @@ -574,6 +580,129 @@ def d(self, q , v ): d = np.zeros(self.dof ) # Default is zero vector return d + + ########################################################################### + def B(self, q , u ): + """ + Actuator Matrix + ------------------ + This placeholder is for a 2D point force [ F_x , F_y ] + applied at a point located at a distance l_t behind the CG + """ + + B = np.zeros(( self.dof, self.m_f )) + + B[0,0] = 1 + B[1,1] = 1 + B[2,1] = - self.l_t + + return B + + ########################################################################### + def forward_kinematic_domain(self, q ): + """ + Place holder graphical output ( box with a force ) + """ + + l = self.l_t * 10 + + x = q[0] + y = q[1] + z = 0 + + if self.dynamic_domain: + + domain = [ ( -l + x , l + x ) , + ( -l + y , l + y ) , + ( -l + z , l + z ) ]# + else: + + domain = [ ( -l , l ) , + ( -l , l ) , + ( -l , l ) ]# + + return domain + + + ########################################################################### + def forward_kinematic_lines(self, q ): + """ + Place holder graphical output ( box with a force ) + """ + + lines_pts = [] # list of array (n_pts x 3) for each lines + lines_style = [] + lines_color = [] + + ########################### + # body + ########################### + + x = q[0] + y = q[1] + theta = q[2] + + W_T_B = geometry.transformation_matrix_2D( theta , x , y ) + + w = self.l_t + + # Points in body frame + pts = np.zeros(( 5 , 3 )) + pts[0,:] = np.array([-w,+w,0]) + pts[1,:] = np.array([-w,-w,0]) + pts[2,:] = np.array([+w,-w,0]) + pts[3,:] = np.array([+w,+w,0]) + pts[4,:] = np.array([-w,+w,0]) + + pts_W = drawing.transform_points_2D( W_T_B , pts ) + + lines_pts.append( pts_W ) + lines_style.append( '-') + lines_color.append( 'b' ) + + ########################### + # C.G. + ########################### + + pts = np.zeros(( 1 , 3 )) + pts[0,:] = np.array([x,y,0]) + + lines_pts.append( pts ) + lines_style.append( 'o') + lines_color.append( 'b' ) + + return lines_pts , lines_style , lines_color + + ########################################################################### + def forward_kinematic_lines_plus(self, x , u , t ): + """ + Place holder graphical output ( box with a force ) + """ + + lines_pts = [] # list of array (n_pts x 3) for each lines + lines_style = [] + lines_color = [] + + # M per Newton of force + f2r = 1.0 / self.u_ub[0] * self.l_t + + ########################### + # force vector + ########################### + + vx = u[0] * f2r + vy = u[1] * f2r + offset = -self.l_t + + pts_body = drawing.arrow_from_components( vx , vy , x = offset, origin = 'tip' ) + W_T_B = geometry.transformation_matrix_2D( x[2], x[0] , x[1] ) + pts_W = drawing.transform_points_2D( W_T_B , pts_body ) + + lines_pts.append( pts_W ) + lines_style.append( '-') + lines_color.append( 'r' ) + + return lines_pts , lines_style , lines_color ''' @@ -586,15 +715,16 @@ def d(self, q , v ): if __name__ == "__main__": """ MAIN TEST """ - sys = GeneralizedMechanicalSystem( dof = 2 , pos = 2 , actuators = 2 ) - sys = GeneralizedMechanicalSystemWithPositionInputs( dof = 3 , pos = 1 , force_inputs= 1 , other_inputs=1 ) + #sys = GeneralizedMechanicalSystem( dof = 2 , pos = 2 , actuators = 2 ) + #sys = GeneralizedMechanicalSystemWithPositionInputs( dof = 3 , pos = 1 , force_inputs= 1 , other_inputs=1 ) + sys = RigidBody2D() - sys.show( q = np.array([ 1.0, 2.0]) ) - sys.show3( q = np.array([-0.5, 1.5]) ) + #sys.show( q = np.array([ 1.0, 2.0, 0.5 ]) ) - sys.ubar = np.array([1,2]) - sys.x0 = np.array([0,0,0,0]) + sys.ubar = np.array([10,2.0]) + sys.x0 = np.array([0,0,0,0,0,0]) + sys.compute_trajectory( tf = 20 ) sys.plot_trajectory() sys.animate_simulation() \ No newline at end of file diff --git a/examples/demos_by_system/acrobot/acrobot_with_lqr.py b/examples/demos_by_system/acrobot/acrobot_with_lqr.py index 77908675..da56bf17 100644 --- a/examples/demos_by_system/acrobot/acrobot_with_lqr.py +++ b/examples/demos_by_system/acrobot/acrobot_with_lqr.py @@ -34,6 +34,6 @@ # Simulation Closed-Loop Non-linear with LQR controller cl_sys = ctl + sys cl_sys.x0 = np.array([-0.1,0.2,0,0]) -cl_sys.compute_trajectory( tf = 2.0 ) +cl_sys.compute_trajectory( tf = 5.0 ) cl_sys.plot_trajectory('xu') -cl_sys.animate_simulation( time_factor_video=0.02 ) \ No newline at end of file +cl_sys.animate_simulation( time_factor_video=1.5 ) \ No newline at end of file diff --git a/pyro/control/controller.py b/pyro/control/controller.py index d4c97e63..79b8c4c8 100644 --- a/pyro/control/controller.py +++ b/pyro/control/controller.py @@ -110,6 +110,23 @@ def t2r( self , t ): return r + ######################################################################### + def forward_kinematic_lines_plus( self, x , u , t ): + """ + Graphical output for the controller + ----------------------------------- + default is nothing + + x,u,t are the state, input and time of the global closed-loop system + + """ + + pts = None + style = None + color = None + + return pts, style, color + ######################################################################### # No need to overwrite the following functions for child classes @@ -382,6 +399,83 @@ def t2u( self , t ): return u + + ########################################################################### + # Place holder graphical output, overload with specific graph output + ########################################################################### + + ############################# + def xut2q( self, x , u , t ): + """ Compute configuration variables ( q vector ) """ + + # Use the plant function + q = self.plant.xut2q( x, u, t) + + return q + + + ########################################################################### + def forward_kinematic_domain(self, q ): + """ Set the domain range for ploting, can be static or dynamic """ + + # Use the plant function + domain = self.plant.forward_kinematic_domain( q ) + + return domain + + + ########################################################################### + def forward_kinematic_lines(self, q ): + """ + Compute points p = [x;y;z] positions given config q + ---------------------------------------------------- + - points of interest for ploting + + Outpus: + lines_pts = [] : a list of array (n_pts x 3) for each lines + + """ + + lines_pts = self.plant.forward_kinematic_lines( q ) + + return lines_pts + + + ########################################################################### + def forward_kinematic_lines_plus(self, x , u , t ): + """ + Return combined graphical output for the controller and the system + """ + + # TODO: this is a quick fix, need to be improved + + sys = self.plant.forward_kinematic_lines_plus( x , u , t ) + ctl = self.controller.forward_kinematic_lines_plus( x , u , t ) + + if type(sys) is tuple: + lines_pts = sys[0] + lines_style = sys[1] + lines_color = sys[2] + else: + # Legacy graph function to remove eventually + lines_pts = sys + lines_style = [] + lines_color = [] + for j, line in enumerate(lines_pts): + lines_style.append( self.plant.linestyle ) # default value + lines_color.append( self.plant.linescolor ) # default value + + if ctl[0] is not None: + lines_pts = ctl[0] + lines_pts + lines_style = ctl[1] + lines_style + lines_color = ctl[2] + lines_color + + return lines_pts, lines_style, lines_color + + ############################################################################# + #### Updated shortcuts + ############################################################################# + ########################################################################### def plot_phase_plane_closed_loop(self , x_axis = 0 , y_axis = 1 ): @@ -447,7 +541,8 @@ def get_plotter(self): ########################################################################### def get_animator(self): - return self.plant.get_animator() + + return graphical.Animator(self) ########################################################################### @@ -680,6 +775,24 @@ def cbar( self , y , t = 0 ): u = self.c( self.zbar, y , self.rbar , t ) return u + + + ######################################################################### + def forward_kinematic_lines_plus( self, x, u , t ): + """ + Graphical output for the controller + ----------------------------------- + default is nothing + + x,u,t are the state, input and time of the global closed-loop system + + """ + + pts = None + style = None + color = None + + return pts, style, color ############################# From eaf2e2fbd2a1d05abf613ffa141d34eb53a1176c Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Fri, 16 Feb 2024 11:56:59 -0500 Subject: [PATCH 52/93] moved and cleaned boat from fossen example --- .../boat2.py => pyro/dynamic/boat.py | 92 +++++++++++++------ {dev/rigidbody => pyro/dynamic}/rigidbody.py | 0 2 files changed, 64 insertions(+), 28 deletions(-) rename dev/rigidbody/boat2.py => pyro/dynamic/boat.py (74%) rename {dev/rigidbody => pyro/dynamic}/rigidbody.py (100%) diff --git a/dev/rigidbody/boat2.py b/pyro/dynamic/boat.py similarity index 74% rename from dev/rigidbody/boat2.py rename to pyro/dynamic/boat.py index b690e1ae..1390ca26 100644 --- a/dev/rigidbody/boat2.py +++ b/pyro/dynamic/boat.py @@ -4,20 +4,49 @@ import numpy as np import matplotlib.pyplot as plt ############################################################################### -from pyro.dynamic import system +from pyro.dynamic import rigidbody from pyro.kinematic import geometry from pyro.kinematic import drawing from pyro.analysis import graphical ############################################################################### -from rigidbody import RigidBody2D - -############################################################################## - ############################################################################### -class Boat2D( RigidBody2D ): +class Boat2D( rigidbody.RigidBody2D ): """ + Simple planar (3 DoF) boat model + + Partialy based on + 'Low-Speed Maneuvering Models for Dynamic Positioning (3 DOFs)' + see Section 6.7 in + Fossen, Thor I. Handbook of marine craft hydrodynamics and motion control + 2nd Editions. John Wiley & Sons, 2021. + + with Equation of motion (in body frame) described by: + ------------------------------------------------------- + M(q) dv + C(q,v) v + d(q,v) = B(q) u + dq = N(q) v + ------------------------------------------------------- + v : dim = (3, 1) : velocity variables ( foward speed, sway speed, yaw rate) in body frame + q : dim = (3, 1) : position variables ( x , y , theta ) in global frame + u : dim = (2, 1) : thruster force vector in body frame + d(q,v) : dim = (3, 1) : state-dependent dissipative forces in body frame + M(q) : dim = (3, 3) : inertia matrix in body frame + C(q,v) : dim = (3, 3) : corriolis matrix in body frame + B(q) : dim = (3, 2) : actuator matrix in body frame + N(q) : dim = (3, 3) : transformation matrix from body frame to global frame + + with the following hypothesis: + - The boat is a planar rigid body with 3 DoF ( surge, sway and yaw) + - The input is a 2D force vector [ F_x , F_y ] applied at a distance l_t behind the CG + - The boat is subject to linear and quadratic damping + - The default quadratic hydronamic forces coef. are taken from + Fossen, Thor I. Handbook of marine craft hydrodynamics and motion control + 2nd Editions. John Wiley & Sons, 2021. + See 6.7.1 and Fig. 6.11 + A rough fit on experimental data from a tanker + - TODO: not fully validated + - The c.g., c.p and body-frame origin are coincident """ @@ -25,7 +54,7 @@ class Boat2D( RigidBody2D ): def __init__(self): """ """ - RigidBody2D.__init__( self , force_inputs = 2, other_inputs = 0) + rigidbody.RigidBody2D.__init__( self , force_inputs = 2, other_inputs = 0) self.input_label = ['Tx','Ty'] self.input_units = ['[N]','[N]'] @@ -34,6 +63,7 @@ def __init__(self): self.x_ub = np.array([+10,+10,+2,10,10,10]) self.x_lb = np.array([-10,-10,-2,-10,-10,-10]) + # Input working range self.u_ub = np.array([+1000,+100]) self.u_lb = np.array([-1000,-100]) @@ -41,8 +71,6 @@ def __init__(self): self.mass = 1000.0 self.inertia = 2000.0 self.l_t = 2.0 # Distance between CG and Thrust vector - - self.gravity = 9.8 # gravity # Hydrodynamic coefficients @@ -59,20 +87,19 @@ def __init__(self): self.Alc = 1.0 # lateral area self.loa = self.l_t * 2 # length over all - # self.v_current = np.array([0,0,0]) # current velocity in world frame - - # Kinematic param - self.width = 2.0 - self.height = self.l_t * 2 + # current velocity in world frame + # TODO: not implemented + # self.v_current = np.array([0,0,0]) # Graphic output parameters + self.width = 2.0 + self.height = self.l_t * 2 self.dynamic_domain = True self.dynamic_range = 10 - pts = np.zeros(( 6 , 3 )) l = self.height * 0.5 w = self.width * 0.5 - + pts = np.zeros(( 6 , 3 )) pts[0,:] = np.array([-l, +w,0]) pts[1,:] = np.array([-l, -w,0]) pts[2,:] = np.array([+l, -w,0]) @@ -82,6 +109,8 @@ def __init__(self): self.drawing_body_pts = pts + self.show_hydrodynamic_forces = False + ########################################################################### def B(self, q , u ): """ @@ -107,10 +136,11 @@ def CurrentCoef(self, alpha ): # Cd = 1 - np.cos( 2 * alpha ) # flat plate approx # Cm = 0.0 - # Fig. 7.6 from Fossen - Cx = -0.5 * np.cos( alpha ) * np.abs( np.cos( alpha ) ) - Cy = +0.6 * np.sin( alpha ) * np.abs( np.sin( alpha ) ) - Cm = +0.1 * np.sin( 2 * alpha ) + # Fig. 7.6 from Fossen (1st edition) + # Fig. 6.11 from Fossen (2nd edition) + Cx = - self.Cx_max * np.cos( alpha ) * np.abs( np.cos( alpha ) ) + Cy = + self.Cy_max * np.sin( alpha ) * np.abs( np.sin( alpha ) ) + Cm = + self.Cm_max * np.sin( 2 * alpha ) return np.array([ Cx , Cy , Cm ]) @@ -134,7 +164,7 @@ def d(self, q , v , u ): Cy = self.CurrentCoef( alpha )[1] Cm = self.CurrentCoef( alpha )[2] - # hydrodynamic forces + # quadratic forces fx = -0.5 * self.rho * self.Afc * Cx * V2 fy = -0.5 * self.rho * self.Alc * Cy * V2 mz = -0.5 * self.rho * self.Alc * self.loa * Cm * V2 @@ -143,6 +173,7 @@ def d(self, q , v , u ): return d_quad + d_lin + ########################################################################### def forward_kinematic_domain(self, q ): @@ -203,6 +234,7 @@ def forward_kinematic_lines(self, q ): return lines_pts , lines_style , lines_color + ########################################################################### def forward_kinematic_lines_plus(self, x , u , t ): """ @@ -237,17 +269,19 @@ def forward_kinematic_lines_plus(self, x , u , t ): # hydro forces ########################### - # q , v = self.x2q( x ) + if self.show_hydrodynamic_forces: - # f = -self.d( q , v , u) + q , v = self.x2q( x ) - # pts_body = drawing.arrow_from_components( f[0] * f2r , f[1] * f2r ) + f = -self.d( q , v , u) - # pts_W = drawing.transform_points_2D( W_T_B , pts_body ) + pts_body = drawing.arrow_from_components( f[0] * f2r , f[1] * f2r ) - # lines_pts.append( pts_W ) - # lines_style.append( '--') - # lines_color.append( 'k' ) + pts_W = drawing.transform_points_2D( W_T_B , pts_body ) + + lines_pts.append( pts_W ) + lines_style.append( '--') + lines_color.append( 'k' ) @@ -323,5 +357,7 @@ def plot_alpha2Coefs(self, alpha_min = -3.15, alpha_max = 3.15 ): sys.plot_alpha2Coefs() + # sys.show_hydrodynamic_forces = True + sys.plot_trajectory('xu') sys.animate_simulation() \ No newline at end of file diff --git a/dev/rigidbody/rigidbody.py b/pyro/dynamic/rigidbody.py similarity index 100% rename from dev/rigidbody/rigidbody.py rename to pyro/dynamic/rigidbody.py From 12fbcb040d3f27d8b183a0c9efa463bec983b0e8 Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Fri, 16 Feb 2024 12:01:20 -0500 Subject: [PATCH 53/93] typo in rigid body --- pyro/dynamic/boat.py | 2 +- pyro/dynamic/rigidbody.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pyro/dynamic/boat.py b/pyro/dynamic/boat.py index 1390ca26..b682453b 100644 --- a/pyro/dynamic/boat.py +++ b/pyro/dynamic/boat.py @@ -348,7 +348,7 @@ def plot_alpha2Coefs(self, alpha_min = -3.15, alpha_max = 3.15 ): sys.x0[1] = 0 sys.x0[2] = 0 - sys.x0[3] = 0.0 + sys.x0[3] = 10.0 sys.x0[4] = 0.0 sys.x0[5] = 0.0 diff --git a/pyro/dynamic/rigidbody.py b/pyro/dynamic/rigidbody.py index be486ca7..9e6b9f45 100644 --- a/pyro/dynamic/rigidbody.py +++ b/pyro/dynamic/rigidbody.py @@ -33,7 +33,7 @@ class GeneralizedMechanicalSystem( system.ContinuousDynamicSystem ): M(q) : dim = (dof, dof) : inertia matrix C(q,v) : dim = (dof, dof) : corriolis matrix B(q) : dim = (dof, m) : actuator matrix - N(q) : dim = (pos, dof) : actuator matrix + N(q) : dim = (pos, dof) : transformation matrix """ From 02c6e048b7ac94d9f9173622d6afac5c59a5688c Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Fri, 16 Feb 2024 15:48:08 -0500 Subject: [PATCH 54/93] cleaned-up exemple --- dev/boat/boat.py | 314 ------------------ dev/boat/boat_parking_with_lqr.py | 53 --- dev/boat/boat_trajectory_planning.py | 35 -- dev/boat/boat_with_lqr_controllability.py | 70 ++++ dev/rigidbody/boat_parking_with_lqr2.py | 67 ---- .../boat/boat_position_controller.py | 131 +++++--- pyro/dynamic/boat.py | 35 +- 7 files changed, 169 insertions(+), 536 deletions(-) delete mode 100644 dev/boat/boat.py delete mode 100644 dev/boat/boat_parking_with_lqr.py delete mode 100644 dev/boat/boat_trajectory_planning.py create mode 100644 dev/boat/boat_with_lqr_controllability.py delete mode 100644 dev/rigidbody/boat_parking_with_lqr2.py rename dev/rigidbody/boat_parking_with_test_alex.py => examples/demos_by_system/boat/boat_position_controller.py (66%) diff --git a/dev/boat/boat.py b/dev/boat/boat.py deleted file mode 100644 index c0038123..00000000 --- a/dev/boat/boat.py +++ /dev/null @@ -1,314 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Created on Sun Oct 3 05:43:08 2021 - -@author: alex -""" - -############################################################################### -import numpy as np -import matplotlib.pyplot as plt -############################################################################### -from pyro.dynamic import mechanical -from pyro.kinematic import geometry -from pyro.kinematic import drawing -############################################################################### - - -############################################################################## -# 2D planar drone -############################################################################## - -class Boat( mechanical.MechanicalSystemWithPositionInputs ): - """ - Equations of Motion - ------------------------- - - """ - - ############################ - def __init__(self): - """ """ - - # initialize standard params - mechanical.MechanicalSystemWithPositionInputs.__init__( self, 3 , 1 , 1 ) - - # Labels - self.name = '2D planar boat model' - self.state_label = ['x','y','theta','vx','vy','w'] - self.input_label = ['Trust', 'delta'] - self.output_label = self.state_label - - # Units - self.state_units = ['[m]','[m]','[rad]','[m/sec]','[m/sec]','[rad/sec]'] - self.input_units = ['[N]', '[Rad]'] - self.output_units = self.state_units - - # State working range - self.x_ub = np.array([+50,+50,+3,+2,+2,+1]) - self.x_lb = np.array([-50,-50,-3,-2,-2,-1]) - - # State working range - self.u_ub = np.array([+500,+1]) - self.u_lb = np.array([-10,-1]) - - # Model param - self.mass = 1000 - self.inertia = self.mass * 0.3 ** 2 - self.ycg = 1 - - self.cd_sway = 0.0 - self.cd_surge = 0.0 - self.cd_yaw = 0.0 - - # Kinematic param - self.width = 1.0 - self.height = 2.0 - - # Graphic output parameters - self.dynamic_domain = True - self.dynamic_range = 10 - - # rocket drawing - pts = np.zeros(( 10 , 3 )) - l = self.height - w = self.width - - pts[0,:] = np.array([ 0, -l,0]) - pts[1,:] = np.array([-w, -l,0]) - pts[2,:] = np.array([-w, +l,0]) - pts[3,:] = np.array([ 0,l+w,0]) - pts[4,:] = np.array([+w, +l,0]) - pts[5,:] = np.array([+w, -l,0]) - pts[6,:] = pts[0,:] - - self.drawing_body_pts = pts - - - ########################################################################### - def H(self, q ): - """ - Inertia matrix - ---------------------------------- - dim( H ) = ( dof , dof ) - - such that --> Kinetic Energy = 0.5 * dq^T * H(q) * dq - - """ - - H = np.zeros((3,3)) - - H[0,0] = self.mass - H[1,1] = self.mass - H[2,2] = self.inertia - - return H - - - ########################################################################### - def C(self, q , dq ): - """ - Corriolis and Centrifugal Matrix - ------------------------------------ - dim( C ) = ( dof , dof ) - - such that --> d H / dt = C + C^T - - - """ - - C = np.zeros((3,3)) - - return C - - - ########################################################################### - def B(self, q , u ): - """ - Actuator Matrix : dof x m - """ - - B = np.zeros((3,1)) - - delta = u[1] - - B[0,0] = -np.sin( q[2] + delta ) - B[1,0] = np.cos( q[2] + delta) - B[2,0] = - self.ycg * np.sin( delta ) - - return B - - - ########################################################################### - def g(self, q ): - """ - Gravitationnal forces vector : dof x 1 - """ - - g = np.zeros(3) - - return g - - - ########################################################################### - def d(self, q , dq , u ): - """ - State-dependent dissipative forces : dof x 1 - """ - - d = np.zeros(3) - - # Linear damping for now: - D = np.diag([ 100 , 100, 100 ]) - - d = D @ dq - - return d - - - ########################################################################### - # Graphical output - ########################################################################### - - ########################################################################### - def forward_kinematic_domain(self, q ): - """ - """ - l = self.height * 3 - - x = q[0] - y = q[1] - z = 0 - - if self.dynamic_domain: - - domain = [ ( -l + x , l + x ) , - ( -l + y , l + y ) , - ( -l + z , l + z ) ]# - else: - - domain = [ ( -l , l ) , - ( -l , l ) , - ( -l , l ) ]# - - return domain - - - ########################################################################### - def forward_kinematic_lines(self, q ): - """ - Compute points p = [x;y;z] positions given config q - ---------------------------------------------------- - - points of interest for ploting - - Outpus: - lines_pts = [] : a list of array (n_pts x 3) for each lines - - """ - - lines_pts = [] # list of array (n_pts x 3) for each lines - lines_style = [] - lines_color = [] - - ############################### - # trailler line - ############################### - - w = 1.2 - l = 2.0 - - pts = np.zeros(( 5 , 3 )) - pts[0,:] = np.array([-w,-l,0]) - pts[1,:] = np.array([+w,-l,0]) - pts[2,:] = np.array([+w,+l,0]) - pts[3,:] = np.array([-w,+l,0]) - pts[4,:] = np.array([-w,-l,0]) - - lines_pts.append( pts ) - lines_style.append( '--') - lines_color.append( 'k' ) - - ########################### - # body - ########################### - - x = q[0] - y = q[1] - theta = q[2] - - W_T_B = geometry.transformation_matrix_2D( theta , x , y ) - - pts_B = self.drawing_body_pts - pts_W = drawing.transform_points_2D( W_T_B , pts_B ) - - lines_pts.append( pts_W ) - lines_style.append( '-') - lines_color.append( 'b' ) - - ########################### - # C.G. - ########################### - - pts = np.zeros(( 1 , 3 )) - pts[0,:] = np.array([x,y,0]) - - lines_pts.append( pts ) - lines_style.append( 'o') - lines_color.append( 'b' ) - - return lines_pts , lines_style , lines_color - - - ########################################################################### - def forward_kinematic_lines_plus(self, x , u , t ): - """ - show trust vectors - - - """ - - lines_pts = [] # list of array (n_pts x 3) for each lines - lines_style = [] - lines_color = [] - - ########################### - # trust force vector - ########################### - - length = u[0] * 0.01 # arrow length - theta = u[1] - 0.5 * np.pi # arrow angle (body frame) - y_offset = -self.height - - pts_body = drawing.arrow_from_length_angle( length, theta, y = y_offset ) - W_T_B = geometry.transformation_matrix_2D( x[2], x[0] , x[1] ) - pts_W = drawing.transform_points_2D( W_T_B , pts_body ) - - lines_pts.append( pts_W ) - lines_style.append( '-') - lines_color.append( 'r' ) - - return lines_pts , lines_style , lines_color - - - -''' -################################################################# -################## Main ######## -################################################################# -''' - - -if __name__ == "__main__": - """ MAIN TEST """ - - sys = Boat() - - sys.x0 = np.array([0,-5,0,0,2,0]) - - sys.ubar[0] = 100 - sys.ubar[1] = 0.1 - - sys.compute_trajectory( tf = 100 ) - sys.plot_trajectory('x') - sys.animate_simulation() \ No newline at end of file diff --git a/dev/boat/boat_parking_with_lqr.py b/dev/boat/boat_parking_with_lqr.py deleted file mode 100644 index c8ab27ef..00000000 --- a/dev/boat/boat_parking_with_lqr.py +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Created on Sun Oct 3 08:27:06 2021 - -@author: alex -""" - -import numpy as np - -from boat import Boat - -from pyro.analysis.costfunction import QuadraticCostFunction -from pyro.dynamic.statespace import linearize -from pyro.control.lqr import synthesize_lqr_controller - - -# Non-linear model -sys = Boat() - -sys.ubar = np.array([100,0]) - -# Linear model -ss = linearize( sys , 0.01 ) - -# Cost function -cf = QuadraticCostFunction.from_sys( sys ) -cf.Q[0,0] = 10000 -cf.Q[1,1] = 1 -cf.Q[2,2] = 100 -cf.Q[3,3] = 1 -cf.Q[4,4] = 1 -cf.Q[5,5] = 1 - -cf.R[0,0] = 1 -cf.R[1,1] = 10000 - -# LQR controller -ctl = synthesize_lqr_controller( ss , cf , sys.xbar , sys.ubar ) - -# Instable when trust reverse!!! - - -# Simulation Closed-Loop Non-linear with LQR controller -cl_sys = ctl + sys -cl_sys.x0 = np.array([1,-10,-0.2,0,0,0]) -cl_sys.x0 = np.array([-2.0,-10,-0.5,0,0,0]) -cl_sys.compute_trajectory(20) -cl_sys.plot_trajectory('xu') -ani = cl_sys.animate_simulation( time_factor_video = 1.0 ) -# cl_sys.animate_simulation( save = True , file_name = 'boat_parking_with_lqr.gif' ) -# ani.save( 'test.mp4', writer='imagemagick', fps=30) -# ani.save( 'test.gif') \ No newline at end of file diff --git a/dev/boat/boat_trajectory_planning.py b/dev/boat/boat_trajectory_planning.py deleted file mode 100644 index 1a99719f..00000000 --- a/dev/boat/boat_trajectory_planning.py +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Created on Sun Oct 3 08:27:06 2021 - -@author: alex -""" - -import numpy as np - -from boat import Boat - -from pyro.planning.trajectoryoptimisation import DirectCollocationTrajectoryOptimisation - -# DO NOT CONVERGE!!! - -sys = Boat() - -planner = DirectCollocationTrajectoryOptimisation( sys , 1.0 , 10 ) - - -planner.init_dynamic_plot() - -planner.x_start = np.array([0,-10,0,0,2,0]) -planner.x_goal = np.array([0,0,0,0,0,0]) - -planner.set_linear_initial_guest() -# planner.set_linear_initial_guest( True ) - -planner.maxiter = 500 - -planner.compute_optimal_trajectory() -# planner.show_solution() -# planner.animate_solution() - diff --git a/dev/boat/boat_with_lqr_controllability.py b/dev/boat/boat_with_lqr_controllability.py new file mode 100644 index 00000000..b5c8d3d2 --- /dev/null +++ b/dev/boat/boat_with_lqr_controllability.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Created on Sun Oct 3 08:27:06 2021 + +@author: alex +""" + +import numpy as np + +from pyro.dynamic.boat import Boat2D +from pyro.analysis.costfunction import QuadraticCostFunction +from pyro.dynamic.statespace import linearize +from pyro.control.lqr import synthesize_lqr_controller + + +# Non-linear model +sys = Boat2D() + +sys.mass = 1000.0 +sys.inertia = 2000.0 +sys.l_t = 2.0 +sys.damping_coef = np.array([ 200.0, 2000.0, 1000.0 ]) + +sys.xbar[3] = 0.0 +sys.ubar[0] = 0.0 + +# Linear model +ss = linearize( sys , 0.01 ) + +ss.A[ abs(ss.A) < 0.00001 ] = 0.0 +ss.B[ abs(ss.B) < 0.00001 ] = 0.0 +print(ss.A) +print(ss.B) + +CA = np.hstack( (ss.B, + ss.A @ ss.B, + ss.A @ ss.A @ ss.B, + ss.A @ ss.A @ ss.A @ ss.B, + ss.A @ ss.A @ ss.A @ ss.A @ ss.B, + ss.A @ ss.A @ ss.A @ ss.A @ ss.A @ ss.B) ) + +rank = np.linalg.matrix_rank(CA) +print('Controllability rank = ',rank) +# Linearized boat is never controllable arround zero velocity + +# Cost function +cf = QuadraticCostFunction.from_sys( sys ) +cf.Q[0,0] = 1E3 +cf.Q[1,1] = 1E3 +cf.Q[2,2] = 1E3 +cf.Q[3,3] = 1E3 +cf.Q[4,4] = 1E3 +cf.Q[5,5] = 1E3 + +cf.R[0,0] = 1E-6 +cf.R[1,1] = 1E-6 + +# LQR controller +ctl = synthesize_lqr_controller( ss , cf , sys.xbar , sys.ubar ) + +ctl.K[ abs(ctl.K) < 0.00001 ] = 0.0 +print(ctl.K) + +# Simulation Closed-Loop Non-linear with LQR controller +# cl_sys = ctl + sys +# cl_sys.x0 = np.array([+1.0,+0.2,-0.2,0,0,0]) +# cl_sys.compute_trajectory( tf = 1000 , n = 10001 ) +# cl_sys.plot_trajectory('xu') +# cl_sys.animate_simulation( time_factor_video = 100.0 ) \ No newline at end of file diff --git a/dev/rigidbody/boat_parking_with_lqr2.py b/dev/rigidbody/boat_parking_with_lqr2.py deleted file mode 100644 index 908a142b..00000000 --- a/dev/rigidbody/boat_parking_with_lqr2.py +++ /dev/null @@ -1,67 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Created on Sun Oct 3 08:27:06 2021 - -@author: alex -""" - -import numpy as np - -from boat2 import Boat2D - -from pyro.analysis.costfunction import QuadraticCostFunction -from pyro.dynamic.statespace import linearize -from pyro.control.lqr import synthesize_lqr_controller - - -# Non-linear model -sys = Boat2D() - - -# from pyro.planning.trajectoryoptimisation import DirectCollocationTrajectoryOptimisation - -# planner = DirectCollocationTrajectoryOptimisation( sys , 0.1 , 20 ) - - -# planner.init_dynamic_plot() - -# planner.x_start = np.array([-5,0,0,2,0,0]) -# planner.x_goal = np.array([0,0,0,0,0,0]) - -# planner.set_linear_initial_guest() - -# planner.maxiter = 500 - -# planner.compute_optimal_trajectory() - -sys.xbar[3] = 0.0 -sys.ubar[0] = 0.0 - - -# Linear model -ss = linearize( sys , 0.01 ) - -# Cost function -cf = QuadraticCostFunction.from_sys( sys ) -cf.Q[0,0] = 1 -cf.Q[1,1] = 1 -cf.Q[2,2] = 1 -cf.Q[3,3] = 1 -cf.Q[4,4] = 1 -cf.Q[5,5] = 1 - -cf.R[0,0] = 0.001 -cf.R[1,1] = 0.001 - -# LQR controller -ctl = synthesize_lqr_controller( ss , cf , sys.xbar , sys.ubar ) - -print(ctl.K) - -# Simulation Closed-Loop Non-linear with LQR controller -cl_sys = ctl + sys -cl_sys.x0 = np.array([+1.2,-1.2,-0.2,0,0,0]) -cl_sys.compute_trajectory(20) -cl_sys.plot_trajectory('xu') -cl_sys.animate_simulation( time_factor_video = 1.0 ) \ No newline at end of file diff --git a/dev/rigidbody/boat_parking_with_test_alex.py b/examples/demos_by_system/boat/boat_position_controller.py similarity index 66% rename from dev/rigidbody/boat_parking_with_test_alex.py rename to examples/demos_by_system/boat/boat_position_controller.py index 04da5187..34de6910 100644 --- a/dev/rigidbody/boat_parking_with_test_alex.py +++ b/examples/demos_by_system/boat/boat_position_controller.py @@ -10,14 +10,10 @@ from scipy.linalg import solve_continuous_are -from boat2 import Boat2D - +from pyro.dynamic.boat import Boat2D from pyro.analysis.costfunction import QuadraticCostFunction from pyro.dynamic.statespace import linearize -from pyro.control.lqr import synthesize_lqr_controller - - -from pyro.control import controller +from pyro.control import controller ############################################################################### def na( theta ): @@ -33,6 +29,17 @@ def na( theta ): ############################################################################### class BoatController( controller.StaticController ) : + """ + A simple boat controller based on a cascade control structure + + High-level position control: + heading and velocity setpoint are based on the + position error computed in the global frame + + Low-level velocity control: + LQR velocity control computed in the body frame + + """ ############################ def __init__( self , sys ): @@ -51,28 +58,32 @@ def __init__( self , sys ): self.sys = sys # Velocity inner loop parameters - self.reference_velocity = np.array([ 5.0, 0.0, 0.0]) + # only used if the position control loop is deactivated + self.reference_velocity = np.array([ 5.0, 0.0, 0.0]) - # Linearized model + # Linearized model for LQR controller ss = linearize( sys , 0.01 ) self.A = ss.A[3:,3:] self.B = ss.B[3:,:] + self.A[ abs(self.A) < 0.00001 ] = 0.0 + self.B[ abs(self.B) < 0.00001 ] = 0.0 print('Velocity linearized dynamic') print('----------------------------') print('A =\n', self.A) print('B =\n', self.B) - # Cost function + # Cost function for LQR controller cf = QuadraticCostFunction(3,2) - cf.Q[0,0] = 1000 - cf.Q[1,1] = 1000 - cf.Q[2,2] = 5000 + cf.Q[0,0] = 10000 + cf.Q[1,1] = 10000 + cf.Q[2,2] = 50000 cf.R[0,0] = 0.0001 cf.R[1,1] = 0.0001 # LQR solution self.S = solve_continuous_are( self.A , self.B , cf.Q , cf.R ) self.K = np.linalg.inv( cf.R ) @ self.B.T @ self.S + self.K[ abs(self.K) < 0.00001 ] = 0.0 print('Velocity inner-loop LQR gain matrix =\n', self.K) # Outer position loop parameters @@ -84,12 +95,12 @@ def __init__( self , sys ): # Trajectory following parameters self.trajectory_following_isactivated = False - self.traj_amplitude = 10.0 - self.traj_frequency = 0.3 + self.traj_amplitude = 12.0 + self.traj_frequency = 0.1 # Saturations - self.f_max = np.array([10000,1000]) - self.f_min = np.array([-1000,-1000]) + self.f_max = np.array([100000,10000]) + self.f_min = np.array([-10000,-10000]) self.v_max = np.array([5.0,1.0,1.0]) self.v_min = np.array([-1.0,-1.0,-1.0]) @@ -102,9 +113,9 @@ def q_d( self , t = 0 ): a = self.traj_amplitude w = self.traj_frequency - q_d = np.array([ a * np.cos(w*t) , a * np.sin(w*t), 0.0 ]) - - q_d[2] = np.arctan2( a * w * np.cos(w * t ), a * w * - np.sin(w*t)) + q_d = np.array([ a * np.cos(w*t), + a * np.sin(w*t), + w * t + np.pi/2]) else: @@ -121,9 +132,7 @@ def dq_d( self , t = 0 ): a = self.traj_amplitude w = self.traj_frequency - dq_d = np.array([ a * w * - np.sin(w*t) , a * w * np.cos(w * t ), 0 ]) - - # TODO: dq_d[2] = ... + dq_d = np.array([ a * w * - np.sin(w*t) , a * w * np.cos(w * t ), w ]) else: @@ -164,8 +173,7 @@ def c( self , y , r , t = 0 ): q_e[2] = na( desired - actual ) # Position outter loop in inertial frame - dq_r = self.KP @ q_e + dq_d * 1.0 - # TODO 0.9 is a quick n dirty fix for the boat to always lag behind the target + dq_r = self.KP @ q_e + dq_d # Desired velocity in body frame v_d = self.sys.N( q ).T @ dq_r @@ -184,7 +192,7 @@ def c( self , y , r , t = 0 ): # Velocity inner loop u = self.K @ v_e - # Max/min thrust + # Max/min propulsion force u = np.clip( u , self.f_min , self.f_max ) return u @@ -200,13 +208,21 @@ def forward_kinematic_lines_plus( self, x , u , t ): """ - # desired boat pose, from model forward kinematics - pts, style, color = self.sys.forward_kinematic_lines( self.q_d(t) ) + if self.position_control_isactivated: + + # desired boat pose, from model forward kinematics + pts, style, color = self.sys.forward_kinematic_lines( self.q_d(t) ) + + # Change the line style and color + style[0] = '--' + color[0] = 'c' + color[1] = 'c' + + else: - # Change the line style and color - style[0] = '--' - color[0] = 'c' - color[1] = 'c' + pts = None + style = None + color = None return pts, style, color @@ -217,34 +233,49 @@ def forward_kinematic_lines_plus( self, x , u , t ): # Cascade controller ctl = BoatController( sys ) -# Simulation Closed-Loop Non-linear with LQR controller +# Simulation of Non-linear Boat Model with LQR linear controller cl_sys = ctl + sys -cl_sys.x0 = np.array([3,3,1.0,5,0,0]) -cl_sys.compute_trajectory(10) +################################ +# Test velocity innerloop +################################ -cl_sys.plot_trajectory('xu') -cl_sys.animate_simulation( time_factor_video = 1.0 ) +# ctl.position_control_isactivated = False +# ctl.reference_velocity = np.array([ 1.0, 0.1, 0.2]) +# cl_sys.x0 = np.array([3,3,1.0,5,0,0]) +# cl_sys.compute_trajectory(10) +# cl_sys.plot_trajectory('xu') +# cl_sys.animate_simulation( time_factor_video = 1.0 ) -# cl_sys.x0 = np.array([-20,10,-2.5,0,0,0]) -# cl_sys.compute_trajectory(20) -# # cl_sys.plot_trajectory('xu') -# cl_sys.animate_simulation( time_factor_video = 1.0 ) +################################ +# Test fixed targets position control +################################ -# cl_sys.x0 = np.array([50,50,0.0,0,0,0]) -# cl_sys.compute_trajectory(50) +ctl.position_control_isactivated = True -# # cl_sys.plot_trajectory('xu') -# # cl_sys.animate_simulation( time_factor_video = 1.0 ) -# cl_sys.animate_simulation( time_factor_video = 1.0 , save = True , file_name = 'boat2' , show = False) +cl_sys.x0 = np.array([3,3,1.0,5,0,0]) +cl_sys.compute_trajectory(20) +cl_sys.plot_trajectory('xu') +cl_sys.animate_simulation( time_factor_video = 1.0 ) +cl_sys.x0 = np.array([-20,10,-2.5,0,0,0]) +cl_sys.compute_trajectory(30) +cl_sys.plot_trajectory('xu') +cl_sys.animate_simulation( time_factor_video = 1.0 ) + +cl_sys.x0 = np.array([50,50,0.0,0,0,0]) +cl_sys.compute_trajectory(80) +cl_sys.plot_trajectory('xu') +cl_sys.animate_simulation( time_factor_video = 1.0 ) + +################################ +# Test trajectory following control +################################ -# cl_sys.x0 = np.array([0,0,0,0,0,0]) ctl.trajectory_following_isactivated = True -# # ctl.d_max = 0.0 -cl_sys.compute_trajectory(40) +cl_sys.x0 = np.array([0,0,0,0,0,0]) +cl_sys.compute_trajectory(60) cl_sys.plot_trajectory('xu') -cl_sys.animate_simulation( time_factor_video = 1.0 ) -# cl_sys.animate_simulation( time_factor_video = 1.0 , save = True , file_name = 'boat1' , show = False) +cl_sys.animate_simulation( time_factor_video = 1.0 ) \ No newline at end of file diff --git a/pyro/dynamic/boat.py b/pyro/dynamic/boat.py index b682453b..8732f506 100644 --- a/pyro/dynamic/boat.py +++ b/pyro/dynamic/boat.py @@ -59,23 +59,15 @@ def __init__(self): self.input_label = ['Tx','Ty'] self.input_units = ['[N]','[N]'] - # State working range - self.x_ub = np.array([+10,+10,+2,10,10,10]) - self.x_lb = np.array([-10,-10,-2,-10,-10,-10]) - - # Input working range - self.u_ub = np.array([+1000,+100]) - self.u_lb = np.array([-1000,-100]) - # Dynamic properties - self.mass = 1000.0 - self.inertia = 2000.0 - self.l_t = 2.0 # Distance between CG and Thrust vector + self.mass = 10000.0 + self.inertia = 10000.0 * 1.0 ** 2 + self.l_t = 3.0 # Distance between CG and Thrust vector # Hydrodynamic coefficients # linear damping - self.damping_coef = np.array([ 200.0, 1000.0, 1000.0 ]) + self.damping_coef = np.array([ 2000.0, 20000.0, 10000.0 ]) # quadratic damping self.Cx_max = 0.5 @@ -83,8 +75,8 @@ def __init__(self): self.Cm_max = 0.1 self.rho = 1000.0 # water density - self.Afc = 0.5 # frontal area - self.Alc = 1.0 # lateral area + self.Alc = self.l_t * 2 # lateral area + self.Afc = 0.25 * self.Alc # frontal area self.loa = self.l_t * 2 # length over all # current velocity in world frame @@ -92,7 +84,7 @@ def __init__(self): # self.v_current = np.array([0,0,0]) # Graphic output parameters - self.width = 2.0 + self.width = self.Afc self.height = self.l_t * 2 self.dynamic_domain = True self.dynamic_range = 10 @@ -111,6 +103,15 @@ def __init__(self): self.show_hydrodynamic_forces = False + # State working range + xy_range = l * 3 + self.x_ub = np.array([+xy_range,+xy_range,+np.pi,10,10,10]) + self.x_lb = np.array([-xy_range,-xy_range,-np.pi,-10,-10,-10]) + + # Input working range + self.u_ub = np.array([+10000,+1000]) + self.u_lb = np.array([-10000,-1000]) + ########################################################################### def B(self, q , u ): """ @@ -348,12 +349,12 @@ def plot_alpha2Coefs(self, alpha_min = -3.15, alpha_max = 3.15 ): sys.x0[1] = 0 sys.x0[2] = 0 - sys.x0[3] = 10.0 + sys.x0[3] = 2.0 sys.x0[4] = 0.0 sys.x0[5] = 0.0 sys.ubar[0] = 10000 - sys.ubar[1] = 10 + sys.ubar[1] = 1000 sys.plot_alpha2Coefs() From 4ccd7a36e5767aaedd01511985fd21bc90f78fc8 Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Thu, 22 Feb 2024 11:05:48 -0500 Subject: [PATCH 55/93] working exemple of order 5 polynomial traj --- .../polynomialtrajectory.py | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 dev/differentialflatness/polynomialtrajectory.py diff --git a/dev/differentialflatness/polynomialtrajectory.py b/dev/differentialflatness/polynomialtrajectory.py new file mode 100644 index 00000000..65ccc7fa --- /dev/null +++ b/dev/differentialflatness/polynomialtrajectory.py @@ -0,0 +1,58 @@ +# -*- coding: utf-8 -*- + +############################################################################### +import numpy as np +import matplotlib.pyplot as plt +from pyro.analysis import graphical +############################################################################### + +p = np.array([ 0 , 0 , 0 , 0 , 0 , 0 ]) + +# Time +t = np.linspace(0,10,100) + +# Position +x = p[0] + p[1]*t + p[2]*t**2 + p[3]*t**3 + p[4]*t**4 + p[5]*t**5 +dx = p[1] + 2*p[2]*t + 3*p[3]*t**2 + 4*p[4]*t**3 + 5*p[5]*t**4 +ddx = 2*p[2] + 6*p[3]*t + 12*p[4]*t**2 + 20*p[5]*t**3 +dddx = 6*p[3] + 24*p[4]*t + 60*p[5]*t**2 +ddddx = 24*p[4] + 120*p[5]*t + + +fig , ax = plt.subplots(6, figsize=graphical.default_figsize, dpi= graphical.default_dpi, frameon=True) + + +ax[0].plot( t , x , 'b') +ax[0].set_ylabel('x', fontsize=graphical.default_fontsize) +ax[0].set_xlabel('v', fontsize=graphical.default_fontsize ) +ax[0].tick_params( labelsize = graphical.default_fontsize ) +ax[0].grid(True) + +ax[1].plot( t , dx , 'b') +ax[1].set_ylabel('dx', fontsize=graphical.default_fontsize) +ax[1].set_xlabel('t', fontsize=graphical.default_fontsize ) +ax[1].tick_params( labelsize = graphical.default_fontsize ) +ax[1].grid(True) + +ax[2].plot( t , ddx , 'b') +ax[2].set_ylabel('ddx', fontsize=graphical.default_fontsize) +ax[2].set_xlabel('t', fontsize=graphical.default_fontsize ) +ax[2].tick_params( labelsize = graphical.default_fontsize ) +ax[2].grid(True) + +ax[3].plot( t , dddx , 'b') +ax[3].set_ylabel('dddx', fontsize=graphical.default_fontsize) +ax[3].set_xlabel('t', fontsize=graphical.default_fontsize ) +ax[3].tick_params( labelsize = graphical.default_fontsize ) +ax[3].grid(True) + +ax[4].plot( t , ddddx , 'b') +ax[4].set_ylabel('ddddx', fontsize=graphical.default_fontsize) +ax[4].set_xlabel('t', fontsize=graphical.default_fontsize ) +ax[4].tick_params( labelsize = graphical.default_fontsize ) +ax[4].grid(True) + +fig.tight_layout() +fig.canvas.draw() + +plt.show() \ No newline at end of file From db77598c6ee32ff9cc2a17d1a1d69cbee5e2cbf3 Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Thu, 22 Feb 2024 14:29:46 -0500 Subject: [PATCH 56/93] basic single axis trajectory planner --- pyro/planning/trajectorygeneration.py | 179 ++++++++++++++++++++++++++ 1 file changed, 179 insertions(+) create mode 100644 pyro/planning/trajectorygeneration.py diff --git a/pyro/planning/trajectorygeneration.py b/pyro/planning/trajectorygeneration.py new file mode 100644 index 00000000..4732e1f7 --- /dev/null +++ b/pyro/planning/trajectorygeneration.py @@ -0,0 +1,179 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Created on 22 Feb 2024 + +@author: alex +""" + +import numpy as np +import matplotlib.pyplot as plt + +# from scipy.optimize import minimize + +from pyro.analysis import graphical + + +############################################################################### +class SingleAxisTrajectoryGenerator: + """ + This class is a tool to generate a point-to-point trajectory for a + single axis based on boundary conditions (position and higher order derivative) + + Multiple methods are implemented: + - Trapezoidal (acceleration profil is discontinuous) + - Polynomial of order N + + if boundary conditions do not fully specify the profile parameter, + then an optimization is conducted (TODO) + + """ + + ################################################ + def __init__(self, tf=10, method="poly", N=5, x0=None, xf=None): + + self.tf = tf + + self.method = method + self.poly_N = N + + if method == "trapz": + self.max_order = 3 + + self.boundary_condition_N = 3 + + self.labels = [ + "position", + "velocity", + "acceleration", + "jerk", + "snap", + "d5", + "d6", + "d7", + ] + + ################################################ + def set_initial_conditions(self, x0): + + N = self.boundary_condition_N + + self.x0 = np.zeros(N) + self.x0[:N] = x0[:N] # First N values only + + ################################################ + def set_final_conditions(self, xf): + + N = self.boundary_condition_N + + self.xf = np.zeros(N) + self.xf[:N] = xf[:N] # First N values only + + ################################################ + def generate_polynomial_parameters(self): + + N = self.poly_N + tf = self.tf + x0 = self.x0 + xf = self.xf + + # TODO: N-order version + # Place holder for 5-order + + b = np.hstack((x0, xf)) + A = np.array( + [ + [1, 0, 0, 0, 0, 0], + [0, 1, 0, 0, 0, 0], + [0, 0, 2, 0, 0, 0], + [1, tf, tf**2, tf**3, tf**4, tf**5], + [0, 1, 2 * tf, 3 * tf**2, 4 * tf**3, 5 * tf**4], + [0, 0, 2, 6 * tf, 12 * tf**2, 20 * tf**3], + ] + ) + + p = np.linalg.solve(A, b) + + return p + + ################################################ + def generate_trajectory(self, p, dt=0.01): + + n = int(self.tf / dt) + t = np.linspace(0, self.tf, n) + + # TODO: N-order version + x = p[0] + p[1] * t + p[2] * t**2 + p[3] * t**3 + p[4] * t**4 + p[5] * t**5 + dx = p[1] + 2 * p[2] * t + 3 * p[3] * t**2 + 4 * p[4] * t**3 + 5 * p[5] * t**4 + ddx = 2 * p[2] + 6 * p[3] * t + 12 * p[4] * t**2 + 20 * p[5] * t**3 + dddx = 6 * p[3] + 24 * p[4] * t + 60 * p[5] * t**2 + ddddx = 24 * p[4] + 120 * p[5] * t + + X = np.vstack((x, dx, ddx, dddx, ddddx)) + + return (X, t) + + ################################################ + def plot_trajectory(self, X, t, n_fig = None): + + n_max = X.shape[0] + + if n_fig is None: + n = n_max + elif n_fig < n_max: + n = n_fig + else: + n = n_max + + fig, ax = plt.subplots( + n, + figsize=graphical.default_figsize, + dpi=graphical.default_dpi, + frameon=True, + ) + + if n==1: ax = [ax] + + for i in range(n): + + ax[i].plot(t, X[i, :], "b") + ax[i].set_ylabel(self.labels[i], fontsize=graphical.default_fontsize) + ax[i].set_xlabel("Time[sec]", fontsize=graphical.default_fontsize) + ax[i].tick_params(labelsize=graphical.default_fontsize) + ax[i].grid(True) + + fig.tight_layout() + fig.canvas.draw() + + plt.show() + + ################################################ + def solve(self): + + p = self.generate_polynomial_parameters() + X, t = self.generate_trajectory(p) + + self.plot_trajectory(X, t) + + return (p, X, t) + + +""" +################################################################# +################## Main ######## +################################################################# +""" + + +if __name__ == "__main__": + """MAIN TEST""" + + ge = SingleAxisTrajectoryGenerator() + + ge.set_initial_conditions(np.array([-1, -1, -1])) + ge.set_final_conditions(np.array([1, 1, 1])) + + p = ge.generate_polynomial_parameters() + X, t = ge.generate_trajectory(p) + + ge.plot_trajectory(X, t) From abcacdae400d51709dd4618ab7fb6b4292061168 Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Thu, 22 Feb 2024 21:56:26 -0500 Subject: [PATCH 57/93] working n-order poly traj gen --- pyro/planning/trajectorygeneration.py | 146 +++++++++++++++++--------- 1 file changed, 96 insertions(+), 50 deletions(-) diff --git a/pyro/planning/trajectorygeneration.py b/pyro/planning/trajectorygeneration.py index 4732e1f7..535dafb1 100644 --- a/pyro/planning/trajectorygeneration.py +++ b/pyro/planning/trajectorygeneration.py @@ -20,9 +20,7 @@ class SingleAxisTrajectoryGenerator: This class is a tool to generate a point-to-point trajectory for a single axis based on boundary conditions (position and higher order derivative) - Multiple methods are implemented: - - Trapezoidal (acceleration profil is discontinuous) - - Polynomial of order N + Polynomial of order N if boundary conditions do not fully specify the profile parameter, then an optimization is conducted (TODO) @@ -30,57 +28,53 @@ class SingleAxisTrajectoryGenerator: """ ################################################ - def __init__(self, tf=10, method="poly", N=5, x0=None, xf=None): + def __init__(self, tf=10, N=5, x0=None, xf=None): self.tf = tf - - self.method = method self.poly_N = N - - if method == "trapz": - self.max_order = 3 + self.diff_N = N + self.x0 = x0 + self.xf = xf self.boundary_condition_N = 3 self.labels = [ - "position", - "velocity", - "acceleration", - "jerk", - "snap", - "d5", - "d6", - "d7", + 'pos', + "vel", + "acc", + "jerk (3th)", + "snap (4th)", + "crac (5th)", + "pop (6th)", + "7th", + "8th", + "9th", + "10th", ] ################################################ - def set_initial_conditions(self, x0): + def compute_b(self): + x0 = self.x0 + xf = self.xf N = self.boundary_condition_N - self.x0 = np.zeros(N) - self.x0[:N] = x0[:N] # First N values only + b = np.hstack((x0[:N], xf[:N])) - ################################################ - def set_final_conditions(self, xf): - - N = self.boundary_condition_N + print(r"[x(0), \dot{x}(x0), \ddot{x}(x0), ...] = ", x0) + print(r"[x(tf), \dot{x}(tf), \ddot{x}(x0), ...] = ", xf) + print("Boundary condition vector: \n", b) - self.xf = np.zeros(N) - self.xf[:N] = xf[:N] # First N values only + self.b = b ################################################ - def generate_polynomial_parameters(self): + def compute_A(self): - N = self.poly_N - tf = self.tf x0 = self.x0 xf = self.xf + N = self.boundary_condition_N - # TODO: N-order version - # Place holder for 5-order - - b = np.hstack((x0, xf)) + # TODO specific poly 5 + N = 3 A = np.array( [ [1, 0, 0, 0, 0, 0], @@ -92,13 +86,27 @@ def generate_polynomial_parameters(self): ] ) + print("Boundary condition matrix: \n", A) + #print("Boundary condition matrix: \n", A2) + + self.A = A + + ################################################ + def compute_parameters(self): + + A = self.A + b = self.b + p = np.linalg.solve(A, b) - return p + print("Polynomial parameters: \n", p) + + self.p = p ################################################ - def generate_trajectory(self, p, dt=0.01): + def generate_trajectory2(self, dt=0.01): + p = self.p n = int(self.tf / dt) t = np.linspace(0, self.tf, n) @@ -111,10 +119,48 @@ def generate_trajectory(self, p, dt=0.01): X = np.vstack((x, dx, ddx, dddx, ddddx)) - return (X, t) + self.X = X + self.t = t ################################################ - def plot_trajectory(self, X, t, n_fig = None): + def generate_trajectory(self, dt=0.01): + + p = self.p + + N = self.poly_N # order of polynomial + + steps = int(self.tf / dt) + ts = np.linspace(0, self.tf, steps) + + m = self.diff_N # number of derivative to compute + + X = np.zeros((m,steps)) + + # For all jth derivative of the signal + for j in range(m): + # For all time steps + for i in range(steps): + t = ts[i] + x = 0 + # For all terms of the polynomical + for n in range(j,N+1): + p_n = p[n] + exp = n - j + mul = 1 + for k in range(j): + mul = mul * ( n - k ) + x = x + mul * p_n * t ** exp + + X[j,i] = x + + self.X = X + self.t = ts + + ################################################ + def plot_trajectory(self, n_fig=None): + + X = self.X + t = self.t n_max = X.shape[0] @@ -132,17 +178,18 @@ def plot_trajectory(self, X, t, n_fig = None): frameon=True, ) - if n==1: ax = [ax] + if n == 1: + ax = [ax] for i in range(n): ax[i].plot(t, X[i, :], "b") ax[i].set_ylabel(self.labels[i], fontsize=graphical.default_fontsize) - ax[i].set_xlabel("Time[sec]", fontsize=graphical.default_fontsize) ax[i].tick_params(labelsize=graphical.default_fontsize) ax[i].grid(True) - fig.tight_layout() + ax[-1].set_xlabel("Time[sec]", fontsize=graphical.default_fontsize) + #fig.tight_layout() fig.canvas.draw() plt.show() @@ -150,12 +197,12 @@ def plot_trajectory(self, X, t, n_fig = None): ################################################ def solve(self): - p = self.generate_polynomial_parameters() - X, t = self.generate_trajectory(p) - - self.plot_trajectory(X, t) + self.compute_b() + self.compute_A() - return (p, X, t) + self.compute_parameters() + self.generate_trajectory() + self.plot_trajectory() """ @@ -170,10 +217,9 @@ def solve(self): ge = SingleAxisTrajectoryGenerator() - ge.set_initial_conditions(np.array([-1, -1, -1])) - ge.set_final_conditions(np.array([1, 1, 1])) + ge.x0 = np.array([-1, -1, 0, 10]) + ge.xf = np.array([1, 0, 0]) - p = ge.generate_polynomial_parameters() - X, t = ge.generate_trajectory(p) + ge.diff_N = 20 - ge.plot_trajectory(X, t) + ge.solve() From 038baa32ace7bb8982e1517c00543db2e8e4a608 Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Thu, 22 Feb 2024 23:41:37 -0500 Subject: [PATCH 58/93] working n-order polynomial trajectory generator including basic over and under constrained situations --- pyro/planning/trajectorygeneration.py | 224 ++++++++++++++++++++------ 1 file changed, 172 insertions(+), 52 deletions(-) diff --git a/pyro/planning/trajectorygeneration.py b/pyro/planning/trajectorygeneration.py index 535dafb1..af413bcb 100644 --- a/pyro/planning/trajectorygeneration.py +++ b/pyro/planning/trajectorygeneration.py @@ -9,7 +9,10 @@ import numpy as np import matplotlib.pyplot as plt -# from scipy.optimize import minimize +import warnings + + +from scipy.optimize import minimize from pyro.analysis import graphical @@ -36,16 +39,17 @@ def __init__(self, tf=10, N=5, x0=None, xf=None): self.x0 = x0 self.xf = xf - self.boundary_condition_N = 3 + self.bc_t0_N = 3 + self.bc_tf_N = 3 self.labels = [ - 'pos', + "pos", "vel", "acc", - "jerk (3th)", - "snap (4th)", - "crac (5th)", - "pop (6th)", + "jerk", + "snap", + "crac", + "pop", "7th", "8th", "9th", @@ -57,9 +61,10 @@ def compute_b(self): x0 = self.x0 xf = self.xf - N = self.boundary_condition_N + N0 = self.bc_t0_N + Nf = self.bc_tf_N - b = np.hstack((x0[:N], xf[:N])) + b = np.hstack((x0[:N0], xf[:Nf])) print(r"[x(0), \dot{x}(x0), \ddot{x}(x0), ...] = ", x0) print(r"[x(tf), \dot{x}(tf), \ddot{x}(x0), ...] = ", xf) @@ -70,71 +75,141 @@ def compute_b(self): ################################################ def compute_A(self): + tf = self.tf x0 = self.x0 xf = self.xf - N = self.boundary_condition_N - - # TODO specific poly 5 + N = 3 - A = np.array( - [ - [1, 0, 0, 0, 0, 0], - [0, 1, 0, 0, 0, 0], - [0, 0, 2, 0, 0, 0], - [1, tf, tf**2, tf**3, tf**4, tf**5], - [0, 1, 2 * tf, 3 * tf**2, 4 * tf**3, 5 * tf**4], - [0, 0, 2, 6 * tf, 12 * tf**2, 20 * tf**3], - ] - ) + N0 = self.bc_t0_N + Nf = self.bc_tf_N + N = self.poly_N + + A = np.zeros((N0 + Nf, N + 1)) + + # For all jth derivative of the initial conditions + t0 = 0 + for j in range(N0): + # For all terms of the polynomical + for n in range(j, N + 1): + exp = n - j + mul = 1 + for k in range(j): + mul = mul * (n - k) + A[j, n] = mul * t0**exp + + # For all jth derivative of the final conditions + for j in range(Nf): + # For all terms of the polynomical + for n in range(j, N + 1): + exp = n - j + mul = 1 + for k in range(j): + mul = mul * (n - k) + A[N0 + j, n] = mul * tf**exp print("Boundary condition matrix: \n", A) - #print("Boundary condition matrix: \n", A2) self.A = A + ################################################ + def compute_Q(self): + + Q = np.zeros((self.poly_N + 1, self.poly_N + 1)) + + for i in range(self.poly_N + 1): + Q[i, i] = 1.0 * i * i + + # TODO compute real Q + # see https://groups.csail.mit.edu/rrg/papers/BryIJRR15.pdf + + self.Q = Q + + ################################################ + def constraints(self, p): + + res = self.A @ p - self.b + + return res + + ################################################ + def cost(self, p): + + Q = self.Q + + # TODO compute real Q + # see https://groups.csail.mit.edu/rrg/papers/BryIJRR15.pdf + + J = p.T @ Q @ p + + # Min Jerk test + self.p = p + self.generate_trajectory() + jerk = self.X[3, :] + snap = self.X[4, :] + + # #J = np.abs(jerk).max() + J = np.abs(snap).max() + + return J + ################################################ def compute_parameters(self): A = self.A b = self.b - p = np.linalg.solve(A, b) + if A.shape[0] == A.shape[1]: - print("Polynomial parameters: \n", p) + print("Fully constrained trajectory parameters") + p = np.linalg.solve(A, b) - self.p = p + elif A.shape[0] > A.shape[1]: - ################################################ - def generate_trajectory2(self, dt=0.01): + warnings.warn( + "Warning! : impossible to respect all boundary condition, raise the order of the polynomial" + ) + print( + "Overconstrained boundary consitions: solving for best solution in the least-square sense" + ) + p = np.linalg.lstsq(A, b)[0] - p = self.p - n = int(self.tf / dt) - t = np.linspace(0, self.tf, n) + else: - # TODO: N-order version - x = p[0] + p[1] * t + p[2] * t**2 + p[3] * t**3 + p[4] * t**4 + p[5] * t**5 - dx = p[1] + 2 * p[2] * t + 3 * p[3] * t**2 + 4 * p[4] * t**3 + 5 * p[5] * t**4 - ddx = 2 * p[2] + 6 * p[3] * t + 12 * p[4] * t**2 + 20 * p[5] * t**3 - dddx = 6 * p[3] + 24 * p[4] * t + 60 * p[5] * t**2 - ddddx = 24 * p[4] + 120 * p[5] * t + print("Optimization over free decision variables") - X = np.vstack((x, dx, ddx, dddx, ddddx)) + self.compute_Q() - self.X = X - self.t = t + p0 = np.zeros(self.poly_N + 1) + + constraints = {"type": "eq", "fun": self.constraints} + + res = minimize( + self.cost, + p0, + method="SLSQP", + constraints=constraints, + options={"disp": True, "maxiter": 500}, + ) + + p = res.x + + # p = np.linalg.lstsq(A, b)[0] + + print("Polynomial parameters: \n", p) + + self.p = p ################################################ def generate_trajectory(self, dt=0.01): p = self.p - N = self.poly_N # order of polynomial + N = self.poly_N # order of polynomial steps = int(self.tf / dt) ts = np.linspace(0, self.tf, steps) - - m = self.diff_N # number of derivative to compute - X = np.zeros((m,steps)) + m = self.diff_N # number of derivative to compute + + X = np.zeros((m, steps)) # For all jth derivative of the signal for j in range(m): @@ -143,15 +218,15 @@ def generate_trajectory(self, dt=0.01): t = ts[i] x = 0 # For all terms of the polynomical - for n in range(j,N+1): + for n in range(j, N + 1): p_n = p[n] exp = n - j mul = 1 for k in range(j): - mul = mul * ( n - k ) - x = x + mul * p_n * t ** exp + mul = mul * (n - k) + x = x + mul * p_n * t**exp - X[j,i] = x + X[j, i] = x self.X = X self.t = ts @@ -189,7 +264,7 @@ def plot_trajectory(self, n_fig=None): ax[i].grid(True) ax[-1].set_xlabel("Time[sec]", fontsize=graphical.default_fontsize) - #fig.tight_layout() + # fig.tight_layout() fig.canvas.draw() plt.show() @@ -217,9 +292,54 @@ def solve(self): ge = SingleAxisTrajectoryGenerator() - ge.x0 = np.array([-1, -1, 0, 10]) - ge.xf = np.array([1, 0, 0]) + ge.x0 = np.array([0, 0, 0, 0, 0, 0, 0, 0]) + ge.xf = np.array([10, 0, 0, 0, 0, 0, 0, 0]) - ge.diff_N = 20 + ge.bc_t0_N = 2 + ge.bc_tf_N = 2 + ge.poly_N = 3 + ge.diff_N = 7 ge.solve() + + ge.bc_t0_N = 3 + ge.bc_tf_N = 3 + ge.poly_N = 5 + ge.diff_N = 7 + + ge.solve() + + ge.bc_t0_N = 4 + ge.bc_tf_N = 4 + ge.poly_N = 7 + ge.diff_N = 7 + + ge.solve() + + ge.bc_t0_N = 5 + ge.bc_tf_N = 5 + ge.poly_N = 9 + ge.diff_N = 7 + + ge.solve() + + ge.bc_t0_N = 6 + ge.bc_tf_N = 6 + ge.poly_N = 11 + ge.diff_N = 7 + + ge.solve() + + ge.bc_t0_N = 7 + ge.bc_tf_N = 7 + ge.poly_N = 13 + ge.diff_N = 7 + + ge.solve() + + # ge.bc_t0_N = 1 + # ge.bc_tf_N = 1 + # ge.poly_N = 3 + # ge.diff_N = 7 + + # ge.solve() From 38064fb537b2a8600d03d3601ce26e52da890e42 Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Fri, 1 Mar 2024 00:23:18 -0500 Subject: [PATCH 59/93] working on diff flatness traj --- .../polynomialtrajectory.py | 79 +- dev/differentialflatness/rigidbodytest.py | 185 ++++ dev/dimensionless/cases_car.py | 321 ------- dev/dimensionless/cases_pendulum.py | 427 --------- .../pendulum_optimal_swingup_demo.py | 4 +- pyro/dynamic/boat.py | 2 +- pyro/dynamic/rigidbody.py | 20 +- pyro/planning/discretizer_legacy.py | 304 ------ pyro/planning/trajectorygeneration.py | 54 +- pyro/planning/valueiteration_legacy.py | 871 ------------------ 10 files changed, 282 insertions(+), 1985 deletions(-) create mode 100644 dev/differentialflatness/rigidbodytest.py delete mode 100644 dev/dimensionless/cases_car.py delete mode 100644 dev/dimensionless/cases_pendulum.py delete mode 100644 pyro/planning/discretizer_legacy.py delete mode 100644 pyro/planning/valueiteration_legacy.py diff --git a/dev/differentialflatness/polynomialtrajectory.py b/dev/differentialflatness/polynomialtrajectory.py index 65ccc7fa..ab05278c 100644 --- a/dev/differentialflatness/polynomialtrajectory.py +++ b/dev/differentialflatness/polynomialtrajectory.py @@ -3,56 +3,73 @@ ############################################################################### import numpy as np import matplotlib.pyplot as plt -from pyro.analysis import graphical +from pyro.analysis import graphical ############################################################################### -p = np.array([ 0 , 0 , 0 , 0 , 0 , 0 ]) +boundary_conditions = np.array([0, 0, 0, 1, 0, 0]) + +tf = 10 + +A = np.array( + [ + [1, 0, 0, 0, 0, 0], + [0, 1, 0, 0, 0, 0], + [0, 0, 2, 0, 0, 0], + [1, tf, tf**2, tf**3, tf**4, tf**5], + [0, 1, 2 * tf, 3 * tf**2, 4 * tf**3, 5 * tf**4], + [0, 0, 2, 6 * tf, 12 * tf**2, 20 * tf**3], + ] +) + +p = np.linalg.solve(A, boundary_conditions) # Time -t = np.linspace(0,10,100) +t = np.linspace(0, tf, 100) # Position -x = p[0] + p[1]*t + p[2]*t**2 + p[3]*t**3 + p[4]*t**4 + p[5]*t**5 -dx = p[1] + 2*p[2]*t + 3*p[3]*t**2 + 4*p[4]*t**3 + 5*p[5]*t**4 -ddx = 2*p[2] + 6*p[3]*t + 12*p[4]*t**2 + 20*p[5]*t**3 -dddx = 6*p[3] + 24*p[4]*t + 60*p[5]*t**2 -ddddx = 24*p[4] + 120*p[5]*t +x = p[0] + p[1] * t + p[2] * t**2 + p[3] * t**3 + p[4] * t**4 + p[5] * t**5 +dx = p[1] + 2 * p[2] * t + 3 * p[3] * t**2 + 4 * p[4] * t**3 + 5 * p[5] * t**4 +ddx = 2 * p[2] + 6 * p[3] * t + 12 * p[4] * t**2 + 20 * p[5] * t**3 +dddx = 6 * p[3] + 24 * p[4] * t + 60 * p[5] * t**2 +ddddx = 24 * p[4] + 120 * p[5] * t + +fig, ax = plt.subplots( + 5, figsize=graphical.default_figsize, dpi=graphical.default_dpi, frameon=True +) -fig , ax = plt.subplots(6, figsize=graphical.default_figsize, dpi= graphical.default_dpi, frameon=True) - -ax[0].plot( t , x , 'b') -ax[0].set_ylabel('x', fontsize=graphical.default_fontsize) -ax[0].set_xlabel('v', fontsize=graphical.default_fontsize ) -ax[0].tick_params( labelsize = graphical.default_fontsize ) +ax[0].plot(t, x, "b") +ax[0].set_ylabel("Pos", fontsize=graphical.default_fontsize) +ax[0].set_xlabel("v", fontsize=graphical.default_fontsize) +ax[0].tick_params(labelsize=graphical.default_fontsize) ax[0].grid(True) -ax[1].plot( t , dx , 'b') -ax[1].set_ylabel('dx', fontsize=graphical.default_fontsize) -ax[1].set_xlabel('t', fontsize=graphical.default_fontsize ) -ax[1].tick_params( labelsize = graphical.default_fontsize ) +ax[1].plot(t, dx, "b") +ax[1].set_ylabel("Vel", fontsize=graphical.default_fontsize) +ax[1].set_xlabel("t", fontsize=graphical.default_fontsize) +ax[1].tick_params(labelsize=graphical.default_fontsize) ax[1].grid(True) -ax[2].plot( t , ddx , 'b') -ax[2].set_ylabel('ddx', fontsize=graphical.default_fontsize) -ax[2].set_xlabel('t', fontsize=graphical.default_fontsize ) -ax[2].tick_params( labelsize = graphical.default_fontsize ) +ax[2].plot(t, ddx, "b") +ax[2].set_ylabel("Acc", fontsize=graphical.default_fontsize) +ax[2].set_xlabel("t", fontsize=graphical.default_fontsize) +ax[2].tick_params(labelsize=graphical.default_fontsize) ax[2].grid(True) -ax[3].plot( t , dddx , 'b') -ax[3].set_ylabel('dddx', fontsize=graphical.default_fontsize) -ax[3].set_xlabel('t', fontsize=graphical.default_fontsize ) -ax[3].tick_params( labelsize = graphical.default_fontsize ) +ax[3].plot(t, dddx, "b") +ax[3].set_ylabel("Jerk", fontsize=graphical.default_fontsize) +ax[3].set_xlabel("t", fontsize=graphical.default_fontsize) +ax[3].tick_params(labelsize=graphical.default_fontsize) ax[3].grid(True) -ax[4].plot( t , ddddx , 'b') -ax[4].set_ylabel('ddddx', fontsize=graphical.default_fontsize) -ax[4].set_xlabel('t', fontsize=graphical.default_fontsize ) -ax[4].tick_params( labelsize = graphical.default_fontsize ) +ax[4].plot(t, ddddx, "b") +ax[4].set_ylabel("Snap", fontsize=graphical.default_fontsize) +ax[4].set_xlabel("t", fontsize=graphical.default_fontsize) +ax[4].tick_params(labelsize=graphical.default_fontsize) ax[4].grid(True) fig.tight_layout() fig.canvas.draw() -plt.show() \ No newline at end of file +plt.show() diff --git a/dev/differentialflatness/rigidbodytest.py b/dev/differentialflatness/rigidbodytest.py new file mode 100644 index 00000000..ee7a6ba3 --- /dev/null +++ b/dev/differentialflatness/rigidbodytest.py @@ -0,0 +1,185 @@ +# -*- coding: utf-8 -*- + +############################################################################### +import numpy as np +import matplotlib.pyplot as plt + + +from pyro.analysis import graphical +from pyro.planning import trajectorygeneration +from pyro.dynamic import rigidbody +from pyro.planning import plan + +############################################################################### + +# fixed initial position for now +# initial angular velocity is related to jerk of trajectory +x0 = -8.0 +y0 = -2.0 +z0 = 0.0 + +# fixed final position for now +xf = 0.0 +yf = 0.0 +zf = 0.0 #np.pi / 2 +tf = 10 + +ddx0 = 1.0 +ddy0 = ddx0 * np.tan(z0) + +ddxf = 0.0 +ddyf = -0.1 # ddxf * np.tan(zf) + +bc_y = np.array([y0, 0, ddy0, yf, 0, ddyf]) + +gex = trajectorygeneration.SingleAxisTrajectoryGenerator(N=9) +gex.bc_t0_N = 5 +gex.bc_tf_N = 5 +gex.x0 = np.array([x0, 0, ddx0,0,0,0]) +gex.xf = np.array([xf, 0, ddxf,0,0,0]) +gex.solve() +x = gex.X[0, :] +dx = gex.X[1, :] +ax = gex.X[2, :] +dax = gex.X[3, :] +ddax = gex.X[4, :] +t = gex.t + +gey = trajectorygeneration.SingleAxisTrajectoryGenerator(N=9) +gey.bc_t0_N = 5 +gey.bc_tf_N = 5 +gey.x0 = np.array([y0, 0, ddy0,0,0,0]) +gey.xf = np.array([yf, 0, ddyf,0,0,0]) +gey.solve() +y = gey.X[0, :] +dy = gey.X[1, :] +ay = gey.X[2, :] +day = gey.X[3, :] +dday = gex.X[4, :] + +# Position theta +theta = np.arctan2(ay, ax) +#theta = np.arctan( (ay/ax)) +s = np.sin(theta) +c = np.cos(theta) +dtheta = (day * c - dax * s) / (ax * s + ay * c) +ddtheta = ( + s * (-ddax + ax * dtheta**2 - 2 * day * dtheta) + + c * (dday - ay * dtheta**2 - 2 * dax * dtheta) +) / ( + ax * c + ay * s +) # TODO check analytical equation, seems wrong + +dtheta_num = np.diff(theta, n=1, prepend=0.0) +ddtheta_num = np.diff(dtheta, n=1, prepend=0.0) + +# dtheta = dtheta_num +# ddtheta = ddtheta_num + +# Create traj +steps = len(t) +xs = np.zeros((steps, 6)) +ys = np.zeros((steps, 6)) +us = np.zeros((steps, 2)) +dxs = np.zeros((steps, 6)) + +sys = rigidbody.RigidBody2D() + +sys.mass = 0.8 +sys.inertia = 1.0 +sys.l_t = 1.0 + +m = sys.mass +J = sys.inertia +r = sys.l_t + +x_cg = x - J / (m * r) * np.cos(theta) +y_cg = y - J / (m * r) * np.sin(theta) + +xs[:, 0] = x_cg +xs[:, 1] = y_cg +xs[:, 2] = theta + +M = np.array([[m, 0], [0, m]]) + +ax_cg = ax + J / (m * r) * np.sin(theta) * ddtheta + J / (m * r) * np.cos(theta) * dtheta**2 +ay_cg = ay - J / (m * r) * np.cos(theta) * ddtheta + J / (m * r) * np.sin(theta) * dtheta**2 + +# COmpute forces +for i in range(steps): + R = np.array([[np.cos(theta[i]), -np.sin(theta[i])], [np.sin(theta[i]), np.cos(theta[i])]]) + a_cg = np.array([ax_cg[i], ay_cg[i]]) + us[i, :] = np.linalg.inv(R) @ M @ a_cg + + +traj = plan.Trajectory(xs, us, t, dxs, ys) + +fig, axes = plt.subplots( + 2, figsize=graphical.default_figsize, dpi=graphical.default_dpi, frameon=True +) + +axes[0].plot(t, ax_cg, "b") +axes[0].set_ylabel("ax_cg", fontsize=graphical.default_fontsize) +axes[0].set_xlabel("t", fontsize=graphical.default_fontsize) +axes[0].tick_params(labelsize=graphical.default_fontsize) +axes[0].grid(True) + +axes[1].plot(t, ay_cg, "b") +axes[1].set_ylabel("ay_cg", fontsize=graphical.default_fontsize) +axes[1].set_xlabel("t", fontsize=graphical.default_fontsize) +axes[1].tick_params(labelsize=graphical.default_fontsize) +axes[1].grid(True) + + +fig, axes = plt.subplots( + 3, figsize=graphical.default_figsize, dpi=graphical.default_dpi, frameon=True +) + +axes[0].plot(t, theta, "b") +axes[0].set_ylabel("Theta", fontsize=graphical.default_fontsize) +axes[0].set_xlabel("v", fontsize=graphical.default_fontsize) +axes[0].tick_params(labelsize=graphical.default_fontsize) +axes[0].grid(True) + +axes[1].plot(t, dtheta, "b") +axes[1].plot(t, dtheta_num, "r") +axes[1].set_ylabel("w", fontsize=graphical.default_fontsize) +axes[1].set_xlabel("t", fontsize=graphical.default_fontsize) +axes[1].tick_params(labelsize=graphical.default_fontsize) +axes[1].grid(True) + +axes[2].plot(t, ddtheta, "b") +axes[2].plot(t, ddtheta_num, "r") +axes[2].set_ylabel("dw", fontsize=graphical.default_fontsize) +axes[2].set_xlabel("t", fontsize=graphical.default_fontsize) +axes[2].tick_params(labelsize=graphical.default_fontsize) +axes[2].grid(True) + +fig.tight_layout() +fig.canvas.draw() + +plt.show() + + +fig, axes = plt.subplots( + 1, figsize=graphical.default_figsize, dpi=graphical.default_dpi, frameon=True +) + +axes.plot(x, y, "r") +axes.plot(x_cg, y_cg, "b") +axes.set_ylabel("y", fontsize=graphical.default_fontsize) +axes.set_xlabel("x", fontsize=graphical.default_fontsize) +axes.axis("equal") +axes.set(xlim=(-10, 10), ylim=(-10, 10)) +axes.tick_params(labelsize=graphical.default_fontsize) +axes.grid(True) + +fig.tight_layout() +fig.canvas.draw() + +plt.show() + + +sys.traj = traj + +sys.animate_simulation() diff --git a/dev/dimensionless/cases_car.py b/dev/dimensionless/cases_car.py deleted file mode 100644 index e4ecece4..00000000 --- a/dev/dimensionless/cases_car.py +++ /dev/null @@ -1,321 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Mon Nov 12 20:28:17 2018 - -@author: Alexandre -""" -############################################################################## -import numpy as np -import matplotlib.pyplot as plt -############################################################################## -from pyro.dynamic import longitudinal_vehicule -from pyro.planning import discretizer -from pyro.analysis import costfunction -from pyro.planning import dynamicprogramming -from pyro.analysis import graphical -############################################################################## - - - -def case( length , x_c_star , y_c_star , x_w_star , case_name = 'test ', show = True, rax = None , rax2 = None, res = 'reg', legend = 1): - - - gravity = 9.8 - - # context - x_c = x_c_star * length - y_c = y_c_star * length - x_w = x_w_star * length - - # additionnal domain - x_max = 10 * length - v_max = 2 * np.sqrt( gravity * length ) - t_max = 10 * x_max / v_max - s_max = 0.07 - j_max = t_max * ( ( x_max / x_w ) **2 + s_max ** 2 ) - - - print('\n\nCase :' + case_name ) - print('----------------------------------------------------') - print(' l=',length,' x_c=', x_c, ' y_c=', y_c,'x_w=', x_w) - - ################################ - # Dynamic system definition - ################################ - - sys = longitudinal_vehicule.LongitudinalFrontWheelDriveCarWithWheelSlipInput() - - # Model param - sys.lenght = length # distance between front wheel and back wheel [m] - sys.xc = x_c # distance from back wheel to c.g. [m] - sys.yc = y_c # height from ground to c.g. [m] - - sys.gravity = gravity # gravity constant [N/kg] - - sys.mass = 1. # total car mass [kg] - sys.rho = 1. # air density [kg/m3] - sys.cdA = 0. # drag coef time area [m2] - - # Ground traction curve parameters - sys.mu_max = 1.0 - sys.mu_slope = 70. - - sys.u_ub[0] = + s_max - sys.u_lb[0] = - s_max - - sys.x_ub = np.array([ + x_max , + v_max]) - sys.x_lb = np.array([ - x_max , - v_max]) - - ################################ - # Discritized grid - ################################ - - if res == 'test' : - - dt = 0.5 - nx = 21 - nu = 3 - - elif res == 'plus' : - - dt = 0.05 - nx = 301 - nu = 101 - - elif res == 'hi' : - - dt = 0.025 - nx = 501 - nu = 101 - - else: - - dt = 0.05 - nx = 101 - nu = 21 - - grid_sys = discretizer.GridDynamicSystem( sys , [nx,nx] , [nu] , dt , True ) - - ################################ - # Cost function - ################################ - - qcf = costfunction.QuadraticCostFunction.from_sys(sys) - - qcf.xbar = np.array([ 0 , 0 ]) # target - - qcf.INF = j_max - - - qcf.Q[0,0] = (1./x_w) ** 2 - qcf.Q[1,1] = 0.0 - - qcf.R[0,0] = 1.0 - - ################################ - # Computing optimal policy - ################################ - - dp = dynamicprogramming.DynamicProgrammingWithLookUpTable( grid_sys, qcf ) - - steps = int( t_max / dt ) - - dp.compute_steps( steps ) - - - #grid_sys.fontsize = 10 - # qcf.INF = 0.1 * qcf.INF - dp.clean_infeasible_set() - - dp.plot_policy() - - - ################################## - # Fig param - ################################## - - dpi = 300 - fontsize = 10 - figsize = (4, 3) - - # ################################## - # # Dimensional policy plot - # ################################## - - fig = plt.figure( figsize = figsize, dpi=dpi, frameon=True) - fig.canvas.manager.set_window_title( 'dimentionless policy' ) - ax = fig.add_subplot(1, 1, 1) - - xname = r'$x \; [m]$' - yname = r'$\dot{x} \; [m/sec]$' - zname = r'$s \; [-]$' - - sys.state_label[0] = r'$x$' - sys.state_label[1] = r'$\dot{x}$' - sys.input_label[0] = r'$s$' - - xrange = 50 - yrange = 15 - zrange = 0.08 - - ax.set_ylabel( yname, fontsize = fontsize ) - ax.set_xlabel( xname, fontsize = fontsize ) - - x_level = grid_sys.x_level[ 0 ] - y_level = grid_sys.x_level[ 1 ] - - - u = grid_sys.get_input_from_policy( dp.pi , 0 ) - J_grid_nd = grid_sys.get_grid_from_array( u ) - J_grid_2d = grid_sys.get_2D_slice_of_grid( J_grid_nd , 0 , 1 ) - - mesh = ax.pcolormesh( x_level, - y_level, - J_grid_2d.T, - shading='gouraud', - cmap = 'bwr', - vmin = -zrange, - vmax = zrange, - rasterized = True ) - - ax.tick_params( labelsize = fontsize ) - ax.grid(True) - ax.set_ylim( -yrange, +yrange) - ax.set_xlim( -xrange, xrange) - - cbar = fig.colorbar( mesh ) - - cbar.set_label(zname, fontsize = fontsize , rotation = 90 ) - - fig.tight_layout() - #fig.show() - fig.savefig( case_name + '_policy.pdf') - # fig.savefig( case_name + '_policy.png') - # fig.savefig( case_name + '_policy.jpg') - - if show: - plt.show() - else: - plt.close( fig ) - - ################################## - # Dimensionless policy plot - ################################## - - fig = plt.figure( figsize= figsize, dpi=dpi, frameon=True) - fig.canvas.manager.set_window_title( 'dimentionless policy' ) - ax = fig.add_subplot(1, 1, 1) - - xname = r'$x^*$'#self.sys.state_label[x] #+ ' ' + self.sys.state_units[x] - yname = r'$\dot{x}^* = \frac{\dot{x}}{\sqrt{gl}}$'#self.sys.state_label[y] #+ ' ' + self.sys.state_units[y] - zname = r'$s$' - - ax.set_ylabel(yname, fontsize = fontsize ) - ax.set_xlabel(xname, fontsize = fontsize ) - - x_level = grid_sys.x_level[ 0 ] * 1./length - y_level = grid_sys.x_level[ 1 ] * (1 / np.sqrt( gravity * length )) - - - u = grid_sys.get_input_from_policy( dp.pi , 0 ) - - u2 = u - - J_grid_nd = grid_sys.get_grid_from_array( u2 ) - - J_grid_2d = grid_sys.get_2D_slice_of_grid( J_grid_nd , 0 , 1 ) - - mesh = ax.pcolormesh( x_level, - y_level, - J_grid_2d.T, - shading='gouraud', - cmap = 'bwr', - vmin = -zrange, - vmax = zrange, - rasterized = True ) - - ax.tick_params( labelsize = fontsize ) - ax.grid(True) - - cbar = fig.colorbar( mesh ) - - cbar.set_label(zname, fontsize = fontsize , rotation = 90 ) - - fig.tight_layout() - #fig.show() - fig.savefig( case_name + '_dimpolicy.pdf') - # fig.savefig( case_name + '_dimpolicy.png') - # fig.savefig( case_name + '_dimpolicy.jpg') - - - # ################################## - # # Trajectory plot - # ################################## - - ctl = dp.get_lookup_table_controller() - - # Simulation - cl_sys = ctl + sys - cl_sys.x0 = np.array([-x_max * 0.5, 0.]) - cl_sys.compute_trajectory( 10 , 6001, 'euler') - - tp = graphical.TrajectoryPlotter( sys ) - tp.fontsize = fontsize - tp.plot( cl_sys.traj , 'xu' , show = False ) - tp.plots[0].set_ylim([-15.5, 15.5]) - tp.plots[1].set_ylim([-15.5, 15.5]) - tp.plots[2].set_ylim([-zrange, zrange]) - tp.fig.savefig( case_name + '_traj.pdf') - - # Simulation - cl_sys = ctl + sys - cl_sys.x0 = np.array([x_max * 0.5, 0.]) - cl_sys.compute_trajectory( 10 , 6001, 'euler') - - tp = graphical.TrajectoryPlotter( sys ) - tp.fontsize = fontsize - tp.plot( cl_sys.traj , 'xu' , show = False ) - tp.plots[0].set_ylim([-15.5, 15.5]) - tp.plots[1].set_ylim([-15.5, 15.5]) - tp.plots[2].set_ylim([-zrange, zrange]) - tp.fig.savefig( case_name + '_traj2.pdf') - # tp.fig.savefig( case_name + '_traj.png') - # tp.fig.savefig( case_name + '_traj.jpg') - - if show: - plt.show() - else: - plt.close( tp.fig ) - - cl_sys.plot_trajectory('xu') - cl_sys.plot_phase_plane_trajectory() - - - -#################################### -### Main figures -#################################### - -res = 'plus' -# res = 'hi' - -case( length = 2.0 , x_c_star = 0.5 , y_c_star = 0.5 , x_w_star = 20.0 , case_name = 'car1', res = res) -# case( length = 1.0 , x_c_star = 0.5 , y_c_star = 0.5 , x_w_star = 20.0 , case_name = 'car2', res = res) -# case( length = 3.0 , x_c_star = 0.5 , y_c_star = 0.5 , x_w_star = 20.0 , case_name = 'car3', res = res) - -# case( length = 2.0 , x_c_star = 0.5 , y_c_star = 1.5 , x_w_star = 10.0 , case_name = 'car4', res = res) -# case( length = 1.0 , x_c_star = 0.5 , y_c_star = 1.5 , x_w_star = 10.0 , case_name = 'car5', res = res) -# case( length = 3.0 , x_c_star = 0.5 , y_c_star = 1.5 , x_w_star = 10.0 , case_name = 'car6', res = res) - -# case( length = 2.0 , x_c_star = 0.5 , y_c_star = 0.1 , x_w_star = 2.0 , case_name = 'car7', res = res) -# case( length = 1.0 , x_c_star = 0.5 , y_c_star = 0.1 , x_w_star = 2.0 , case_name = 'car8', res = res) -# case( length = 3.0 , x_c_star = 0.5 , y_c_star = 0.1 , x_w_star = 2.0 , case_name = 'car9', res = res) - -# case( length = 2.0 , x_c_star = 0.5 , y_c_star = 0.9 , x_w_star = 20.0 , case_name = 'car10', res = res) -# case( length = 1.0 , x_c_star = 0.5 , y_c_star = 0.9 , x_w_star = 20.0 , case_name = 'car11', res = res) -# case( length = 3.0 , x_c_star = 0.5 , y_c_star = 0.9 , x_w_star = 20.0 , case_name = 'car12', res = res) - - - - - \ No newline at end of file diff --git a/dev/dimensionless/cases_pendulum.py b/dev/dimensionless/cases_pendulum.py deleted file mode 100644 index 736f4434..00000000 --- a/dev/dimensionless/cases_pendulum.py +++ /dev/null @@ -1,427 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Mon Nov 12 20:28:17 2018 - -@author: Alexandre -""" -############################################################################## -import numpy as np -import matplotlib.pyplot as plt -############################################################################## -from pyro.dynamic import pendulum -from pyro.planning import discretizer -from pyro.analysis import costfunction -from pyro.planning import dynamicprogramming -from pyro.analysis import graphical -############################################################################## - - -def case( m , g , l , t_max_star , q_star , case_name = 'test ', show = True, rax = None , rax2 = None, res = 'reg', legend = 1): - - # Additionnal fixed domain dimensionless parameters - theta_star = 2.0 * np.pi - dtheta_star = 1.0 * np.pi - time_star = 2.0 * np.pi * 20.0 - - # Combined system parameters - omega = np.sqrt( ( g / l ) ) - mgl = m * g * l - - # Dimensional parameters - t_max = t_max_star * mgl - q = q_star * mgl - theta = theta_star - dtheta = dtheta_star * omega - time = time_star / omega - J_max = mgl**2 / omega * time_star * ( ( q_star * theta_star )**2 + t_max_star**2 ) - - print('\n\nCase :' + case_name ) - print('----------------------------------------------------') - print(' m=',m,' g=',g,' l=',l,' t_max=', t_max, ' q=', q) - - ################################ - # Dynamic system definition - ################################ - - sys = pendulum.InvertedPendulum() - - # kinematic - sys.lc1 = l - - sys.l1 = sys.lc1 - sys.l_domain = sys.lc1 * 2 - - # dynamic - sys.m1 = m - sys.I1 = 0 - sys.gravity = g - sys.d1 = 0 - - sys.u_ub[0] = + t_max - sys.u_lb[0] = - t_max - - sys.x_ub = np.array([ + theta , + dtheta ]) - sys.x_lb = np.array([ - theta , - dtheta ]) - - ################################ - # Discritized grid - ################################ - - if res == 'test' : - - dt = 0.5 - nx = 21 - nu = 3 - - elif res == 'plus' : - - dt = 0.05 - nx = 301 - nu = 101 - - elif res == 'hi' : - - dt = 0.025 - nx = 501 - nu = 101 - - else: - - dt = 0.05 - nx = 301 - nu = 21 - - grid_sys = discretizer.GridDynamicSystem( sys , [nx,nx] , [nu] , dt , True ) - - ################################ - # Cost function - ################################ - - qcf = costfunction.QuadraticCostFunction.from_sys(sys) - - qcf.xbar = np.array([ 0 , 0 ]) # target - qcf.INF = J_max - - qcf.Q[0,0] = q ** 2 - qcf.Q[1,1] = 0.0 - - qcf.R[0,0] = 1.0 - - ################################ - # Computing optimal policy - ################################ - - dp = dynamicprogramming.DynamicProgrammingWithLookUpTable( grid_sys, qcf ) - - steps = int( time / dt ) - - dp.compute_steps( steps ) - - - #grid_sys.fontsize = 10 - qcf.INF = 0.1 * J_max - dp.clean_infeasible_set() - - - ################################## - # Fig param - ################################## - - dpi = 300 - fontsize = 10 - figsize = (4, 3) - - ################################## - # Dimensional policy plot - ################################## - - fig = plt.figure( figsize = figsize, dpi=dpi, frameon=True) - fig.canvas.manager.set_window_title( 'dimentionless policy' ) - ax = fig.add_subplot(1, 1, 1) - - xname = r'$\theta \; [rad]$' - yname = r'$\dot{\theta} \; [rad/sec]$' - zname = r'$\tau \; [Nm]$' - - sys.state_label[0] = r'$\theta$' - sys.state_label[1] = r'$\dot{\theta}$' - sys.input_label[0] = r'$\tau$' - - xrange = 2.0 * np.pi - yrange = np.pi * np.sqrt( 10 / 1. ) - zrange = 20. - - ax.set_ylabel( yname, fontsize = fontsize ) - ax.set_xlabel( xname, fontsize = fontsize ) - - x_level = grid_sys.x_level[ 0 ] - y_level = grid_sys.x_level[ 1 ] - - - u = grid_sys.get_input_from_policy( dp.pi , 0 ) - J_grid_nd = grid_sys.get_grid_from_array( u ) - J_grid_2d = grid_sys.get_2D_slice_of_grid( J_grid_nd , 0 , 1 ) - - mesh = ax.pcolormesh( x_level, - y_level, - J_grid_2d.T, - shading='gouraud', - cmap = 'bwr', - vmin = -zrange, - vmax = zrange, - rasterized = True ) - - ax.tick_params( labelsize = fontsize ) - ax.grid(True) - ax.set_ylim( -yrange, +yrange) - ax.set_xlim( -xrange, xrange) - - cbar = fig.colorbar( mesh ) - - cbar.set_label(zname, fontsize = fontsize , rotation = 90 ) - - fig.tight_layout() - #fig.show() - fig.savefig( case_name + '_policy.pdf') - fig.savefig( case_name + '_policy.png') - fig.savefig( case_name + '_policy.jpg') - - if show: - plt.show() - else: - plt.close( fig ) - - - ################################## - # Trajectory plot - ################################## - - ctl = dp.get_lookup_table_controller() - - # Simulation - cl_sys = ctl + sys - cl_sys.x0 = np.array([-3.14, 0.]) - cl_sys.compute_trajectory( 10 , 6001, 'euler') - - tp = graphical.TrajectoryPlotter( sys ) - tp.fontsize = fontsize - tp.plot( cl_sys.traj , 'xu' , show = False ) - tp.plots[1].set_ylim([-5.5, 5.5]) - tp.plots[2].set_ylim([-zrange, zrange]) - tp.fig.savefig( case_name + '_traj.pdf') - tp.fig.savefig( case_name + '_traj.png') - tp.fig.savefig( case_name + '_traj.jpg') - - if show: - plt.show() - else: - plt.close( tp.fig ) - - - ################################## - # Dimensionless policy plot - ################################## - - fig = plt.figure( figsize= figsize, dpi=dpi, frameon=True) - fig.canvas.manager.set_window_title( 'dimentionless policy' ) - ax = fig.add_subplot(1, 1, 1) - - xname = r'$\theta^*$'#self.sys.state_label[x] #+ ' ' + self.sys.state_units[x] - yname = r'$\dot{\theta}^* = \frac{\dot{\theta}}{\omega}$'#self.sys.state_label[y] #+ ' ' + self.sys.state_units[y] - zname = r'$\tau^*=\frac{\tau}{mgl}$' - - ax.set_ylabel(yname, fontsize = fontsize ) - ax.set_xlabel(xname, fontsize = fontsize ) - - x_level = grid_sys.x_level[ 0 ] * 1 - y_level = grid_sys.x_level[ 1 ] * (1 / omega) - - - u = grid_sys.get_input_from_policy( dp.pi , 0 ) - - u2 = u * (1/mgl) - - J_grid_nd = grid_sys.get_grid_from_array( u2 ) - - J_grid_2d = grid_sys.get_2D_slice_of_grid( J_grid_nd , 0 , 1 ) - - mesh = ax.pcolormesh( x_level, - y_level, - J_grid_2d.T, - shading='gouraud', - cmap = 'bwr', - rasterized = True ) - - ax.tick_params( labelsize = fontsize ) - ax.grid(True) - - cbar = fig.colorbar( mesh ) - - cbar.set_label(zname, fontsize = fontsize , rotation = 90 ) - - fig.tight_layout() - #fig.show() - fig.savefig( case_name + '_dimpolicy.pdf') - fig.savefig( case_name + '_dimpolicy.png') - fig.savefig( case_name + '_dimpolicy.jpg') - - if show: - plt.show() - else: - plt.close( fig ) - - - if rax is not None: - - ############################### - # 2D policy regime figure (dtheta = 0 ) - ############################### - - n = 101 - x_min = - theta_star - 0.1 - x_max = + theta_star + 0.1 - - x = np.linspace( x_min, x_max, n) - u = np.zeros(n) - - for i in range(n): - ri = 0 - xi = np.array([ x[i] , 0.0 ]) - ti = 0 - u[i] = ctl.c( xi, ri, ti) * (1/mgl) - - - if legend == 1: - rax.plot( x , u , label= r'$\tau_{max}^* =$ %0.1f' % t_max_star ) - elif legend == 2: - rax.plot( x , u , label= r'$q^* =$ %0.2f' % q_star ) - else: - rax.plot( x , u ) - - rax.set_xlim([ x_min, x_max ]) - rax.set_xlabel( xname, fontsize = fontsize ) - rax.grid(True) - rax.tick_params( labelsize = fontsize ) - rax.set_ylabel( zname, fontsize = fontsize ) - - if rax2 is not None: - - ############################### - # 2D policy regime figure (theta = -np.pi ) - ############################### - - n = 101 - x_min = - dtheta_star - 0.1 - x_max = + dtheta_star + 0.1 - - x = np.linspace( x_min, x_max, n) - u = np.zeros(n) - - for i in range(n): - ri = 0 - xi = np.array([ -np.pi , x[i] ]) - ti = 0 - u[i] = ctl.c( xi, ri, ti) * (1/mgl) - - - if legend == 1: - rax2.plot( x , u , label= r'$\tau_{max}^* =$ %0.1f' % t_max_star ) - elif legend == 2: - rax2.plot( x , u , label= r'$q^* =$ %0.2f' % q_star ) - else: - rax2.plot( x , u ) - - rax2.set_xlim([ x_min, x_max ]) - rax2.set_xlabel( yname, fontsize = fontsize ) - rax2.grid(True) - rax2.tick_params( labelsize = fontsize ) - rax2.set_ylabel( zname, fontsize = fontsize) - - - return dp , cl_sys - - - - -def sensitivity( ts , qs , res = 'mid' , name = 'sensitivity' , legend = 1): - - rfig = plt.figure(figsize= (4, 3), dpi=300, frameon=True) - rax = rfig.add_subplot(1, 1, 1) - - rfig2 = plt.figure(figsize= (4, 3), dpi=300, frameon=True) - rax2 = rfig2.add_subplot(1, 1, 1) - - n = ts.size - - - for i in range(n): - - case( m=1 , g=10 , l=1 , t_max_star= ts[i] , q_star= qs[i] , case_name = name + '_level_' + str(i+1) , show = False, rax = rax, rax2 = rax2, res = res, legend = legend) - - - rax.legend( loc = 'upper right' ) - rfig.tight_layout() - rfig.show() - rfig.savefig( name + '.pdf') - rfig.savefig( name + '.png') - rfig.savefig( name + '.jpg') - - rax2.legend( loc = 'upper right' ) - rfig2.tight_layout() - rfig2.show() - rfig2.savefig( name + '2.pdf') - rfig2.savefig( name + '2.png') - rfig2.savefig( name + '2.jpg') - - return (rfig, rax, rfig2, rax2) - - - -#################################### -### Quick tests -#################################### - -res = 'std' - - -dp , cl_sys = case( m=1 , g=10 , l=1 , t_max_star=0.5 , q_star= 0.1 , case_name = 'test', res = res, show = True ) - -# ts = np.array([ 0.1, 0.8 ]) -# qs = np.array([ 0.05, 0.05 ]) -# fig, ax, fig2, ax2 = sensitivity(ts, qs , res = res , name = 's1_test', legend = 1) -# fig, ax, fig2, ax2 = sensitivity(ts, qs , res = res , name = 's2_test', legend = 2) - -#################################### -### Main figures -#################################### - -# res = 'hi' - -# case( m=1 , g=10 , l=1 , t_max_star=0.5 , q_star= 0.1 , case_name = 'c1', res = res) -# case( m=1 , g=10 , l=2 , t_max_star=0.5 , q_star= 0.1 , case_name = 'c2', res = res) -# case( m=2 , g=10 , l=1 , t_max_star=0.5 , q_star= 0.1 , case_name = 'c3', res = res) -# case( m=1 , g=10 , l=1 , t_max_star=1.0 , q_star= 0.05 , case_name = 'c4', res = res) -# case( m=1 , g=10 , l=2 , t_max_star=1.0 , q_star= 0.05 , case_name = 'c5', res = res) -# case( m=2 , g=10 , l=1 , t_max_star=1.0 , q_star= 0.05 , case_name = 'c6', res = res) -# case( m=1 , g=10 , l=1 , t_max_star=1.0 , q_star= 10.0 , case_name = 'c7', res = res) -# case( m=1 , g=10 , l=2 , t_max_star=1.0 , q_star= 10.0 , case_name = 'c8', res = res) -# case( m=2 , g=10 , l=1 , t_max_star=1.0 , q_star= 10.0 , case_name = 'c9', res = res) - -#################################### -### Sensitivity Figures -#################################### - -# res = 'hi' - -ts = np.array([ 0.1, 0.3, 0.5, 1.0, 2.5, 5.0 ]) -qs = np.array([ 0.05, 0.05, 0.05, 0.05, 0.05, 0.05 ]) - -# fig, ax, fig2, ax2 = sensitivity(ts, qs , res = res , name = 's_tmax', legend = 1) - -ts = np.array([ 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5 ]) -qs = np.array([ 0.05, 0.10, 0.12, 0.15, 0.30, 0.50, 1.0, 2.0 ]) - -# fig, ax, fig2, ax2 = sensitivity(ts, qs , res = res , name = 's_q', legend = 2) - - - \ No newline at end of file diff --git a/examples/demos_by_tool/dynamicprogramming/pendulum_optimal_swingup_demo.py b/examples/demos_by_tool/dynamicprogramming/pendulum_optimal_swingup_demo.py index 260afca4..916ad22a 100644 --- a/examples/demos_by_tool/dynamicprogramming/pendulum_optimal_swingup_demo.py +++ b/examples/demos_by_tool/dynamicprogramming/pendulum_optimal_swingup_demo.py @@ -43,8 +43,8 @@ # dp.plot_policy() #dp.solve_bellman_equation( tol = 1) -dp.solve_bellman_equation( tol = 0.1 , animate_cost2go = True ) -#dp.solve_bellman_equation( tol = 1 , animate_policy = True ) +# dp.solve_bellman_equation( tol = 0.1 , animate_cost2go = True ) +dp.solve_bellman_equation( tol = 1 , animate_policy = True ) #dp.plot_cost2go(150) #dp.animate_cost2go( show = False , save = True ) diff --git a/pyro/dynamic/boat.py b/pyro/dynamic/boat.py index 8732f506..6110999a 100644 --- a/pyro/dynamic/boat.py +++ b/pyro/dynamic/boat.py @@ -358,7 +358,7 @@ def plot_alpha2Coefs(self, alpha_min = -3.15, alpha_max = 3.15 ): sys.plot_alpha2Coefs() - # sys.show_hydrodynamic_forces = True + #sys.show_hydrodynamic_forces = True sys.plot_trajectory('xu') sys.animate_simulation() \ No newline at end of file diff --git a/pyro/dynamic/rigidbody.py b/pyro/dynamic/rigidbody.py index 9e6b9f45..ab628fce 100644 --- a/pyro/dynamic/rigidbody.py +++ b/pyro/dynamic/rigidbody.py @@ -670,6 +670,24 @@ def forward_kinematic_lines(self, q ): lines_pts.append( pts ) lines_style.append( 'o') lines_color.append( 'b' ) + + ########################### + # Diff flat output + ########################### + + J = self.inertia + m = self.mass + r = self.l_t + + xp = x + J / (m * r) * np.cos(theta) + yp = y + J / (m * r) * np.sin(theta) + + pts = np.zeros(( 1 , 3 )) + pts[0,:] = np.array([xp,yp,0]) + + lines_pts.append( pts ) + lines_style.append( 'o') + lines_color.append( 'r' ) return lines_pts , lines_style , lines_color @@ -721,7 +739,7 @@ def forward_kinematic_lines_plus(self, x , u , t ): #sys.show( q = np.array([ 1.0, 2.0, 0.5 ]) ) - sys.ubar = np.array([10,2.0]) + sys.ubar = np.array([1,0.2]) sys.x0 = np.array([0,0,0,0,0,0]) sys.compute_trajectory( tf = 20 ) diff --git a/pyro/planning/discretizer_legacy.py b/pyro/planning/discretizer_legacy.py deleted file mode 100644 index 65f73cc7..00000000 --- a/pyro/planning/discretizer_legacy.py +++ /dev/null @@ -1,304 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Wed Jul 12 10:02:12 2017 - -@author: alxgr -""" - -import numpy as np - -''' -################################################################################ -''' - - -class GridDynamicSystem: - """ Create a discrete gird state-action space for a continous dynamic system """ - - ############################ - def __init__(self, sys , xgriddim = ( 101 , 101 ), ugriddim = ( 11 , 1 ) , dt = 0.05 , lookup = True ): - - self.sys = sys # Dynamic system class - - # Discretization Parameters - - self.dt = dt # time discretization - - # Grid size - self.xgriddim = xgriddim - self.ugriddim = ugriddim - - # Options - self.uselookuptable = lookup - - self.compute() - - ############################## - def compute(self): - """ """ - - self.discretizespace() - self.discretizeactions() - - print('\nDiscretization:\n---------------------------------') - print('State space dimensions:', self.sys.n , ' Input space dimension:', self.sys.m ) - print('Number of nodes:', self.nodes_n , ' Number of actions:', self.actions_n ) - print('Number of node-action pairs:', self.nodes_n * self.actions_n ) - - self.generate_nodes() - self.generate_actions() - - if self.uselookuptable: - self.compute_lookuptable() - - - ############################# - def discretizespace(self): - """ Grid the state space """ - - self.xd = [] - self.nodes_n = 1 - - # n-D grid - self.x_grid2node = np.zeros( self.xgriddim , dtype = int ) # grid of corresponding index - - # linespace for each x-axis and total number of nodes - for i in range(self.sys.n): - self.xd.append( np.linspace( self.sys.x_lb[i] , self.sys.x_ub[i] , self.xgriddim[i] ) ) - self.nodes_n = self.nodes_n * self.xgriddim[i] - - # 1-D List of nodes - self.nodes_state = np.zeros(( self.nodes_n , self.sys.n ), dtype = float ) # Number of nodes x state dimensions - self.nodes_index = np.zeros(( self.nodes_n , self.sys.n ), dtype = int ) # Number of nodes x state dimensions - - - ############################# - def discretizeactions(self): - """ Grid the action space """ - - self.ud = [] - self.actions_n = 1 - - # linespace for each u-axis and total number of actions - for i in range(self.sys.m): - self.ud.append( np.linspace( self.sys.u_lb[i] , self.sys.u_ub[i] , self.ugriddim[i] ) ) - self.actions_n = self.actions_n * self.ugriddim[i] - - # 1-D List of actions - self.actions_input = np.zeros(( self.actions_n , self.sys.m ), dtype = float ) # Number of actions x inputs dimensions - self.actions_index = np.zeros(( self.actions_n , self.sys.m ), dtype = int ) # Number of actions x inputs dimensions - - - ############################## - def generate_nodes(self): - """ Compute 1-D list of nodes """ - - # For all state nodes - node = 0 - - if self.sys.n == 2 : - - for i in range(self.xgriddim[0]): - for j in range(self.xgriddim[1]): - - # State - x = np.array([ self.xd[0][i] , self.xd[1][j] ]) - - # State and grid index based on node # - self.nodes_state[node,:] = x - self.nodes_index[node,:] = np.array([i,j]) - - # Node # based on index ij - self.x_grid2node[i,j] = node - - # Increment node number - node = node + 1 - - - elif self.sys.n == 3: - - for i in range(self.xgriddim[0]): - for j in range(self.xgriddim[1]): - for k in range(self.xgriddim[2]): - - # State - x = np.array([ self.xd[0][i] , self.xd[1][j] , self.xd[2][k] ]) - - # State and grid index based on node # - self.nodes_state[node,:] = x - self.nodes_index[node,:] = np.array([i,j,k]) - - # Node # based on index ijk - self.x_grid2node[i,j,k] = node - - # Increment node number - node = node + 1 - - - - elif self.sys.n == 4: - - # NOT yet validated!!! - - for i in range(self.xgriddim[0]): - for j in range(self.xgriddim[1]): - for k in range(self.xgriddim[2]): - for l in range(self.xgriddim[3]): - - # State - x = np.array([ self.xd[0][i] , self.xd[1][j] , self.xd[2][k] , self.xd[3][l]]) - - # State and grid index based on node # - self.nodes_state[node,:] = x - self.nodes_index[node,:] = np.array([i,j,k,l]) - - # Node # based on index ijkl - self.x_grid2node[i,j,k,l] = node - - # Increment node number - node = node + 1 - - else: - - raise NotImplementedError - - - ############################## - def generate_actions(self): - """ Compute 1-D list of actions """ - - # For all state nodes - action = 0 - - # Single input - - if self.sys.m == 1 : - - for k in range(self.ugriddim[0]): - - u = np.array([ self.ud[0][k] ]) - - # State and grid index based on node # - self.actions_input[action,:] = u - self.actions_index[action,:] = k - - # Increment node number - action = action + 1 - - elif self.sys.m == 2 : - - for k in range(self.ugriddim[0]): - for l in range(self.ugriddim[1]): - - u = np.array([ self.ud[0][k] , self.ud[1][l] ]) - - # State and grid index based on node # - self.actions_input[action,:] = u - self.actions_index[action,:] = np.array([k,l]) - - # Increment node number - action = action + 1 - - else: - - raise NotImplementedError - - - ############################## - def compute_lookuptable(self): - """ Compute lookup table for faster evaluation """ - - if self.uselookuptable: - # Evaluation lookup tables - self.action_isok = np.zeros( ( self.nodes_n , self.actions_n ) , dtype = bool ) - self.x_next = np.zeros( ( self.nodes_n , self.actions_n , self.sys.n ) , dtype = float ) # lookup table for dynamic - - # For all state nodes - for node in range( self.nodes_n ): - - x = self.nodes_state[ node , : ] - - # For all control actions - for action in range( self.actions_n ): - - u = self.actions_input[ action , : ] - - # Compute next state for all inputs - x_next = self.sys.f( x , u ) * self.dt + x - - # validity of the options - x_ok = self.sys.isavalidstate(x_next) - u_ok = self.sys.isavalidinput(x,u) - - self.x_next[ node, action, : ] = x_next - self.action_isok[ node, action] = ( u_ok & x_ok ) - - - - ############################## - ### Quick shorcut - ############################## - - ############################## - def x2node(self, x ): - """ """ - raise NotImplementedError - - s = 0 - - return s - - ############################## - def x2index(self, x ): - """ """ - raise NotImplementedError - - i = 0 - j = 0 - - return (i,j) - - ############################## - def node2x(self, x ): - """ """ - raise NotImplementedError - - s = 0 - - return s - - - ############################## - def index2x(self, u ): - """ """ - raise NotImplementedError - - a = 0 - - return a - - ############################## - def u2index(self, u ): - """ """ - raise NotImplementedError - - k = 0 - - return k - - - - - - -''' -################################################################# -################## Main ######## -################################################################# -''' - - -if __name__ == "__main__": - """ MAIN TEST """ - - pass \ No newline at end of file diff --git a/pyro/planning/trajectorygeneration.py b/pyro/planning/trajectorygeneration.py index af413bcb..a21a034d 100644 --- a/pyro/planning/trajectorygeneration.py +++ b/pyro/planning/trajectorygeneration.py @@ -292,50 +292,50 @@ def solve(self): ge = SingleAxisTrajectoryGenerator() - ge.x0 = np.array([0, 0, 0, 0, 0, 0, 0, 0]) + ge.x0 = np.array([0, 0, 0, 0, 0, 0, 0, 0]) ge.xf = np.array([10, 0, 0, 0, 0, 0, 0, 0]) ge.bc_t0_N = 2 ge.bc_tf_N = 2 ge.poly_N = 3 - ge.diff_N = 7 + ge.diff_N = 3 ge.solve() - ge.bc_t0_N = 3 - ge.bc_tf_N = 3 - ge.poly_N = 5 - ge.diff_N = 7 + # ge.bc_t0_N = 3 + # ge.bc_tf_N = 3 + # ge.poly_N = 5 + # ge.diff_N = 7 - ge.solve() + # ge.solve() - ge.bc_t0_N = 4 - ge.bc_tf_N = 4 - ge.poly_N = 7 - ge.diff_N = 7 + # ge.bc_t0_N = 4 + # ge.bc_tf_N = 4 + # ge.poly_N = 7 + # ge.diff_N = 7 - ge.solve() + # ge.solve() - ge.bc_t0_N = 5 - ge.bc_tf_N = 5 - ge.poly_N = 9 - ge.diff_N = 7 + # ge.bc_t0_N = 5 + # ge.bc_tf_N = 5 + # ge.poly_N = 9 + # ge.diff_N = 7 - ge.solve() + # ge.solve() - ge.bc_t0_N = 6 - ge.bc_tf_N = 6 - ge.poly_N = 11 - ge.diff_N = 7 + # ge.bc_t0_N = 6 + # ge.bc_tf_N = 6 + # ge.poly_N = 11 + # ge.diff_N = 7 - ge.solve() + # ge.solve() - ge.bc_t0_N = 7 - ge.bc_tf_N = 7 - ge.poly_N = 13 - ge.diff_N = 7 + # ge.bc_t0_N = 7 + # ge.bc_tf_N = 7 + # ge.poly_N = 13 + # ge.diff_N = 7 - ge.solve() + # ge.solve() # ge.bc_t0_N = 1 # ge.bc_tf_N = 1 diff --git a/pyro/planning/valueiteration_legacy.py b/pyro/planning/valueiteration_legacy.py deleted file mode 100644 index 4d94665d..00000000 --- a/pyro/planning/valueiteration_legacy.py +++ /dev/null @@ -1,871 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Wed Jul 12 12:09:37 2017 - -@author: alxgr -""" - -import sys - -import numpy as np -import matplotlib.pyplot as plt -from scipy.interpolate import RectBivariateSpline as interpol2D -from scipy.interpolate import RegularGridInterpolator as rgi - -from pyro.control import controller - - -''' -################################################################################ -''' - - -class ViController(controller.StaticController): - """ - Feedback controller - --------------------------------------- - r : reference signal vector k x 1 - y : sensor signal vector m x 1 - u : control inputs vector p x 1 - t : time 1 x 1 - --------------------------------------- - u = c( y , r , t ) - - """ - ############################ - def __init__(self, k, m, p): - """ """ - - # Dimensions - self.k = k - self.m = m - self.p = p - - super().__init__(self.k, self.m, self.p) - - # Label - self.name = 'Value Iteration Controller' - - - ############################# - def vi_law( self , x ): - """ """ - u = np.zeros(self.m) # State derivative vector - return u - - ############################# - def c( self , y , r , t = 0 ): - """ State feedback (y=x) - no reference - time independent """ - x = y - u = self.vi_law( x ) - - return u - - - -############################################################################## -# Value Iteration x dim = 2, u dim = 1 -############################################################################## - -class ValueIteration_2D: - """ Dynamic programming for 2D continous dynamic system, one continuous input u """ - - ############################ - def __init__(self, grid_sys , cost_function ): - - # Dynamic system - self.grid_sys = grid_sys # Discretized Dynamic system class - self.sys = grid_sys.sys # Base Dynamic system class - - # Controller - self.ctl = ViController( self.sys.n , self.sys.m , self.sys.n) - - # Cost function - self.cf = cost_function - - # Print params - self.fontsize = 10 - - - # Options - self.uselookuptable = True - - - ############################## - def initialize(self): - """ initialize cost-to-go and policy """ - - self.J = np.zeros( self.grid_sys.xgriddim , dtype = float ) - self.action_policy = np.zeros( self.grid_sys.xgriddim , dtype = int ) - - self.Jnew = self.J.copy() - self.Jplot = self.J.copy() - - # Initial evaluation - - # For all state nodes - for node in range( self.grid_sys.nodes_n ): - - x = self.grid_sys.nodes_state[ node , : ] - - i = self.grid_sys.nodes_index[ node , 0 ] - j = self.grid_sys.nodes_index[ node , 1 ] - - # Final Cost - self.J[i,j] = self.cf.h( x ) - - - ############################### - def compute_step(self): - """ One step of value iteration """ - - # Get interpolation of current cost space - J_interpol = interpol2D( self.grid_sys.xd[0] , self.grid_sys.xd[1] , self.J , bbox=[None, None, None, None], kx=1, ky=1,) - - # For all state nodes - for node in range( self.grid_sys.nodes_n ): - - x = self.grid_sys.nodes_state[ node , : ] - - i = self.grid_sys.nodes_index[ node , 0 ] - j = self.grid_sys.nodes_index[ node , 1 ] - - # One steps costs - Q values - Q = np.zeros( self.grid_sys.actions_n ) - - # For all control actions - for action in range( self.grid_sys.actions_n ): - - u = self.grid_sys.actions_input[ action , : ] - - # Compute next state and validity of the action - - if self.uselookuptable: - - x_next = self.grid_sys.x_next[node,action,:] - action_isok = self.grid_sys.action_isok[node,action] - - else: - - x_next = self.sys.f( x , u ) * self.grid_sys.dt + x - x_ok = self.sys.isavalidstate(x_next) - u_ok = self.sys.isavalidinput(x,u) - action_isok = ( u_ok & x_ok ) - - # If the current option is allowable - if action_isok: - - J_next = J_interpol( x_next[0] , x_next[1] ) - - # Cost-to-go of a given action - y = self.sys.h(x, u, 0) - Q[action] = self.cf.g(x, u, y, 0) * self.grid_sys.dt + J_next[0,0] - - else: - # Not allowable states or inputs/states combinations - Q[action] = self.cf.INF - - - self.Jnew[i,j] = Q.min() - self.action_policy[i,j] = Q.argmin() - - # Impossible situation ( unaceptable situation for any control actions ) - if self.Jnew[i,j] > (self.cf.INF-1) : - self.action_policy[i,j] = -1 - - - # Convergence check - delta = self.J - self.Jnew - j_max = self.Jnew.max() - delta_max = delta.max() - delta_min = delta.min() - print('Max:',j_max,'Delta max:',delta_max, 'Delta min:',delta_min) - - self.J = self.Jnew.copy() - - - ################################ - def assign_interpol_controller(self): - """ controller from optimal actions """ - - # Compute grid of u - self.u_policy_grid = [] - - # for all inputs - for k in range(self.sys.m): - self.u_policy_grid.append( np.zeros( self.grid_sys.xgriddim , dtype = float ) ) - - # For all state nodes - for node in range( self.grid_sys.nodes_n ): - - i = self.grid_sys.nodes_index[ node , 0 ] - j = self.grid_sys.nodes_index[ node , 1 ] - - # If no action is good - if ( self.action_policy[i,j] == -1 ): - - # for all inputs - for k in range(self.sys.m): - self.u_policy_grid[k][i,j] = 0 - - else: - # for all inputs - for k in range(self.sys.m): - self.u_policy_grid[k][i,j] = self.grid_sys.actions_input[ self.action_policy[i,j] , k ] - - - # Compute Interpol function - self.x2u_interpol_functions = [] - - # for all inputs - for k in range(self.sys.m): - self.x2u_interpol_functions.append( - interpol2D( self.grid_sys.xd[0] , - self.grid_sys.xd[1] , - self.u_policy_grid[k] , - bbox=[None, None, None, None], - kx=1, ky=1,) ) - - # Asign Controller - self.ctl.vi_law = self.vi_law - - - - ################################ - def vi_law(self, x , t = 0 ): - """ controller from optimal actions """ - - u = np.zeros( self.sys.m ) - - # for all inputs - for k in range(self.sys.m): - u[k] = self.x2u_interpol_functions[k]( x[0] , x[1] ) - - return u - - - - ################################ - def compute_steps(self, l = 50, plot = False): - """ compute number of step """ - - for i in range(l): - print('Step:',i) - self.compute_step() - - - - ################################ - def plot_cost2go(self, maxJ = 1000 ): - """ print graphic """ - - xname = self.sys.state_label[0] + ' ' + self.sys.state_units[0] - yname = self.sys.state_label[1] + ' ' + self.sys.state_units[1] - - self.Jplot = self.J.copy() - - ## Saturation function for cost - for i in range(self.grid_sys.xgriddim[0]): - for j in range(self.grid_sys.xgriddim[1]): - if self.J[i,j] >= maxJ : - self.Jplot[i,j] = maxJ - else: - self.Jplot[i,j] = self.J[i,j] - - self.fig1 = plt.figure(figsize=(4, 4),dpi=300, frameon=True) - self.fig1.canvas.manager.set_window_title('Cost-to-go') - self.ax1 = self.fig1.add_subplot(1,1,1) - - plt.ylabel(yname, fontsize = self.fontsize) - plt.xlabel(xname, fontsize = self.fontsize) - self.im1 = plt.pcolormesh( self.grid_sys.xd[0] , - self.grid_sys.xd[1] , - self.Jplot.T, - shading='gouraud') - - plt.axis([self.sys.x_lb[0], - self.sys.x_ub[0], - self.sys.x_lb[1], - self.sys.x_ub[1]]) - - plt.colorbar() - plt.grid(True) - plt.tight_layout() - - - ################################ - def plot_policy(self, i = 0 ): - """ print graphic """ - - xname = self.sys.state_label[0] + ' ' + self.sys.state_units[0] - yname = self.sys.state_label[1] + ' ' + self.sys.state_units[1] - - policy_plot = self.u_policy_grid[i].copy() - - self.fig1 = plt.figure(figsize=(4, 4),dpi=300, frameon=True) - self.fig1.canvas.manager.set_window_title('Policy for u[%i]'%i) - self.ax1 = self.fig1.add_subplot(1,1,1) - - plt.ylabel(yname, fontsize = self.fontsize ) - plt.xlabel(xname, fontsize = self.fontsize ) - self.im1 = plt.pcolormesh( self.grid_sys.xd[0] , - self.grid_sys.xd[1] , - policy_plot.T, - shading='gouraud') - - plt.axis([self.sys.x_lb[0], - self.sys.x_ub[0], - self.sys.x_lb[1], - self.sys.x_ub[1]]) - - plt.colorbar() - plt.grid(True) - plt.tight_layout() - - - ################################ - def load_data(self, name = 'DP_data'): - """ Save optimal controller policy and cost to go """ - - try: - - self.J = np.load( name + '_J' + '.npy' ) - self.action_policy = np.load( name + '_a' + '.npy' ).astype(int) - - except: - - print('Failed to load DP data ' ) - - - ################################ - def save_data(self, name='DP_data', prefix=''): - """ Save optimal controller policy and cost to go """ - - np.save(prefix + name + '_J', self.J) - np.save(prefix + name + '_a', self.action_policy.astype(int)) - - - - -############################################################################## -# Value Iteration generic algo -############################################################################## - -class ValueIteration_ND: - """ Dynamic programming for continuous dynamic system """ - - ############################ - def __init__(self, grid_sys, cost_function): - - # Dynamic system - self.grid_sys = grid_sys # Discretized Dynamic system class - self.sys = grid_sys.sys # Base Dynamic system class - - # initializes nb of dimensions and continuous inputs u - self.n_dim = self.sys.n - - # Controller - self.ctl = ViController(self.sys.n, self.sys.m, self.sys.n) - - # Cost function - self.cf = cost_function - - # Print params - self.fontsize = 5 - self.figsize = (4, 3) - self.dpi = 300 - self.plot_max_J = 10000 - - # Options - self.uselookuptable = True - - ############################## - def initialize(self): - """ initialize cost-to-go and policy """ - # Initial evaluation - - # J-arrays and action policy arrays - self.J = np.zeros(self.grid_sys.xgriddim, dtype=float) - self.action_policy = np.zeros(self.grid_sys.xgriddim, dtype=int) - - self.Jnew = self.J.copy() - self.Jplot = self.J.copy() - - # For all state nodes - for node in range(self.grid_sys.nodes_n): - x = self.grid_sys.nodes_state[node, :] - - # use tuple to get dynamic list of indices - indices = tuple(self.grid_sys.nodes_index[node, i] for i in range(self.n_dim)) - - # Final cost - self.J[indices] = self.cf.h(x) - - print('J shape:', self.J.shape) - - ############################### - def compute_step(self): - """ One step of value iteration """ - - # Get interpolation of current cost space - if self.n_dim == 2: - J_interpol = interpol2D(self.grid_sys.xd[0], self.grid_sys.xd[1], - self.J, bbox=[None, None, None, None], kx=1, ky=1) - elif self.n_dim == 3: - # call function for random shape - J_interpol = rgi([self.grid_sys.xd[0], self.grid_sys.xd[1], self.grid_sys.xd[2]], self.J) - else: - points = tuple(self.grid_sys.xd[i] for i in range(self.n_dim)) - J_interpol = rgi(points, self.J) - - # For all state nodes - for node in range(self.grid_sys.nodes_n): - - x = self.grid_sys.nodes_state[node, :] - - # use tuple to get dynamic list of indices - indices = tuple(self.grid_sys.nodes_index[node, i] for i in range(self.n_dim)) - - # One steps costs - Q values - Q = np.zeros(self.grid_sys.actions_n) - - # For all control actions - for action in range(self.grid_sys.actions_n): - - u = self.grid_sys.actions_input[action, :] - - # Compute next state and validity of the action - - if self.uselookuptable: - - x_next = self.grid_sys.x_next[node, action, :] - action_isok = self.grid_sys.action_isok[node, action] - - else: - - x_next = self.sys.f(x, u) * self.grid_sys.dt + x - x_ok = self.sys.isavalidstate(x_next) - u_ok = self.sys.isavalidinput(x, u) - action_isok = (u_ok & x_ok) - - # If the current option is allowable - if action_isok: - if self.n_dim == 2: - J_next = J_interpol(x_next[0], x_next[1]) - elif self.n_dim == 3: - J_next = J_interpol([x_next[0], x_next[1], x_next[2]]) - else: - J_next = J_interpol([x_next[0], x_next[1], x_next[2], x_next[3]]) - - # Cost-to-go of a given action - y = self.sys.h(x, u, 0) - if self.n_dim == 2: - Q[action] = ( self.cf.g(x, u, y, 0) * self.grid_sys.dt - + J_next[0, 0] ) - else: - Q[action] = ( self.cf.g(x, u, y, 0) * self.grid_sys.dt - + J_next) - - else: - # Not allowable states or inputs/states combinations - Q[action] = self.cf.INF - - self.Jnew[indices] = Q.min() - self.action_policy[indices] = Q.argmin() - - # Impossible situation ( unaceptable situation for any control actions ) - if self.Jnew[indices] > (self.cf.INF - 1): - self.action_policy[indices] = -1 - - # Convergence check - delta = self.J - self.Jnew - j_max = self.Jnew.max() - delta_max = delta.max() - delta_min = delta.min() - print('Max:', j_max, 'Delta max:', delta_max, 'Delta min:', delta_min) - - self.J = self.Jnew.copy() - - # TODO: Combine deltas? Check if delta_min or max changes - return delta_min - - ################################ - def assign_interpol_controller(self): - """ controller from optimal actions """ - - # Compute grid of u - self.u_policy_grid = [] - - # for all inputs - for k in range(self.sys.m): - self.u_policy_grid.append(np.zeros(self.grid_sys.xgriddim, dtype=float)) - - # For all state nodes - for node in range(self.grid_sys.nodes_n): - - # use tuple to get dynamic list of indices - indices = tuple(self.grid_sys.nodes_index[node, i] for i in range(self.n_dim)) - - # If no action is good - if (self.action_policy[indices] == -1): - - # for all inputs - for k in range(self.sys.m): - self.u_policy_grid[k][indices] = 0 - - else: - # for all inputs - for k in range(self.sys.m): - self.u_policy_grid[k][indices] = self.grid_sys.actions_input[self.action_policy[indices], k] - - # Compute Interpol function - self.interpol_functions = [] - - # for all inputs - for k in range(self.sys.m): - if self.n_dim == 2: - self.interpol_functions.append( - interpol2D(self.grid_sys.xd[0], - self.grid_sys.xd[1], - self.u_policy_grid[k], - bbox=[None, None, None, None], - kx=1, ky=1, )) - elif self.n_dim == 3: - self.interpol_functions.append( - rgi([self.grid_sys.xd[0], self.grid_sys.xd[1], self.grid_sys.xd[2]], self.u_policy_grid[k])) - else: - points = tuple(self.grid_sys.xd[i] for i in range(self.n_dim)) - self.interpol_functions.append( - rgi(points, - self.u_policy_grid[k], - method='linear')) - - # Asign Controller - self.ctl.vi_law = self.vi_law - - ################################ - def vi_law(self, x, t=0): - """ controller from optimal actions """ - - u = np.zeros(self.sys.m) - - x_clipped = x.copy() - - for i in range(self.sys.n): - if x[i] < self.sys.x_lb[i]: - x_clipped[i] = self.sys.x_lb[i] - if x[i] > self.sys.x_ub[i]: - x_clipped[i] = self.sys.x_ub[i] - - # for all inputs - for k in range(self.sys.m): - if self.n_dim == 2: - u[k] = self.interpol_functions[k]( x_clipped[0], x_clipped[1]) - else: - u[k] = self.interpol_functions[k]( x_clipped ) - - return u - - ################################ - def compute_steps(self, l=50, plot=False, threshold=1.0e-25, maxJ=1000): - """ compute number of step """ - step = 0 - print('Step:', step) - cur_threshold = self.compute_step() - print('Current threshold', cur_threshold) - if plot: - self.plot_dynamic_cost2go() - # while abs(cur_threshold) > threshold: - while step < l: - step = step + 1 - print('Step:', step) - cur_threshold = self.compute_step() - print('Current threshold', cur_threshold) - if plot: - self.draw_cost2go( step, maxJ ) - - ################################ - def plot_dynamic_cost2go(self): - """ print graphic """ - - maxJ = self.plot_max_J - - #plt.ion() - - xname = self.sys.state_label[0] + ' ' + self.sys.state_units[0] - yname = self.sys.state_label[1] + ' ' + self.sys.state_units[1] - - self.fig_dynamic = plt.figure(figsize= self.figsize, dpi=self.dpi, frameon=True) - self.fig_dynamic.canvas.manager.set_window_title('Dynamic Cost-to-go') - self.ax1_dynamic = self.fig_dynamic.add_subplot(1, 1, 1) - - plt.ylabel(yname, fontsize=self.fontsize) - plt.xlabel(xname, fontsize=self.fontsize) - - plt.axis([self.sys.x_lb[0], - self.sys.x_ub[0], - self.sys.x_lb[1], - self.sys.x_ub[1]]) - - self.Jplot = self.J.copy() - self.create_Jplot() - - - #TODO update next line for deciding axis to plot - #plot = self.Jplot.T if self.n_dim == 2 else self.Jplot[..., 0].T - - if self.n_dim == 2: - plot = self.Jplot.T - - elif self.n_dim == 3: - plot = self.Jplot[..., 0].T - - elif self.n_dim == 4: - plot = self.Jplot[...,0,0].T - - else: - raise NotImplementedError() - - - self.im1_dynamic = plt.pcolormesh(self.grid_sys.xd[0], - self.grid_sys.xd[1], - plot, - shading='gouraud') - - self.step_text_template = 'Number of steps = %i' - self.time_text = self.ax1_dynamic.text( - 0.05, 0.05, '', transform=self.ax1_dynamic.transAxes, - fontsize=self.fontsize ) - - self.ax1_dynamic.tick_params( labelsize = self.fontsize ) - - plt.colorbar() - plt.grid(True) - plt.tight_layout() - - #plt.draw() - plt.pause(0.001) - - plt.ion() - - - ############################# - def draw_cost2go(self, step, maxJ=1000): - self.Jplot = self.J.copy() - self.create_Jplot() - - if self.n_dim == 2: - plot = self.Jplot.T - - elif self.n_dim == 3: - plot = self.Jplot[..., 0].T - - elif self.n_dim == 4: - plot = self.Jplot[...,0,0].T - - else: - raise NotImplementedError() - - self.im1_dynamic.set_array(np.ravel(plot)) - self.time_text.set_text(self.step_text_template % ( step )) - #plt.draw() - plt.pause(0.001) - - - ################################ - def create_Jplot(self): - - maxJ = self.plot_max_J - - ## Saturation function for cost - if self.n_dim == 2: - for i in range(self.grid_sys.xgriddim[0]): - for j in range(self.grid_sys.xgriddim[1]): - self.Jplot[i, j] = maxJ if self.J[i, j] >= maxJ else self.J[i, j] - - elif self.n_dim == 3: - for i in range(self.grid_sys.xgriddim[0]): - for j in range(self.grid_sys.xgriddim[1]): - for k in range(len(self.J[i, j])): - self.Jplot[i, j, k] = maxJ if self.J[i, j, k] >= maxJ else self.J[i, j, k] - - elif self.n_dim == 4: - for i in range(self.grid_sys.xgriddim[0]): - for j in range(self.grid_sys.xgriddim[1]): - for k in range(self.grid_sys.xgriddim[2]): - for l in range(self.grid_sys.xgriddim[3]): - self.Jplot[i, j, k, l] = maxJ if self.J[i, j, k, l] >= maxJ else self.J[i, j, k, l] - - - ################################ - def plot_cost2go(self, maxJ=1000): - """ print graphic """ - - self.plot_max_J = maxJ - - self.plot_dynamic_cost2go() - #self.draw_cost2go() - - - ################################ - def plot_policy(self, i=0): - """ print graphic """ - - plt.ion() - - xname = self.sys.state_label[0] + ' ' + self.sys.state_units[0] - yname = self.sys.state_label[1] + ' ' + self.sys.state_units[1] - - policy_plot = self.u_policy_grid[i].copy() - - self.fig1 = plt.figure(figsize=(4, 4), dpi=300, frameon=True) - self.fig1.canvas.manager.set_window_title('Policy for u[%i]' % i) - self.ax1 = self.fig1.add_subplot(1, 1, 1) - - #plot = policy_plot.T if self.n_dim == 2 else policy_plot[..., 0].T - - if self.n_dim == 2: - plot = policy_plot.T - - elif self.n_dim == 3: - plot = policy_plot[..., 0].T - - elif self.n_dim == 4: - plot = policy_plot[...,0,0].T - - else: - raise NotImplementedError() - - plt.ylabel(yname, fontsize=self.fontsize) - plt.xlabel(xname, fontsize=self.fontsize) - self.im1 = plt.pcolormesh(self.grid_sys.xd[0], - self.grid_sys.xd[1], - plot, - shading='gouraud') - - plt.axis([self.sys.x_lb[0], - self.sys.x_ub[0], - self.sys.x_lb[1], - self.sys.x_ub[1]]) - - plt.colorbar() - plt.grid(True) - plt.tight_layout() - - plt.draw() - plt.pause(0.001) - - - ################################ - def plot_3D_policy(self, i=0): - """ print graphic """ - - plt.ion() - - xname = self.sys.state_label[0] + ' ' + self.sys.state_units[0] - yname = self.sys.state_label[1] + ' ' + self.sys.state_units[1] - - policy_plot = self.u_policy_grid[i].copy() - - self.fig1 = plt.figure() - self.fig1.canvas.manager.set_window_title('Policy for u[%i]' % i) - self.ax1 = self.fig1.gca(projection='3d') - - if self.n_dim == 2: - plot = policy_plot.T - - elif self.n_dim == 3: - plot = policy_plot[..., 0].T - - elif self.n_dim == 4: - plot = policy_plot[...,0,0].T - - else: - raise NotImplementedError() - - plt.ylabel(yname, fontsize=self.fontsize) - plt.xlabel(xname, fontsize=self.fontsize) - x = self.grid_sys.xd[0] - y = self.grid_sys.xd[1] - X, Y = np.meshgrid(x, y) - Z = plot - self.ax1.plot_surface(X, Y, Z) - - plt.axis([self.sys.x_lb[0], - self.sys.x_ub[0], - self.sys.x_lb[1], - self.sys.x_ub[1]]) - - # plt.colorbar() - - plt.draw() - plt.pause(1) - - ################################ - def plot_3D_cost(self): - """ print graphic """ - - xname = self.sys.state_label[0] + ' ' + self.sys.state_units[0] - yname = self.sys.state_label[1] + ' ' + self.sys.state_units[1] - - cost_plot = self.J.copy() - - self.fig1 = plt.figure() - self.fig1.canvas.manager.set_window_title('Cost-to-go') - self.ax1 = self.fig1.gca(projection='3d') - - if self.n_dim == 2: - plot = cost_plot.T - - elif self.n_dim == 3: - plot = cost_plot[..., 0].T - - elif self.n_dim == 4: - plot = cost_plot[...,0,0].T - - else: - raise NotImplementedError() - - plt.ylabel(yname, fontsize=self.fontsize) - plt.xlabel(xname, fontsize=self.fontsize) - x = self.grid_sys.xd[0] - y = self.grid_sys.xd[1] - X, Y = np.meshgrid(x, y) - Z = plot - self.ax1.plot_surface(X, Y, Z) - - plt.axis([self.sys.x_lb[0], - self.sys.x_ub[0], - self.sys.x_lb[1], - self.sys.x_ub[1]]) - - plt.show() - - - - ################################ - def load_data(self, name='DP_data', prefix=''): - """ Save optimal controller policy and cost to go """ - - try: - self.J = np.load(prefix + name + '_J' + '.npy') - self.action_policy = np.load(prefix + name + '_a' + '.npy').astype(int) - print('Data loaded from file:',name) - - except IOError: - type, value, traceback = sys.exc_info() - print('Error opening %s: %s' % (value.filename, value.strerror)) - - # returns filled array to signal that the trajectory has been loaded - # used in Slash library - return [1] - - ################################ - def save_data(self, name='DP_data', prefix=''): - """ Save optimal controller policy and cost to go """ - - print('Final J', self.J) - print('Final policy', self.action_policy) - - np.save(prefix + name + '_J', self.J) - np.save(prefix + name + '_a', self.action_policy.astype(int)) From b6413a084a339f2185dfc9ea6ffaf0bde88eb9b0 Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Fri, 1 Mar 2024 14:38:24 -0500 Subject: [PATCH 60/93] started a sys2gym wrapper --- dev/gym/sys2gym.py | 183 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 183 insertions(+) create mode 100644 dev/gym/sys2gym.py diff --git a/dev/gym/sys2gym.py b/dev/gym/sys2gym.py new file mode 100644 index 00000000..9ee5644e --- /dev/null +++ b/dev/gym/sys2gym.py @@ -0,0 +1,183 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Created on Fri Mar 1 14:08:03 2024 + +@author: alex +""" + +import numpy as np + +import gymnasium as gym +from gymnasium import spaces + +from pyro.dynamic import pendulum + +# %% +# Declaration and Initialization +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# Our custom environment will inherit from the abstract class +# ``gymnasium.Env``. You shouldn’t forget to add the ``metadata`` +# attribute to your class. There, you should specify the render-modes that +# are supported by your environment (e.g. ``"human"``, ``"rgb_array"``, +# ``"ansi"``) and the framerate at which your environment should be +# rendered. Every environment should support ``None`` as render-mode; you +# don’t need to add it in the metadata. In ``GridWorldEnv``, we will +# support the modes “rgb_array” and “human” and render at 4 FPS. +# +# The ``__init__`` method of our environment will accept the integer +# ``size``, that determines the size of the square grid. We will set up +# some variables for rendering and define ``self.observation_space`` and +# ``self.action_space``. In our case, observations should provide +# information about the location of the agent and target on the +# 2-dimensional grid. We will choose to represent observations in the form +# of dictionaries with keys ``"agent"`` and ``"target"``. An observation +# may look like ``{"agent": array([1, 0]), "target": array([0, 3])}``. +# Since we have 4 actions in our environment (“right”, “up”, “left”, +# “down”), we will use ``Discrete(4)`` as an action space. Here is the +# declaration of ``GridWorldEnv`` and the implementation of ``__init__``: + +class SysEnv(gym.Env): + + def __init__(self, sys, dt = 0.1 , tf = 10.0, render_mode=None): + + self.sys = sys + self.dt = dt + + self.tf = tf + self.render_mode = render_mode + + # Memory + self.x = sys.x0 + self.u = sys.ubar + self.t = 0.0 + + +# %% +# Constructing Observations From Environment States +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# Since we will need to compute observations both in ``reset`` and +# ``step``, it is often convenient to have a (private) method ``_get_obs`` +# that translates the environment’s state into an observation. However,ƒ +# this is not mandatory and you may as well compute observations in +# ``reset`` and ``step`` separately:ƒ + + def _get_obs(self): + + y = self.sys.h(self.x,self.u,self.t) + + return y + +# %% +# We can also implement a similar method for the auxiliary information +# that is returned by ``step`` and ``reset``. In our case, we would like +# to provide the manhattan distance between the agent and the target: + + def _get_info(self): + + return { + "state": self.x, + "action": self.u + } + +# %% +# Reset +# ~~~~~ +# +# The ``reset`` method will be called to initiate a new episode. You may +# assume that the ``step`` method will not be called before ``reset`` has +# been called. Moreover, ``reset`` should be called whenever a done signal +# has been issued. Users may pass the ``seed`` keyword to ``reset`` to +# initialize any random number generator that is used by the environment +# to a deterministic state. It is recommended to use the random number +# generator ``self.np_random`` that is provided by the environment’s base +# class, ``gymnasium.Env``. If you only use this RNG, you do not need to +# worry much about seeding, *but you need to remember to call +# ``super().reset(seed=seed)``* to make sure that ``gymnasium.Env`` +# correctly seeds the RNG. Once this is done, we can randomly set the +# state of our environment. In our case, we randomly choose the agent’s +# location and the random sample target positions, until it does not +# coincide with the agent’s position. +# +# The ``reset`` method should return a tuple of the initial observation +# and some auxiliary information. We can use the methods ``_get_obs`` and +# ``_get_info`` that we implemented earlier for that: + + def reset(self, seed=None, options=None): + + + super().reset(seed=seed) + + self.x = self.sys.x0 + self.u = self.sys.ubar + self.t = 0.0 + + observation = self._get_obs() + info = self._get_info() + + return observation, info + + # %% + # Step + # ~~~~ + # + # The ``step`` method usually contains most of the logic of your + # environment. It accepts an ``action``, computes the state of the + # environment after applying that action and returns the 5-tuple + # ``(observation, reward, terminated, truncated, info)``. See + # :meth:`gymnasium.Env.step`. Once the new state of the environment has + # been computed, we can check whether it is a terminal state and we set + # ``done`` accordingly. Since we are using sparse binary rewards in + # ``GridWorldEnv``, computing ``reward`` is trivial once we know + # ``done``.To gather ``observation`` and ``info``, we can again make + # use of ``_get_obs`` and ``_get_info``: + + def step(self, u): + + + # Derivatives + dx = self.sys.f( self.x , self.u , self.t ) + + # Euler integration + x_new = self.x + dx * self.dt + t_new = self.t + self.dt + + # Cost function + r = -self.sys.cost_function.g( self.x , self.u , self.t ) + + terminated = self.t > self.tf + + truncated = not self.sys.isavalidstate( x_new ) + + y = self._get_obs() + info = self._get_info() + + # Memory update + self.x = x_new + self.t = t_new + self.u = u + + if self.render_mode == "human": + self._render_frame() + + return y, r, terminated, truncated, info + + + +# %% +# Rendering +# ~~~~~~~~~ +# +# Here, we are using PyGame for rendering. A similar approach to rendering +# is used in many environments that are included with Gymnasium and you +# can use it as a skeleton for your own environments: + + def render(self): + if self.render_mode == "rgb_array": + return self._render_frame() + + def _render_frame(self): + + pass \ No newline at end of file From 76824968688fbec0b0750e227de93523a1a29ff1 Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Fri, 1 Mar 2024 15:58:53 -0500 Subject: [PATCH 61/93] pyro sys working with ppo from stablebaseline3 !! --- dev/gym/pendulum_gym.py | 223 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 223 insertions(+) create mode 100644 dev/gym/pendulum_gym.py diff --git a/dev/gym/pendulum_gym.py b/dev/gym/pendulum_gym.py new file mode 100644 index 00000000..12162e3d --- /dev/null +++ b/dev/gym/pendulum_gym.py @@ -0,0 +1,223 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Created on Fri Mar 1 14:08:03 2024 + +@author: alex +""" + +import numpy as np +import matplotlib.pyplot as plt + +import gymnasium as gym +from gymnasium import spaces + +from pyro.dynamic import pendulum + + +from stable_baselines3 import PPO +from stable_baselines3.common.env_util import make_vec_env + +# %% +# Declaration and Initialization +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# Our custom environment will inherit from the abstract class +# ``gymnasium.Env``. You shouldn’t forget to add the ``metadata`` +# attribute to your class. There, you should specify the render-modes that +# are supported by your environment (e.g. ``"human"``, ``"rgb_array"``, +# ``"ansi"``) and the framerate at which your environment should be +# rendered. Every environment should support ``None`` as render-mode; you +# don’t need to add it in the metadata. In ``GridWorldEnv``, we will +# support the modes “rgb_array” and “human” and render at 4 FPS. +# +# The ``__init__`` method of our environment will accept the integer +# ``size``, that determines the size of the square grid. We will set up +# some variables for rendering and define ``self.observation_space`` and +# ``self.action_space``. In our case, observations should provide +# information about the location of the agent and target on the +# 2-dimensional grid. We will choose to represent observations in the form +# of dictionaries with keys ``"agent"`` and ``"target"``. An observation +# may look like ``{"agent": array([1, 0]), "target": array([0, 3])}``. +# Since we have 4 actions in our environment (“right”, “up”, “left”, +# “down”), we will use ``Discrete(4)`` as an action space. Here is the +# declaration of ``GridWorldEnv`` and the implementation of ``__init__``: + +class SysEnv(gym.Env): + + def __init__(self, sys, dt = 0.1 , tf = 10.0, render_mode=None): + + self.observation_space = spaces.Box( sys.x_lb, sys.x_ub) + self.action_space = spaces.Box( sys.u_lb, sys.u_ub ) + + + self.sys = sys + self.dt = dt + + self.tf = tf + self.render_mode = render_mode + + # Memory + self.x = sys.x0 + self.u = sys.ubar + self.t = 0.0 + + if self.render_mode == "human": + + self.animator = self.sys.get_animator() + self.animator.show_plus(self.x, self.u, self.t) + plt.pause( 0.001 ) + + +# %% +# Constructing Observations From Environment States +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# Since we will need to compute observations both in ``reset`` and +# ``step``, it is often convenient to have a (private) method ``_get_obs`` +# that translates the environment’s state into an observation. However,ƒ +# this is not mandatory and you may as well compute observations in +# ``reset`` and ``step`` separately:ƒ + + def _get_obs(self): + + y = self.sys.h(self.x,self.u,self.t) + + return y + +# %% +# We can also implement a similar method for the auxiliary information +# that is returned by ``step`` and ``reset``. In our case, we would like +# to provide the manhattan distance between the agent and the target: + + def _get_info(self): + + return { + "state": self.x, + "action": self.u + } + +# %% +# Reset +# ~~~~~ +# +# The ``reset`` method will be called to initiate a new episode. You may +# assume that the ``step`` method will not be called before ``reset`` has +# been called. Moreover, ``reset`` should be called whenever a done signal +# has been issued. Users may pass the ``seed`` keyword to ``reset`` to +# initialize any random number generator that is used by the environment +# to a deterministic state. It is recommended to use the random number +# generator ``self.np_random`` that is provided by the environment’s base +# class, ``gymnasium.Env``. If you only use this RNG, you do not need to +# worry much about seeding, *but you need to remember to call +# ``super().reset(seed=seed)``* to make sure that ``gymnasium.Env`` +# correctly seeds the RNG. Once this is done, we can randomly set the +# state of our environment. In our case, we randomly choose the agent’s +# location and the random sample target positions, until it does not +# coincide with the agent’s position. +# +# The ``reset`` method should return a tuple of the initial observation +# and some auxiliary information. We can use the methods ``_get_obs`` and +# ``_get_info`` that we implemented earlier for that: + + def reset(self, seed=None, options=None): + + + super().reset(seed=seed) + + self.x = self.sys.x0 + self.u = self.sys.ubar + self.t = 0.0 + + observation = self._get_obs() + info = self._get_info() + + return observation, info + + # %% + # Step + # ~~~~ + # + # The ``step`` method usually contains most of the logic of your + # environment. It accepts an ``action``, computes the state of the + # environment after applying that action and returns the 5-tuple + # ``(observation, reward, terminated, truncated, info)``. See + # :meth:`gymnasium.Env.step`. Once the new state of the environment has + # been computed, we can check whether it is a terminal state and we set + # ``done`` accordingly. Since we are using sparse binary rewards in + # ``GridWorldEnv``, computing ``reward`` is trivial once we know + # ``done``.To gather ``observation`` and ``info``, we can again make + # use of ``_get_obs`` and ``_get_info``: + + def step(self, u): + + + # Derivatives + dx = self.sys.f( self.x , self.u , self.t ) + + # Euler integration + x_new = self.x + dx * self.dt + t_new = self.t + self.dt + + # Cost function + r = -self.sys.cost_function.g( self.x , self.u , self.t ) + + terminated = self.t > self.tf + + truncated = not self.sys.isavalidstate( x_new ) + + y = self._get_obs() + info = self._get_info() + + # Memory update + self.x = x_new + self.t = t_new + self.u = u + + if self.render_mode == "human": + self._render_frame() + + return y, r, terminated, truncated, info + + + +# %% +# Rendering +# ~~~~~~~~~ +# +# Here, we are using PyGame for rendering. A similar approach to rendering +# is used in many environments that are included with Gymnasium and you +# can use it as a skeleton for your own environments: + + def render(self): + if self.render_mode == "rgb_array": + return self._render_frame() + + def _render_frame(self): + + self.animator.show_plus_update(self.x, self.u, self.t) + plt.pause( 0.001 ) + + + + +sys = pendulum.SinglePendulum() + +gym_env = SysEnv( sys , render_mode=None) +gym_env = SysEnv( sys , render_mode='human') + +# vec_env = make_vec_env( gym_env ) + +model = PPO("MlpPolicy", gym_env, verbose=1) +model.learn(total_timesteps=25000) +model.save("ppo_pendulum") + +del model # remove to demonstrate saving and loading + +model = PPO.load("ppo_pendulum") + +gym_env = SysEnv( sys , render_mode='human') +y, info = gym_env.reset() +while True: + action, _states = model.predict(y) + y, r, terminated, truncated, info = gym_env.step(action) \ No newline at end of file From 67315d550f4c86579af7dcfb4c17755cbebacafb Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Sat, 2 Mar 2024 11:17:37 -0500 Subject: [PATCH 62/93] working on pyro to gym wrapper --- dev/gym/pendulum.py | 25 +++++ dev/gym/pendulum_gym.py | 239 +++++++++++++++++++++++----------------- dev/gym/sys2gym.py | 183 ------------------------------ pyro/tools/sys2gym.py | 156 ++++++++++++++++++++++++++ 4 files changed, 317 insertions(+), 286 deletions(-) create mode 100644 dev/gym/pendulum.py delete mode 100644 dev/gym/sys2gym.py create mode 100644 pyro/tools/sys2gym.py diff --git a/dev/gym/pendulum.py b/dev/gym/pendulum.py new file mode 100644 index 00000000..e44987e5 --- /dev/null +++ b/dev/gym/pendulum.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Created on Tue Feb 13 13:37:57 2024 + +@author: alex +""" + +import gymnasium as gym + +from stable_baselines3 import PPO +from stable_baselines3.common.env_util import make_vec_env + +# Parallel environments +vec_env = make_vec_env("Pendulum-v1", n_envs=1) + +model = PPO("MlpPolicy", vec_env, verbose=1) +model.learn(total_timesteps=250000) + +obs = vec_env.reset() + +while True: + action, _states = model.predict(obs) + obs, rewards, dones, info = vec_env.step(action) + vec_env.render("human") \ No newline at end of file diff --git a/dev/gym/pendulum_gym.py b/dev/gym/pendulum_gym.py index 12162e3d..20b8c8a7 100644 --- a/dev/gym/pendulum_gym.py +++ b/dev/gym/pendulum_gym.py @@ -14,7 +14,6 @@ from pyro.dynamic import pendulum - from stable_baselines3 import PPO from stable_baselines3.common.env_util import make_vec_env @@ -42,90 +41,92 @@ # Since we have 4 actions in our environment (“right”, “up”, “left”, # “down”), we will use ``Discrete(4)`` as an action space. Here is the # declaration of ``GridWorldEnv`` and the implementation of ``__init__``: - + + class SysEnv(gym.Env): - def __init__(self, sys, dt = 0.1 , tf = 10.0, render_mode=None): - - self.observation_space = spaces.Box( sys.x_lb, sys.x_ub) - self.action_space = spaces.Box( sys.u_lb, sys.u_ub ) + def __init__(self, sys, dt=0.1, tf=10.0, render_mode=None): + + # x-y ouputs + y_ub = np.array([+1, +1, sys.x_ub[1]]) + y_lb = np.array([-1, -1, sys.x_lb[1]]) + + self.observation_space = spaces.Box(y_lb, y_ub) + self.action_space = spaces.Box(sys.u_lb, sys.u_ub) - self.sys = sys - self.dt = dt - + self.dt = dt + self.tf = tf self.render_mode = render_mode - + # Memory self.x = sys.x0 self.u = sys.ubar self.t = 0.0 - + if self.render_mode == "human": - + self.animator = self.sys.get_animator() self.animator.show_plus(self.x, self.u, self.t) - plt.pause( 0.001 ) + plt.pause(0.001) + # %% + # Constructing Observations From Environment States + # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # + # Since we will need to compute observations both in ``reset`` and + # ``step``, it is often convenient to have a (private) method ``_get_obs`` + # that translates the environment’s state into an observation. However,ƒ + # this is not mandatory and you may as well compute observations in + # ``reset`` and ``step`` separately:ƒ -# %% -# Constructing Observations From Environment States -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# Since we will need to compute observations both in ``reset`` and -# ``step``, it is often convenient to have a (private) method ``_get_obs`` -# that translates the environment’s state into an observation. However,ƒ -# this is not mandatory and you may as well compute observations in -# ``reset`` and ``step`` separately:ƒ - def _get_obs(self): - - y = self.sys.h(self.x,self.u,self.t) - + + theta = self.x[0] + thetadot = self.x[1] + + y = np.array([np.cos(theta), np.sin(theta), thetadot], dtype=np.float32) + return y - -# %% -# We can also implement a similar method for the auxiliary information -# that is returned by ``step`` and ``reset``. In our case, we would like -# to provide the manhattan distance between the agent and the target: + + # %% + # We can also implement a similar method for the auxiliary information + # that is returned by ``step`` and ``reset``. In our case, we would like + # to provide the manhattan distance between the agent and the target: def _get_info(self): - - return { - "state": self.x, - "action": self.u - } -# %% -# Reset -# ~~~~~ -# -# The ``reset`` method will be called to initiate a new episode. You may -# assume that the ``step`` method will not be called before ``reset`` has -# been called. Moreover, ``reset`` should be called whenever a done signal -# has been issued. Users may pass the ``seed`` keyword to ``reset`` to -# initialize any random number generator that is used by the environment -# to a deterministic state. It is recommended to use the random number -# generator ``self.np_random`` that is provided by the environment’s base -# class, ``gymnasium.Env``. If you only use this RNG, you do not need to -# worry much about seeding, *but you need to remember to call -# ``super().reset(seed=seed)``* to make sure that ``gymnasium.Env`` -# correctly seeds the RNG. Once this is done, we can randomly set the -# state of our environment. In our case, we randomly choose the agent’s -# location and the random sample target positions, until it does not -# coincide with the agent’s position. -# -# The ``reset`` method should return a tuple of the initial observation -# and some auxiliary information. We can use the methods ``_get_obs`` and -# ``_get_info`` that we implemented earlier for that: + return {"state": self.x, "action": self.u} + + # %% + # Reset + # ~~~~~ + # + # The ``reset`` method will be called to initiate a new episode. You may + # assume that the ``step`` method will not be called before ``reset`` has + # been called. Moreover, ``reset`` should be called whenever a done signal + # has been issued. Users may pass the ``seed`` keyword to ``reset`` to + # initialize any random number generator that is used by the environment + # to a deterministic state. It is recommended to use the random number + # generator ``self.np_random`` that is provided by the environment’s base + # class, ``gymnasium.Env``. If you only use this RNG, you do not need to + # worry much about seeding, *but you need to remember to call + # ``super().reset(seed=seed)``* to make sure that ``gymnasium.Env`` + # correctly seeds the RNG. Once this is done, we can randomly set the + # state of our environment. In our case, we randomly choose the agent’s + # location and the random sample target positions, until it does not + # coincide with the agent’s position. + # + # The ``reset`` method should return a tuple of the initial observation + # and some auxiliary information. We can use the methods ``_get_obs`` and + # ``_get_info`` that we implemented earlier for that: def reset(self, seed=None, options=None): - - + super().reset(seed=seed) - self.x = self.sys.x0 + self.x = np.random.uniform(np.array([-np.pi, -1]), np.array([np.pi, 1])) self.u = self.sys.ubar self.t = 0.0 @@ -133,7 +134,7 @@ def reset(self, seed=None, options=None): info = self._get_info() return observation, info - + # %% # Step # ~~~~ @@ -150,74 +151,106 @@ def reset(self, seed=None, options=None): # use of ``_get_obs`` and ``_get_info``: def step(self, u): - - + + u = np.clip(u, self.sys.u_lb, self.sys.u_ub) + x = self.x + t = self.t + dt = self.dt + # Derivatives - dx = self.sys.f( self.x , self.u , self.t ) - + dx = self.sys.f(x, u, t) + # Euler integration - x_new = self.x + dx * self.dt - t_new = self.t + self.dt - + x_new = x + dx * dt + t_new = t + dt + + x_new[0] = self.angle_normalize(x_new[0]) + + # Sat speed --> I hate they do this in gym env + if x_new[1] > sys.x_ub[1]: + x_new[1] = sys.x_ub[1] + if x_new[1] < sys.x_lb[1]: + x_new[1] = sys.x_lb[1] + # Cost function - r = -self.sys.cost_function.g( self.x , self.u , self.t ) - - terminated = self.t > self.tf - - truncated = not self.sys.isavalidstate( x_new ) - - y = self._get_obs() - info = self._get_info() - + r = -self.sys.cost_function.g(x, u, t) + + terminated = t > self.tf + + truncated = not self.sys.isavalidstate(x_new) + # Memory update self.x = x_new self.t = t_new self.u = u + y = self._get_obs() + info = self._get_info() + if self.render_mode == "human": self._render_frame() return y, r, terminated, truncated, info - - -# %% -# Rendering -# ~~~~~~~~~ -# -# Here, we are using PyGame for rendering. A similar approach to rendering -# is used in many environments that are included with Gymnasium and you -# can use it as a skeleton for your own environments: + def angle_normalize(self, x): + + return ((x + np.pi) % (2 * np.pi)) - np.pi + + # %% + # Rendering + # ~~~~~~~~~ + # + # Here, we are using PyGame for rendering. A similar approach to rendering + # is used in many environments that are included with Gymnasium and you + # can use it as a skeleton for your own environments: def render(self): if self.render_mode == "rgb_array": return self._render_frame() def _render_frame(self): - + self.animator.show_plus_update(self.x, self.u, self.t) - plt.pause( 0.001 ) + plt.pause(0.001) +sys = pendulum.InvertedPendulum() +# Physical parameters +sys.gravity = 10.0 +sys.m1 = 1.0 +sys.lc1 = 1.0 -sys = pendulum.SinglePendulum() +# Min/max state and control inputs +sys.x_ub = np.array([+2 * np.pi, +8]) +sys.x_lb = np.array([-2 * np.pi, -8]) +sys.u_ub = np.array([+2.0]) +sys.u_lb = np.array([-2.0]) -gym_env = SysEnv( sys , render_mode=None) -gym_env = SysEnv( sys , render_mode='human') +# Cost Function +# The reward function is defined as: r = -(theta2 + 0.1 * theta_dt2 + 0.001 * torque2) +sys.cost_function.xbar = np.array([0, 0]) # target +sys.cost_function.R[0, 0] = 0.001 +sys.cost_function.Q[0, 0] = 1.0 +sys.cost_function.Q[1, 1] = 0.1 -# vec_env = make_vec_env( gym_env ) +gym_env = SysEnv(sys, dt=0.05, render_mode=None) model = PPO("MlpPolicy", gym_env, verbose=1) -model.learn(total_timesteps=25000) -model.save("ppo_pendulum") +model.learn(total_timesteps=250000) +# model.learn(total_timesteps=1000) -del model # remove to demonstrate saving and loading - -model = PPO.load("ppo_pendulum") - -gym_env = SysEnv( sys , render_mode='human') +gym_env = SysEnv(sys, render_mode="human") y, info = gym_env.reset() -while True: - action, _states = model.predict(y) - y, r, terminated, truncated, info = gym_env.step(action) \ No newline at end of file + +episodes = 10 +for episode in range(episodes): + y, info = gym_env.reset() + terminated = False + truncated = False + + print("\n Episode:", episode) + while not (terminated or truncated): + u, _states = model.predict(y) + y, r, terminated, truncated, info = gym_env.step(u) + print("t=", gym_env.t, "x=", gym_env.x, "u=", gym_env.u, "r=", r) diff --git a/dev/gym/sys2gym.py b/dev/gym/sys2gym.py deleted file mode 100644 index 9ee5644e..00000000 --- a/dev/gym/sys2gym.py +++ /dev/null @@ -1,183 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Created on Fri Mar 1 14:08:03 2024 - -@author: alex -""" - -import numpy as np - -import gymnasium as gym -from gymnasium import spaces - -from pyro.dynamic import pendulum - -# %% -# Declaration and Initialization -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# Our custom environment will inherit from the abstract class -# ``gymnasium.Env``. You shouldn’t forget to add the ``metadata`` -# attribute to your class. There, you should specify the render-modes that -# are supported by your environment (e.g. ``"human"``, ``"rgb_array"``, -# ``"ansi"``) and the framerate at which your environment should be -# rendered. Every environment should support ``None`` as render-mode; you -# don’t need to add it in the metadata. In ``GridWorldEnv``, we will -# support the modes “rgb_array” and “human” and render at 4 FPS. -# -# The ``__init__`` method of our environment will accept the integer -# ``size``, that determines the size of the square grid. We will set up -# some variables for rendering and define ``self.observation_space`` and -# ``self.action_space``. In our case, observations should provide -# information about the location of the agent and target on the -# 2-dimensional grid. We will choose to represent observations in the form -# of dictionaries with keys ``"agent"`` and ``"target"``. An observation -# may look like ``{"agent": array([1, 0]), "target": array([0, 3])}``. -# Since we have 4 actions in our environment (“right”, “up”, “left”, -# “down”), we will use ``Discrete(4)`` as an action space. Here is the -# declaration of ``GridWorldEnv`` and the implementation of ``__init__``: - -class SysEnv(gym.Env): - - def __init__(self, sys, dt = 0.1 , tf = 10.0, render_mode=None): - - self.sys = sys - self.dt = dt - - self.tf = tf - self.render_mode = render_mode - - # Memory - self.x = sys.x0 - self.u = sys.ubar - self.t = 0.0 - - -# %% -# Constructing Observations From Environment States -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# Since we will need to compute observations both in ``reset`` and -# ``step``, it is often convenient to have a (private) method ``_get_obs`` -# that translates the environment’s state into an observation. However,ƒ -# this is not mandatory and you may as well compute observations in -# ``reset`` and ``step`` separately:ƒ - - def _get_obs(self): - - y = self.sys.h(self.x,self.u,self.t) - - return y - -# %% -# We can also implement a similar method for the auxiliary information -# that is returned by ``step`` and ``reset``. In our case, we would like -# to provide the manhattan distance between the agent and the target: - - def _get_info(self): - - return { - "state": self.x, - "action": self.u - } - -# %% -# Reset -# ~~~~~ -# -# The ``reset`` method will be called to initiate a new episode. You may -# assume that the ``step`` method will not be called before ``reset`` has -# been called. Moreover, ``reset`` should be called whenever a done signal -# has been issued. Users may pass the ``seed`` keyword to ``reset`` to -# initialize any random number generator that is used by the environment -# to a deterministic state. It is recommended to use the random number -# generator ``self.np_random`` that is provided by the environment’s base -# class, ``gymnasium.Env``. If you only use this RNG, you do not need to -# worry much about seeding, *but you need to remember to call -# ``super().reset(seed=seed)``* to make sure that ``gymnasium.Env`` -# correctly seeds the RNG. Once this is done, we can randomly set the -# state of our environment. In our case, we randomly choose the agent’s -# location and the random sample target positions, until it does not -# coincide with the agent’s position. -# -# The ``reset`` method should return a tuple of the initial observation -# and some auxiliary information. We can use the methods ``_get_obs`` and -# ``_get_info`` that we implemented earlier for that: - - def reset(self, seed=None, options=None): - - - super().reset(seed=seed) - - self.x = self.sys.x0 - self.u = self.sys.ubar - self.t = 0.0 - - observation = self._get_obs() - info = self._get_info() - - return observation, info - - # %% - # Step - # ~~~~ - # - # The ``step`` method usually contains most of the logic of your - # environment. It accepts an ``action``, computes the state of the - # environment after applying that action and returns the 5-tuple - # ``(observation, reward, terminated, truncated, info)``. See - # :meth:`gymnasium.Env.step`. Once the new state of the environment has - # been computed, we can check whether it is a terminal state and we set - # ``done`` accordingly. Since we are using sparse binary rewards in - # ``GridWorldEnv``, computing ``reward`` is trivial once we know - # ``done``.To gather ``observation`` and ``info``, we can again make - # use of ``_get_obs`` and ``_get_info``: - - def step(self, u): - - - # Derivatives - dx = self.sys.f( self.x , self.u , self.t ) - - # Euler integration - x_new = self.x + dx * self.dt - t_new = self.t + self.dt - - # Cost function - r = -self.sys.cost_function.g( self.x , self.u , self.t ) - - terminated = self.t > self.tf - - truncated = not self.sys.isavalidstate( x_new ) - - y = self._get_obs() - info = self._get_info() - - # Memory update - self.x = x_new - self.t = t_new - self.u = u - - if self.render_mode == "human": - self._render_frame() - - return y, r, terminated, truncated, info - - - -# %% -# Rendering -# ~~~~~~~~~ -# -# Here, we are using PyGame for rendering. A similar approach to rendering -# is used in many environments that are included with Gymnasium and you -# can use it as a skeleton for your own environments: - - def render(self): - if self.render_mode == "rgb_array": - return self._render_frame() - - def _render_frame(self): - - pass \ No newline at end of file diff --git a/pyro/tools/sys2gym.py b/pyro/tools/sys2gym.py new file mode 100644 index 00000000..5d962ec4 --- /dev/null +++ b/pyro/tools/sys2gym.py @@ -0,0 +1,156 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Created on Fri Mar 1 14:08:03 2024 + +@author: alex +""" + +import numpy as np +import matplotlib.pyplot as plt + +import gymnasium as gym +from gymnasium import spaces + +################################################################# +# Create a Gym Environment from a Pyro System +################################################################# + + +class Sys2Gym(gym.Env): + + metadata = {"render_modes": ["human"]} + + ################################################################# + def __init__(self, sys, dt=0.05, tf=10.0, t0=0.0, render_mode=None): + + self.observation_space = spaces.Box(sys.x_lb, sys.x_ub) + self.action_space = spaces.Box(sys.u_lb, sys.u_ub) + + self.sys = sys + self.dt = dt + + self.tf = tf # For truncation of episodes + self.render_mode = render_mode + + # Memory + self.x = sys.x0 + self.u = sys.ubar + self.t = t0 + + if self.render_mode == "human": + + self.animator = self.sys.get_animator() + self.animator.show_plus(self.x, self.u, self.t) + plt.pause(0.001) + + ################################################################# + def _get_obs(self): + + y = self.sys.h(self.x, self.u, self.t) + + return y + + ################################################################# + def _get_info(self): + + return {"state": self.x, "action": self.u} + + ################################################################# + def reset(self, seed=None, options=None): + + super().reset(seed=seed) + + self.x = np.random.uniform(self.sys.x_lb, self.sys.x_ub) + self.u = self.sys.ubar + self.t = 0.0 + + observation = self._get_obs() + info = self._get_info() + + return observation, info + + ################################################################# + def step(self, u): + + u = np.clip(u, self.sys.u_lb, self.sys.u_ub) + x = self.x + t = self.t + dt = self.dt + + # Derivatives + dx = self.sys.f(x, u, t) + + # Euler integration + x_new = x + dx * dt + t_new = t + dt + + # Reward = negative of cost function + r = -self.sys.cost_function.g(x, u, t) + + # Termination of episodes + terminated = t_new > self.tf + + # Truncation of episodes if out of bounds + truncated = not self.sys.isavalidstate(x_new) + + # Memory update + self.x = x_new + self.t = t_new + self.u = u + + # Observation + y = self._get_obs() + + # Info + info = self._get_info() + + if self.render_mode == "human": + self._render_frame() + + return y, r, terminated, truncated, info + + ################################################################# + def render(self): + if self.render_mode == "rgb_array": + return self._render_frame() + + ################################################################# + def _render_frame(self): + + self.animator.show_plus_update(self.x, self.u, self.t) + plt.pause(0.001) + + +""" +################################################################# +################## Main ######## +################################################################# +""" + + +if __name__ == "__main__": + """MAIN TEST""" + + from pyro.dynamic import pendulum + + from stable_baselines3 import PPO + + sys = pendulum.SinglePendulum() + + gym_env = Sys2Gym(sys, render_mode="human") + + model = PPO("MlpPolicy", gym_env, verbose=1) + model.learn(total_timesteps=1000) + + episodes = 10 + for episode in range(episodes): + y, info = gym_env.reset() + terminated = False + truncated = False + + print("\n Episode:", episode) + while not (terminated or truncated): + u, _states = model.predict(y) + y, r, terminated, truncated, info = gym_env.step(u) + print("t=", gym_env.t, "x=", gym_env.x, "u=", gym_env.u, "r=", r) From feaaf122ebaac42a51ca47ad88db5c9e113a32aa Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Sat, 2 Mar 2024 11:24:01 -0500 Subject: [PATCH 63/93] hello world first working PPO pyro --- dev/gym/{pendulum_gym.py => pendulum_gym_reproduction.py} | 4 ++-- dev/gym/{pendulum.py => pendulum_ppo_example.py} | 0 2 files changed, 2 insertions(+), 2 deletions(-) rename dev/gym/{pendulum_gym.py => pendulum_gym_reproduction.py} (98%) rename dev/gym/{pendulum.py => pendulum_ppo_example.py} (100%) diff --git a/dev/gym/pendulum_gym.py b/dev/gym/pendulum_gym_reproduction.py similarity index 98% rename from dev/gym/pendulum_gym.py rename to dev/gym/pendulum_gym_reproduction.py index 20b8c8a7..be69afe2 100644 --- a/dev/gym/pendulum_gym.py +++ b/dev/gym/pendulum_gym_reproduction.py @@ -175,9 +175,9 @@ def step(self, u): # Cost function r = -self.sys.cost_function.g(x, u, t) - terminated = t > self.tf + terminated = False # t > self.tf - truncated = not self.sys.isavalidstate(x_new) + truncated = False #not self.sys.isavalidstate(x_new) # Memory update self.x = x_new diff --git a/dev/gym/pendulum.py b/dev/gym/pendulum_ppo_example.py similarity index 100% rename from dev/gym/pendulum.py rename to dev/gym/pendulum_ppo_example.py From 79fb9afbdfe5570025275115b91bc9b5a3458ac3 Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Sun, 3 Mar 2024 06:48:54 -0500 Subject: [PATCH 64/93] learn put not the exact same performance as the original gym --- dev/gym/pendulum_gym_reproduction.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dev/gym/pendulum_gym_reproduction.py b/dev/gym/pendulum_gym_reproduction.py index be69afe2..7a55d98e 100644 --- a/dev/gym/pendulum_gym_reproduction.py +++ b/dev/gym/pendulum_gym_reproduction.py @@ -177,7 +177,7 @@ def step(self, u): terminated = False # t > self.tf - truncated = False #not self.sys.isavalidstate(x_new) + truncated = t > self.tf #False #not self.sys.isavalidstate(x_new) # Memory update self.x = x_new @@ -237,7 +237,7 @@ def _render_frame(self): gym_env = SysEnv(sys, dt=0.05, render_mode=None) model = PPO("MlpPolicy", gym_env, verbose=1) -model.learn(total_timesteps=250000) +model.learn(total_timesteps=2500000) # model.learn(total_timesteps=1000) gym_env = SysEnv(sys, render_mode="human") From 2691214255c5a8a948e8b5ad65d65531b444d8f3 Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Sun, 3 Mar 2024 09:35:06 -0500 Subject: [PATCH 65/93] exact same behavior from gym pendulum with pyro pendulum object --- dev/gym/pendulum_gym_reproduction.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/dev/gym/pendulum_gym_reproduction.py b/dev/gym/pendulum_gym_reproduction.py index 7a55d98e..23fa58bb 100644 --- a/dev/gym/pendulum_gym_reproduction.py +++ b/dev/gym/pendulum_gym_reproduction.py @@ -216,14 +216,20 @@ def _render_frame(self): sys = pendulum.InvertedPendulum() +# Setting physical parameter to reflect the gym environment + # Physical parameters sys.gravity = 10.0 sys.m1 = 1.0 -sys.lc1 = 1.0 +sys.l1 = 1.0 +sys.lc1 = 0.5 * sys.l1 +sys.I1 = (1.0 / 12.0) * sys.m1 * sys.l1 ** 2 + +sys.l_domain = 2 * sys.l1 # graphical domain # Min/max state and control inputs -sys.x_ub = np.array([+2 * np.pi, +8]) -sys.x_lb = np.array([-2 * np.pi, -8]) +sys.x_ub = np.array([+np.pi, +8]) +sys.x_lb = np.array([-np.pi, -8]) sys.u_ub = np.array([+2.0]) sys.u_lb = np.array([-2.0]) @@ -237,7 +243,7 @@ def _render_frame(self): gym_env = SysEnv(sys, dt=0.05, render_mode=None) model = PPO("MlpPolicy", gym_env, verbose=1) -model.learn(total_timesteps=2500000) +model.learn(total_timesteps=250000) # model.learn(total_timesteps=1000) gym_env = SysEnv(sys, render_mode="human") From b7efb0d5454491a4ff8a49a79ccd049dbe830a5b Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Mon, 4 Mar 2024 06:40:19 -0500 Subject: [PATCH 66/93] testing state feedback as observations --- dev/gym/pendulum_gym_reproduction.py | 9 +- dev/gym/pendulum_gym_test.py | 332 +++++++++++++++++++++++++++ 2 files changed, 337 insertions(+), 4 deletions(-) create mode 100644 dev/gym/pendulum_gym_test.py diff --git a/dev/gym/pendulum_gym_reproduction.py b/dev/gym/pendulum_gym_reproduction.py index 23fa58bb..5d608105 100644 --- a/dev/gym/pendulum_gym_reproduction.py +++ b/dev/gym/pendulum_gym_reproduction.py @@ -175,9 +175,9 @@ def step(self, u): # Cost function r = -self.sys.cost_function.g(x, u, t) - terminated = False # t > self.tf + terminated = False # t > self.tf - truncated = t > self.tf #False #not self.sys.isavalidstate(x_new) + truncated = t > self.tf # False #not self.sys.isavalidstate(x_new) # Memory update self.x = x_new @@ -223,9 +223,9 @@ def _render_frame(self): sys.m1 = 1.0 sys.l1 = 1.0 sys.lc1 = 0.5 * sys.l1 -sys.I1 = (1.0 / 12.0) * sys.m1 * sys.l1 ** 2 +sys.I1 = (1.0 / 12.0) * sys.m1 * sys.l1**2 -sys.l_domain = 2 * sys.l1 # graphical domain +sys.l_domain = 2 * sys.l1 # graphical domain # Min/max state and control inputs sys.x_ub = np.array([+np.pi, +8]) @@ -244,6 +244,7 @@ def _render_frame(self): model = PPO("MlpPolicy", gym_env, verbose=1) model.learn(total_timesteps=250000) +model.learn(total_timesteps=2500000) # model.learn(total_timesteps=1000) gym_env = SysEnv(sys, render_mode="human") diff --git a/dev/gym/pendulum_gym_test.py b/dev/gym/pendulum_gym_test.py new file mode 100644 index 00000000..9b8b4c7f --- /dev/null +++ b/dev/gym/pendulum_gym_test.py @@ -0,0 +1,332 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Created on Fri Mar 1 14:08:03 2024 + +@author: alex +""" + +import numpy as np +import matplotlib.pyplot as plt + +import gymnasium as gym +from gymnasium import spaces + +from pyro.dynamic import pendulum +from pyro.control import controller + +from stable_baselines3 import PPO +from stable_baselines3.common.env_util import make_vec_env + +# %% +# Declaration and Initialization +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# Our custom environment will inherit from the abstract class +# ``gymnasium.Env``. You shouldn’t forget to add the ``metadata`` +# attribute to your class. There, you should specify the render-modes that +# are supported by your environment (e.g. ``"human"``, ``"rgb_array"``, +# ``"ansi"``) and the framerate at which your environment should be +# rendered. Every environment should support ``None`` as render-mode; you +# don’t need to add it in the metadata. In ``GridWorldEnv``, we will +# support the modes “rgb_array” and “human” and render at 4 FPS. +# +# The ``__init__`` method of our environment will accept the integer +# ``size``, that determines the size of the square grid. We will set up +# some variables for rendering and define ``self.observation_space`` and +# ``self.action_space``. In our case, observations should provide +# information about the location of the agent and target on the +# 2-dimensional grid. We will choose to represent observations in the form +# of dictionaries with keys ``"agent"`` and ``"target"``. An observation +# may look like ``{"agent": array([1, 0]), "target": array([0, 3])}``. +# Since we have 4 actions in our environment (“right”, “up”, “left”, +# “down”), we will use ``Discrete(4)`` as an action space. Here is the +# declaration of ``GridWorldEnv`` and the implementation of ``__init__``: + + +class SysEnv(gym.Env): + + def __init__(self, sys, dt=0.1, tf=10.0, render_mode=None): + + # x-y ouputs + # y_ub = np.array([+1, +1, sys.x_ub[1]]) + # y_lb = np.array([-1, -1, sys.x_lb[1]]) + + self.observation_space = spaces.Box(sys.x_lb, sys.x_lb) + self.action_space = spaces.Box(sys.u_lb, sys.u_ub) + + self.sys = sys + self.dt = dt + + self.tf = tf + self.render_mode = render_mode + + # Memory + self.x = sys.x0 + self.u = sys.ubar + self.t = 0.0 + + if self.render_mode == "human": + + self.animator = self.sys.get_animator() + self.animator.show_plus(self.x, self.u, self.t) + plt.pause(0.001) + + # %% + # Constructing Observations From Environment States + # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # + # Since we will need to compute observations both in ``reset`` and + # ``step``, it is often convenient to have a (private) method ``_get_obs`` + # that translates the environment’s state into an observation. However,ƒ + # this is not mandatory and you may as well compute observations in + # ``reset`` and ``step`` separately:ƒ + + def _get_obs(self): + + theta = self.x[0] + thetadot = self.x[1] + + y = np.array([np.cos(theta), np.sin(theta), thetadot], dtype=np.float32) + + y = self.x + + return y + + # %% + # We can also implement a similar method for the auxiliary information + # that is returned by ``step`` and ``reset``. In our case, we would like + # to provide the manhattan distance between the agent and the target: + + def _get_info(self): + + return {"state": self.x, "action": self.u} + + # %% + # Reset + # ~~~~~ + # + # The ``reset`` method will be called to initiate a new episode. You may + # assume that the ``step`` method will not be called before ``reset`` has + # been called. Moreover, ``reset`` should be called whenever a done signal + # has been issued. Users may pass the ``seed`` keyword to ``reset`` to + # initialize any random number generator that is used by the environment + # to a deterministic state. It is recommended to use the random number + # generator ``self.np_random`` that is provided by the environment’s base + # class, ``gymnasium.Env``. If you only use this RNG, you do not need to + # worry much about seeding, *but you need to remember to call + # ``super().reset(seed=seed)``* to make sure that ``gymnasium.Env`` + # correctly seeds the RNG. Once this is done, we can randomly set the + # state of our environment. In our case, we randomly choose the agent’s + # location and the random sample target positions, until it does not + # coincide with the agent’s position. + # + # The ``reset`` method should return a tuple of the initial observation + # and some auxiliary information. We can use the methods ``_get_obs`` and + # ``_get_info`` that we implemented earlier for that: + + def reset(self, seed=None, options=None): + + super().reset(seed=seed) + + self.x = np.random.uniform(np.array([-np.pi, -1]), np.array([np.pi, 1])) + self.u = self.sys.ubar + self.t = 0.0 + + observation = self._get_obs() + info = self._get_info() + + return observation, info + + # %% + # Step + # ~~~~ + # + # The ``step`` method usually contains most of the logic of your + # environment. It accepts an ``action``, computes the state of the + # environment after applying that action and returns the 5-tuple + # ``(observation, reward, terminated, truncated, info)``. See + # :meth:`gymnasium.Env.step`. Once the new state of the environment has + # been computed, we can check whether it is a terminal state and we set + # ``done`` accordingly. Since we are using sparse binary rewards in + # ``GridWorldEnv``, computing ``reward`` is trivial once we know + # ``done``.To gather ``observation`` and ``info``, we can again make + # use of ``_get_obs`` and ``_get_info``: + + def step(self, u): + + u = np.clip(u, self.sys.u_lb, self.sys.u_ub) + x = self.x + t = self.t + dt = self.dt + + # Derivatives + dx = self.sys.f(x, u, t) + + # Euler integration + x_new = x + dx * dt + t_new = t + dt + + x_new[0] = self.angle_normalize(x_new[0]) + + # Sat speed --> I hate they do this in gym env + if x_new[1] > sys.x_ub[1]: + x_new[1] = sys.x_ub[1] + if x_new[1] < sys.x_lb[1]: + x_new[1] = sys.x_lb[1] + + # Cost function + r = -self.sys.cost_function.g(x, u, t) + + terminated = False # t > self.tf + + truncated = t > self.tf # False #not self.sys.isavalidstate(x_new) + + # Memory update + self.x = x_new + self.t = t_new + self.u = u + + y = self._get_obs() + info = self._get_info() + + if self.render_mode == "human": + self._render_frame() + + return y, r, terminated, truncated, info + + def angle_normalize(self, x): + + return ((x + np.pi) % (2 * np.pi)) - np.pi + + # %% + # Rendering + # ~~~~~~~~~ + # + # Here, we are using PyGame for rendering. A similar approach to rendering + # is used in many environments that are included with Gymnasium and you + # can use it as a skeleton for your own environments: + + def render(self): + if self.render_mode == "rgb_array": + return self._render_frame() + + def _render_frame(self): + + self.animator.show_plus_update(self.x, self.u, self.t) + plt.pause(0.001) + + +sys = pendulum.InvertedPendulum() + +# Setting physical parameter to reflect the gym environment + +# Physical parameters +sys.gravity = 10.0 +sys.m1 = 1.0 +sys.l1 = 1.0 +sys.lc1 = 0.5 * sys.l1 +sys.I1 = (1.0 / 12.0) * sys.m1 * sys.l1**2 + +sys.l_domain = 2 * sys.l1 # graphical domain + +# Min/max state and control inputs +sys.x_ub = np.array([+np.pi, +8]) +sys.x_lb = np.array([-np.pi, -8]) +sys.u_ub = np.array([+12.0]) +sys.u_lb = np.array([-12.0]) + +# Cost Function +# The reward function is defined as: r = -(theta2 + 0.1 * theta_dt2 + 0.001 * torque2) +sys.cost_function.xbar = np.array([0, 0]) # target +sys.cost_function.R[0, 0] = 0.001 +sys.cost_function.Q[0, 0] = 1.0 +sys.cost_function.Q[1, 1] = 0.1 + +gym_env = SysEnv(sys, dt=0.05, render_mode=None) + +model = PPO("MlpPolicy", gym_env, verbose=1) + + +class rl_controller(controller.StaticController): + + def __init__(self, model): + + controller.StaticController.__init__(self, 1, 1, 2) + self.model = model + + self.name = "PPO Controller" + + def c(self, y , r , t ): + + u, _states = self.model.predict(y) + + return u + +ctl = rl_controller(model) + +ctl.plot_control_law( sys=sys ,n = 100) +plt.show() +plt.pause(0.001) + +batches = 5 +for batch in range(batches): + + model.learn(total_timesteps=200000) + ctl.plot_control_law( sys=sys ,n = 100) + plt.show() + plt.pause(0.001) + +# model.learn(total_timesteps=2500000) +# model.learn(total_timesteps=1000) + +gym_env = SysEnv(sys, render_mode="human") +y, info = gym_env.reset() + +episodes = 10 +for episode in range(episodes): + y, info = gym_env.reset() + terminated = False + truncated = False + + print("\n Episode:", episode) + while not (terminated or truncated): + u, _states = model.predict(y) + y, r, terminated, truncated, info = gym_env.step(u) + print("t=", gym_env.t, "x=", gym_env.x, "u=", gym_env.u, "r=", r) + + + + +cl_sys = ctl + sys + +cl_sys.x0=np.array([-0.2, 0.0]) +cl_sys.compute_trajectory( tf=10.0, n=2000, solver="euler" ) +cl_sys.plot_trajectory('xu') +cl_sys.animate_simulation() + +from pyro.planning import discretizer +from pyro.planning import dynamicprogramming + +sys.x_ub = np.array([+2*np.pi, +8]) +sys.x_lb = np.array([-2*np.pi, -8]) + +# Discrete world +grid_sys = discretizer.GridDynamicSystem( sys , [201,201] , [11] ) + +# Cost Function +qcf = sys.cost_function + +# DP algo +dp = dynamicprogramming.DynamicProgrammingWithLookUpTable( grid_sys, qcf) + +dp.solve_bellman_equation( tol = 1) +#dp.solve_bellman_equation( tol = 1 , animate_cost2go = True ) +#dp.solve_bellman_equation( tol = 1 , animate_policy = True ) + +#dp.animate_cost2go( show = False , save = True ) +#dp.animate_policy( show = False , save = True ) + +dp.clean_infeasible_set() +dp.plot_cost2go_3D() +dp.plot_policy() \ No newline at end of file From c39311226cf25e81415ddd96f77c48413e3c9bf7 Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Mon, 4 Mar 2024 14:18:20 -0500 Subject: [PATCH 67/93] lunar lander test --- .gitignore | 1 + dev/gym/lunar_lander_ppo_example.py | 25 +++++++++++++++++++++++++ dev/gym/pendulum_gym_reproduction.py | 7 ++++--- dev/gym/pendulum_gym_test.py | 4 ++-- pyro/tools/sys2gym.py | 4 ++-- 5 files changed, 34 insertions(+), 7 deletions(-) create mode 100644 dev/gym/lunar_lander_ppo_example.py diff --git a/.gitignore b/.gitignore index 73551789..a80e6628 100644 --- a/.gitignore +++ b/.gitignore @@ -29,6 +29,7 @@ MANIFEST *.npy *.csv *.mat +*.zip # Output files # ############################ diff --git a/dev/gym/lunar_lander_ppo_example.py b/dev/gym/lunar_lander_ppo_example.py new file mode 100644 index 00000000..7fe8562e --- /dev/null +++ b/dev/gym/lunar_lander_ppo_example.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Created on Tue Feb 13 13:37:57 2024 + +@author: alex +""" + +import gymnasium as gym + +from stable_baselines3 import PPO +from stable_baselines3.common.env_util import make_vec_env + +# Parallel environments +env = gym.make("LunarLander-v2", render_mode="human") + +model = PPO("MlpPolicy", env, verbose=1) +model.learn(total_timesteps=250000) + +obs, info = env.reset() + +while True: + action, _states = model.predict(obs) + obs, rewards, dones, trunc, info = env.step(action) + env.render("human") \ No newline at end of file diff --git a/dev/gym/pendulum_gym_reproduction.py b/dev/gym/pendulum_gym_reproduction.py index 5d608105..b9b55bed 100644 --- a/dev/gym/pendulum_gym_reproduction.py +++ b/dev/gym/pendulum_gym_reproduction.py @@ -240,12 +240,13 @@ def _render_frame(self): sys.cost_function.Q[0, 0] = 1.0 sys.cost_function.Q[1, 1] = 0.1 -gym_env = SysEnv(sys, dt=0.05, render_mode=None) +# gym_env = SysEnv(sys, dt=0.05, render_mode=None) +gym_env = SysEnv(sys, render_mode="human") model = PPO("MlpPolicy", gym_env, verbose=1) model.learn(total_timesteps=250000) -model.learn(total_timesteps=2500000) -# model.learn(total_timesteps=1000) +# model.learn(total_timesteps=2500000) +# # model.learn(total_timesteps=1000) gym_env = SysEnv(sys, render_mode="human") y, info = gym_env.reset() diff --git a/dev/gym/pendulum_gym_test.py b/dev/gym/pendulum_gym_test.py index 9b8b4c7f..6dfac416 100644 --- a/dev/gym/pendulum_gym_test.py +++ b/dev/gym/pendulum_gym_test.py @@ -283,7 +283,7 @@ def c(self, y , r , t ): gym_env = SysEnv(sys, render_mode="human") y, info = gym_env.reset() -episodes = 10 +episodes = 3 for episode in range(episodes): y, info = gym_env.reset() terminated = False @@ -291,7 +291,7 @@ def c(self, y , r , t ): print("\n Episode:", episode) while not (terminated or truncated): - u, _states = model.predict(y) + u, _states = model.predict(y, deterministic=True) y, r, terminated, truncated, info = gym_env.step(u) print("t=", gym_env.t, "x=", gym_env.x, "u=", gym_env.u, "r=", r) diff --git a/pyro/tools/sys2gym.py b/pyro/tools/sys2gym.py index 5d962ec4..3563ea5f 100644 --- a/pyro/tools/sys2gym.py +++ b/pyro/tools/sys2gym.py @@ -89,10 +89,10 @@ def step(self, u): r = -self.sys.cost_function.g(x, u, t) # Termination of episodes - terminated = t_new > self.tf + terminated = False # Truncation of episodes if out of bounds - truncated = not self.sys.isavalidstate(x_new) + truncated = ( t_new > self.tf ) or ( not self.sys.isavalidstate(x_new) ) # Memory update self.x = x_new From 88c51af5595f11ca3bcce83da7817b8297ded1a7 Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Mon, 4 Mar 2024 15:01:16 -0500 Subject: [PATCH 68/93] sys2gym update --- dev/gym/lunar_lander_ppo_example.py | 3 +- pyro/tools/sys2gym.py | 100 +++++++++++++++++++--------- 2 files changed, 70 insertions(+), 33 deletions(-) diff --git a/dev/gym/lunar_lander_ppo_example.py b/dev/gym/lunar_lander_ppo_example.py index 7fe8562e..65581cc5 100644 --- a/dev/gym/lunar_lander_ppo_example.py +++ b/dev/gym/lunar_lander_ppo_example.py @@ -12,11 +12,12 @@ from stable_baselines3.common.env_util import make_vec_env # Parallel environments -env = gym.make("LunarLander-v2", render_mode="human") +env = gym.make("LunarLander-v2", render_mode=None) model = PPO("MlpPolicy", env, verbose=1) model.learn(total_timesteps=250000) +env = gym.make("LunarLander-v2", render_mode="human") obs, info = env.reset() while True: diff --git a/pyro/tools/sys2gym.py b/pyro/tools/sys2gym.py index 3563ea5f..588e8551 100644 --- a/pyro/tools/sys2gym.py +++ b/pyro/tools/sys2gym.py @@ -33,6 +33,8 @@ def __init__(self, sys, dt=0.05, tf=10.0, t0=0.0, render_mode=None): self.tf = tf # For truncation of episodes self.render_mode = render_mode + self.reset_mode = "random" + # Memory self.x = sys.x0 self.u = sys.ubar @@ -45,30 +47,39 @@ def __init__(self, sys, dt=0.05, tf=10.0, t0=0.0, render_mode=None): plt.pause(0.001) ################################################################# - def _get_obs(self): + def reset(self, seed=None, options=None): - y = self.sys.h(self.x, self.u, self.t) + if self.reset_mode == "random": - return y + super().reset(seed=seed) - ################################################################# - def _get_info(self): + self.x = self.np_random.uniform(self.sys.x_lb, self.sys.x_ub) + self.u = self.sys.ubar + self.t = 0.0 - return {"state": self.x, "action": self.u} + elif self.reset_mode == "noisy_x0": - ################################################################# - def reset(self, seed=None, options=None): + super().reset(seed=seed) - super().reset(seed=seed) + self.x = self.sys.x0 + 0.1 * self.np_random.uniform( + self.sys.x_lb, self.sys.x_ub + ) + self.u = self.sys.ubar + self.t = 0.0 - self.x = np.random.uniform(self.sys.x_lb, self.sys.x_ub) - self.u = self.sys.ubar - self.t = 0.0 + else: - observation = self._get_obs() - info = self._get_info() + self.x = self.sys.x0 + self.u = self.sys.ubar + self.t = 0.0 - return observation, info + # Observation + y = self.sys.h(self.x, self.u, self.t) + + # Info + info = {"state": self.x, "action": self.u} + + return y, info ################################################################# def step(self, u): @@ -92,7 +103,7 @@ def step(self, u): terminated = False # Truncation of episodes if out of bounds - truncated = ( t_new > self.tf ) or ( not self.sys.isavalidstate(x_new) ) + truncated = (t_new > self.tf) or (not self.sys.isavalidstate(x_new)) # Memory update self.x = x_new @@ -100,26 +111,22 @@ def step(self, u): self.u = u # Observation - y = self._get_obs() + y = self.sys.h(self.x, self.u, self.t) # Info - info = self._get_info() + info = {"state": self.x, "action": self.u} if self.render_mode == "human": - self._render_frame() + self.render() return y, r, terminated, truncated, info ################################################################# def render(self): - if self.render_mode == "rgb_array": - return self._render_frame() - ################################################################# - def _render_frame(self): - - self.animator.show_plus_update(self.x, self.u, self.t) - plt.pause(0.001) + if self.render_mode == "human": + self.animator.show_plus_update(self.x, self.u, self.t) + plt.pause(0.001) """ @@ -136,14 +143,43 @@ def _render_frame(self): from stable_baselines3 import PPO - sys = pendulum.SinglePendulum() + sys = pendulum.InvertedPendulum() - gym_env = Sys2Gym(sys, render_mode="human") + # Physical parameters + sys.gravity = 10.0 + sys.m1 = 1.0 + sys.l1 = 1.0 + sys.lc1 = 0.5 * sys.l1 + sys.I1 = (1.0 / 12.0) * sys.m1 * sys.l1**2 + + sys.l_domain = 2 * sys.l1 # graphical domain + + # Min/max state and control inputs + sys.x_ub = np.array([+3.0 * np.pi, +20]) + sys.x_lb = np.array([-3.0 * np.pi, -20]) + sys.u_ub = np.array([+2.0]) + sys.u_lb = np.array([-2.0]) + + # Cost Function + # The reward function is defined as: r = -(theta2 + 0.1 * theta_dt2 + 0.001 * torque2) + sys.cost_function.xbar = np.array([0, 0]) # target + sys.cost_function.R[0, 0] = 0.001 + sys.cost_function.Q[0, 0] = 1.0 + sys.cost_function.Q[1, 1] = 0.1 + + sys.x0 = np.array([ -np.pi, 0.0]) + + gym_env = Sys2Gym(sys, render_mode=None) + gym_env.reset_mode = "noisy_x0" model = PPO("MlpPolicy", gym_env, verbose=1) - model.learn(total_timesteps=1000) + model.learn(total_timesteps=10000) + + gym_env = Sys2Gym(sys, render_mode="human") + + gym_env.reset_mode = "x0" - episodes = 10 + episodes = 3 for episode in range(episodes): y, info = gym_env.reset() terminated = False @@ -151,6 +187,6 @@ def _render_frame(self): print("\n Episode:", episode) while not (terminated or truncated): - u, _states = model.predict(y) + u, _states = model.predict(y, deterministic=True) y, r, terminated, truncated, info = gym_env.step(u) - print("t=", gym_env.t, "x=", gym_env.x, "u=", gym_env.u, "r=", r) + # print("t=", gym_env.t, "x=", gym_env.x, "u=", gym_env.u, "r=", r) From 5c5a439ed3da9f2635a0c7c6419fe7f6fa9d65c4 Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Mon, 4 Mar 2024 15:58:16 -0500 Subject: [PATCH 69/93] added method to convert sys to env --- dev/gym/pendulum_gym_reproduction.py | 5 +- dev/gym/rocket_drone_tests.py | 48 ++++ .../gym/pendulum_gym_reproduction.py | 263 ++++++++++++++++++ pyro/dynamic/system.py | 26 ++ pyro/tools/sys2gym.py | 21 +- 5 files changed, 356 insertions(+), 7 deletions(-) create mode 100644 dev/gym/rocket_drone_tests.py create mode 100644 examples/demos_by_tool/gym/pendulum_gym_reproduction.py diff --git a/dev/gym/pendulum_gym_reproduction.py b/dev/gym/pendulum_gym_reproduction.py index b9b55bed..bd40ed64 100644 --- a/dev/gym/pendulum_gym_reproduction.py +++ b/dev/gym/pendulum_gym_reproduction.py @@ -240,8 +240,7 @@ def _render_frame(self): sys.cost_function.Q[0, 0] = 1.0 sys.cost_function.Q[1, 1] = 0.1 -# gym_env = SysEnv(sys, dt=0.05, render_mode=None) -gym_env = SysEnv(sys, render_mode="human") +gym_env = SysEnv(sys, dt=0.05, render_mode=None) model = PPO("MlpPolicy", gym_env, verbose=1) model.learn(total_timesteps=250000) @@ -261,4 +260,4 @@ def _render_frame(self): while not (terminated or truncated): u, _states = model.predict(y) y, r, terminated, truncated, info = gym_env.step(u) - print("t=", gym_env.t, "x=", gym_env.x, "u=", gym_env.u, "r=", r) + #print("t=", gym_env.t, "x=", gym_env.x, "u=", gym_env.u, "r=", r) diff --git a/dev/gym/rocket_drone_tests.py b/dev/gym/rocket_drone_tests.py new file mode 100644 index 00000000..5dd558a0 --- /dev/null +++ b/dev/gym/rocket_drone_tests.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Created on Sun Oct 3 08:27:06 2021 + +@author: alex +""" + +import numpy as np + +from pyro.dynamic.rocket import Rocket +from pyro.dynamic.drone import Drone2D + +from stable_baselines3 import PPO + +# Non-linear model +sys = Rocket() +sys = Drone2D() + +sys.x_ub = np.array([10, 10, 10, 10, 10, 10]) +sys.x_lb = -sys.x_ub + +sys.u_ub = np.array([100, 100]) +sys.u_lb = np.array([0, 0]) + +env = sys.convert_to_gymnasium() + +env.reset_mode = "noisy_x0" + +#env.render_mode = "human" + +model = PPO("MlpPolicy", env, verbose=1) +model.learn(total_timesteps=1000000) + +env = sys.convert_to_gymnasium() +env.render_mode = "human" +env.reset_mode = "noisy_x0" + +episodes = 10 +for episode in range(episodes): + y, info = env.reset() + terminated = False + truncated = False + + print("\n Episode:", episode) + while not (terminated or truncated): + u, _states = model.predict(y, deterministic=True) + y, r, terminated, truncated, info = env.step(u) diff --git a/examples/demos_by_tool/gym/pendulum_gym_reproduction.py b/examples/demos_by_tool/gym/pendulum_gym_reproduction.py new file mode 100644 index 00000000..bd40ed64 --- /dev/null +++ b/examples/demos_by_tool/gym/pendulum_gym_reproduction.py @@ -0,0 +1,263 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Created on Fri Mar 1 14:08:03 2024 + +@author: alex +""" + +import numpy as np +import matplotlib.pyplot as plt + +import gymnasium as gym +from gymnasium import spaces + +from pyro.dynamic import pendulum + +from stable_baselines3 import PPO +from stable_baselines3.common.env_util import make_vec_env + +# %% +# Declaration and Initialization +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# Our custom environment will inherit from the abstract class +# ``gymnasium.Env``. You shouldn’t forget to add the ``metadata`` +# attribute to your class. There, you should specify the render-modes that +# are supported by your environment (e.g. ``"human"``, ``"rgb_array"``, +# ``"ansi"``) and the framerate at which your environment should be +# rendered. Every environment should support ``None`` as render-mode; you +# don’t need to add it in the metadata. In ``GridWorldEnv``, we will +# support the modes “rgb_array” and “human” and render at 4 FPS. +# +# The ``__init__`` method of our environment will accept the integer +# ``size``, that determines the size of the square grid. We will set up +# some variables for rendering and define ``self.observation_space`` and +# ``self.action_space``. In our case, observations should provide +# information about the location of the agent and target on the +# 2-dimensional grid. We will choose to represent observations in the form +# of dictionaries with keys ``"agent"`` and ``"target"``. An observation +# may look like ``{"agent": array([1, 0]), "target": array([0, 3])}``. +# Since we have 4 actions in our environment (“right”, “up”, “left”, +# “down”), we will use ``Discrete(4)`` as an action space. Here is the +# declaration of ``GridWorldEnv`` and the implementation of ``__init__``: + + +class SysEnv(gym.Env): + + def __init__(self, sys, dt=0.1, tf=10.0, render_mode=None): + + # x-y ouputs + y_ub = np.array([+1, +1, sys.x_ub[1]]) + y_lb = np.array([-1, -1, sys.x_lb[1]]) + + self.observation_space = spaces.Box(y_lb, y_ub) + self.action_space = spaces.Box(sys.u_lb, sys.u_ub) + + self.sys = sys + self.dt = dt + + self.tf = tf + self.render_mode = render_mode + + # Memory + self.x = sys.x0 + self.u = sys.ubar + self.t = 0.0 + + if self.render_mode == "human": + + self.animator = self.sys.get_animator() + self.animator.show_plus(self.x, self.u, self.t) + plt.pause(0.001) + + # %% + # Constructing Observations From Environment States + # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # + # Since we will need to compute observations both in ``reset`` and + # ``step``, it is often convenient to have a (private) method ``_get_obs`` + # that translates the environment’s state into an observation. However,ƒ + # this is not mandatory and you may as well compute observations in + # ``reset`` and ``step`` separately:ƒ + + def _get_obs(self): + + theta = self.x[0] + thetadot = self.x[1] + + y = np.array([np.cos(theta), np.sin(theta), thetadot], dtype=np.float32) + + return y + + # %% + # We can also implement a similar method for the auxiliary information + # that is returned by ``step`` and ``reset``. In our case, we would like + # to provide the manhattan distance between the agent and the target: + + def _get_info(self): + + return {"state": self.x, "action": self.u} + + # %% + # Reset + # ~~~~~ + # + # The ``reset`` method will be called to initiate a new episode. You may + # assume that the ``step`` method will not be called before ``reset`` has + # been called. Moreover, ``reset`` should be called whenever a done signal + # has been issued. Users may pass the ``seed`` keyword to ``reset`` to + # initialize any random number generator that is used by the environment + # to a deterministic state. It is recommended to use the random number + # generator ``self.np_random`` that is provided by the environment’s base + # class, ``gymnasium.Env``. If you only use this RNG, you do not need to + # worry much about seeding, *but you need to remember to call + # ``super().reset(seed=seed)``* to make sure that ``gymnasium.Env`` + # correctly seeds the RNG. Once this is done, we can randomly set the + # state of our environment. In our case, we randomly choose the agent’s + # location and the random sample target positions, until it does not + # coincide with the agent’s position. + # + # The ``reset`` method should return a tuple of the initial observation + # and some auxiliary information. We can use the methods ``_get_obs`` and + # ``_get_info`` that we implemented earlier for that: + + def reset(self, seed=None, options=None): + + super().reset(seed=seed) + + self.x = np.random.uniform(np.array([-np.pi, -1]), np.array([np.pi, 1])) + self.u = self.sys.ubar + self.t = 0.0 + + observation = self._get_obs() + info = self._get_info() + + return observation, info + + # %% + # Step + # ~~~~ + # + # The ``step`` method usually contains most of the logic of your + # environment. It accepts an ``action``, computes the state of the + # environment after applying that action and returns the 5-tuple + # ``(observation, reward, terminated, truncated, info)``. See + # :meth:`gymnasium.Env.step`. Once the new state of the environment has + # been computed, we can check whether it is a terminal state and we set + # ``done`` accordingly. Since we are using sparse binary rewards in + # ``GridWorldEnv``, computing ``reward`` is trivial once we know + # ``done``.To gather ``observation`` and ``info``, we can again make + # use of ``_get_obs`` and ``_get_info``: + + def step(self, u): + + u = np.clip(u, self.sys.u_lb, self.sys.u_ub) + x = self.x + t = self.t + dt = self.dt + + # Derivatives + dx = self.sys.f(x, u, t) + + # Euler integration + x_new = x + dx * dt + t_new = t + dt + + x_new[0] = self.angle_normalize(x_new[0]) + + # Sat speed --> I hate they do this in gym env + if x_new[1] > sys.x_ub[1]: + x_new[1] = sys.x_ub[1] + if x_new[1] < sys.x_lb[1]: + x_new[1] = sys.x_lb[1] + + # Cost function + r = -self.sys.cost_function.g(x, u, t) + + terminated = False # t > self.tf + + truncated = t > self.tf # False #not self.sys.isavalidstate(x_new) + + # Memory update + self.x = x_new + self.t = t_new + self.u = u + + y = self._get_obs() + info = self._get_info() + + if self.render_mode == "human": + self._render_frame() + + return y, r, terminated, truncated, info + + def angle_normalize(self, x): + + return ((x + np.pi) % (2 * np.pi)) - np.pi + + # %% + # Rendering + # ~~~~~~~~~ + # + # Here, we are using PyGame for rendering. A similar approach to rendering + # is used in many environments that are included with Gymnasium and you + # can use it as a skeleton for your own environments: + + def render(self): + if self.render_mode == "rgb_array": + return self._render_frame() + + def _render_frame(self): + + self.animator.show_plus_update(self.x, self.u, self.t) + plt.pause(0.001) + + +sys = pendulum.InvertedPendulum() + +# Setting physical parameter to reflect the gym environment + +# Physical parameters +sys.gravity = 10.0 +sys.m1 = 1.0 +sys.l1 = 1.0 +sys.lc1 = 0.5 * sys.l1 +sys.I1 = (1.0 / 12.0) * sys.m1 * sys.l1**2 + +sys.l_domain = 2 * sys.l1 # graphical domain + +# Min/max state and control inputs +sys.x_ub = np.array([+np.pi, +8]) +sys.x_lb = np.array([-np.pi, -8]) +sys.u_ub = np.array([+2.0]) +sys.u_lb = np.array([-2.0]) + +# Cost Function +# The reward function is defined as: r = -(theta2 + 0.1 * theta_dt2 + 0.001 * torque2) +sys.cost_function.xbar = np.array([0, 0]) # target +sys.cost_function.R[0, 0] = 0.001 +sys.cost_function.Q[0, 0] = 1.0 +sys.cost_function.Q[1, 1] = 0.1 + +gym_env = SysEnv(sys, dt=0.05, render_mode=None) + +model = PPO("MlpPolicy", gym_env, verbose=1) +model.learn(total_timesteps=250000) +# model.learn(total_timesteps=2500000) +# # model.learn(total_timesteps=1000) + +gym_env = SysEnv(sys, render_mode="human") +y, info = gym_env.reset() + +episodes = 10 +for episode in range(episodes): + y, info = gym_env.reset() + terminated = False + truncated = False + + print("\n Episode:", episode) + while not (terminated or truncated): + u, _states = model.predict(y) + y, r, terminated, truncated, info = gym_env.step(u) + #print("t=", gym_env.t, "x=", gym_env.x, "u=", gym_env.u, "r=", r) diff --git a/pyro/dynamic/system.py b/pyro/dynamic/system.py index 242b88b1..0945d329 100644 --- a/pyro/dynamic/system.py +++ b/pyro/dynamic/system.py @@ -632,6 +632,32 @@ def animate_linearized_modes(self ): animations.append( ani ) return linearized_sys , animations + + + ############################# + def convert_to_gymnasium(self ): + """ + Create a gym environment from the system + + """ + + try: + + import gymnasium as gym + from gymnasium import spaces + from pyro.tools.sys2gym import Sys2Gym + + gym_sys = Sys2Gym( self ) + + return gym_sys + + except: + + raise ImportError('gym library is not installed') + + + + diff --git a/pyro/tools/sys2gym.py b/pyro/tools/sys2gym.py index 588e8551..09ee8a6b 100644 --- a/pyro/tools/sys2gym.py +++ b/pyro/tools/sys2gym.py @@ -34,17 +34,19 @@ def __init__(self, sys, dt=0.05, tf=10.0, t0=0.0, render_mode=None): self.render_mode = render_mode self.reset_mode = "random" + # Memory self.x = sys.x0 self.u = sys.ubar self.t = t0 + # Init + self.render_is_initiated = False + if self.render_mode == "human": - self.animator = self.sys.get_animator() - self.animator.show_plus(self.x, self.u, self.t) - plt.pause(0.001) + self.init_render() ################################################################# def reset(self, seed=None, options=None): @@ -120,11 +122,22 @@ def step(self, u): self.render() return y, r, terminated, truncated, info + + ################################################################# + def init_render(self): + + self.render_is_initiated = True + + self.animator = self.sys.get_animator() + self.animator.show_plus(self.x, self.u, self.t) + plt.pause(0.001) ################################################################# def render(self): if self.render_mode == "human": + if not self.render_is_initiated: + self.init_render() self.animator.show_plus_update(self.x, self.u, self.t) plt.pause(0.001) @@ -173,7 +186,7 @@ def render(self): gym_env.reset_mode = "noisy_x0" model = PPO("MlpPolicy", gym_env, verbose=1) - model.learn(total_timesteps=10000) + model.learn(total_timesteps=100000) gym_env = Sys2Gym(sys, render_mode="human") From edc913fbe9dbe6f943197887ece012d4548550f3 Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Tue, 5 Mar 2024 07:01:03 -0500 Subject: [PATCH 70/93] clean up rl tests --- dev/gym/lunar_lander_ppo_example.py | 26 - dev/gym/pendulum_dp_vs_ppo.py | 101 ++++ dev/gym/pendulum_gym_reproduction.py | 263 -------- dev/gym/pendulum_gym_test.py | 332 ---------- ...ocket_drone_tests.py => pyro_gym_tests.py} | 12 +- .../gym/pendulum_gym_reproduction.py | 263 -------- .../pendulum_with_PPO_baseline_gym_example.py | 0 ...lum_with_PPO_baseline_pyro_reproduction.py | 173 ++++++ pyro/dynamic/system.py | 568 ++++++++---------- 9 files changed, 547 insertions(+), 1191 deletions(-) delete mode 100644 dev/gym/lunar_lander_ppo_example.py create mode 100644 dev/gym/pendulum_dp_vs_ppo.py delete mode 100644 dev/gym/pendulum_gym_reproduction.py delete mode 100644 dev/gym/pendulum_gym_test.py rename dev/gym/{rocket_drone_tests.py => pyro_gym_tests.py} (78%) delete mode 100644 examples/demos_by_tool/gym/pendulum_gym_reproduction.py rename dev/gym/pendulum_ppo_example.py => examples/demos_by_tool/rl_with_stable_baseline3/pendulum_with_PPO_baseline_gym_example.py (100%) create mode 100644 examples/demos_by_tool/rl_with_stable_baseline3/pendulum_with_PPO_baseline_pyro_reproduction.py diff --git a/dev/gym/lunar_lander_ppo_example.py b/dev/gym/lunar_lander_ppo_example.py deleted file mode 100644 index 65581cc5..00000000 --- a/dev/gym/lunar_lander_ppo_example.py +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Created on Tue Feb 13 13:37:57 2024 - -@author: alex -""" - -import gymnasium as gym - -from stable_baselines3 import PPO -from stable_baselines3.common.env_util import make_vec_env - -# Parallel environments -env = gym.make("LunarLander-v2", render_mode=None) - -model = PPO("MlpPolicy", env, verbose=1) -model.learn(total_timesteps=250000) - -env = gym.make("LunarLander-v2", render_mode="human") -obs, info = env.reset() - -while True: - action, _states = model.predict(obs) - obs, rewards, dones, trunc, info = env.step(action) - env.render("human") \ No newline at end of file diff --git a/dev/gym/pendulum_dp_vs_ppo.py b/dev/gym/pendulum_dp_vs_ppo.py new file mode 100644 index 00000000..0759af05 --- /dev/null +++ b/dev/gym/pendulum_dp_vs_ppo.py @@ -0,0 +1,101 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Created on Fri Mar 1 14:08:03 2024 + +@author: alex +""" + +import numpy as np +import matplotlib.pyplot as plt + +from pyro.dynamic import pendulum +from pyro.control import controller +from stable_baselines3 import PPO +from stable_baselines3.common.env_util import make_vec_env + +sys = pendulum.InvertedPendulum() + +# Physical parameters +sys.gravity = 10.0 +sys.m1 = 1.0 +sys.l1 = 1.0 +sys.lc1 = 0.5 * sys.l1 +sys.I1 = (1.0 / 12.0) * sys.m1 * sys.l1**2 + +sys.l_domain = 2 * sys.l1 # graphical domain + +# Min/max state and control inputs +sys.x_ub = np.array([+2 * np.pi, +8]) +sys.x_lb = np.array([-2 * np.pi, -8]) +sys.u_ub = np.array([+8.0]) +sys.u_lb = np.array([-8.0]) + +# Cost Function +sys.cost_function.xbar = np.array([0, 0]) # target +sys.cost_function.R[0, 0] = 1.0 +sys.cost_function.Q[0, 0] = 1.0 +sys.cost_function.Q[1, 1] = 0.0 + +# DP solution +from pyro.planning import discretizer +from pyro.planning import dynamicprogramming + +grid_sys = discretizer.GridDynamicSystem(sys, [201, 201], [11]) + +dp = dynamicprogramming.DynamicProgrammingWithLookUpTable(grid_sys, sys.cost_function) + +dp.solve_bellman_equation(tol=0.01) +dp.clean_infeasible_set() +dp.plot_policy() +dp_ctl = dp.get_lookup_table_controller() + +cl_sys = dp_ctl + sys + +cl_sys.x0 = np.array([-3.0, 0.0]) +cl_sys.compute_trajectory(tf=10.0, n=20000, solver="euler") +cl_sys.plot_trajectory("xu") +cl_sys.animate_simulation() + + +# Learning +env = sys.convert_to_gymnasium(dt=0.05, render_mode=None) +env.reset_mode = "noisy_x0" +model = PPO("MlpPolicy", env, verbose=1) +class rl_controller(controller.StaticController): + + def __init__(self, model): + + controller.StaticController.__init__(self, 1, 1, 2) + self.model = model + + self.name = "PPO Controller" + + def c(self, y, r, t): + + u, _states = self.model.predict(y, deterministic=True) + + return u + +ppo_ctl = rl_controller(model) + +ppo_ctl.plot_control_law(sys=sys, n=100) +plt.show() +plt.pause(0.001) + +n_time_steps = 250000 +batches = 5 +env.render_mode = None +for batch in range(batches): + model.learn(total_timesteps=int(n_time_steps / batches)) + ppo_ctl.plot_control_law(sys=sys, n=100) + plt.show() + plt.pause(0.001) + +# Animating rl closed-loop +cl_sys = ppo_ctl + sys + +cl_sys.x0 = np.array([-4.0, -0.5]) +cl_sys.compute_trajectory(tf=10.0, n=10000, solver="euler") +cl_sys.plot_trajectory("xu") +cl_sys.animate_simulation() diff --git a/dev/gym/pendulum_gym_reproduction.py b/dev/gym/pendulum_gym_reproduction.py deleted file mode 100644 index bd40ed64..00000000 --- a/dev/gym/pendulum_gym_reproduction.py +++ /dev/null @@ -1,263 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Created on Fri Mar 1 14:08:03 2024 - -@author: alex -""" - -import numpy as np -import matplotlib.pyplot as plt - -import gymnasium as gym -from gymnasium import spaces - -from pyro.dynamic import pendulum - -from stable_baselines3 import PPO -from stable_baselines3.common.env_util import make_vec_env - -# %% -# Declaration and Initialization -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# Our custom environment will inherit from the abstract class -# ``gymnasium.Env``. You shouldn’t forget to add the ``metadata`` -# attribute to your class. There, you should specify the render-modes that -# are supported by your environment (e.g. ``"human"``, ``"rgb_array"``, -# ``"ansi"``) and the framerate at which your environment should be -# rendered. Every environment should support ``None`` as render-mode; you -# don’t need to add it in the metadata. In ``GridWorldEnv``, we will -# support the modes “rgb_array” and “human” and render at 4 FPS. -# -# The ``__init__`` method of our environment will accept the integer -# ``size``, that determines the size of the square grid. We will set up -# some variables for rendering and define ``self.observation_space`` and -# ``self.action_space``. In our case, observations should provide -# information about the location of the agent and target on the -# 2-dimensional grid. We will choose to represent observations in the form -# of dictionaries with keys ``"agent"`` and ``"target"``. An observation -# may look like ``{"agent": array([1, 0]), "target": array([0, 3])}``. -# Since we have 4 actions in our environment (“right”, “up”, “left”, -# “down”), we will use ``Discrete(4)`` as an action space. Here is the -# declaration of ``GridWorldEnv`` and the implementation of ``__init__``: - - -class SysEnv(gym.Env): - - def __init__(self, sys, dt=0.1, tf=10.0, render_mode=None): - - # x-y ouputs - y_ub = np.array([+1, +1, sys.x_ub[1]]) - y_lb = np.array([-1, -1, sys.x_lb[1]]) - - self.observation_space = spaces.Box(y_lb, y_ub) - self.action_space = spaces.Box(sys.u_lb, sys.u_ub) - - self.sys = sys - self.dt = dt - - self.tf = tf - self.render_mode = render_mode - - # Memory - self.x = sys.x0 - self.u = sys.ubar - self.t = 0.0 - - if self.render_mode == "human": - - self.animator = self.sys.get_animator() - self.animator.show_plus(self.x, self.u, self.t) - plt.pause(0.001) - - # %% - # Constructing Observations From Environment States - # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - # - # Since we will need to compute observations both in ``reset`` and - # ``step``, it is often convenient to have a (private) method ``_get_obs`` - # that translates the environment’s state into an observation. However,ƒ - # this is not mandatory and you may as well compute observations in - # ``reset`` and ``step`` separately:ƒ - - def _get_obs(self): - - theta = self.x[0] - thetadot = self.x[1] - - y = np.array([np.cos(theta), np.sin(theta), thetadot], dtype=np.float32) - - return y - - # %% - # We can also implement a similar method for the auxiliary information - # that is returned by ``step`` and ``reset``. In our case, we would like - # to provide the manhattan distance between the agent and the target: - - def _get_info(self): - - return {"state": self.x, "action": self.u} - - # %% - # Reset - # ~~~~~ - # - # The ``reset`` method will be called to initiate a new episode. You may - # assume that the ``step`` method will not be called before ``reset`` has - # been called. Moreover, ``reset`` should be called whenever a done signal - # has been issued. Users may pass the ``seed`` keyword to ``reset`` to - # initialize any random number generator that is used by the environment - # to a deterministic state. It is recommended to use the random number - # generator ``self.np_random`` that is provided by the environment’s base - # class, ``gymnasium.Env``. If you only use this RNG, you do not need to - # worry much about seeding, *but you need to remember to call - # ``super().reset(seed=seed)``* to make sure that ``gymnasium.Env`` - # correctly seeds the RNG. Once this is done, we can randomly set the - # state of our environment. In our case, we randomly choose the agent’s - # location and the random sample target positions, until it does not - # coincide with the agent’s position. - # - # The ``reset`` method should return a tuple of the initial observation - # and some auxiliary information. We can use the methods ``_get_obs`` and - # ``_get_info`` that we implemented earlier for that: - - def reset(self, seed=None, options=None): - - super().reset(seed=seed) - - self.x = np.random.uniform(np.array([-np.pi, -1]), np.array([np.pi, 1])) - self.u = self.sys.ubar - self.t = 0.0 - - observation = self._get_obs() - info = self._get_info() - - return observation, info - - # %% - # Step - # ~~~~ - # - # The ``step`` method usually contains most of the logic of your - # environment. It accepts an ``action``, computes the state of the - # environment after applying that action and returns the 5-tuple - # ``(observation, reward, terminated, truncated, info)``. See - # :meth:`gymnasium.Env.step`. Once the new state of the environment has - # been computed, we can check whether it is a terminal state and we set - # ``done`` accordingly. Since we are using sparse binary rewards in - # ``GridWorldEnv``, computing ``reward`` is trivial once we know - # ``done``.To gather ``observation`` and ``info``, we can again make - # use of ``_get_obs`` and ``_get_info``: - - def step(self, u): - - u = np.clip(u, self.sys.u_lb, self.sys.u_ub) - x = self.x - t = self.t - dt = self.dt - - # Derivatives - dx = self.sys.f(x, u, t) - - # Euler integration - x_new = x + dx * dt - t_new = t + dt - - x_new[0] = self.angle_normalize(x_new[0]) - - # Sat speed --> I hate they do this in gym env - if x_new[1] > sys.x_ub[1]: - x_new[1] = sys.x_ub[1] - if x_new[1] < sys.x_lb[1]: - x_new[1] = sys.x_lb[1] - - # Cost function - r = -self.sys.cost_function.g(x, u, t) - - terminated = False # t > self.tf - - truncated = t > self.tf # False #not self.sys.isavalidstate(x_new) - - # Memory update - self.x = x_new - self.t = t_new - self.u = u - - y = self._get_obs() - info = self._get_info() - - if self.render_mode == "human": - self._render_frame() - - return y, r, terminated, truncated, info - - def angle_normalize(self, x): - - return ((x + np.pi) % (2 * np.pi)) - np.pi - - # %% - # Rendering - # ~~~~~~~~~ - # - # Here, we are using PyGame for rendering. A similar approach to rendering - # is used in many environments that are included with Gymnasium and you - # can use it as a skeleton for your own environments: - - def render(self): - if self.render_mode == "rgb_array": - return self._render_frame() - - def _render_frame(self): - - self.animator.show_plus_update(self.x, self.u, self.t) - plt.pause(0.001) - - -sys = pendulum.InvertedPendulum() - -# Setting physical parameter to reflect the gym environment - -# Physical parameters -sys.gravity = 10.0 -sys.m1 = 1.0 -sys.l1 = 1.0 -sys.lc1 = 0.5 * sys.l1 -sys.I1 = (1.0 / 12.0) * sys.m1 * sys.l1**2 - -sys.l_domain = 2 * sys.l1 # graphical domain - -# Min/max state and control inputs -sys.x_ub = np.array([+np.pi, +8]) -sys.x_lb = np.array([-np.pi, -8]) -sys.u_ub = np.array([+2.0]) -sys.u_lb = np.array([-2.0]) - -# Cost Function -# The reward function is defined as: r = -(theta2 + 0.1 * theta_dt2 + 0.001 * torque2) -sys.cost_function.xbar = np.array([0, 0]) # target -sys.cost_function.R[0, 0] = 0.001 -sys.cost_function.Q[0, 0] = 1.0 -sys.cost_function.Q[1, 1] = 0.1 - -gym_env = SysEnv(sys, dt=0.05, render_mode=None) - -model = PPO("MlpPolicy", gym_env, verbose=1) -model.learn(total_timesteps=250000) -# model.learn(total_timesteps=2500000) -# # model.learn(total_timesteps=1000) - -gym_env = SysEnv(sys, render_mode="human") -y, info = gym_env.reset() - -episodes = 10 -for episode in range(episodes): - y, info = gym_env.reset() - terminated = False - truncated = False - - print("\n Episode:", episode) - while not (terminated or truncated): - u, _states = model.predict(y) - y, r, terminated, truncated, info = gym_env.step(u) - #print("t=", gym_env.t, "x=", gym_env.x, "u=", gym_env.u, "r=", r) diff --git a/dev/gym/pendulum_gym_test.py b/dev/gym/pendulum_gym_test.py deleted file mode 100644 index 6dfac416..00000000 --- a/dev/gym/pendulum_gym_test.py +++ /dev/null @@ -1,332 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Created on Fri Mar 1 14:08:03 2024 - -@author: alex -""" - -import numpy as np -import matplotlib.pyplot as plt - -import gymnasium as gym -from gymnasium import spaces - -from pyro.dynamic import pendulum -from pyro.control import controller - -from stable_baselines3 import PPO -from stable_baselines3.common.env_util import make_vec_env - -# %% -# Declaration and Initialization -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# Our custom environment will inherit from the abstract class -# ``gymnasium.Env``. You shouldn’t forget to add the ``metadata`` -# attribute to your class. There, you should specify the render-modes that -# are supported by your environment (e.g. ``"human"``, ``"rgb_array"``, -# ``"ansi"``) and the framerate at which your environment should be -# rendered. Every environment should support ``None`` as render-mode; you -# don’t need to add it in the metadata. In ``GridWorldEnv``, we will -# support the modes “rgb_array” and “human” and render at 4 FPS. -# -# The ``__init__`` method of our environment will accept the integer -# ``size``, that determines the size of the square grid. We will set up -# some variables for rendering and define ``self.observation_space`` and -# ``self.action_space``. In our case, observations should provide -# information about the location of the agent and target on the -# 2-dimensional grid. We will choose to represent observations in the form -# of dictionaries with keys ``"agent"`` and ``"target"``. An observation -# may look like ``{"agent": array([1, 0]), "target": array([0, 3])}``. -# Since we have 4 actions in our environment (“right”, “up”, “left”, -# “down”), we will use ``Discrete(4)`` as an action space. Here is the -# declaration of ``GridWorldEnv`` and the implementation of ``__init__``: - - -class SysEnv(gym.Env): - - def __init__(self, sys, dt=0.1, tf=10.0, render_mode=None): - - # x-y ouputs - # y_ub = np.array([+1, +1, sys.x_ub[1]]) - # y_lb = np.array([-1, -1, sys.x_lb[1]]) - - self.observation_space = spaces.Box(sys.x_lb, sys.x_lb) - self.action_space = spaces.Box(sys.u_lb, sys.u_ub) - - self.sys = sys - self.dt = dt - - self.tf = tf - self.render_mode = render_mode - - # Memory - self.x = sys.x0 - self.u = sys.ubar - self.t = 0.0 - - if self.render_mode == "human": - - self.animator = self.sys.get_animator() - self.animator.show_plus(self.x, self.u, self.t) - plt.pause(0.001) - - # %% - # Constructing Observations From Environment States - # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - # - # Since we will need to compute observations both in ``reset`` and - # ``step``, it is often convenient to have a (private) method ``_get_obs`` - # that translates the environment’s state into an observation. However,ƒ - # this is not mandatory and you may as well compute observations in - # ``reset`` and ``step`` separately:ƒ - - def _get_obs(self): - - theta = self.x[0] - thetadot = self.x[1] - - y = np.array([np.cos(theta), np.sin(theta), thetadot], dtype=np.float32) - - y = self.x - - return y - - # %% - # We can also implement a similar method for the auxiliary information - # that is returned by ``step`` and ``reset``. In our case, we would like - # to provide the manhattan distance between the agent and the target: - - def _get_info(self): - - return {"state": self.x, "action": self.u} - - # %% - # Reset - # ~~~~~ - # - # The ``reset`` method will be called to initiate a new episode. You may - # assume that the ``step`` method will not be called before ``reset`` has - # been called. Moreover, ``reset`` should be called whenever a done signal - # has been issued. Users may pass the ``seed`` keyword to ``reset`` to - # initialize any random number generator that is used by the environment - # to a deterministic state. It is recommended to use the random number - # generator ``self.np_random`` that is provided by the environment’s base - # class, ``gymnasium.Env``. If you only use this RNG, you do not need to - # worry much about seeding, *but you need to remember to call - # ``super().reset(seed=seed)``* to make sure that ``gymnasium.Env`` - # correctly seeds the RNG. Once this is done, we can randomly set the - # state of our environment. In our case, we randomly choose the agent’s - # location and the random sample target positions, until it does not - # coincide with the agent’s position. - # - # The ``reset`` method should return a tuple of the initial observation - # and some auxiliary information. We can use the methods ``_get_obs`` and - # ``_get_info`` that we implemented earlier for that: - - def reset(self, seed=None, options=None): - - super().reset(seed=seed) - - self.x = np.random.uniform(np.array([-np.pi, -1]), np.array([np.pi, 1])) - self.u = self.sys.ubar - self.t = 0.0 - - observation = self._get_obs() - info = self._get_info() - - return observation, info - - # %% - # Step - # ~~~~ - # - # The ``step`` method usually contains most of the logic of your - # environment. It accepts an ``action``, computes the state of the - # environment after applying that action and returns the 5-tuple - # ``(observation, reward, terminated, truncated, info)``. See - # :meth:`gymnasium.Env.step`. Once the new state of the environment has - # been computed, we can check whether it is a terminal state and we set - # ``done`` accordingly. Since we are using sparse binary rewards in - # ``GridWorldEnv``, computing ``reward`` is trivial once we know - # ``done``.To gather ``observation`` and ``info``, we can again make - # use of ``_get_obs`` and ``_get_info``: - - def step(self, u): - - u = np.clip(u, self.sys.u_lb, self.sys.u_ub) - x = self.x - t = self.t - dt = self.dt - - # Derivatives - dx = self.sys.f(x, u, t) - - # Euler integration - x_new = x + dx * dt - t_new = t + dt - - x_new[0] = self.angle_normalize(x_new[0]) - - # Sat speed --> I hate they do this in gym env - if x_new[1] > sys.x_ub[1]: - x_new[1] = sys.x_ub[1] - if x_new[1] < sys.x_lb[1]: - x_new[1] = sys.x_lb[1] - - # Cost function - r = -self.sys.cost_function.g(x, u, t) - - terminated = False # t > self.tf - - truncated = t > self.tf # False #not self.sys.isavalidstate(x_new) - - # Memory update - self.x = x_new - self.t = t_new - self.u = u - - y = self._get_obs() - info = self._get_info() - - if self.render_mode == "human": - self._render_frame() - - return y, r, terminated, truncated, info - - def angle_normalize(self, x): - - return ((x + np.pi) % (2 * np.pi)) - np.pi - - # %% - # Rendering - # ~~~~~~~~~ - # - # Here, we are using PyGame for rendering. A similar approach to rendering - # is used in many environments that are included with Gymnasium and you - # can use it as a skeleton for your own environments: - - def render(self): - if self.render_mode == "rgb_array": - return self._render_frame() - - def _render_frame(self): - - self.animator.show_plus_update(self.x, self.u, self.t) - plt.pause(0.001) - - -sys = pendulum.InvertedPendulum() - -# Setting physical parameter to reflect the gym environment - -# Physical parameters -sys.gravity = 10.0 -sys.m1 = 1.0 -sys.l1 = 1.0 -sys.lc1 = 0.5 * sys.l1 -sys.I1 = (1.0 / 12.0) * sys.m1 * sys.l1**2 - -sys.l_domain = 2 * sys.l1 # graphical domain - -# Min/max state and control inputs -sys.x_ub = np.array([+np.pi, +8]) -sys.x_lb = np.array([-np.pi, -8]) -sys.u_ub = np.array([+12.0]) -sys.u_lb = np.array([-12.0]) - -# Cost Function -# The reward function is defined as: r = -(theta2 + 0.1 * theta_dt2 + 0.001 * torque2) -sys.cost_function.xbar = np.array([0, 0]) # target -sys.cost_function.R[0, 0] = 0.001 -sys.cost_function.Q[0, 0] = 1.0 -sys.cost_function.Q[1, 1] = 0.1 - -gym_env = SysEnv(sys, dt=0.05, render_mode=None) - -model = PPO("MlpPolicy", gym_env, verbose=1) - - -class rl_controller(controller.StaticController): - - def __init__(self, model): - - controller.StaticController.__init__(self, 1, 1, 2) - self.model = model - - self.name = "PPO Controller" - - def c(self, y , r , t ): - - u, _states = self.model.predict(y) - - return u - -ctl = rl_controller(model) - -ctl.plot_control_law( sys=sys ,n = 100) -plt.show() -plt.pause(0.001) - -batches = 5 -for batch in range(batches): - - model.learn(total_timesteps=200000) - ctl.plot_control_law( sys=sys ,n = 100) - plt.show() - plt.pause(0.001) - -# model.learn(total_timesteps=2500000) -# model.learn(total_timesteps=1000) - -gym_env = SysEnv(sys, render_mode="human") -y, info = gym_env.reset() - -episodes = 3 -for episode in range(episodes): - y, info = gym_env.reset() - terminated = False - truncated = False - - print("\n Episode:", episode) - while not (terminated or truncated): - u, _states = model.predict(y, deterministic=True) - y, r, terminated, truncated, info = gym_env.step(u) - print("t=", gym_env.t, "x=", gym_env.x, "u=", gym_env.u, "r=", r) - - - - -cl_sys = ctl + sys - -cl_sys.x0=np.array([-0.2, 0.0]) -cl_sys.compute_trajectory( tf=10.0, n=2000, solver="euler" ) -cl_sys.plot_trajectory('xu') -cl_sys.animate_simulation() - -from pyro.planning import discretizer -from pyro.planning import dynamicprogramming - -sys.x_ub = np.array([+2*np.pi, +8]) -sys.x_lb = np.array([-2*np.pi, -8]) - -# Discrete world -grid_sys = discretizer.GridDynamicSystem( sys , [201,201] , [11] ) - -# Cost Function -qcf = sys.cost_function - -# DP algo -dp = dynamicprogramming.DynamicProgrammingWithLookUpTable( grid_sys, qcf) - -dp.solve_bellman_equation( tol = 1) -#dp.solve_bellman_equation( tol = 1 , animate_cost2go = True ) -#dp.solve_bellman_equation( tol = 1 , animate_policy = True ) - -#dp.animate_cost2go( show = False , save = True ) -#dp.animate_policy( show = False , save = True ) - -dp.clean_infeasible_set() -dp.plot_cost2go_3D() -dp.plot_policy() \ No newline at end of file diff --git a/dev/gym/rocket_drone_tests.py b/dev/gym/pyro_gym_tests.py similarity index 78% rename from dev/gym/rocket_drone_tests.py rename to dev/gym/pyro_gym_tests.py index 5dd558a0..af2fa2ab 100644 --- a/dev/gym/rocket_drone_tests.py +++ b/dev/gym/pyro_gym_tests.py @@ -10,18 +10,20 @@ from pyro.dynamic.rocket import Rocket from pyro.dynamic.drone import Drone2D +from pyro.dynamic.boat import Boat2D from stable_baselines3 import PPO # Non-linear model sys = Rocket() sys = Drone2D() +sys = Boat2D() -sys.x_ub = np.array([10, 10, 10, 10, 10, 10]) -sys.x_lb = -sys.x_ub +# sys.x_ub = np.array([10, 10, 10, 10, 10, 10]) +# sys.x_lb = -sys.x_ub -sys.u_ub = np.array([100, 100]) -sys.u_lb = np.array([0, 0]) +# sys.u_ub = np.array([1000, 100]) +# sys.u_lb = np.array([0, -100]) env = sys.convert_to_gymnasium() @@ -30,7 +32,7 @@ #env.render_mode = "human" model = PPO("MlpPolicy", env, verbose=1) -model.learn(total_timesteps=1000000) +model.learn(total_timesteps=250000) env = sys.convert_to_gymnasium() env.render_mode = "human" diff --git a/examples/demos_by_tool/gym/pendulum_gym_reproduction.py b/examples/demos_by_tool/gym/pendulum_gym_reproduction.py deleted file mode 100644 index bd40ed64..00000000 --- a/examples/demos_by_tool/gym/pendulum_gym_reproduction.py +++ /dev/null @@ -1,263 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Created on Fri Mar 1 14:08:03 2024 - -@author: alex -""" - -import numpy as np -import matplotlib.pyplot as plt - -import gymnasium as gym -from gymnasium import spaces - -from pyro.dynamic import pendulum - -from stable_baselines3 import PPO -from stable_baselines3.common.env_util import make_vec_env - -# %% -# Declaration and Initialization -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# Our custom environment will inherit from the abstract class -# ``gymnasium.Env``. You shouldn’t forget to add the ``metadata`` -# attribute to your class. There, you should specify the render-modes that -# are supported by your environment (e.g. ``"human"``, ``"rgb_array"``, -# ``"ansi"``) and the framerate at which your environment should be -# rendered. Every environment should support ``None`` as render-mode; you -# don’t need to add it in the metadata. In ``GridWorldEnv``, we will -# support the modes “rgb_array” and “human” and render at 4 FPS. -# -# The ``__init__`` method of our environment will accept the integer -# ``size``, that determines the size of the square grid. We will set up -# some variables for rendering and define ``self.observation_space`` and -# ``self.action_space``. In our case, observations should provide -# information about the location of the agent and target on the -# 2-dimensional grid. We will choose to represent observations in the form -# of dictionaries with keys ``"agent"`` and ``"target"``. An observation -# may look like ``{"agent": array([1, 0]), "target": array([0, 3])}``. -# Since we have 4 actions in our environment (“right”, “up”, “left”, -# “down”), we will use ``Discrete(4)`` as an action space. Here is the -# declaration of ``GridWorldEnv`` and the implementation of ``__init__``: - - -class SysEnv(gym.Env): - - def __init__(self, sys, dt=0.1, tf=10.0, render_mode=None): - - # x-y ouputs - y_ub = np.array([+1, +1, sys.x_ub[1]]) - y_lb = np.array([-1, -1, sys.x_lb[1]]) - - self.observation_space = spaces.Box(y_lb, y_ub) - self.action_space = spaces.Box(sys.u_lb, sys.u_ub) - - self.sys = sys - self.dt = dt - - self.tf = tf - self.render_mode = render_mode - - # Memory - self.x = sys.x0 - self.u = sys.ubar - self.t = 0.0 - - if self.render_mode == "human": - - self.animator = self.sys.get_animator() - self.animator.show_plus(self.x, self.u, self.t) - plt.pause(0.001) - - # %% - # Constructing Observations From Environment States - # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - # - # Since we will need to compute observations both in ``reset`` and - # ``step``, it is often convenient to have a (private) method ``_get_obs`` - # that translates the environment’s state into an observation. However,ƒ - # this is not mandatory and you may as well compute observations in - # ``reset`` and ``step`` separately:ƒ - - def _get_obs(self): - - theta = self.x[0] - thetadot = self.x[1] - - y = np.array([np.cos(theta), np.sin(theta), thetadot], dtype=np.float32) - - return y - - # %% - # We can also implement a similar method for the auxiliary information - # that is returned by ``step`` and ``reset``. In our case, we would like - # to provide the manhattan distance between the agent and the target: - - def _get_info(self): - - return {"state": self.x, "action": self.u} - - # %% - # Reset - # ~~~~~ - # - # The ``reset`` method will be called to initiate a new episode. You may - # assume that the ``step`` method will not be called before ``reset`` has - # been called. Moreover, ``reset`` should be called whenever a done signal - # has been issued. Users may pass the ``seed`` keyword to ``reset`` to - # initialize any random number generator that is used by the environment - # to a deterministic state. It is recommended to use the random number - # generator ``self.np_random`` that is provided by the environment’s base - # class, ``gymnasium.Env``. If you only use this RNG, you do not need to - # worry much about seeding, *but you need to remember to call - # ``super().reset(seed=seed)``* to make sure that ``gymnasium.Env`` - # correctly seeds the RNG. Once this is done, we can randomly set the - # state of our environment. In our case, we randomly choose the agent’s - # location and the random sample target positions, until it does not - # coincide with the agent’s position. - # - # The ``reset`` method should return a tuple of the initial observation - # and some auxiliary information. We can use the methods ``_get_obs`` and - # ``_get_info`` that we implemented earlier for that: - - def reset(self, seed=None, options=None): - - super().reset(seed=seed) - - self.x = np.random.uniform(np.array([-np.pi, -1]), np.array([np.pi, 1])) - self.u = self.sys.ubar - self.t = 0.0 - - observation = self._get_obs() - info = self._get_info() - - return observation, info - - # %% - # Step - # ~~~~ - # - # The ``step`` method usually contains most of the logic of your - # environment. It accepts an ``action``, computes the state of the - # environment after applying that action and returns the 5-tuple - # ``(observation, reward, terminated, truncated, info)``. See - # :meth:`gymnasium.Env.step`. Once the new state of the environment has - # been computed, we can check whether it is a terminal state and we set - # ``done`` accordingly. Since we are using sparse binary rewards in - # ``GridWorldEnv``, computing ``reward`` is trivial once we know - # ``done``.To gather ``observation`` and ``info``, we can again make - # use of ``_get_obs`` and ``_get_info``: - - def step(self, u): - - u = np.clip(u, self.sys.u_lb, self.sys.u_ub) - x = self.x - t = self.t - dt = self.dt - - # Derivatives - dx = self.sys.f(x, u, t) - - # Euler integration - x_new = x + dx * dt - t_new = t + dt - - x_new[0] = self.angle_normalize(x_new[0]) - - # Sat speed --> I hate they do this in gym env - if x_new[1] > sys.x_ub[1]: - x_new[1] = sys.x_ub[1] - if x_new[1] < sys.x_lb[1]: - x_new[1] = sys.x_lb[1] - - # Cost function - r = -self.sys.cost_function.g(x, u, t) - - terminated = False # t > self.tf - - truncated = t > self.tf # False #not self.sys.isavalidstate(x_new) - - # Memory update - self.x = x_new - self.t = t_new - self.u = u - - y = self._get_obs() - info = self._get_info() - - if self.render_mode == "human": - self._render_frame() - - return y, r, terminated, truncated, info - - def angle_normalize(self, x): - - return ((x + np.pi) % (2 * np.pi)) - np.pi - - # %% - # Rendering - # ~~~~~~~~~ - # - # Here, we are using PyGame for rendering. A similar approach to rendering - # is used in many environments that are included with Gymnasium and you - # can use it as a skeleton for your own environments: - - def render(self): - if self.render_mode == "rgb_array": - return self._render_frame() - - def _render_frame(self): - - self.animator.show_plus_update(self.x, self.u, self.t) - plt.pause(0.001) - - -sys = pendulum.InvertedPendulum() - -# Setting physical parameter to reflect the gym environment - -# Physical parameters -sys.gravity = 10.0 -sys.m1 = 1.0 -sys.l1 = 1.0 -sys.lc1 = 0.5 * sys.l1 -sys.I1 = (1.0 / 12.0) * sys.m1 * sys.l1**2 - -sys.l_domain = 2 * sys.l1 # graphical domain - -# Min/max state and control inputs -sys.x_ub = np.array([+np.pi, +8]) -sys.x_lb = np.array([-np.pi, -8]) -sys.u_ub = np.array([+2.0]) -sys.u_lb = np.array([-2.0]) - -# Cost Function -# The reward function is defined as: r = -(theta2 + 0.1 * theta_dt2 + 0.001 * torque2) -sys.cost_function.xbar = np.array([0, 0]) # target -sys.cost_function.R[0, 0] = 0.001 -sys.cost_function.Q[0, 0] = 1.0 -sys.cost_function.Q[1, 1] = 0.1 - -gym_env = SysEnv(sys, dt=0.05, render_mode=None) - -model = PPO("MlpPolicy", gym_env, verbose=1) -model.learn(total_timesteps=250000) -# model.learn(total_timesteps=2500000) -# # model.learn(total_timesteps=1000) - -gym_env = SysEnv(sys, render_mode="human") -y, info = gym_env.reset() - -episodes = 10 -for episode in range(episodes): - y, info = gym_env.reset() - terminated = False - truncated = False - - print("\n Episode:", episode) - while not (terminated or truncated): - u, _states = model.predict(y) - y, r, terminated, truncated, info = gym_env.step(u) - #print("t=", gym_env.t, "x=", gym_env.x, "u=", gym_env.u, "r=", r) diff --git a/dev/gym/pendulum_ppo_example.py b/examples/demos_by_tool/rl_with_stable_baseline3/pendulum_with_PPO_baseline_gym_example.py similarity index 100% rename from dev/gym/pendulum_ppo_example.py rename to examples/demos_by_tool/rl_with_stable_baseline3/pendulum_with_PPO_baseline_gym_example.py diff --git a/examples/demos_by_tool/rl_with_stable_baseline3/pendulum_with_PPO_baseline_pyro_reproduction.py b/examples/demos_by_tool/rl_with_stable_baseline3/pendulum_with_PPO_baseline_pyro_reproduction.py new file mode 100644 index 00000000..9a6d18b2 --- /dev/null +++ b/examples/demos_by_tool/rl_with_stable_baseline3/pendulum_with_PPO_baseline_pyro_reproduction.py @@ -0,0 +1,173 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Created on Fri Mar 1 14:08:03 2024 + +@author: alex +""" + +import numpy as np +import matplotlib.pyplot as plt + +import gymnasium as gym +from gymnasium import spaces + +from pyro.dynamic import pendulum + +from stable_baselines3 import PPO +from stable_baselines3.common.env_util import make_vec_env + + +class SysEnv(gym.Env): + + def __init__(self, sys, dt=0.1, tf=10.0, render_mode=None): + + # x-y ouputs + y_ub = np.array([+1, +1, sys.x_ub[1]]) + y_lb = np.array([-1, -1, sys.x_lb[1]]) + + self.observation_space = spaces.Box(y_lb, y_ub) + self.action_space = spaces.Box(sys.u_lb, sys.u_ub) + + self.sys = sys + self.dt = dt + + self.tf = tf + self.render_mode = render_mode + + # Memory + self.x = sys.x0 + self.u = sys.ubar + self.t = 0.0 + + if self.render_mode == "human": + + self.animator = self.sys.get_animator() + self.animator.show_plus(self.x, self.u, self.t) + plt.pause(0.001) + + def _get_obs(self): + + theta = self.x[0] + thetadot = self.x[1] + + y = np.array([np.cos(theta), np.sin(theta), thetadot], dtype=np.float32) + + return y + + def _get_info(self): + + return {"state": self.x, "action": self.u} + + def reset(self, seed=None, options=None): + + super().reset(seed=seed) + + self.x = np.random.uniform(np.array([-np.pi, -1]), np.array([np.pi, 1])) + self.u = self.sys.ubar + self.t = 0.0 + + observation = self._get_obs() + info = self._get_info() + + return observation, info + + def step(self, u): + + u = np.clip(u, self.sys.u_lb, self.sys.u_ub) + x = self.x + t = self.t + dt = self.dt + + # Derivatives + dx = self.sys.f(x, u, t) + + # Euler integration + x_new = x + dx * dt + t_new = t + dt + + x_new[0] = self.angle_normalize(x_new[0]) + + # Sat speed --> I hate they do this in gym env + if x_new[1] > sys.x_ub[1]: + x_new[1] = sys.x_ub[1] + if x_new[1] < sys.x_lb[1]: + x_new[1] = sys.x_lb[1] + + # Cost function + r = -self.sys.cost_function.g(x, u, t) + + terminated = False # t > self.tf + + truncated = t > self.tf # False #not self.sys.isavalidstate(x_new) + + # Memory update + self.x = x_new + self.t = t_new + self.u = u + + y = self._get_obs() + info = self._get_info() + + if self.render_mode == "human": + self._render_frame() + + return y, r, terminated, truncated, info + + def angle_normalize(self, x): + + return ((x + np.pi) % (2 * np.pi)) - np.pi + + def render(self): + if self.render_mode == "human": + return self._render_frame() + + def _render_frame(self): + + self.animator.show_plus_update(self.x, self.u, self.t) + plt.pause(0.001) + + +sys = pendulum.InvertedPendulum() + +# Setting physical parameter to reflect the gym environment + +# Physical parameters +sys.gravity = 10.0 +sys.m1 = 1.0 +sys.l1 = 1.0 +sys.lc1 = 0.5 * sys.l1 +sys.I1 = (1.0 / 12.0) * sys.m1 * sys.l1**2 + +sys.l_domain = 2 * sys.l1 # graphical domain + +# Min/max state and control inputs +sys.x_ub = np.array([+np.pi, +8]) +sys.x_lb = np.array([-np.pi, -8]) +sys.u_ub = np.array([+2.0]) +sys.u_lb = np.array([-2.0]) + +# Cost Function +# The reward function is defined as: r = -(theta2 + 0.1 * theta_dt2 + 0.001 * torque2) +sys.cost_function.xbar = np.array([0, 0]) # target +sys.cost_function.R[0, 0] = 0.001 +sys.cost_function.Q[0, 0] = 1.0 +sys.cost_function.Q[1, 1] = 0.1 + +# Learning +gym_env = SysEnv(sys, dt=0.05, render_mode=None) +model = PPO("MlpPolicy", gym_env, verbose=1) +model.learn(total_timesteps=250000) + +# Animation of the pendulum with the learned policy +gym_env = SysEnv(sys, render_mode="human") +y, info = gym_env.reset() +episodes = 10 +for episode in range(episodes): + y, info = gym_env.reset() + terminated = False + truncated = False + print("\n Episode:", episode) + while not (terminated or truncated): + u, _states = model.predict(y, deterministic=True) + y, r, terminated, truncated, info = gym_env.step(u) \ No newline at end of file diff --git a/pyro/dynamic/system.py b/pyro/dynamic/system.py index 0945d329..7536f9eb 100644 --- a/pyro/dynamic/system.py +++ b/pyro/dynamic/system.py @@ -11,29 +11,29 @@ from pyro.analysis import phaseanalysis from pyro.analysis import graphical from pyro.analysis import costfunction - -''' + +""" ############################################################################### -''' +""" ############################################################################### class ContinuousDynamicSystem: - """ + """ Mother class for continuous dynamical systems - + Main functions --------------------------------------------- dx = f( x , u , t ) : dynamic equation - - optionnal: + + optionnal: y = h( x , u , t ) : output equation (by default y = x ) u = t2u( t ) : time-dependent input signal (by default u = ubar) q = xut2q( x , u , t ) : get configuration vector (by default q = x ) - + graphic output: lines = forward_kinematic_lines( q ) : draw the system based on q - + Signal and Dimentions ---------------------------------------------- x : state vector n x 1 @@ -41,306 +41,297 @@ class ContinuousDynamicSystem: t : time 1 x 1 y : output vector p x 1 q : configuration vector ? x 1 (dimension is not imposed) - + """ + ########################################################################### # The two following functions needs to be implemented by child classes ########################################################################### - + ############################ - def __init__(self, n = 1, m = 1, p = 1): - """ + def __init__(self, n=1, m=1, p=1): + """ The __init__ method of the Mother class can be used to fill-in default labels, units, bounds, and base values. """ - + ############################# # Parameters ############################# # Dimensions - self.n = n - self.m = m + self.n = n + self.m = m self.p = p - + # Labels - self.name = 'ContinuousDynamicSystem' - self.state_label = [] - self.input_label = [] + self.name = "ContinuousDynamicSystem" + self.state_label = [] + self.input_label = [] self.output_label = [] - + # Units - self.state_units = [] - self.input_units = [] + self.state_units = [] + self.input_units = [] self.output_units = [] - + # Default Label and units for i in range(n): - self.state_label.append('State '+str(i)) - self.state_units.append('') + self.state_label.append("State " + str(i)) + self.state_units.append("") for i in range(m): - self.input_label.append('Input '+str(i)) - self.input_units.append('') + self.input_label.append("Input " + str(i)) + self.input_units.append("") for i in range(p): - self.output_label.append('Output '+str(i)) - self.output_units.append('') - + self.output_label.append("Output " + str(i)) + self.output_units.append("") + # Default state and input domain - self.x_ub = np.zeros(self.n) +10 # States Upper Bounds - self.x_lb = np.zeros(self.n) -10 # States Lower Bounds - self.u_ub = np.zeros(self.m) +1 # Control Upper Bounds - self.u_lb = np.zeros(self.m) -1 # Control Lower Bounds - - # Default state and inputs values + self.x_ub = np.zeros(self.n) + 10 # States Upper Bounds + self.x_lb = np.zeros(self.n) - 10 # States Lower Bounds + self.u_ub = np.zeros(self.m) + 1 # Control Upper Bounds + self.u_lb = np.zeros(self.m) - 1 # Control Lower Bounds + + # Default state and inputs values self.xbar = np.zeros(self.n) self.ubar = np.zeros(self.m) self.tbar = 0 - + # Plot params - self.domain = [ (-10,10) , (-10,10) , (-10,10) ] - self.linestyle = 'o-' - self.linestyle_plus = '--' - self.linescolor = 'b' - self.linescolor_plus = 'r' - self.lines_plus = True # Bool to active second graphic outpout - self.is_3d = False # Use 2d plot by default - + self.domain = [(-10, 10), (-10, 10), (-10, 10)] + self.linestyle = "o-" + self.linestyle_plus = "--" + self.linescolor = "b" + self.linescolor_plus = "r" + self.lines_plus = True # Bool to active second graphic outpout + self.is_3d = False # Use 2d plot by default + ################################ # Variables ################################ - + # Initial value for simulations - self.x0 = np.zeros(self.n) - + self.x0 = np.zeros(self.n) + # Result of last simulation self.traj = None - + # Cost function for evaluation # default is a quadratic cost function with diag Q and R matrices self.cost_function = costfunction.QuadraticCostFunction.from_sys(self) - ############################# - def f( self , x , u , t ): - """ + def f(self, x, u, t): + """ Continuous time foward dynamics evaluation dx = f(x,u,t) - + INPUTS x : state vector n x 1 u : control inputs vector m x 1 t : time 1 x 1 - + OUPUTS dx : state derivative vector n x 1 - + """ - - dx = np.zeros(self.n) # State derivative vector - + + dx = np.zeros(self.n) # State derivative vector + ################################################ # Place holder: put the equations of motion here raise NotImplementedError ################################################ - + return dx - - + ########################################################################### # The following functions can be overloaded when necessary by child classes ########################################################################### - + ############################# - def h( self , x , u , t ): - """ + def h(self, x, u, t): + """ Output fonction y = h(x,u,t) - + INPUTS x : state vector n x 1 u : control inputs vector m x 1 t : time 1 x 1 - + OUTPUTS y : output vector p x 1 - + """ - - #y = np.zeros(self.p) # Output vector - - y = x # default output is all states - + + # y = np.zeros(self.p) # Output vector + + y = x # default output is all states + return y - + ############################# - def t2u( self , t ): - """ + def t2u(self, t): + """ Reference signal fonction u = t2u(t) - + INPUTS t : time 1 x 1 - + OUTPUTS u : control inputs vector m x 1 - + Defaul is a constant signal equal to self.ubar, can be overloaded - with a more complexe reference signal time-function - + with a more complexe reference signal time-function + """ - + # Default is a constant signal u = self.ubar - + return u - - + ########################################################################### # Basic domain checks, overload if something more complex is needed ########################################################################### - + ############################# - def isavalidstate(self , x ): - """ check if x is in the state domain """ + def isavalidstate(self, x): + """check if x is in the state domain""" ans = False for i in range(self.n): - ans = ans or ( x[i] < self.x_lb[i] ) - ans = ans or ( x[i] > self.x_ub[i] ) - - return not(ans) - + ans = ans or (x[i] < self.x_lb[i]) + ans = ans or (x[i] > self.x_ub[i]) + + return not (ans) + ############################# - def isavalidinput(self , x , u): - """ check if u is in the control inputs domain given x """ + def isavalidinput(self, x, u): + """check if u is in the control inputs domain given x""" ans = False for i in range(self.m): - ans = ans or ( u[i] < self.u_lb[i] ) - ans = ans or ( u[i] > self.u_ub[i] ) - - return not(ans) - - + ans = ans or (u[i] < self.u_lb[i]) + ans = ans or (u[i] > self.u_ub[i]) + + return not (ans) + ########################################################################### # Place holder graphical output, overload with specific graph output ########################################################################### - + ############################# - def xut2q( self, x , u , t ): - """ Compute configuration variables ( q vector ) """ - + def xut2q(self, x, u, t): + """Compute configuration variables ( q vector )""" + # default is q = x - + return x - - + ########################################################################### - def forward_kinematic_domain(self, q ): - """ Set the domain range for ploting, can be static or dynamic """ - + def forward_kinematic_domain(self, q): + """Set the domain range for ploting, can be static or dynamic""" + return self.domain - - + ########################################################################### - def forward_kinematic_lines(self, q ): - """ - Compute points p = [x;y;z] positions given config q + def forward_kinematic_lines(self, q): + """ + Compute points p = [x;y;z] positions given config q ---------------------------------------------------- - points of interest for ploting - + Outpus: lines_pts = [] : a list of array (n_pts x 3) for each lines - + """ - - lines_pts = [] # list of array (n_pts x 3) for each lines - + + lines_pts = [] # list of array (n_pts x 3) for each lines + ########################### # Your graphical code here ########################### - + # simple place holder for q_i in q: - pts = np.zeros(( 1 , 3 )) # array of 1 pts for the line - pts[0,0] = q_i # x cord of point 0 = q - lines_pts.append( pts ) # list of all arrays of pts - + pts = np.zeros((1, 3)) # array of 1 pts for the line + pts[0, 0] = q_i # x cord of point 0 = q + lines_pts.append(pts) # list of all arrays of pts + return lines_pts - - + ########################################################################### - def forward_kinematic_lines_plus(self, x , u , t ): - """ + def forward_kinematic_lines_plus(self, x, u, t): + """ Additionnal optionnal graphic output used in animations - + Compute points p = [x;y;z] positions given state x , u , t ------------------------------------------------------------- - additionnal points of interest for ploting - for instances forces arrows illustrating control inputs - + Outpus: lines_pts = [] : a list of array (n_pts x 3) for each lines - + """ - - lines_pts = [] # list of array (n_pts x 3) for each lines - + + lines_pts = [] # list of array (n_pts x 3) for each lines + ########################### # Your graphical code here ########################### - + # simple place holder for u_i in u: - pts = np.zeros(( 1 , 3 )) # array of 1 pts for the line - pts[0,0] = u_i # x cord of point 0 = u - lines_pts.append( pts ) # list of all arrays of pts - + pts = np.zeros((1, 3)) # array of 1 pts for the line + pts[0, 0] = u_i # x cord of point 0 = u + lines_pts.append(pts) # list of all arrays of pts + return lines_pts - - + ########################################################################### # No need to overwrite the following functions for custom dynamic systems ########################################################################### - + ############################# - def fsim( self, x , t = 0 ): - """ + def fsim(self, x, t=0): + """ Continuous time foward dynamics evaluation dx = f(x,t), inlcuding the internal reference input signal computation - + INPUTS x : state vector n x 1 t : time 1 x 1 - + OUPUTS dx : state derivative vector n x 1 - + """ - - u = self.t2u( t ) - dx = self.f( x, u, t) - + + u = self.t2u(t) + dx = self.f(x, u, t) + return dx - ############################# - def x_next( self , x , u , t = 0 , dt = 0.1 , steps = 1 ): - """ - Discrete time foward dynamics evaluation + def x_next(self, x, u, t=0, dt=0.1, steps=1): + """ + Discrete time foward dynamics evaluation ------------------------------------- - using Euler integration - + """ - - x_next = np.zeros(self.n) # k+1 State vector - + + x_next = np.zeros(self.n) # k+1 State vector + # Multiple integration steps for i in range(steps): - - x_next = self.f(x,u,t) * dt + x - + + x_next = self.f(x, u, t) * dt + x + # Multiple steps - x = x_next - + x = x_next + return x_next - - + ########################################################################### # Quick Analysis Shorcuts ########################################################################### @@ -370,41 +361,35 @@ def new_h(x, u, t): pass """ - - - + ############################# def get_plotter(self): - """ Return a Plotter object with param based on sys instance """ - + """Return a Plotter object with param based on sys instance""" + return graphical.TrajectoryPlotter(self) - - + ############################# def get_animator(self): - """ Return an Animator object with param based on sys instance """ - + """Return an Animator object with param based on sys instance""" + return graphical.Animator(self) - ############################# - def plot_phase_plane(self , x_axis = 0 , y_axis = 1 ): - """ + def plot_phase_plane(self, x_axis=0, y_axis=1): + """ Plot Phase Plane vector field of the system ------------------------------------------------ x_axis : index of state on x axis y_axis : index of state on y axis - + """ - pp = phaseanalysis.PhasePlot( self , x_axis , y_axis ) - + pp = phaseanalysis.PhasePlot(self, x_axis, y_axis) + pp.plot() - - + ############################# - def compute_trajectory( - self, tf=10, n=10001, solver='solve_ivt', **solver_args): + def compute_trajectory(self, tf=10, n=10001, solver="solve_ivt", **solver_args): """ Simulation of time evolution of the system ------------------------------------------------ @@ -419,24 +404,22 @@ def compute_trajectory( return self.traj - ############################# - def plot_trajectory(self, plot='x', **kwargs): + def plot_trajectory(self, plot="x", **kwargs): """ Plot time evolution of a simulation of this system ------------------------------------------------ note: will call compute_trajectory if no simulation data is present """ - + # Check if trajectory is already computed if self.traj == None: self.compute_trajectory() - + plotter = self.get_plotter() - - return plotter.plot( self.traj, plot, **kwargs) + return plotter.plot(self.traj, plot, **kwargs) ############################# def plot_phase_plane_trajectory(self, x_axis=0, y_axis=1): @@ -444,56 +427,51 @@ def plot_phase_plane_trajectory(self, x_axis=0, y_axis=1): Plot a trajectory in the Phase Plane --------------------------------------------------------------- note: will call compute_trajectory if no simulation data is present - + """ - + # Check is trajectory is already computed if self.traj == None: self.compute_trajectory() - + plotter = self.get_plotter() - - return plotter.phase_plane_trajectory( self.traj, x_axis , y_axis) + return plotter.phase_plane_trajectory(self.traj, x_axis, y_axis) ############################# - def plot_phase_plane_trajectory_3d(self , x_axis=0, y_axis=1, z_axis=2): + def plot_phase_plane_trajectory_3d(self, x_axis=0, y_axis=1, z_axis=2): """ Plot the trajectory in the Phase Plane --------------------------------------------------------------- note: will call compute_trajectory if no simulation data is present """ - + # Check is trajectory is already computed if self.traj == None: self.compute_trajectory() - + plotter = self.get_plotter() - - return plotter.phase_plane_trajectory_3d( - self.traj, x_axis , y_axis, z_axis) + return plotter.phase_plane_trajectory_3d(self.traj, x_axis, y_axis, z_axis) ############################################# - def show(self, q , x_axis = 0 , y_axis = 1 ): - """ Plot figure of configuration q """ - + def show(self, q, x_axis=0, y_axis=1): + """Plot figure of configuration q""" + ani = self.get_animator() - ani.x_axis = x_axis - ani.y_axis = y_axis - - ani.show( q ) - - + ani.x_axis = x_axis + ani.y_axis = y_axis + + ani.show(q) + ############################################# - def show3(self, q ): - """ Plot figure of configuration q """ - + def show3(self, q): + """Plot figure of configuration q""" + ani = self.get_animator() - - ani.show3( q ) - + + ani.show3(q) ############################## def animate_simulation(self, **kwargs): @@ -505,16 +483,15 @@ def animate_simulation(self, **kwargs): note: will call compute_trajectory if no simulation data is present """ - + # Check is trajectory is already computed if self.traj == None: self.compute_trajectory() - + ani = self.get_animator() - - return ani.animate_simulation( self.traj, **kwargs) - - + + return ani.animate_simulation(self.traj, **kwargs) + ############################## def generate_simulation_html_video(self, **kwargs): """ @@ -525,117 +502,109 @@ def generate_simulation_html_video(self, **kwargs): note: will call compute_trajectory if no simulation data is present """ - + # Check is trajectory is already computed if self.traj == None: self.compute_trajectory() - + animator = self.get_animator() - animator.animate_simulation( self.traj, show = False , **kwargs ) + animator.animate_simulation(self.traj, show=False, **kwargs) html_video = animator.ani.to_html5_video() - + return html_video - - + ############################# - def generate_mode_animation_html(self, i = 0 , is_3d = False ): + def generate_mode_animation_html(self, i=0, is_3d=False): """ Linearize and show eigen mode i with html output """ - + from pyro.dynamic.statespace import linearize - - # - ss = linearize( self ) - + + # + ss = linearize(self) + # Compute eigen decomposition ss.compute_eigen_modes() - + # Simulate one mode - traj = ss.compute_eigen_mode_traj( i ) - + traj = ss.compute_eigen_mode_traj(i) + # Animate mode - animator = ss.get_animator() - - #label - template = 'Mode %i \n%0.1f+%0.1fj' - label = template % (i, ss.poles[i].real, ss.poles[i].imag) + animator = ss.get_animator() + + # label + template = "Mode %i \n%0.1f+%0.1fj" + label = template % (i, ss.poles[i].real, ss.poles[i].imag) animator.top_right_label = label - - animator.animate_simulation( traj, 3.0, is_3d , show = False) - + + animator.animate_simulation(traj, 3.0, is_3d, show=False) + html_video = animator.ani.to_html5_video() - + return html_video - - + ############################# def plot_linearized_bode(self, u_index=0, y_index=0): """ Bode plot of linearized siso """ - + from pyro.dynamic.statespace import linearize from pyro.dynamic.tranferfunction import ss2tf - - linearized_sys = linearize( self ) - siso_sys = ss2tf( linearized_sys, u_index, y_index) + + linearized_sys = linearize(self) + siso_sys = ss2tf(linearized_sys, u_index, y_index) siso_sys.bode_plot() - - + ############################# - def plot_linearized_pz_map(self, u_index = 0 , y_index = 0 ): - """ + def plot_linearized_pz_map(self, u_index=0, y_index=0): + """ """ - """ - from pyro.dynamic.statespace import linearize from pyro.dynamic.tranferfunction import ss2tf - - linearized_sys = linearize( self ) - siso_sys = ss2tf( linearized_sys, u_index, y_index) + + linearized_sys = linearize(self) + siso_sys = ss2tf(linearized_sys, u_index, y_index) siso_sys.pz_map() - - + ############################# - def animate_linearized_mode(self, i = 0 ): + def animate_linearized_mode(self, i=0): """ Linearize and show eigen mode i """ - + from pyro.dynamic.statespace import linearize - - linearized_sys = linearize( self ) - - linearized_sys.animate_eigen_mode( i , self.is_3d ) - + + linearized_sys = linearize(self) + + linearized_sys.animate_eigen_mode(i, self.is_3d) + return linearized_sys - - + ############################# - def animate_linearized_modes(self ): + def animate_linearized_modes(self): """ Linearize and show eigen modes """ - + from pyro.dynamic.statespace import linearize - - linearized_sys = linearize( self ) - + + linearized_sys = linearize(self) + animations = [] - + for i in range(self.n): - ani = linearized_sys.animate_eigen_mode( i , self.is_3d ) - - animations.append( ani ) - - return linearized_sys , animations - + ani = linearized_sys.animate_eigen_mode(i, self.is_3d) + + animations.append(ani) + + return linearized_sys, animations ############################# - def convert_to_gymnasium(self ): + def convert_to_gymnasium(self, dt=0.05, tf=10.0, t0=0.0, render_mode=None): """ Create a gym environment from the system @@ -647,28 +616,23 @@ def convert_to_gymnasium(self ): from gymnasium import spaces from pyro.tools.sys2gym import Sys2Gym - gym_sys = Sys2Gym( self ) + gym_sys = Sys2Gym(self, dt, tf, t0, render_mode) return gym_sys - - except: - - raise ImportError('gym library is not installed') - - - + except: + raise ImportError("gym library is not installed") -''' +""" ################################################################# ################## Main ######## ################################################################# -''' +""" -if __name__ == "__main__": - """ MAIN TEST """ - - pass \ No newline at end of file +if __name__ == "__main__": + """MAIN TEST""" + + pass From 357e34870bc2e45c83e729249cc0f9eeeca6f2e2 Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Tue, 5 Mar 2024 10:07:40 -0500 Subject: [PATCH 71/93] added pyro wrapper for stable baseline 3 controller object to use it in closed-loop --- dev/gym/pendulum_dp_vs_ppo.py | 19 +++-------- pyro/control/reinforcementlearning.py | 47 +++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 15 deletions(-) create mode 100644 pyro/control/reinforcementlearning.py diff --git a/dev/gym/pendulum_dp_vs_ppo.py b/dev/gym/pendulum_dp_vs_ppo.py index 0759af05..e5ec42e6 100644 --- a/dev/gym/pendulum_dp_vs_ppo.py +++ b/dev/gym/pendulum_dp_vs_ppo.py @@ -41,7 +41,7 @@ from pyro.planning import discretizer from pyro.planning import dynamicprogramming -grid_sys = discretizer.GridDynamicSystem(sys, [201, 201], [11]) +grid_sys = discretizer.GridDynamicSystem(sys, [101, 101], [11]) dp = dynamicprogramming.DynamicProgrammingWithLookUpTable(grid_sys, sys.cost_function) @@ -62,29 +62,18 @@ env = sys.convert_to_gymnasium(dt=0.05, render_mode=None) env.reset_mode = "noisy_x0" model = PPO("MlpPolicy", env, verbose=1) -class rl_controller(controller.StaticController): - def __init__(self, model): +from pyro.control.reinforcementlearning import stable_baseline3_controller - controller.StaticController.__init__(self, 1, 1, 2) - self.model = model +ppo_ctl = stable_baseline3_controller(model) - self.name = "PPO Controller" - - def c(self, y, r, t): - - u, _states = self.model.predict(y, deterministic=True) - - return u - -ppo_ctl = rl_controller(model) ppo_ctl.plot_control_law(sys=sys, n=100) plt.show() plt.pause(0.001) n_time_steps = 250000 -batches = 5 +batches = 1 env.render_mode = None for batch in range(batches): model.learn(total_timesteps=int(n_time_steps / batches)) diff --git a/pyro/control/reinforcementlearning.py b/pyro/control/reinforcementlearning.py new file mode 100644 index 00000000..276fa6f2 --- /dev/null +++ b/pyro/control/reinforcementlearning.py @@ -0,0 +1,47 @@ +# -*- coding: utf-8 -*- + +############################################################################### +import numpy as np +############################################################################### +from pyro.control.controller import StaticController +############################################################################### + +class stable_baseline3_controller( StaticController ) : + """ + Wrap a stable baseline 3 model to use it as a pyro controller + """ + + def __init__(self, model): + + self.model = model + + # Dimensions + self.k = model.observation_space.shape[0] + self.m = model.action_space.shape[0] + self.p = model.observation_space.shape[0] + + StaticController.__init__( self, self.k, self.m, self.p) + + self.name = "Stable Baseline 3 Controller" + + + ############################# + def c( self , y , r , t = 0 ): + """ + Feedback static computation u = c( y, r, t) + + INPUTS + y : sensor signal vector p x 1 + r : reference signal vector k x 1 + t : time 1 x 1 + + OUTPUTS + u : control inputs vector m x 1 + + """ + + u, _x = self.model.predict( y , deterministic = True ) + + return u + + \ No newline at end of file From 4b4f56bdd2ad5fb7b7eed4371ab9600cf625e0b1 Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Tue, 5 Mar 2024 12:32:29 -0500 Subject: [PATCH 72/93] testing learning with real prototype values --- dev/gym/pendulum_swingup_experiment_tests.py | 92 ++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 dev/gym/pendulum_swingup_experiment_tests.py diff --git a/dev/gym/pendulum_swingup_experiment_tests.py b/dev/gym/pendulum_swingup_experiment_tests.py new file mode 100644 index 00000000..e1a4ee2b --- /dev/null +++ b/dev/gym/pendulum_swingup_experiment_tests.py @@ -0,0 +1,92 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Created on Fri Mar 1 14:08:03 2024 + +@author: alex +""" + +import numpy as np +import matplotlib.pyplot as plt + +from pyro.dynamic import pendulum +from pyro.control import controller +from stable_baselines3 import PPO +from stable_baselines3.common.env_util import make_vec_env + +sys = pendulum.InvertedPendulum() + +# Physical parameters of the prototype +sys.gravity = 9.8 +sys.m1 = 0.300 +sys.l1 = 0.4 +sys.lc1 = 0.4 +sys.I1 = 0.0 + +max_torque = 3.0 + +sys.l_domain = 2 * sys.l1 # graphical domain + +# Min/max state and control inputs +sys.x_ub = np.array([+2 * np.pi, +12]) +sys.x_lb = np.array([-2 * np.pi, -12]) +sys.u_ub = np.array([+max_torque]) +sys.u_lb = np.array([-max_torque]) + +# Cost Function +sys.cost_function.xbar = np.array([0, 0]) # target +sys.cost_function.R[0, 0] = 0.0 +sys.cost_function.Q[0, 0] = 1.0 +sys.cost_function.Q[1, 1] = 0.0 + +# DP solution +from pyro.planning import discretizer +from pyro.planning import dynamicprogramming + +grid_sys = discretizer.GridDynamicSystem(sys, [301, 301], [21]) + +dp = dynamicprogramming.DynamicProgrammingWithLookUpTable(grid_sys, sys.cost_function) + +dp.solve_bellman_equation(tol=0.01) +dp.clean_infeasible_set() +dp.plot_policy() +dp_ctl = dp.get_lookup_table_controller() + +cl_sys = dp_ctl + sys + +cl_sys.x0 = np.array([-np.pi, 0.0]) +cl_sys.compute_trajectory(tf=10.0, n=20000, solver="euler") +cl_sys.plot_trajectory("xu") +cl_sys.animate_simulation() + + +# Learning +env = sys.convert_to_gymnasium(dt=0.05, render_mode=None) +env.reset_mode = "x0" +model = PPO("MlpPolicy", env, verbose=1) + +from pyro.control.reinforcementlearning import stable_baseline3_controller + +ppo_ctl = stable_baseline3_controller(model) + + +ppo_ctl.plot_control_law(sys=sys, n=100) +plt.show() +plt.pause(0.001) + +n_time_steps = 250000 +batches = 1 +env.render_mode = None +for batch in range(batches): + model.learn(total_timesteps=int(n_time_steps / batches)) + ppo_ctl.plot_control_law(sys=sys, n=100) + plt.show() + plt.pause(0.001) + +# Animating rl closed-loop +cl_sys = ppo_ctl + sys + +cl_sys.x0 = np.array([-3.14, -0.0]) +cl_sys.compute_trajectory(tf=10.0, n=10000, solver="euler") +cl_sys.plot_trajectory("xu") +cl_sys.animate_simulation() From 9a06fd4ad8dc82e02c904a295bb19efd87120715 Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Mon, 18 Mar 2024 20:31:47 -0400 Subject: [PATCH 73/93] many things in parallel... --- dev/differentialflatness/rigidbodytest.py | 16 ++-- ..._ppo.py => pendulum_dp_vs_ppo_bangbang.py} | 9 +- dev/gym/pendulum_dp_vs_ppo_pump.py | 91 +++++++++++++++++++ pyro/planning/trajectorygeneration.py | 41 +++++---- 4 files changed, 125 insertions(+), 32 deletions(-) rename dev/gym/{pendulum_dp_vs_ppo.py => pendulum_dp_vs_ppo_bangbang.py} (94%) create mode 100644 dev/gym/pendulum_dp_vs_ppo_pump.py diff --git a/dev/differentialflatness/rigidbodytest.py b/dev/differentialflatness/rigidbodytest.py index ee7a6ba3..f4d6c4d6 100644 --- a/dev/differentialflatness/rigidbodytest.py +++ b/dev/differentialflatness/rigidbodytest.py @@ -14,7 +14,7 @@ # fixed initial position for now # initial angular velocity is related to jerk of trajectory -x0 = -8.0 +x0 = -10.0 y0 = -2.0 z0 = 0.0 @@ -24,11 +24,11 @@ zf = 0.0 #np.pi / 2 tf = 10 -ddx0 = 1.0 -ddy0 = ddx0 * np.tan(z0) +ddx0 = 0.0 +ddy0 = -1.0#ddx0 * np.tan(z0) -ddxf = 0.0 -ddyf = -0.1 # ddxf * np.tan(zf) +ddxf = 1.0 +ddyf =+0.0 # ddxf * np.tan(zf) bc_y = np.array([y0, 0, ddy0, yf, 0, ddyf]) @@ -59,7 +59,7 @@ # Position theta theta = np.arctan2(ay, ax) -#theta = np.arctan( (ay/ax)) +# theta = np.arctan( (ay/ax)) s = np.sin(theta) c = np.cos(theta) dtheta = (day * c - dax * s) / (ax * s + ay * c) @@ -142,14 +142,14 @@ axes[0].grid(True) axes[1].plot(t, dtheta, "b") -axes[1].plot(t, dtheta_num, "r") +# axes[1].plot(t, dtheta_num, "r") axes[1].set_ylabel("w", fontsize=graphical.default_fontsize) axes[1].set_xlabel("t", fontsize=graphical.default_fontsize) axes[1].tick_params(labelsize=graphical.default_fontsize) axes[1].grid(True) axes[2].plot(t, ddtheta, "b") -axes[2].plot(t, ddtheta_num, "r") +# axes[2].plot(t, ddtheta_num, "r") axes[2].set_ylabel("dw", fontsize=graphical.default_fontsize) axes[2].set_xlabel("t", fontsize=graphical.default_fontsize) axes[2].tick_params(labelsize=graphical.default_fontsize) diff --git a/dev/gym/pendulum_dp_vs_ppo.py b/dev/gym/pendulum_dp_vs_ppo_bangbang.py similarity index 94% rename from dev/gym/pendulum_dp_vs_ppo.py rename to dev/gym/pendulum_dp_vs_ppo_bangbang.py index e5ec42e6..6a54c667 100644 --- a/dev/gym/pendulum_dp_vs_ppo.py +++ b/dev/gym/pendulum_dp_vs_ppo_bangbang.py @@ -33,9 +33,9 @@ # Cost Function sys.cost_function.xbar = np.array([0, 0]) # target -sys.cost_function.R[0, 0] = 1.0 +sys.cost_function.R[0, 0] = 0.1 sys.cost_function.Q[0, 0] = 1.0 -sys.cost_function.Q[1, 1] = 0.0 +sys.cost_function.Q[1, 1] = 0.1 # DP solution from pyro.planning import discretizer @@ -58,6 +58,7 @@ cl_sys.animate_simulation() + # Learning env = sys.convert_to_gymnasium(dt=0.05, render_mode=None) env.reset_mode = "noisy_x0" @@ -72,7 +73,7 @@ plt.show() plt.pause(0.001) -n_time_steps = 250000 +n_time_steps = 50000 batches = 1 env.render_mode = None for batch in range(batches): @@ -84,7 +85,7 @@ # Animating rl closed-loop cl_sys = ppo_ctl + sys -cl_sys.x0 = np.array([-4.0, -0.5]) +cl_sys.x0 = np.array([-3.0, -0.0]) cl_sys.compute_trajectory(tf=10.0, n=10000, solver="euler") cl_sys.plot_trajectory("xu") cl_sys.animate_simulation() diff --git a/dev/gym/pendulum_dp_vs_ppo_pump.py b/dev/gym/pendulum_dp_vs_ppo_pump.py new file mode 100644 index 00000000..e0470e44 --- /dev/null +++ b/dev/gym/pendulum_dp_vs_ppo_pump.py @@ -0,0 +1,91 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Created on Fri Mar 1 14:08:03 2024 + +@author: alex +""" + +import numpy as np +import matplotlib.pyplot as plt + +from pyro.dynamic import pendulum +from pyro.control import controller +from stable_baselines3 import PPO +from stable_baselines3.common.env_util import make_vec_env + +sys = pendulum.InvertedPendulum() + +# Physical parameters +sys.gravity = 10.0 +sys.m1 = 1.0 +sys.l1 = 1.0 +sys.lc1 = 0.5 * sys.l1 +sys.I1 = (1.0 / 12.0) * sys.m1 * sys.l1**2 + +sys.l_domain = 2 * sys.l1 # graphical domain + +# Min/max state and control inputs +sys.x_ub = np.array([+4 * np.pi, +12]) +sys.x_lb = np.array([-4 * np.pi, -12]) +sys.u_ub = np.array([+1.0]) +sys.u_lb = np.array([-1.0]) + +# Cost Function +sys.cost_function.xbar = np.array([0, 0]) # target +sys.cost_function.R[0, 0] = 2.0 +sys.cost_function.Q[0, 0] = 1.0 +sys.cost_function.Q[1, 1] = 0.1 + +# DP solution +from pyro.planning import discretizer +from pyro.planning import dynamicprogramming + +grid_sys = discretizer.GridDynamicSystem(sys, [101, 101], [11]) + +dp = dynamicprogramming.DynamicProgrammingWithLookUpTable(grid_sys, sys.cost_function) + +dp.solve_bellman_equation(tol=0.01) +dp.clean_infeasible_set() +dp.plot_policy() +dp_ctl = dp.get_lookup_table_controller() + +cl_sys = dp_ctl + sys + +cl_sys.x0 = np.array([-3.0, 0.0]) +cl_sys.compute_trajectory(tf=10.0, n=20000, solver="euler") +cl_sys.plot_trajectory("xu") +cl_sys.animate_simulation() + + + +# Learning +env = sys.convert_to_gymnasium(dt=0.05, render_mode=None) +env.reset_mode = "noisy_x0" +model = PPO("MlpPolicy", env, verbose=1) + +from pyro.control.reinforcementlearning import stable_baseline3_controller + +ppo_ctl = stable_baseline3_controller(model) + + +ppo_ctl.plot_control_law(sys=sys, n=100) +plt.show() +plt.pause(0.001) + +n_time_steps = 250000 +batches = 1 +env.render_mode = None +for batch in range(batches): + model.learn(total_timesteps=int(n_time_steps / batches)) + ppo_ctl.plot_control_law(sys=sys, n=100) + plt.show() + plt.pause(0.001) + +# Animating rl closed-loop +cl_sys = ppo_ctl + sys + +cl_sys.x0 = np.array([-3.0, -0.0]) +cl_sys.compute_trajectory(tf=50.0, n=10000, solver="euler") +cl_sys.plot_trajectory("xu") +cl_sys.animate_simulation() diff --git a/pyro/planning/trajectorygeneration.py b/pyro/planning/trajectorygeneration.py index a21a034d..54ca997c 100644 --- a/pyro/planning/trajectorygeneration.py +++ b/pyro/planning/trajectorygeneration.py @@ -218,6 +218,7 @@ def generate_trajectory(self, dt=0.01): t = ts[i] x = 0 # For all terms of the polynomical + # TODO could replace this with A(t) generic code for n in range(j, N + 1): p_n = p[n] exp = n - j @@ -295,19 +296,19 @@ def solve(self): ge.x0 = np.array([0, 0, 0, 0, 0, 0, 0, 0]) ge.xf = np.array([10, 0, 0, 0, 0, 0, 0, 0]) - ge.bc_t0_N = 2 - ge.bc_tf_N = 2 - ge.poly_N = 3 - ge.diff_N = 3 + # ge.bc_t0_N = 2 + # ge.bc_tf_N = 2 + # ge.poly_N = 3 + # ge.diff_N = 3 - ge.solve() + # ge.solve() - # ge.bc_t0_N = 3 - # ge.bc_tf_N = 3 - # ge.poly_N = 5 - # ge.diff_N = 7 + ge.bc_t0_N = 3 + ge.bc_tf_N = 3 + ge.poly_N = 5 + ge.diff_N = 7 - # ge.solve() + ge.solve() # ge.bc_t0_N = 4 # ge.bc_tf_N = 4 @@ -330,16 +331,16 @@ def solve(self): # ge.solve() - # ge.bc_t0_N = 7 - # ge.bc_tf_N = 7 - # ge.poly_N = 13 - # ge.diff_N = 7 + ge.bc_t0_N = 7 + ge.bc_tf_N = 7 + ge.poly_N = 13 + ge.diff_N = 7 - # ge.solve() + ge.solve() - # ge.bc_t0_N = 1 - # ge.bc_tf_N = 1 - # ge.poly_N = 3 - # ge.diff_N = 7 + ge.bc_t0_N = 1 + ge.bc_tf_N = 1 + ge.poly_N = 3 + ge.diff_N = 7 - # ge.solve() + ge.solve() From c11bb88cd80bb85765ac20b4b164caba889b5a53 Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Tue, 19 Mar 2024 16:49:44 -0400 Subject: [PATCH 74/93] upgraded sys2gym --- dev/gym/pendulum_dp_vs_ppo_bangbang.py | 41 +++---- dev/gym/pendulum_dp_vs_ppo_pump.py | 23 ++-- ... => pendulum_dp_vs_ppo_tmotor_bangbang.py} | 0 pyro/tools/sys2gym.py | 112 +++++++++++++----- 4 files changed, 110 insertions(+), 66 deletions(-) rename dev/gym/{pendulum_swingup_experiment_tests.py => pendulum_dp_vs_ppo_tmotor_bangbang.py} (100%) diff --git a/dev/gym/pendulum_dp_vs_ppo_bangbang.py b/dev/gym/pendulum_dp_vs_ppo_bangbang.py index 6a54c667..cfc8e7e8 100644 --- a/dev/gym/pendulum_dp_vs_ppo_bangbang.py +++ b/dev/gym/pendulum_dp_vs_ppo_bangbang.py @@ -16,20 +16,22 @@ sys = pendulum.InvertedPendulum() -# Physical parameters -sys.gravity = 10.0 -sys.m1 = 1.0 -sys.l1 = 1.0 -sys.lc1 = 0.5 * sys.l1 -sys.I1 = (1.0 / 12.0) * sys.m1 * sys.l1**2 +# Physical parameters of the prototype +sys.gravity = 9.8 +sys.m1 = 0.300 +sys.l1 = 0.4 +sys.lc1 = 0.4 +sys.I1 = 0.0 + +max_torque = 2.0 sys.l_domain = 2 * sys.l1 # graphical domain # Min/max state and control inputs -sys.x_ub = np.array([+2 * np.pi, +8]) -sys.x_lb = np.array([-2 * np.pi, -8]) -sys.u_ub = np.array([+8.0]) -sys.u_lb = np.array([-8.0]) +sys.x_ub = np.array([+2 * np.pi, +12]) +sys.x_lb = np.array([-2 * np.pi, -12]) +sys.u_ub = np.array([+max_torque]) +sys.u_lb = np.array([-max_torque]) # Cost Function sys.cost_function.xbar = np.array([0, 0]) # target @@ -48,22 +50,14 @@ dp.solve_bellman_equation(tol=0.01) dp.clean_infeasible_set() dp.plot_policy() -dp_ctl = dp.get_lookup_table_controller() - -cl_sys = dp_ctl + sys - -cl_sys.x0 = np.array([-3.0, 0.0]) -cl_sys.compute_trajectory(tf=10.0, n=20000, solver="euler") -cl_sys.plot_trajectory("xu") -cl_sys.animate_simulation() - - # Learning env = sys.convert_to_gymnasium(dt=0.05, render_mode=None) env.reset_mode = "noisy_x0" model = PPO("MlpPolicy", env, verbose=1) +model.load('pendulum_dp_vs_ppo_bangbang') + from pyro.control.reinforcementlearning import stable_baseline3_controller ppo_ctl = stable_baseline3_controller(model) @@ -73,8 +67,8 @@ plt.show() plt.pause(0.001) -n_time_steps = 50000 -batches = 1 +n_time_steps = 2.5E5 +batches = 5 env.render_mode = None for batch in range(batches): model.learn(total_timesteps=int(n_time_steps / batches)) @@ -82,6 +76,9 @@ plt.show() plt.pause(0.001) +# Save the model +model.save('pendulum_dp_vs_ppo_bangbang') + # Animating rl closed-loop cl_sys = ppo_ctl + sys diff --git a/dev/gym/pendulum_dp_vs_ppo_pump.py b/dev/gym/pendulum_dp_vs_ppo_pump.py index e0470e44..5602d94a 100644 --- a/dev/gym/pendulum_dp_vs_ppo_pump.py +++ b/dev/gym/pendulum_dp_vs_ppo_pump.py @@ -26,14 +26,14 @@ sys.l_domain = 2 * sys.l1 # graphical domain # Min/max state and control inputs -sys.x_ub = np.array([+4 * np.pi, +12]) -sys.x_lb = np.array([-4 * np.pi, -12]) -sys.u_ub = np.array([+1.0]) -sys.u_lb = np.array([-1.0]) +sys.x_ub = np.array([+2 * np.pi, +12]) +sys.x_lb = np.array([-2 * np.pi, -12]) +sys.u_ub = np.array([+3.0]) +sys.u_lb = np.array([-3.0]) # Cost Function sys.cost_function.xbar = np.array([0, 0]) # target -sys.cost_function.R[0, 0] = 2.0 +sys.cost_function.R[0, 0] = 1.0 sys.cost_function.Q[0, 0] = 1.0 sys.cost_function.Q[1, 1] = 0.1 @@ -41,22 +41,13 @@ from pyro.planning import discretizer from pyro.planning import dynamicprogramming -grid_sys = discretizer.GridDynamicSystem(sys, [101, 101], [11]) +grid_sys = discretizer.GridDynamicSystem(sys, [201, 201], [21]) dp = dynamicprogramming.DynamicProgrammingWithLookUpTable(grid_sys, sys.cost_function) dp.solve_bellman_equation(tol=0.01) dp.clean_infeasible_set() dp.plot_policy() -dp_ctl = dp.get_lookup_table_controller() - -cl_sys = dp_ctl + sys - -cl_sys.x0 = np.array([-3.0, 0.0]) -cl_sys.compute_trajectory(tf=10.0, n=20000, solver="euler") -cl_sys.plot_trajectory("xu") -cl_sys.animate_simulation() - # Learning @@ -74,7 +65,7 @@ plt.pause(0.001) n_time_steps = 250000 -batches = 1 +batches = 5 env.render_mode = None for batch in range(batches): model.learn(total_timesteps=int(n_time_steps / batches)) diff --git a/dev/gym/pendulum_swingup_experiment_tests.py b/dev/gym/pendulum_dp_vs_ppo_tmotor_bangbang.py similarity index 100% rename from dev/gym/pendulum_swingup_experiment_tests.py rename to dev/gym/pendulum_dp_vs_ppo_tmotor_bangbang.py diff --git a/pyro/tools/sys2gym.py b/pyro/tools/sys2gym.py index 09ee8a6b..8efeca5d 100644 --- a/pyro/tools/sys2gym.py +++ b/pyro/tools/sys2gym.py @@ -12,29 +12,67 @@ import gymnasium as gym from gymnasium import spaces + ################################################################# # Create a Gym Environment from a Pyro System ################################################################# - - class Sys2Gym(gym.Env): + """Create a Gym Environment from a Pyro System + + Taken from the Pyro system: + - x0: nominal initial state + - f: state evolution function xdot = f(x,u,t) + - g: cost function g(x,u,t) (reward = -g) + - h: observation function y = h(x,u,t) + - x_ub, x_lb: state boundaries + - u_ub, u_lb: control boundaries + + Additionnal parameters of the gym wrapper are: + - dt: time step for the integration + - tf: maximum duration of an episode + - t0: initial time (only relevant if the system is time dependent) + - render_mode: None or "human" for rendering the system + - reset_mode: "uniform", "gaussian" or "determinist" + - clipping_inputs: True if the system clips the control inputs + - clipping_states: True if the system clips the states + - x0_lb, x0_ub: boundaries for the initial state (only relevant if reset_mode is "uniform") + - x0_std: standard deviation for the initial state (only relevant if reset_mode is "gaussian") + - termination_is_reached: method to define the termination condition of the task (default is always False representing a continous time task) + + """ metadata = {"render_modes": ["human"]} ################################################################# - def __init__(self, sys, dt=0.05, tf=10.0, t0=0.0, render_mode=None): + def __init__( + self, sys, dt=0.05, tf=10.0, t0=0.0, render_mode=None, reset_mode="uniform" + ): - self.observation_space = spaces.Box(sys.x_lb, sys.x_ub) + # Boundaries + self.t0 = t0 + self.tf = tf # For truncation of episodes + self.observation_space = spaces.Box( + sys.x_lb, sys.x_ub + ) # default is state feedback self.action_space = spaces.Box(sys.u_lb, sys.u_ub) + # Dynamic evolution self.sys = sys self.dt = dt + self.clipping_inputs = True # The sys is assumed to clip out of bounds inputs + self.clipping_states = False # The sys is assumed to clip out of bounds states (some gym env do that but this is a very fake behavior not exibiited by real systems, to use with caution) - self.tf = tf # For truncation of episodes + # Rendering self.render_mode = render_mode - self.reset_mode = "random" - + # Reset parameters (stocasticity of the initial state) + self.reset_mode = reset_mode + # self.reset_mode = "uniform" + self.x0_lb = sys.x0 + 0.1 * sys.x_lb + self.x0_ub = sys.x0 + 0.1 * sys.x_ub + # self.reset_mode = "gaussian" + self.x0_std = 0.1 * (sys.x_ub - sys.x_lb) + # self.reset_mode = "determinist" # Memory self.x = sys.x0 @@ -45,32 +83,29 @@ def __init__(self, sys, dt=0.05, tf=10.0, t0=0.0, render_mode=None): self.render_is_initiated = False if self.render_mode == "human": - self.init_render() ################################################################# def reset(self, seed=None, options=None): - if self.reset_mode == "random": + if self.reset_mode == "uniform": super().reset(seed=seed) - self.x = self.np_random.uniform(self.sys.x_lb, self.sys.x_ub) + self.x = self.np_random.uniform(self.x0_lb, self.x0_ub) self.u = self.sys.ubar - self.t = 0.0 + self.t = self.t0 - elif self.reset_mode == "noisy_x0": + elif self.reset_mode == "gaussian": super().reset(seed=seed) - self.x = self.sys.x0 + 0.1 * self.np_random.uniform( - self.sys.x_lb, self.sys.x_ub - ) + self.x = self.np_random.normal(self.sys.x0, self.x0_std) self.u = self.sys.ubar self.t = 0.0 else: - + # Deterministic self.x = self.sys.x0 self.u = self.sys.ubar self.t = 0.0 @@ -86,7 +121,11 @@ def reset(self, seed=None, options=None): ################################################################# def step(self, u): - u = np.clip(u, self.sys.u_lb, self.sys.u_ub) + # Clipping of inputs + if self.clipping_inputs: + u = np.clip(u, self.sys.u_lb, self.sys.u_ub) + + # State and time at the beginning of the step x = self.x t = self.t dt = self.dt @@ -94,23 +133,32 @@ def step(self, u): # Derivatives dx = self.sys.f(x, u, t) - # Euler integration + # Euler integration #TODO use a better integrator x_new = x + dx * dt t_new = t + dt - # Reward = negative of cost function - r = -self.sys.cost_function.g(x, u, t) + # Horrible optionnal hack to avoid the system to go out of bounds + if self.clipping_states: + x_new = np.clip(x_new, self.sys.x_lb, self.sys.x_ub) # Termination of episodes - terminated = False + terminated = self.termination_is_reached() + + # Reward = negative of cost function + if terminated: + r = -self.sys.cost_funtion.h(x, t) # Terminal cost + else: + r = ( + -self.sys.cost_function.g(x, u, t) * dt + ) # Instantaneous cost integrated over the time step - # Truncation of episodes if out of bounds + # Truncation of episodes if out of space-time bounds truncated = (t_new > self.tf) or (not self.sys.isavalidstate(x_new)) # Memory update self.x = x_new self.t = t_new - self.u = u + self.u = u # The memory of the control input is only used for redering # Observation y = self.sys.h(self.x, self.u, self.t) @@ -118,11 +166,12 @@ def step(self, u): # Info info = {"state": self.x, "action": self.u} + # Rendering if self.render_mode == "human": self.render() return y, r, terminated, truncated, info - + ################################################################# def init_render(self): @@ -141,6 +190,12 @@ def render(self): self.animator.show_plus_update(self.x, self.u, self.t) plt.pause(0.001) + ################################################################# + def termination_is_reached(self): + """This method should be overloaded in the child class to define the termination condition for a task that is not time defined in continous time.""" + + return False + """ ################################################################# @@ -180,17 +235,19 @@ def render(self): sys.cost_function.Q[0, 0] = 1.0 sys.cost_function.Q[1, 1] = 0.1 - sys.x0 = np.array([ -np.pi, 0.0]) + sys.x0 = np.array([-np.pi, 0.0]) gym_env = Sys2Gym(sys, render_mode=None) - gym_env.reset_mode = "noisy_x0" + gym_env.reset_mode = "uniform" + gym_env.x0_lb = np.array([-np.pi - 0.2, -0.2]) + gym_env.x0_ub = np.array([-np.pi + 0.2, +0.2]) model = PPO("MlpPolicy", gym_env, verbose=1) model.learn(total_timesteps=100000) gym_env = Sys2Gym(sys, render_mode="human") - gym_env.reset_mode = "x0" + #gym_env.reset_mode = "uniform" episodes = 3 for episode in range(episodes): @@ -202,4 +259,3 @@ def render(self): while not (terminated or truncated): u, _states = model.predict(y, deterministic=True) y, r, terminated, truncated, info = gym_env.step(u) - # print("t=", gym_env.t, "x=", gym_env.x, "u=", gym_env.u, "r=", r) From 95c9d352d579b4b722edbc7de3e421b74f6d3602 Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Wed, 20 Mar 2024 09:32:23 -0400 Subject: [PATCH 75/93] sys2gym pendulum exemple working --- pyro/tools/sys2gym.py | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/pyro/tools/sys2gym.py b/pyro/tools/sys2gym.py index 8efeca5d..7b616ad1 100644 --- a/pyro/tools/sys2gym.py +++ b/pyro/tools/sys2gym.py @@ -223,27 +223,33 @@ def termination_is_reached(self): sys.l_domain = 2 * sys.l1 # graphical domain # Min/max state and control inputs - sys.x_ub = np.array([+3.0 * np.pi, +20]) - sys.x_lb = np.array([-3.0 * np.pi, -20]) + sys.x_ub = np.array([+3.0 * np.pi, +8]) + sys.x_lb = np.array([-3.0 * np.pi, -8]) sys.u_ub = np.array([+2.0]) sys.u_lb = np.array([-2.0]) + # Time constant + dt = 0.05 + # Cost Function # The reward function is defined as: r = -(theta2 + 0.1 * theta_dt2 + 0.001 * torque2) sys.cost_function.xbar = np.array([0, 0]) # target - sys.cost_function.R[0, 0] = 0.001 - sys.cost_function.Q[0, 0] = 1.0 - sys.cost_function.Q[1, 1] = 0.1 + sys.cost_function.R[0, 0] = 0.001 / dt + sys.cost_function.Q[0, 0] = 1.0 / dt + sys.cost_function.Q[1, 1] = 0.1 / dt sys.x0 = np.array([-np.pi, 0.0]) - gym_env = Sys2Gym(sys, render_mode=None) + gym_env = Sys2Gym(sys, dt=dt, render_mode=None) + + gym_env.clipping_states = True # To reproduce the behavior of gym pendulum + gym_env.reset_mode = "uniform" - gym_env.x0_lb = np.array([-np.pi - 0.2, -0.2]) - gym_env.x0_ub = np.array([-np.pi + 0.2, +0.2]) + gym_env.x0_lb = np.array([-np.pi , -1.0]) + gym_env.x0_ub = np.array([+np.pi , +1.0]) model = PPO("MlpPolicy", gym_env, verbose=1) - model.learn(total_timesteps=100000) + model.learn(total_timesteps=250000) gym_env = Sys2Gym(sys, render_mode="human") From efb4b7ab7b9b6dc3b453f93eb1f0f1eb4bcda349 Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Wed, 20 Mar 2024 10:05:35 -0400 Subject: [PATCH 76/93] demo with tmotor param proto kind of working --- dev/gym/pendulum_dp_vs_ppo_tmotor_bangbang.py | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/dev/gym/pendulum_dp_vs_ppo_tmotor_bangbang.py b/dev/gym/pendulum_dp_vs_ppo_tmotor_bangbang.py index e1a4ee2b..d69307e8 100644 --- a/dev/gym/pendulum_dp_vs_ppo_tmotor_bangbang.py +++ b/dev/gym/pendulum_dp_vs_ppo_tmotor_bangbang.py @@ -43,26 +43,19 @@ from pyro.planning import discretizer from pyro.planning import dynamicprogramming -grid_sys = discretizer.GridDynamicSystem(sys, [301, 301], [21]) +grid_sys = discretizer.GridDynamicSystem(sys, [201, 201], [21]) dp = dynamicprogramming.DynamicProgrammingWithLookUpTable(grid_sys, sys.cost_function) dp.solve_bellman_equation(tol=0.01) dp.clean_infeasible_set() dp.plot_policy() -dp_ctl = dp.get_lookup_table_controller() - -cl_sys = dp_ctl + sys - -cl_sys.x0 = np.array([-np.pi, 0.0]) -cl_sys.compute_trajectory(tf=10.0, n=20000, solver="euler") -cl_sys.plot_trajectory("xu") -cl_sys.animate_simulation() - # Learning env = sys.convert_to_gymnasium(dt=0.05, render_mode=None) -env.reset_mode = "x0" + +env.clipping_states = True # test + model = PPO("MlpPolicy", env, verbose=1) from pyro.control.reinforcementlearning import stable_baseline3_controller @@ -75,7 +68,7 @@ plt.pause(0.001) n_time_steps = 250000 -batches = 1 +batches = 5 env.render_mode = None for batch in range(batches): model.learn(total_timesteps=int(n_time_steps / batches)) From d823e7c3ff9d64beccb9f4cf702a2a469a9f03d5 Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Wed, 20 Mar 2024 10:45:10 -0400 Subject: [PATCH 77/93] two working dp vs ppo example for simple policies --- .../pendulum_dp_vs_ppo_bangbang.py | 55 +++++++++++-------- .../pendulum_dp_vs_ppo_tmotor_bangbang.py | 1 + ...lum_with_PPO_baseline_pyro_reproduction.py | 4 +- pyro/tools/sys2gym.py | 14 +++++ 4 files changed, 51 insertions(+), 23 deletions(-) rename {dev/gym => examples/demos_by_tool/rl_with_stable_baseline3}/pendulum_dp_vs_ppo_bangbang.py (60%) rename {dev/gym => examples/demos_by_tool/rl_with_stable_baseline3}/pendulum_dp_vs_ppo_tmotor_bangbang.py (98%) diff --git a/dev/gym/pendulum_dp_vs_ppo_bangbang.py b/examples/demos_by_tool/rl_with_stable_baseline3/pendulum_dp_vs_ppo_bangbang.py similarity index 60% rename from dev/gym/pendulum_dp_vs_ppo_bangbang.py rename to examples/demos_by_tool/rl_with_stable_baseline3/pendulum_dp_vs_ppo_bangbang.py index cfc8e7e8..adb5c282 100644 --- a/dev/gym/pendulum_dp_vs_ppo_bangbang.py +++ b/examples/demos_by_tool/rl_with_stable_baseline3/pendulum_dp_vs_ppo_bangbang.py @@ -16,28 +16,32 @@ sys = pendulum.InvertedPendulum() -# Physical parameters of the prototype -sys.gravity = 9.8 -sys.m1 = 0.300 -sys.l1 = 0.4 -sys.lc1 = 0.4 -sys.I1 = 0.0 - -max_torque = 2.0 +# Physical parameters +sys.gravity = 10.0 +sys.m1 = 1.0 +sys.l1 = 1.0 +sys.lc1 = 0.5 * sys.l1 +sys.I1 = (1.0 / 12.0) * sys.m1 * sys.l1**2 sys.l_domain = 2 * sys.l1 # graphical domain # Min/max state and control inputs -sys.x_ub = np.array([+2 * np.pi, +12]) -sys.x_lb = np.array([-2 * np.pi, -12]) -sys.u_ub = np.array([+max_torque]) -sys.u_lb = np.array([-max_torque]) +sys.x_ub = np.array([+2.0 * np.pi, +12]) +sys.x_lb = np.array([-2.0 * np.pi, -12]) +sys.u_ub = np.array([+8.0]) +sys.u_lb = np.array([-8.0]) + +# Time constant +dt = 0.05 # Cost Function +# The reward function is defined as: r = -(theta2 + 0.1 * theta_dt2 + 0.001 * torque2) sys.cost_function.xbar = np.array([0, 0]) # target -sys.cost_function.R[0, 0] = 0.1 -sys.cost_function.Q[0, 0] = 1.0 -sys.cost_function.Q[1, 1] = 0.1 +sys.cost_function.R[0, 0] = 0.001 / dt +sys.cost_function.Q[0, 0] = 1.0 / dt +sys.cost_function.Q[1, 1] = 0.1 / dt + +sys.x0 = np.array([-np.pi, 0.0]) # DP solution from pyro.planning import discretizer @@ -50,13 +54,20 @@ dp.solve_bellman_equation(tol=0.01) dp.clean_infeasible_set() dp.plot_policy() +dp.plot_cost2go_3D(jmax=5000) # Learning -env = sys.convert_to_gymnasium(dt=0.05, render_mode=None) -env.reset_mode = "noisy_x0" -model = PPO("MlpPolicy", env, verbose=1) +gym_env = sys.convert_to_gymnasium(dt=dt, render_mode=None) + +gym_env.clipping_states = True # To reproduce the behavior of gym pendulum + +gym_env.reset_mode = "uniform" +gym_env.x0_lb = np.array([-np.pi , -1.0]) +gym_env.x0_ub = np.array([+np.pi , +1.0]) + +model = PPO("MlpPolicy", gym_env, verbose=1) -model.load('pendulum_dp_vs_ppo_bangbang') +#model.load('pendulum_dp_vs_ppo_bangbang') from pyro.control.reinforcementlearning import stable_baseline3_controller @@ -68,8 +79,8 @@ plt.pause(0.001) n_time_steps = 2.5E5 -batches = 5 -env.render_mode = None +batches = 3 +gym_env.render_mode = None for batch in range(batches): model.learn(total_timesteps=int(n_time_steps / batches)) ppo_ctl.plot_control_law(sys=sys, n=100) @@ -82,7 +93,7 @@ # Animating rl closed-loop cl_sys = ppo_ctl + sys -cl_sys.x0 = np.array([-3.0, -0.0]) +cl_sys.x0 = np.array([-3.2, -0.0]) cl_sys.compute_trajectory(tf=10.0, n=10000, solver="euler") cl_sys.plot_trajectory("xu") cl_sys.animate_simulation() diff --git a/dev/gym/pendulum_dp_vs_ppo_tmotor_bangbang.py b/examples/demos_by_tool/rl_with_stable_baseline3/pendulum_dp_vs_ppo_tmotor_bangbang.py similarity index 98% rename from dev/gym/pendulum_dp_vs_ppo_tmotor_bangbang.py rename to examples/demos_by_tool/rl_with_stable_baseline3/pendulum_dp_vs_ppo_tmotor_bangbang.py index d69307e8..acb2517d 100644 --- a/dev/gym/pendulum_dp_vs_ppo_tmotor_bangbang.py +++ b/examples/demos_by_tool/rl_with_stable_baseline3/pendulum_dp_vs_ppo_tmotor_bangbang.py @@ -50,6 +50,7 @@ dp.solve_bellman_equation(tol=0.01) dp.clean_infeasible_set() dp.plot_policy() +dp.plot_cost2go(jmax=10) # Learning env = sys.convert_to_gymnasium(dt=0.05, render_mode=None) diff --git a/examples/demos_by_tool/rl_with_stable_baseline3/pendulum_with_PPO_baseline_pyro_reproduction.py b/examples/demos_by_tool/rl_with_stable_baseline3/pendulum_with_PPO_baseline_pyro_reproduction.py index 9a6d18b2..b2bf8a89 100644 --- a/examples/demos_by_tool/rl_with_stable_baseline3/pendulum_with_PPO_baseline_pyro_reproduction.py +++ b/examples/demos_by_tool/rl_with_stable_baseline3/pendulum_with_PPO_baseline_pyro_reproduction.py @@ -170,4 +170,6 @@ def _render_frame(self): print("\n Episode:", episode) while not (terminated or truncated): u, _states = model.predict(y, deterministic=True) - y, r, terminated, truncated, info = gym_env.step(u) \ No newline at end of file + y, r, terminated, truncated, info = gym_env.step(u) + + \ No newline at end of file diff --git a/pyro/tools/sys2gym.py b/pyro/tools/sys2gym.py index 7b616ad1..a785927a 100644 --- a/pyro/tools/sys2gym.py +++ b/pyro/tools/sys2gym.py @@ -265,3 +265,17 @@ def termination_is_reached(self): while not (terminated or truncated): u, _states = model.predict(y, deterministic=True) y, r, terminated, truncated, info = gym_env.step(u) + + + # from pyro.control.reinforcementlearning import stable_baseline3_controller + + # ppo_ctl = stable_baseline3_controller(model) + + + # ppo_ctl.plot_control_law(sys=sys, n=100) + # cl_sys = ppo_ctl + sys + + # cl_sys.x0 = np.array([-3.0, -0.0]) + # cl_sys.compute_trajectory(tf=10.0, n=10000, solver="euler") + # cl_sys.plot_trajectory("xu") + # cl_sys.animate_simulation() From 201affc1986bfed36fa859d624b931bd3fb089eb Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Wed, 20 Mar 2024 11:27:59 -0400 Subject: [PATCH 78/93] basic learning of pumping with PPO --- .../pendulum_dp_vs_ppo_pump.py | 47 +++++++++++++------ 1 file changed, 32 insertions(+), 15 deletions(-) rename {dev/gym => examples/demos_by_tool/rl_with_stable_baseline3}/pendulum_dp_vs_ppo_pump.py (62%) diff --git a/dev/gym/pendulum_dp_vs_ppo_pump.py b/examples/demos_by_tool/rl_with_stable_baseline3/pendulum_dp_vs_ppo_pump.py similarity index 62% rename from dev/gym/pendulum_dp_vs_ppo_pump.py rename to examples/demos_by_tool/rl_with_stable_baseline3/pendulum_dp_vs_ppo_pump.py index 5602d94a..074488c8 100644 --- a/dev/gym/pendulum_dp_vs_ppo_pump.py +++ b/examples/demos_by_tool/rl_with_stable_baseline3/pendulum_dp_vs_ppo_pump.py @@ -26,16 +26,22 @@ sys.l_domain = 2 * sys.l1 # graphical domain # Min/max state and control inputs -sys.x_ub = np.array([+2 * np.pi, +12]) -sys.x_lb = np.array([-2 * np.pi, -12]) -sys.u_ub = np.array([+3.0]) -sys.u_lb = np.array([-3.0]) +sys.x_ub = np.array([+2.0 * np.pi, +12]) +sys.x_lb = np.array([-2.0 * np.pi, -12]) +sys.u_ub = np.array([+2.0]) +sys.u_lb = np.array([-2.0]) + +# Time constant +dt = 0.05 # Cost Function +# The reward function is defined as: r = -(theta2 + 0.1 * theta_dt2 + 0.001 * torque2) sys.cost_function.xbar = np.array([0, 0]) # target -sys.cost_function.R[0, 0] = 1.0 -sys.cost_function.Q[0, 0] = 1.0 -sys.cost_function.Q[1, 1] = 0.1 +sys.cost_function.R[0, 0] = 0.0 / dt +sys.cost_function.Q[0, 0] = 1.0 / dt +sys.cost_function.Q[1, 1] = 0.1 / dt + +sys.x0 = np.array([-np.pi, 0.0]) # DP solution from pyro.planning import discretizer @@ -48,12 +54,20 @@ dp.solve_bellman_equation(tol=0.01) dp.clean_infeasible_set() dp.plot_policy() - +dp.plot_cost2go_3D(jmax=5000) # Learning -env = sys.convert_to_gymnasium(dt=0.05, render_mode=None) -env.reset_mode = "noisy_x0" -model = PPO("MlpPolicy", env, verbose=1) +gym_env = sys.convert_to_gymnasium(dt=dt, render_mode=None) + +gym_env.clipping_states = True # To reproduce the behavior of gym pendulum + +gym_env.reset_mode = "uniform" +gym_env.x0_lb = np.array([-np.pi , -1.0]) +gym_env.x0_ub = np.array([+np.pi , +1.0]) + +model = PPO("MlpPolicy", gym_env, verbose=1) + +#model.load('pendulum_dp_vs_ppo_bangbang') from pyro.control.reinforcementlearning import stable_baseline3_controller @@ -64,19 +78,22 @@ plt.show() plt.pause(0.001) -n_time_steps = 250000 -batches = 5 -env.render_mode = None +n_time_steps = 2.0E6 +batches = 4 +gym_env.render_mode = None for batch in range(batches): model.learn(total_timesteps=int(n_time_steps / batches)) ppo_ctl.plot_control_law(sys=sys, n=100) plt.show() plt.pause(0.001) +# Save the model +model.save('pendulum_dp_vs_ppo_bangbang') + # Animating rl closed-loop cl_sys = ppo_ctl + sys -cl_sys.x0 = np.array([-3.0, -0.0]) +cl_sys.x0 = np.array([-3.2, -0.0]) cl_sys.compute_trajectory(tf=50.0, n=10000, solver="euler") cl_sys.plot_trajectory("xu") cl_sys.animate_simulation() From 08dc4835dc20b301402cd7cbe689daea499e8c37 Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Wed, 20 Mar 2024 16:34:30 -0400 Subject: [PATCH 79/93] min snap trajectory working!!! --- pyro/planning/trajectorygeneration.py | 88 +++++++++++++++++---------- 1 file changed, 57 insertions(+), 31 deletions(-) diff --git a/pyro/planning/trajectorygeneration.py b/pyro/planning/trajectorygeneration.py index 54ca997c..70ebba00 100644 --- a/pyro/planning/trajectorygeneration.py +++ b/pyro/planning/trajectorygeneration.py @@ -42,6 +42,10 @@ def __init__(self, tf=10, N=5, x0=None, xf=None): self.bc_t0_N = 3 self.bc_tf_N = 3 + # Optimization + self.regulation = 0.0 + self.Cr = np.array([0, 0, 0, 0.0]) + self.labels = [ "pos", "vel", @@ -112,15 +116,41 @@ def compute_A(self): ################################################ def compute_Q(self): - Q = np.zeros((self.poly_N + 1, self.poly_N + 1)) + N = self.poly_N + R = self.diff_N - for i in range(self.poly_N + 1): - Q[i, i] = 1.0 * i * i + C = np.zeros((R)) + C[:self.Cr.shape[0]] = self.Cr - # TODO compute real Q - # see https://groups.csail.mit.edu/rrg/papers/BryIJRR15.pdf + Q = np.zeros((N + 1, N + 1, R)) - self.Q = Q + tf = self.tf + + # see https://groups.csail.mit.edu/rrg/papers/BryIJRR15.pdf + # TODO debug + for r in range(R): + for i in range(N + 1): + for l in range(N + 1): + if (i >= r) and (l >= r): + mul = 1 + for m in range(r): + mul = mul * (i-m)*(l-m) + exp = i + l - 2 * r + 1 + Q[i, l, r] = 2 * mul * tf**exp / (i+l-2*r+1) + else: + Q[i, l, r] = 0 + + Q_sum = np.zeros((N + 1, N + 1)) + + for r in range(R): + Q_sum = Q_sum + C[r] * Q[:, :, r] + + # Regulation + Q_sum = Q_sum + self.regulation * np.eye(N + 1) + + self.Q = Q_sum + + return Q ################################################ def constraints(self, p): @@ -134,20 +164,8 @@ def cost(self, p): Q = self.Q - # TODO compute real Q - # see https://groups.csail.mit.edu/rrg/papers/BryIJRR15.pdf - J = p.T @ Q @ p - # Min Jerk test - self.p = p - self.generate_trajectory() - jerk = self.X[3, :] - snap = self.X[4, :] - - # #J = np.abs(jerk).max() - J = np.abs(snap).max() - return J ################################################ @@ -181,12 +199,17 @@ def compute_parameters(self): constraints = {"type": "eq", "fun": self.constraints} + grad = lambda p: 2 * p.T @ self.Q; + hess = lambda p: 2 * self.Q; + res = minimize( self.cost, p0, method="SLSQP", + jac=grad, + hess=hess, constraints=constraints, - options={"disp": True, "maxiter": 500}, + options={"disp": True, "maxiter": 5000}, ) p = res.x @@ -293,7 +316,7 @@ def solve(self): ge = SingleAxisTrajectoryGenerator() - ge.x0 = np.array([0, 0, 0, 0, 0, 0, 0, 0]) + ge.x0 = np.array([0, 0, 0, 0, 0, 0, 0, 0]) ge.xf = np.array([10, 0, 0, 0, 0, 0, 0, 0]) # ge.bc_t0_N = 2 @@ -305,9 +328,12 @@ def solve(self): ge.bc_t0_N = 3 ge.bc_tf_N = 3 - ge.poly_N = 5 + ge.poly_N = 9 ge.diff_N = 7 + ge.regulation = 0. + ge.Cr = np.array([0, 0.0, 1.0, 1.0, 1.0, 0, 0]) + ge.solve() # ge.bc_t0_N = 4 @@ -331,16 +357,16 @@ def solve(self): # ge.solve() - ge.bc_t0_N = 7 - ge.bc_tf_N = 7 - ge.poly_N = 13 - ge.diff_N = 7 + # ge.bc_t0_N = 7 + # ge.bc_tf_N = 7 + # ge.poly_N = 13 + # ge.diff_N = 7 - ge.solve() + # ge.solve() - ge.bc_t0_N = 1 - ge.bc_tf_N = 1 - ge.poly_N = 3 - ge.diff_N = 7 + # ge.bc_t0_N = 1 + # ge.bc_tf_N = 1 + # ge.poly_N = 3 + # ge.diff_N = 7 - ge.solve() + # ge.solve() From 6142f17d6d7095d49e15ff4b50eef844c809e423 Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Thu, 21 Mar 2024 10:19:03 -0400 Subject: [PATCH 80/93] nicer organization of polynomial trajectory generator --- pyro/planning/trajectorygeneration.py | 319 +++++++++++++++----------- 1 file changed, 181 insertions(+), 138 deletions(-) diff --git a/pyro/planning/trajectorygeneration.py b/pyro/planning/trajectorygeneration.py index 70ebba00..9aaed2a1 100644 --- a/pyro/planning/trajectorygeneration.py +++ b/pyro/planning/trajectorygeneration.py @@ -14,37 +14,92 @@ from scipy.optimize import minimize -from pyro.analysis import graphical +# Import standard graphical parameters if part of pyro +try: + from pyro.analysis import graphical + default_figsize = graphical.default_figsize + default_dpi = graphical.default_dpi + default_fontsize = graphical.default_fontsize +except: + default_figsize = (10, 6) + default_dpi = 100 + default_fontsize = 12 ############################################################################### -class SingleAxisTrajectoryGenerator: +class SingleAxisPolynomialTrajectoryGenerator: """ This class is a tool to generate a point-to-point trajectory for a single axis based on boundary conditions (position and higher order derivative) Polynomial of order N - if boundary conditions do not fully specify the profile parameter, - then an optimization is conducted (TODO) + x(t) = p0 + p1*t + p2*t^2 + ... + pN*t^N + + if boundary conditions do not fully specify the parameters of the polynomial, + then an optimization is conducted to minimize the cost function which is defined + as a weighted sum of the integral of the square of the ith derivative of the profile. + + Parameters: + ----------- + tf : float + duration of the trajectory + poly_N : int + order of the polynomial + diff_N : int + order of the highest derivative to compute + x0 : array + initial conditions (position, velocity, acceleration, jerk, snap, crackle, pop, ...) + xf : array + final conditions (position, velocity, acceleration, jerk, snap, crackle, pop, ...) + x0_N : int + number of initial conditions to impose (higher order derivative of the initial conditions) + xf_N : int + number of final conditions to impose (higher order derivative of the final conditions) + Rs : array + weights for the cost function penalizing the ith polynomial parameters directly + Ws : array + weights for the cost function penalizing the ith derivative of the profile + dt : float + time step for the numerical solution of the trajectory + + Output: + ------- + p : array + polynomial parameters + X : array + profile of the trajectory X[i, j] is the ith derivative of the profile at time t[j] + t : array + time vector """ ################################################ - def __init__(self, tf=10, N=5, x0=None, xf=None): + def __init__( + self, + tf=10, + poly_N=5, + diff_N=7, + x0=np.array([0.0, 0.0, 0.0]), + xf=np.array([0.0, 0.0, 10.0]), + dt=0.01, + ): self.tf = tf - self.poly_N = N - self.diff_N = N + self.poly_N = poly_N + self.diff_N = diff_N self.x0 = x0 self.xf = xf + self.x0_N = x0.shape[0] + self.xf_N = xf.shape[0] + self.Rs = np.zeros((self.poly_N+1)) + self.Ws = np.zeros((self.diff_N)) + self.dt = dt - self.bc_t0_N = 3 - self.bc_tf_N = 3 - - # Optimization - self.regulation = 0.0 - self.Cr = np.array([0, 0, 0, 0.0]) + # Outputs + self.t = None + self.X = None + self.p = None self.labels = [ "pos", @@ -61,38 +116,26 @@ def __init__(self, tf=10, N=5, x0=None, xf=None): ] ################################################ - def compute_b(self): - - x0 = self.x0 - xf = self.xf - N0 = self.bc_t0_N - Nf = self.bc_tf_N + def compute_b(self, x0, xf, N0, Nf): + """Compute the boundary condition vector b = [x0;xf] which represents the initial and final conditions on the trajectory and its derivatives""" b = np.hstack((x0[:N0], xf[:Nf])) - print(r"[x(0), \dot{x}(x0), \ddot{x}(x0), ...] = ", x0) - print(r"[x(tf), \dot{x}(tf), \ddot{x}(x0), ...] = ", xf) - print("Boundary condition vector: \n", b) + print("Boundary condition vector b = [x0;xf]: \n", b) - self.b = b + return b ################################################ - def compute_A(self): + def compute_A(self, tf, N0, Nf, poly_N): + """Compute the boundary condition matrix A which represents on the polynomial parameters are related to the boundary conditions""" - tf = self.tf - x0 = self.x0 - xf = self.xf - N0 = self.bc_t0_N - Nf = self.bc_tf_N - N = self.poly_N - - A = np.zeros((N0 + Nf, N + 1)) + A = np.zeros((N0 + Nf, poly_N + 1)) # For all jth derivative of the initial conditions t0 = 0 for j in range(N0): # For all terms of the polynomical - for n in range(j, N + 1): + for n in range(j, poly_N + 1): exp = n - j mul = 1 for k in range(j): @@ -102,7 +145,7 @@ def compute_A(self): # For all jth derivative of the final conditions for j in range(Nf): # For all terms of the polynomical - for n in range(j, N + 1): + for n in range(j, poly_N + 1): exp = n - j mul = 1 for k in range(j): @@ -111,68 +154,61 @@ def compute_A(self): print("Boundary condition matrix: \n", A) - self.A = A + return A ################################################ - def compute_Q(self): - - N = self.poly_N - R = self.diff_N + def compute_Q(self, poly_N, diff_N, tf, Ws, Rs): + """Compute the cost function matrix Q, only used if the boundary conditions do not fully specify the parameters of the polynomial""" - C = np.zeros((R)) - C[:self.Cr.shape[0]] = self.Cr + # Quadratic cost matrix + Q = np.zeros((poly_N + 1, poly_N + 1)) - Q = np.zeros((N + 1, N + 1, R)) - - tf = self.tf + # Quadratic cost matrix for each derivative + Qs = np.zeros((poly_N + 1, poly_N + 1, diff_N)) + # Qs are weight corresponding to computing the integral of the square of the ith derivative of the profile + # J = p.T @ Qs[i] @ p = integral( [ d_dt(ith)x(t) ]^2 dt) # see https://groups.csail.mit.edu/rrg/papers/BryIJRR15.pdf - # TODO debug - for r in range(R): - for i in range(N + 1): - for l in range(N + 1): + for r in range(diff_N): + for i in range(poly_N + 1): + for l in range(poly_N + 1): if (i >= r) and (l >= r): mul = 1 for m in range(r): - mul = mul * (i-m)*(l-m) + mul = mul * (i - m) * (l - m) exp = i + l - 2 * r + 1 - Q[i, l, r] = 2 * mul * tf**exp / (i+l-2*r+1) + Qs[i, l, r] = 2 * mul * tf**exp / (i + l - 2 * r + 1) else: - Q[i, l, r] = 0 - - Q_sum = np.zeros((N + 1, N + 1)) + Qs[i, l, r] = 0 - for r in range(R): - Q_sum = Q_sum + C[r] * Q[:, :, r] - - # Regulation - Q_sum = Q_sum + self.regulation * np.eye(N + 1) + # Total cost for all derivatives + for r in range(diff_N): + Q = Q + Ws[r] * Qs[:, :, r] - self.Q = Q_sum + # Regulation term penalizing the polynomial parameters directly + Q = Q + np.diag(Rs) return Q ################################################ - def constraints(self, p): - - res = self.A @ p - self.b - - return res - - ################################################ - def cost(self, p): - - Q = self.Q - - J = p.T @ Q @ p + def solve_for_polynomial_parameters(self, A, b, Q): + """Solve for the polynomial parameters pç - return J + Parameters: + ----------- + A : array + boundary condition matrix + b : array + boundary condition vector + Q : array + cost function matrix - ################################################ - def compute_parameters(self): + Output: + ------- + p : array + polynomial parameters - A = self.A - b = self.b + """ if A.shape[0] == A.shape[1]: @@ -193,17 +229,16 @@ def compute_parameters(self): print("Optimization over free decision variables") - self.compute_Q() - - p0 = np.zeros(self.poly_N + 1) - - constraints = {"type": "eq", "fun": self.constraints} + p0 = np.zeros(A.shape[1]) - grad = lambda p: 2 * p.T @ self.Q; - hess = lambda p: 2 * self.Q; + constraints = {"type": "eq", "fun": lambda p: A @ p - b} + cost = lambda p: p.T @ Q @ p + grad = lambda p: 2 * p.T @ Q + hess = lambda p: 2 * Q + # TODO: Change to a solver specifc to quadratic optimization res = minimize( - self.cost, + cost, p0, method="SLSQP", jac=grad, @@ -214,35 +249,27 @@ def compute_parameters(self): p = res.x - # p = np.linalg.lstsq(A, b)[0] + print("Computed polynomial parameters: \n", p) - print("Polynomial parameters: \n", p) - - self.p = p + return p ################################################ - def generate_trajectory(self, dt=0.01): - - p = self.p + def generate_trajectory(self, tf, p, diff_N, dt=0.01): - N = self.poly_N # order of polynomial - - steps = int(self.tf / dt) - ts = np.linspace(0, self.tf, steps) - - m = self.diff_N # number of derivative to compute - - X = np.zeros((m, steps)) + Np1 = p.shape[0] # order of polynomial + steps = int(tf / dt) # number of time steps + ts = np.linspace(0, tf, steps) + X = np.zeros((diff_N, steps)) # For all jth derivative of the signal - for j in range(m): + for j in range(diff_N): # For all time steps for i in range(steps): t = ts[i] x = 0 # For all terms of the polynomical # TODO could replace this with A(t) generic code - for n in range(j, N + 1): + for n in range(j, Np1): p_n = p[n] exp = n - j mul = 1 @@ -252,17 +279,13 @@ def generate_trajectory(self, dt=0.01): X[j, i] = x - self.X = X - self.t = ts + return X, ts ################################################ - def plot_trajectory(self, n_fig=None): - - X = self.X - t = self.t + def plot_trajectory(self, X, t, n_fig=None): + # Number of derivatives to plot n_max = X.shape[0] - if n_fig is None: n = n_max elif n_fig < n_max: @@ -272,8 +295,8 @@ def plot_trajectory(self, n_fig=None): fig, ax = plt.subplots( n, - figsize=graphical.default_figsize, - dpi=graphical.default_dpi, + figsize=default_figsize, + dpi=default_dpi, frameon=True, ) @@ -283,25 +306,42 @@ def plot_trajectory(self, n_fig=None): for i in range(n): ax[i].plot(t, X[i, :], "b") - ax[i].set_ylabel(self.labels[i], fontsize=graphical.default_fontsize) - ax[i].tick_params(labelsize=graphical.default_fontsize) + ax[i].set_ylabel(self.labels[i], fontsize=default_fontsize) + ax[i].tick_params(labelsize=default_fontsize) ax[i].grid(True) - ax[-1].set_xlabel("Time[sec]", fontsize=graphical.default_fontsize) + ax[-1].set_xlabel("Time[sec]", fontsize=default_fontsize) # fig.tight_layout() fig.canvas.draw() plt.show() ################################################ - def solve(self): + def solve(self, show = True): + + tf = self.tf + x0 = self.x0 + xf = self.xf + N0 = self.x0_N + Nf = self.xf_N + Np = self.poly_N + Nd = self.diff_N + Ws = self.Ws + Rs = self.Rs + dt = self.dt - self.compute_b() - self.compute_A() + b = self.compute_b(x0, xf, N0, Nf) + A = self.compute_A(tf, N0, Nf, Np) + Q = self.compute_Q(Np, Nd, tf, Ws, Rs) - self.compute_parameters() - self.generate_trajectory() - self.plot_trajectory() + p = self.solve_for_polynomial_parameters(A, b, Q) + + X, t = self.generate_trajectory(tf, p, Nd, dt) + + if show: + self.plot_trajectory(X, t) + + return p, X, t """ @@ -314,58 +354,61 @@ def solve(self): if __name__ == "__main__": """MAIN TEST""" - ge = SingleAxisTrajectoryGenerator() + ge = SingleAxisPolynomialTrajectoryGenerator() ge.x0 = np.array([0, 0, 0, 0, 0, 0, 0, 0]) ge.xf = np.array([10, 0, 0, 0, 0, 0, 0, 0]) - # ge.bc_t0_N = 2 - # ge.bc_tf_N = 2 + # ge.x0_N = 2 + # ge.xf_N = 2 # ge.poly_N = 3 # ge.diff_N = 3 # ge.solve() - ge.bc_t0_N = 3 - ge.bc_tf_N = 3 - ge.poly_N = 9 + ge.x0_N = 3 + ge.xf_N = 3 + ge.poly_N = 5 ge.diff_N = 7 - ge.regulation = 0. - ge.Cr = np.array([0, 0.0, 1.0, 1.0, 1.0, 0, 0]) + ge.solve() # order 5 fully constrained + + ge.poly_N = 12 + ge.Rs = 0.0 * np.ones(ge.poly_N + 1) + ge.Ws = np.array([0, 0.0, 10.0, 1.0, 1.0, 1.0, 1.0]) - ge.solve() + ge.solve() # order 12 with optimization on polynomial parameters - # ge.bc_t0_N = 4 - # ge.bc_tf_N = 4 + # ge.x0_N = 4 + # ge.xf_N = 4 # ge.poly_N = 7 # ge.diff_N = 7 # ge.solve() - # ge.bc_t0_N = 5 - # ge.bc_tf_N = 5 + # ge.x0_N = 5 + # ge.xf_N = 5 # ge.poly_N = 9 # ge.diff_N = 7 # ge.solve() - # ge.bc_t0_N = 6 - # ge.bc_tf_N = 6 + # ge.x0_N = 6 + # ge.xf_N = 6 # ge.poly_N = 11 # ge.diff_N = 7 # ge.solve() - # ge.bc_t0_N = 7 - # ge.bc_tf_N = 7 + # ge.x0_N = 7 + # ge.xf_N = 7 # ge.poly_N = 13 # ge.diff_N = 7 # ge.solve() - # ge.bc_t0_N = 1 - # ge.bc_tf_N = 1 + # ge.x0_N = 1 + # ge.xf_N = 1 # ge.poly_N = 3 # ge.diff_N = 7 From c03f72ff45438c005b97d2dc08eaa49d706ee5ea Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Thu, 21 Mar 2024 10:38:27 -0400 Subject: [PATCH 81/93] added method to compute directly the jth derivative at time t based on the vector of parameter p --- pyro/planning/trajectorygeneration.py | 94 +++++++++++++++++++-------- 1 file changed, 66 insertions(+), 28 deletions(-) diff --git a/pyro/planning/trajectorygeneration.py b/pyro/planning/trajectorygeneration.py index 9aaed2a1..d91b4dbe 100644 --- a/pyro/planning/trajectorygeneration.py +++ b/pyro/planning/trajectorygeneration.py @@ -17,6 +17,7 @@ # Import standard graphical parameters if part of pyro try: from pyro.analysis import graphical + default_figsize = graphical.default_figsize default_dpi = graphical.default_dpi default_fontsize = graphical.default_fontsize @@ -92,7 +93,7 @@ def __init__( self.xf = xf self.x0_N = x0.shape[0] self.xf_N = xf.shape[0] - self.Rs = np.zeros((self.poly_N+1)) + self.Rs = np.zeros((self.poly_N + 1)) self.Ws = np.zeros((self.diff_N)) self.dt = dt @@ -186,7 +187,7 @@ def compute_Q(self, poly_N, diff_N, tf, Ws, Rs): Q = Q + Ws[r] * Qs[:, :, r] # Regulation term penalizing the polynomial parameters directly - Q = Q + np.diag(Rs) + Q = Q + np.diag(Rs[: (poly_N + 1)]) return Q @@ -255,9 +256,10 @@ def solve_for_polynomial_parameters(self, A, b, Q): ################################################ def generate_trajectory(self, tf, p, diff_N, dt=0.01): + """Generate a numerical trajectory based on the polynomial parameters""" Np1 = p.shape[0] # order of polynomial - steps = int(tf / dt) # number of time steps + steps = int(tf / dt) # number of time steps ts = np.linspace(0, tf, steps) X = np.zeros((diff_N, steps)) @@ -281,6 +283,24 @@ def generate_trajectory(self, tf, p, diff_N, dt=0.01): return X, ts + ################################################ + def get_trajectory(self, j, t, p): + """Get the jth derivative of the trajectory at time t based on the polynomial parameters p""" + + Np1 = p.shape[0] # order of polynomial + x = 0 + + # For all terms of the polynomical + for n in range(j, Np1): + p_n = p[n] + exp = n - j + mul = 1 + for k in range(j): + mul = mul * (n - k) + x = x + mul * p_n * t**exp + + return x + ################################################ def plot_trajectory(self, X, t, n_fig=None): @@ -317,7 +337,7 @@ def plot_trajectory(self, X, t, n_fig=None): plt.show() ################################################ - def solve(self, show = True): + def solve(self, show=True): tf = self.tf x0 = self.x0 @@ -354,10 +374,16 @@ def solve(self, show = True): if __name__ == "__main__": """MAIN TEST""" - ge = SingleAxisPolynomialTrajectoryGenerator() + x0 = np.array([0, 0, 0, 0, 0, 0, 0, 0]) + xf = np.array([10, 0, 0, 0, 0, 0, 0, 0]) + + ge = SingleAxisPolynomialTrajectoryGenerator( + x0=x0, xf=xf, tf=10, poly_N=5, diff_N=7, dt=0.01 + ) - ge.x0 = np.array([0, 0, 0, 0, 0, 0, 0, 0]) - ge.xf = np.array([10, 0, 0, 0, 0, 0, 0, 0]) + ############################# + ### Fully constrained order 3 + ############################# # ge.x0_N = 2 # ge.xf_N = 2 @@ -366,6 +392,10 @@ def solve(self, show = True): # ge.solve() + ############################# + ### Fully constrained order 5 + ############################# + ge.x0_N = 3 ge.xf_N = 3 ge.poly_N = 5 @@ -373,43 +403,51 @@ def solve(self, show = True): ge.solve() # order 5 fully constrained + ########################################### + ### Optimization on polynomial parameters + ########################################### + ge.poly_N = 12 ge.Rs = 0.0 * np.ones(ge.poly_N + 1) ge.Ws = np.array([0, 0.0, 10.0, 1.0, 1.0, 1.0, 1.0]) - ge.solve() # order 12 with optimization on polynomial parameters + p, X, t = ge.solve() # order 12 with optimization on polynomial parameters + + ############################# + ### Fully constrained order 7 + ############################# + + # ge = SingleAxisPolynomialTrajectoryGenerator( + # x0=x0, xf=xf, tf=10, poly_N=7, diff_N=7, dt=0.01 + # ) # ge.x0_N = 4 # ge.xf_N = 4 - # ge.poly_N = 7 - # ge.diff_N = 7 # ge.solve() - # ge.x0_N = 5 - # ge.xf_N = 5 - # ge.poly_N = 9 - # ge.diff_N = 7 + ############################# + ### Fully constrained order 9 + ############################# - # ge.solve() + # ge = SingleAxisPolynomialTrajectoryGenerator( + # x0=x0, xf=xf, tf=10, poly_N=9, diff_N=7, dt=0.01 + # ) - # ge.x0_N = 6 - # ge.xf_N = 6 - # ge.poly_N = 11 - # ge.diff_N = 7 + # ge.x0_N = 5 + # ge.xf_N = 5 # ge.solve() - # ge.x0_N = 7 - # ge.xf_N = 7 - # ge.poly_N = 13 - # ge.diff_N = 7 + ############################# + ### Overconstrained order 3 + ############################# - # ge.solve() + # ge = SingleAxisPolynomialTrajectoryGenerator( + # x0=x0, xf=xf, tf=10, poly_N=3, diff_N=7, dt=0.01 + # ) - # ge.x0_N = 1 - # ge.xf_N = 1 - # ge.poly_N = 3 - # ge.diff_N = 7 + # ge.x0_N = 3 + # ge.xf_N = 3 # ge.solve() From 869e76d17aad817817ed7ee3b5c739ca15e3313e Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Thu, 21 Mar 2024 11:01:33 -0400 Subject: [PATCH 82/93] ajusted min snap traj exemple --- dev/differentialflatness/rigidbodytest.py | 80 ++++++++++++----------- pyro/planning/trajectorygeneration.py | 5 +- 2 files changed, 44 insertions(+), 41 deletions(-) diff --git a/dev/differentialflatness/rigidbodytest.py b/dev/differentialflatness/rigidbodytest.py index f4d6c4d6..cf85a7d9 100644 --- a/dev/differentialflatness/rigidbodytest.py +++ b/dev/differentialflatness/rigidbodytest.py @@ -21,41 +21,41 @@ # fixed final position for now xf = 0.0 yf = 0.0 -zf = 0.0 #np.pi / 2 +zf = 0.0 # np.pi / 2 tf = 10 -ddx0 = 0.0 -ddy0 = -1.0#ddx0 * np.tan(z0) - -ddxf = 1.0 -ddyf =+0.0 # ddxf * np.tan(zf) - -bc_y = np.array([y0, 0, ddy0, yf, 0, ddyf]) - -gex = trajectorygeneration.SingleAxisTrajectoryGenerator(N=9) -gex.bc_t0_N = 5 -gex.bc_tf_N = 5 -gex.x0 = np.array([x0, 0, ddx0,0,0,0]) -gex.xf = np.array([xf, 0, ddxf,0,0,0]) -gex.solve() -x = gex.X[0, :] -dx = gex.X[1, :] -ax = gex.X[2, :] -dax = gex.X[3, :] -ddax = gex.X[4, :] -t = gex.t - -gey = trajectorygeneration.SingleAxisTrajectoryGenerator(N=9) -gey.bc_t0_N = 5 -gey.bc_tf_N = 5 -gey.x0 = np.array([y0, 0, ddy0,0,0,0]) -gey.xf = np.array([yf, 0, ddyf,0,0,0]) -gey.solve() -y = gey.X[0, :] -dy = gey.X[1, :] -ay = gey.X[2, :] -day = gey.X[3, :] -dday = gex.X[4, :] +ddx0 = 1.0 +ddy0 = -0.0 # ddx0 * np.tan(z0) + +ddxf = 0.0 +ddyf = -0.2 # ddxf * np.tan(zf) + + +gex = trajectorygeneration.SingleAxisPolynomialTrajectoryGenerator(poly_N=9) +gex.x0_N = 3 +gex.xf_N = 3 +gex.x0 = np.array([x0, 0, ddx0, 0, 0, 0]) +gex.xf = np.array([xf, 0, ddxf, 0, 0, 0]) +gex.Ws = np.array([0, 0.0, 10.0, 1.0, 100.0, 1.0, 1.0]) +px, X, t = gex.solve() +x = X[0, :] +dx = X[1, :] +ax = X[2, :] +dax = X[3, :] +ddax = X[4, :] + +gey = trajectorygeneration.SingleAxisPolynomialTrajectoryGenerator(poly_N=9) +gey.x0_N = 3 +gey.xf_N = 3 +gey.x0 = np.array([y0, 0, ddy0, 0, 0, 0]) +gey.xf = np.array([yf, 0, ddyf, 0, 0, 0]) +gey.Ws = np.array([0, 0.0, 10.0, 1.0, 100.0, 1.0, 1.0]) +py, Y, t = gey.solve() +y = Y[0, :] +dy = Y[1, :] +ay = Y[2, :] +day = Y[3, :] +dday = Y[4, :] # Position theta theta = np.arctan2(ay, ax) @@ -102,12 +102,18 @@ M = np.array([[m, 0], [0, m]]) -ax_cg = ax + J / (m * r) * np.sin(theta) * ddtheta + J / (m * r) * np.cos(theta) * dtheta**2 -ay_cg = ay - J / (m * r) * np.cos(theta) * ddtheta + J / (m * r) * np.sin(theta) * dtheta**2 +ax_cg = ( + ax + J / (m * r) * np.sin(theta) * ddtheta + J / (m * r) * np.cos(theta) * dtheta**2 +) +ay_cg = ( + ay - J / (m * r) * np.cos(theta) * ddtheta + J / (m * r) * np.sin(theta) * dtheta**2 +) # COmpute forces for i in range(steps): - R = np.array([[np.cos(theta[i]), -np.sin(theta[i])], [np.sin(theta[i]), np.cos(theta[i])]]) + R = np.array( + [[np.cos(theta[i]), -np.sin(theta[i])], [np.sin(theta[i]), np.cos(theta[i])]] + ) a_cg = np.array([ax_cg[i], ay_cg[i]]) us[i, :] = np.linalg.inv(R) @ M @ a_cg @@ -170,7 +176,7 @@ axes.set_ylabel("y", fontsize=graphical.default_fontsize) axes.set_xlabel("x", fontsize=graphical.default_fontsize) axes.axis("equal") -axes.set(xlim=(-10, 10), ylim=(-10, 10)) +axes.set(xlim=(-20, 10), ylim=(-20, 10)) axes.tick_params(labelsize=graphical.default_fontsize) axes.grid(True) diff --git a/pyro/planning/trajectorygeneration.py b/pyro/planning/trajectorygeneration.py index d91b4dbe..5019b5d3 100644 --- a/pyro/planning/trajectorygeneration.py +++ b/pyro/planning/trajectorygeneration.py @@ -8,11 +8,8 @@ import numpy as np import matplotlib.pyplot as plt - -import warnings - - from scipy.optimize import minimize +import warnings # Import standard graphical parameters if part of pyro try: From d4117feb02f687a745b5cb3ee85122471d7a92a6 Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Fri, 22 Mar 2024 17:46:45 -0400 Subject: [PATCH 83/93] fixed error in eqs --- dev/differentialflatness/rigidbodytest.py | 18 ++++++++++-------- pyro/planning/trajectorygeneration.py | 3 ++- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/dev/differentialflatness/rigidbodytest.py b/dev/differentialflatness/rigidbodytest.py index cf85a7d9..c5ed5964 100644 --- a/dev/differentialflatness/rigidbodytest.py +++ b/dev/differentialflatness/rigidbodytest.py @@ -21,14 +21,14 @@ # fixed final position for now xf = 0.0 yf = 0.0 -zf = 0.0 # np.pi / 2 -tf = 10 +zf = np.pi +tf = 5 -ddx0 = 1.0 -ddy0 = -0.0 # ddx0 * np.tan(z0) +ddx0 = 1.00 +ddy0 = 0.0# ddx0 * np.tan(z0) ddxf = 0.0 -ddyf = -0.2 # ddxf * np.tan(zf) +ddyf = -1. #-ddxf * np.tan(zf) gex = trajectorygeneration.SingleAxisPolynomialTrajectoryGenerator(poly_N=9) @@ -36,7 +36,8 @@ gex.xf_N = 3 gex.x0 = np.array([x0, 0, ddx0, 0, 0, 0]) gex.xf = np.array([xf, 0, ddxf, 0, 0, 0]) -gex.Ws = np.array([0, 0.0, 10.0, 1.0, 100.0, 1.0, 1.0]) +gex.poly_N = 7 +gex.Ws = np.array([0, 1.0, 1.0, 1.0, 10.0, 1.0, 1.0]) px, X, t = gex.solve() x = X[0, :] dx = X[1, :] @@ -49,7 +50,8 @@ gey.xf_N = 3 gey.x0 = np.array([y0, 0, ddy0, 0, 0, 0]) gey.xf = np.array([yf, 0, ddyf, 0, 0, 0]) -gey.Ws = np.array([0, 0.0, 10.0, 1.0, 100.0, 1.0, 1.0]) +gey.poly_N = 7 +gey.Ws = np.array([0, 1.0, 1.0, 1.0, 10.0, 1.0, 1.0]) py, Y, t = gey.solve() y = Y[0, :] dy = Y[1, :] @@ -62,7 +64,7 @@ # theta = np.arctan( (ay/ax)) s = np.sin(theta) c = np.cos(theta) -dtheta = (day * c - dax * s) / (ax * s + ay * c) +dtheta = (day * c - dax * s) / (ay * s + ax * c) # TODO check analytical equation, seems wrong ddtheta = ( s * (-ddax + ax * dtheta**2 - 2 * day * dtheta) + c * (dday - ay * dtheta**2 - 2 * dax * dtheta) diff --git a/pyro/planning/trajectorygeneration.py b/pyro/planning/trajectorygeneration.py index 5019b5d3..60aff733 100644 --- a/pyro/planning/trajectorygeneration.py +++ b/pyro/planning/trajectorygeneration.py @@ -406,7 +406,8 @@ def solve(self, show=True): ge.poly_N = 12 ge.Rs = 0.0 * np.ones(ge.poly_N + 1) - ge.Ws = np.array([0, 0.0, 10.0, 1.0, 1.0, 1.0, 1.0]) + #ge.Ws = np.array([0, 0.0, 10.0, 1.0, 1.0, 1.0, 1.0]) + ge.Ws = np.array([0, 0.0, 1.0, 0,0,0,0]) p, X, t = ge.solve() # order 12 with optimization on polynomial parameters From c7178deb1526a30a40440b1ee035fb27af3d704f Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Mon, 25 Mar 2024 21:53:22 -0400 Subject: [PATCH 84/93] added min snap traj for drone test --- dev/differentialflatness/droneminsnap.py | 166 ++++++++++++++++++++++ dev/differentialflatness/rigidbodytest.py | 18 +-- pyro/planning/trajectorygeneration.py | 38 ++--- 3 files changed, 195 insertions(+), 27 deletions(-) create mode 100644 dev/differentialflatness/droneminsnap.py diff --git a/dev/differentialflatness/droneminsnap.py b/dev/differentialflatness/droneminsnap.py new file mode 100644 index 00000000..2eede6e7 --- /dev/null +++ b/dev/differentialflatness/droneminsnap.py @@ -0,0 +1,166 @@ +# -*- coding: utf-8 -*- + +############################################################################### +import numpy as np +import matplotlib.pyplot as plt + +from pyro.analysis import graphical +from pyro.planning import trajectorygeneration +from pyro.dynamic import drone +from pyro.planning import plan + +############################################################################### +# Define the c.g. trajectory +############################################################################### + +# initial position +x0 = -10.0 +y0 = 0.0 + +# final position +xf = 20.0 +yf = 1.0 + +# time of flight +tf = 4.0 + +traj_x = trajectorygeneration.SingleAxisPolynomialTrajectoryGenerator( + poly_N=9, tf=tf, x0=np.array([x0, 0, 0]), xf=np.array([xf, 0, 0]) +) + +traj_x.Ws[4] = 1.0 # set the weight of the snap term to 1 + +px, X, t = traj_x.solve() # find the min snap trajectory + +x = X[0, :] +dx = X[1, :] +ax = X[2, :] +dax = X[3, :] +ddax = X[4, :] + +traj_y = trajectorygeneration.SingleAxisPolynomialTrajectoryGenerator( + poly_N=9, tf=tf, x0=np.array([y0, 0, 0]), xf=np.array([yf, 0, 0]) +) + +traj_y.Ws[4] = 1.0 # set the weight of the snap term to 1 + +py, Y, t = traj_y.solve() # find the min snap trajectory + +y = Y[0, :] +dy = Y[1, :] +ay = Y[2, :] + 9.81 # added gravity +day = Y[3, :] +dday = Y[4, :] + +############################################################################### +# Compute angular trajectory +############################################################################### + +theta = np.arctan2(ax, ay) +# theta = np.arctan( (ay/ax)) +s = np.sin(theta) +c = np.cos(theta) +dtheta = (dax * c - day * s) / (ax * s + ay * c) +ddtheta = ( + s * (-dday + ay * dtheta**2 - 2 * dax * dtheta) + + c * (ddax - ax * dtheta**2 - 2 * day * dtheta) +) / (ay * c + ax * s) + +############################################################################### +# Compute Forces +############################################################################### + +sys = drone.Drone2D() + +m = sys.mass +J = sys.inertia +r = sys.truster_offset + +F_tot = m * ax / np.sin(theta) +F_del = J * ddtheta / r + +T1 = 0.5 * (F_tot + F_del) +T2 = 0.5 * (F_tot - F_del) + +# for i in range(steps): +# M = np.array([[1, 1], [-1, 1]]) +# f = np.array([F_tot[i], F_del[i]]) +# us[i, :] = np.linalg.inv(M) @ f + + +############################################################################### +# Plots +############################################################################### + +# Create traj +steps = len(t) +us = np.zeros((steps, 2)) # control inputs +xs = np.zeros((steps, 6)) +ys = np.zeros((steps, 6)) +dxs = np.zeros((steps, 6)) + +xs[:, 0] = x +xs[:, 1] = y +xs[:, 2] = -theta +xs[:, 3] = dx +xs[:, 4] = dy +xs[:, 5] = -dtheta + +us[:, 0] = T1 +us[:, 1] = T2 + +traj = plan.Trajectory(xs, us, t, dxs, ys) + + +fig, axes = plt.subplots( + 3, figsize=graphical.default_figsize, dpi=graphical.default_dpi, frameon=True +) + +axes[0].plot(t, theta, "b") +axes[0].set_ylabel("Theta", fontsize=graphical.default_fontsize) +axes[0].set_xlabel("v", fontsize=graphical.default_fontsize) +axes[0].tick_params(labelsize=graphical.default_fontsize) +axes[0].grid(True) + +axes[1].plot(t, dtheta, "b") +# axes[1].plot(t, dtheta_num, "r") +axes[1].set_ylabel("w", fontsize=graphical.default_fontsize) +axes[1].set_xlabel("t", fontsize=graphical.default_fontsize) +axes[1].tick_params(labelsize=graphical.default_fontsize) +axes[1].grid(True) + +axes[2].plot(t, ddtheta, "b") +# axes[2].plot(t, ddtheta_num, "r") +axes[2].set_ylabel("dw", fontsize=graphical.default_fontsize) +axes[2].set_xlabel("t", fontsize=graphical.default_fontsize) +axes[2].tick_params(labelsize=graphical.default_fontsize) +axes[2].grid(True) + +fig.tight_layout() +fig.canvas.draw() + +plt.show() + + +fig, axes = plt.subplots( + 1, figsize=graphical.default_figsize, dpi=graphical.default_dpi, frameon=True +) + +axes.plot(x, y, "r") +axes.set_ylabel("y", fontsize=graphical.default_fontsize) +axes.set_xlabel("x", fontsize=graphical.default_fontsize) +axes.axis("equal") +axes.set(xlim=(-25, 25), ylim=(-25, 25)) +axes.tick_params(labelsize=graphical.default_fontsize) +axes.grid(True) + +fig.tight_layout() +fig.canvas.draw() + +plt.show() + + +sys.traj = traj + +sys.plot_trajectory("xu") +sys.animate_simulation() diff --git a/dev/differentialflatness/rigidbodytest.py b/dev/differentialflatness/rigidbodytest.py index c5ed5964..d97eec91 100644 --- a/dev/differentialflatness/rigidbodytest.py +++ b/dev/differentialflatness/rigidbodytest.py @@ -14,21 +14,21 @@ # fixed initial position for now # initial angular velocity is related to jerk of trajectory -x0 = -10.0 -y0 = -2.0 +x0 = 0.0 +y0 = 0.0 z0 = 0.0 # fixed final position for now -xf = 0.0 -yf = 0.0 +xf = 10.0 +yf = 10.0 zf = np.pi tf = 5 -ddx0 = 1.00 +ddx0 = xf * 0.1 ddy0 = 0.0# ddx0 * np.tan(z0) ddxf = 0.0 -ddyf = -1. #-ddxf * np.tan(zf) +ddyf = yf * -0.1#-ddxf * np.tan(zf) gex = trajectorygeneration.SingleAxisPolynomialTrajectoryGenerator(poly_N=9) @@ -37,7 +37,7 @@ gex.x0 = np.array([x0, 0, ddx0, 0, 0, 0]) gex.xf = np.array([xf, 0, ddxf, 0, 0, 0]) gex.poly_N = 7 -gex.Ws = np.array([0, 1.0, 1.0, 1.0, 10.0, 1.0, 1.0]) +gex.Ws = np.array([0, 1.0, 1.0, 1.0, 1.0, 1.0, .0]) px, X, t = gex.solve() x = X[0, :] dx = X[1, :] @@ -51,7 +51,7 @@ gey.x0 = np.array([y0, 0, ddy0, 0, 0, 0]) gey.xf = np.array([yf, 0, ddyf, 0, 0, 0]) gey.poly_N = 7 -gey.Ws = np.array([0, 1.0, 1.0, 1.0, 10.0, 1.0, 1.0]) +gey.Ws = np.array([0, 1.0, 1.0, 1.0, 1.0, 1.0, .0]) py, Y, t = gey.solve() y = Y[0, :] dy = Y[1, :] @@ -178,7 +178,7 @@ axes.set_ylabel("y", fontsize=graphical.default_fontsize) axes.set_xlabel("x", fontsize=graphical.default_fontsize) axes.axis("equal") -axes.set(xlim=(-20, 10), ylim=(-20, 10)) +axes.set(xlim=(-5, 25), ylim=(-5, 25)) axes.tick_params(labelsize=graphical.default_fontsize) axes.grid(True) diff --git a/pyro/planning/trajectorygeneration.py b/pyro/planning/trajectorygeneration.py index 60aff733..0ab74017 100644 --- a/pyro/planning/trajectorygeneration.py +++ b/pyro/planning/trajectorygeneration.py @@ -375,39 +375,41 @@ def solve(self, show=True): xf = np.array([10, 0, 0, 0, 0, 0, 0, 0]) ge = SingleAxisPolynomialTrajectoryGenerator( - x0=x0, xf=xf, tf=10, poly_N=5, diff_N=7, dt=0.01 + x0=x0, xf=xf, tf=10, poly_N=12, diff_N=7, dt=0.01 ) ############################# ### Fully constrained order 3 ############################# - # ge.x0_N = 2 - # ge.xf_N = 2 - # ge.poly_N = 3 - # ge.diff_N = 3 + ge.x0_N = 2 + ge.xf_N = 2 + ge.poly_N = 3 + ge.diff_N = 7 - # ge.solve() + ge.solve() ############################# ### Fully constrained order 5 ############################# - ge.x0_N = 3 - ge.xf_N = 3 - ge.poly_N = 5 - ge.diff_N = 7 + # ge.x0_N = 3 + # ge.xf_N = 3 + # ge.poly_N = 5 + # ge.diff_N = 7 - ge.solve() # order 5 fully constrained + # ge.solve() # order 5 fully constrained ########################################### ### Optimization on polynomial parameters ########################################### ge.poly_N = 12 + ge.x0_N = 3 + ge.xf_N = 3 ge.Rs = 0.0 * np.ones(ge.poly_N + 1) #ge.Ws = np.array([0, 0.0, 10.0, 1.0, 1.0, 1.0, 1.0]) - ge.Ws = np.array([0, 0.0, 1.0, 0,0,0,0]) + ge.Ws = np.array([0, 1.0, 0.0, 0, 0.0,0,0]) p, X, t = ge.solve() # order 12 with optimization on polynomial parameters @@ -428,14 +430,14 @@ def solve(self, show=True): ### Fully constrained order 9 ############################# - # ge = SingleAxisPolynomialTrajectoryGenerator( - # x0=x0, xf=xf, tf=10, poly_N=9, diff_N=7, dt=0.01 - # ) + ge = SingleAxisPolynomialTrajectoryGenerator( + x0=x0, xf=xf, tf=10, poly_N=9, diff_N=7, dt=0.01 + ) - # ge.x0_N = 5 - # ge.xf_N = 5 + ge.x0_N = 5 + ge.xf_N = 5 - # ge.solve() + ge.solve() ############################# ### Overconstrained order 3 From 13cbcaeef510a2f246fc5dd18f6b4c37bec9ea05 Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Tue, 26 Mar 2024 11:54:56 -0400 Subject: [PATCH 85/93] working beta version of polynomial traj with waypoints --- dev/differentialflatness/droneminsnap.py | 6 - dev/differentialflatness/rigidbodytest.py | 4 +- pyro/planning/trajectorygeneration.py | 341 ++++++++++++++++++++-- 3 files changed, 320 insertions(+), 31 deletions(-) diff --git a/dev/differentialflatness/droneminsnap.py b/dev/differentialflatness/droneminsnap.py index 2eede6e7..d496eefd 100644 --- a/dev/differentialflatness/droneminsnap.py +++ b/dev/differentialflatness/droneminsnap.py @@ -82,12 +82,6 @@ T1 = 0.5 * (F_tot + F_del) T2 = 0.5 * (F_tot - F_del) -# for i in range(steps): -# M = np.array([[1, 1], [-1, 1]]) -# f = np.array([F_tot[i], F_del[i]]) -# us[i, :] = np.linalg.inv(M) @ f - - ############################################################################### # Plots ############################################################################### diff --git a/dev/differentialflatness/rigidbodytest.py b/dev/differentialflatness/rigidbodytest.py index d97eec91..44e8910b 100644 --- a/dev/differentialflatness/rigidbodytest.py +++ b/dev/differentialflatness/rigidbodytest.py @@ -37,7 +37,7 @@ gex.x0 = np.array([x0, 0, ddx0, 0, 0, 0]) gex.xf = np.array([xf, 0, ddxf, 0, 0, 0]) gex.poly_N = 7 -gex.Ws = np.array([0, 1.0, 1.0, 1.0, 1.0, 1.0, .0]) +#gex.Ws = np.array([0, 1.0, 1.0, 1.0, 1.0, 1.0, .0]) px, X, t = gex.solve() x = X[0, :] dx = X[1, :] @@ -51,7 +51,7 @@ gey.x0 = np.array([y0, 0, ddy0, 0, 0, 0]) gey.xf = np.array([yf, 0, ddyf, 0, 0, 0]) gey.poly_N = 7 -gey.Ws = np.array([0, 1.0, 1.0, 1.0, 1.0, 1.0, .0]) +#gey.Ws = np.array([0, 1.0, 1.0, 1.0, 1.0, 1.0, .0]) py, Y, t = gey.solve() y = Y[0, :] dy = Y[1, :] diff --git a/pyro/planning/trajectorygeneration.py b/pyro/planning/trajectorygeneration.py index 0ab74017..01a18cd5 100644 --- a/pyro/planning/trajectorygeneration.py +++ b/pyro/planning/trajectorygeneration.py @@ -361,6 +361,284 @@ def solve(self, show=True): return p, X, t +############################################################################### +class MultiPointSingleAxisPolynomialTrajectoryGenerator( + SingleAxisPolynomialTrajectoryGenerator +): + """ + This class is a tool to generate a point-to-point trajectory for a + single axis based on boundary conditions (position and higher order derivative) + + k Polynomial segments of order N + + pi(t) = p0i + p1i*t + p2i*t^2 + ... + pNi*t^N + + x(T) = pi(t) with i s.t. ti < T < ti+1 and t = T - ti + + if boundary conditions do not fully specify the parameters of the polynomial, + then an optimization is conducted to minimize the cost function which is defined + as a weighted sum of the integral of the square of the ith derivative of the profile. + + Parameters: + ----------- + poly_N : int + order of the polynomials + diff_N : int + order of the highest derivative to compute + tc : array + array of time for initial, intermediate and final points + x0 : array + array of initial conditions + xf : array + array of final conditions + xc : array ( # of constrained derivatives , pts_N) + matrice of intermediate conditions at waypoints + Rs : array + weights for the cost function penalizing the ith polynomial parameters directly + Ws : array + weights for the cost function penalizing the ith derivative of the profile + dt : float + time step for the numerical solution of the trajectory + + Output: + ------- + p : array + polynomial parameters + X : array + profile of the trajectory X[i, j] is the ith derivative of the profile at time t[j] + t : array + time vector + + """ + + ################################################ + def __init__( + self, + poly_N=5, + diff_N=7, + con_N=3, + x0=np.array([0.0, 0.0, 0.0]), + xf=np.array([10.0, 0.0, 0.0]), + tc=np.array([0.0, 2.0, 8.0, 10.0]), + xc=np.array([[3.0, 7.0], [0.0, 0.0]]), + dt=0.01, + ): + self.poly_N = poly_N + self.diff_N = diff_N + self.x0 = x0 + self.xf = xf + self.xc = xc + self.tc = tc + self.x0_N = x0.shape[0] + self.xf_N = xf.shape[0] + self.K = xc.shape[0] # number of waypoints + self.way_N = xc.shape[1] # number of derivative to impose at each waypoint + self.con_N = con_N # number continuity constraints + self.Rs = np.zeros((self.poly_N + 1)) + self.Ws = np.zeros((self.diff_N)) + self.dt = dt + + # Outputs + self.t = None + self.X = None + self.p = None + + self.labels = [ + "pos", + "vel", + "acc", + "jerk", + "snap", + "crac", + "pop", + "7th", + "8th", + "9th", + "10th", + ] + + ################################################ + def compute_b(self, x0, xf, xc, N0, Nf, Nw, Nc): + """ """ + + K = xc.shape[1] # number of waypoint + + b = x0[:N0] # initial conditions + + for k in range(K): + b = np.hstack((b, xc[:Nw, k])) # waypoint conditions + + b = np.hstack((b, xf[:Nf])) + + for k in range(K): + b = np.hstack((b, np.zeros(Nc))) # continuity conditions + + print("Constraints vector b = \n", b) + + return b + + ################################################ + def A_t(self, t, poly_N, diff_N): + """ + Compute the matrix X(t) = A(t) @ p + where + p = [p0, p1, p2, ..., pN] are the polynomial parameters + X(t) = [x(t), dx(t), d2x(t), ..., dNx(t)] are the derivatives of the trajectory at time t + + """ + + A = np.zeros((diff_N, poly_N + 1)) + + # For all jth derivative of the final conditions + for j in range(diff_N): + # For all terms of the polynomical + for n in range(j, poly_N + 1): + exp = n - j + mul = 1 + for k in range(j): + mul = mul * (n - k) + A[j, n] = mul * t**exp + + return A + + + ################################################ + def compute_A(self, tc, N0, Nf, Nw, Nc, Np): + """""" + Kp1 = tc.shape[0]-1 # number of segments + K = Kp1 - 1 # number of waypoints + N = Np + 1 # number of polynomial parameters per segments + N_params = Kp1 * N + + N_contraints = N0 + Nf + Nw * K + Nc * K + + A = np.zeros((N_contraints, N_params)) + + # Times on segments + dt = np.diff(tc) # time between waypoints + + # Initial conditions + A[0:N0, 0:N] = self.A_t(0, Np, N0) + + # Waypoint conditions + for k in range(K): + A[N0 + Nw * k : N0 + Nw * (k + 1), N * (k + 1) : N * (k + 2)] = self.A_t(0, Np, Nw) + + # Final conditions + A[N0 + Nw * K : N0 + Nw * K + Nf, N * K : N * (K + 1)] = self.A_t(dt[-1], Np, Nf) + + # Continuity conditions + for k in range(K): + A[N0 + Nw * K + Nf + Nc * k : N0 + Nw * K + Nf + Nc * (k + 1), N * k : N * (k + 1)] = self.A_t(dt[k], Np, Nc) + A[N0 + Nw * K + Nf + Nc * k : N0 + Nw * K + Nf + Nc * (k + 1), N * (k + 1) : N * (k + 2)] = -self.A_t(0, Np, Nc) + + print("Constraints matrix: \n", A) + + return A + + ################################################ + def compute_Q(self, poly_N, diff_N, tf, Ws, Rs): + """Compute the cost function matrix Q, only used if the boundary conditions do not fully specify the parameters of the polynomial""" + + # Quadratic cost matrix + Q = np.zeros((poly_N + 1, poly_N + 1)) + + # Quadratic cost matrix for each derivative + Qs = np.zeros((poly_N + 1, poly_N + 1, diff_N)) + + # Qs are weight corresponding to computing the integral of the square of the ith derivative of the profile + # J = p.T @ Qs[i] @ p = integral( [ d_dt(ith)x(t) ]^2 dt) + # see https://groups.csail.mit.edu/rrg/papers/BryIJRR15.pdf + for r in range(diff_N): + for i in range(poly_N + 1): + for l in range(poly_N + 1): + if (i >= r) and (l >= r): + mul = 1 + for m in range(r): + mul = mul * (i - m) * (l - m) + exp = i + l - 2 * r + 1 + Qs[i, l, r] = 2 * mul * tf**exp / (i + l - 2 * r + 1) + else: + Qs[i, l, r] = 0 + + # Total cost for all derivatives + for r in range(diff_N): + Q = Q + Ws[r] * Qs[:, :, r] + + # Regulation term penalizing the polynomial parameters directly + Q = Q + np.diag(Rs[: (poly_N + 1)]) + + return Q + + ################################################ + def generate_trajectory(self, tc, p, poly_N, diff_N, dt=0.01): + """Generate a numerical trajectory based on the polynomial parameters""" + + Np1 = poly_N + 1 # order of polynomial + tf =tc[-1] + steps = int(tf / dt) # number of time steps + ts = np.linspace(0, tf, steps) + X = np.zeros((diff_N, steps)) + + for i in range(steps): + t = ts[i] + k = 0 + t0_segment = 0.0 + while (t > tc[k+1]): + t0_segment = tc[k+1] + k = k + 1 + + t_local = t - t0_segment + p_local = p[Np1 * k : Np1 * (k + 1)] + At = self.A_t(t_local, Np1 - 1, diff_N) + X[:, i] = At @ p_local + + return X, ts + + ################################################ + def get_local_trajectory(self, t_local, p_local, diff_N): + """Get the jth derivative of the trajectory at time t based on the polynomial parameters p""" + + p = p_local + Np = p.shape[0] - 1 # order of polynomial + t = t_local + At = self.A_t(t, Np, diff_N) + + X = At @ p + + return X + + ################################################ + def solve(self, show=True): + + x0 = self.x0 + xf = self.xf + xc = self.xc + tc = self.tc + N0 = self.x0_N + Nf = self.xf_N + Nw = self.way_N + Nc = self.con_N + Np = self.poly_N + Nd = self.diff_N + Ws = self.Ws + Rs = self.Rs + dt = self.dt + + b = self.compute_b(x0, xf, xc, N0, Nf, Nw, Nc) + A = self.compute_A(tc, N0, Nf, Nw, Nc, Np) # tc, N0, Nf, Nw, Nc, Np): + # Q = self.compute_Q(Np, Nd, tf, Ws, Rs) + + p = self.solve_for_polynomial_parameters(A, b, None) + + X, t = self.generate_trajectory(tc, p, Np, Nd, dt) + + if show: + self.plot_trajectory(X, t) + + return b, A, p, X, t + + """ ################################################################# ################## Main ######## @@ -371,23 +649,23 @@ def solve(self, show=True): if __name__ == "__main__": """MAIN TEST""" - x0 = np.array([0, 0, 0, 0, 0, 0, 0, 0]) - xf = np.array([10, 0, 0, 0, 0, 0, 0, 0]) + # x0 = np.array([0, 0, 0, 0, 0, 0, 0, 0]) + # xf = np.array([10, 0, 0, 0, 0, 0, 0, 0]) - ge = SingleAxisPolynomialTrajectoryGenerator( - x0=x0, xf=xf, tf=10, poly_N=12, diff_N=7, dt=0.01 - ) + # ge = SingleAxisPolynomialTrajectoryGenerator( + # x0=x0, xf=xf, tf=10, poly_N=12, diff_N=7, dt=0.01 + # ) ############################# ### Fully constrained order 3 ############################# - ge.x0_N = 2 - ge.xf_N = 2 - ge.poly_N = 3 - ge.diff_N = 7 + # ge.x0_N = 2 + # ge.xf_N = 2 + # ge.poly_N = 3 + # ge.diff_N = 7 - ge.solve() + # ge.solve() ############################# ### Fully constrained order 5 @@ -404,14 +682,14 @@ def solve(self, show=True): ### Optimization on polynomial parameters ########################################### - ge.poly_N = 12 - ge.x0_N = 3 - ge.xf_N = 3 - ge.Rs = 0.0 * np.ones(ge.poly_N + 1) - #ge.Ws = np.array([0, 0.0, 10.0, 1.0, 1.0, 1.0, 1.0]) - ge.Ws = np.array([0, 1.0, 0.0, 0, 0.0,0,0]) + # ge.poly_N = 12 + # ge.x0_N = 3 + # ge.xf_N = 3 + # ge.Rs = 0.0 * np.ones(ge.poly_N + 1) + # # ge.Ws = np.array([0, 0.0, 10.0, 1.0, 1.0, 1.0, 1.0]) + # ge.Ws = np.array([0, 1.0, 0.0, 0, 0.0, 0, 0]) - p, X, t = ge.solve() # order 12 with optimization on polynomial parameters + # p, X, t = ge.solve() # order 12 with optimization on polynomial parameters ############################# ### Fully constrained order 7 @@ -430,14 +708,14 @@ def solve(self, show=True): ### Fully constrained order 9 ############################# - ge = SingleAxisPolynomialTrajectoryGenerator( - x0=x0, xf=xf, tf=10, poly_N=9, diff_N=7, dt=0.01 - ) + # ge = SingleAxisPolynomialTrajectoryGenerator( + # x0=x0, xf=xf, tf=10, poly_N=9, diff_N=7, dt=0.01 + # ) - ge.x0_N = 5 - ge.xf_N = 5 + # ge.x0_N = 5 + # ge.xf_N = 5 - ge.solve() + # ge.solve() ############################# ### Overconstrained order 3 @@ -451,3 +729,20 @@ def solve(self, show=True): # ge.xf_N = 3 # ge.solve() + + ############################# + ### Waypoint test + ############################# + + traj = MultiPointSingleAxisPolynomialTrajectoryGenerator( + poly_N=3, + diff_N=3, + con_N=2, + x0=np.array([0.0, 0.0]), + xf=np.array([10.0, 0.0]), + tc=np.array([0.0, 2.0, 8.0, 10.0]), + xc=np.array([[3.0, 7.0], [2.0, 2.0]]), + dt=0.01, + ) + + b, A, p, X, t = traj.solve() From 06028fd9aa4ae9a17518a5c7b03fcee64b011404 Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Tue, 26 Mar 2024 12:03:55 -0400 Subject: [PATCH 86/93] 3 fully constrained tests --- pyro/planning/trajectorygeneration.py | 48 +++++++++++++++++++++++---- 1 file changed, 41 insertions(+), 7 deletions(-) diff --git a/pyro/planning/trajectorygeneration.py b/pyro/planning/trajectorygeneration.py index 01a18cd5..261da346 100644 --- a/pyro/planning/trajectorygeneration.py +++ b/pyro/planning/trajectorygeneration.py @@ -431,8 +431,8 @@ def __init__( self.tc = tc self.x0_N = x0.shape[0] self.xf_N = xf.shape[0] - self.K = xc.shape[0] # number of waypoints - self.way_N = xc.shape[1] # number of derivative to impose at each waypoint + self.K = xc.shape[1] # number of waypoints + self.way_N = xc.shape[0] # number of derivative to impose at each waypoint self.con_N = con_N # number continuity constraints self.Rs = np.zeros((self.poly_N + 1)) self.Ws = np.zeros((self.diff_N)) @@ -626,8 +626,8 @@ def solve(self, show=True): dt = self.dt b = self.compute_b(x0, xf, xc, N0, Nf, Nw, Nc) - A = self.compute_A(tc, N0, Nf, Nw, Nc, Np) # tc, N0, Nf, Nw, Nc, Np): - # Q = self.compute_Q(Np, Nd, tf, Ws, Rs) + A = self.compute_A(tc, N0, Nf, Nw, Nc, Np) + # Q = self.compute_Q(Np, Nd, tf, Ws, Rs) # TODO p = self.solve_for_polynomial_parameters(A, b, None) @@ -731,17 +731,51 @@ def solve(self, show=True): # ge.solve() ############################# - ### Waypoint test + ### Waypoint simple fully constraint test ############################# traj = MultiPointSingleAxisPolynomialTrajectoryGenerator( poly_N=3, - diff_N=3, + diff_N=5, con_N=2, x0=np.array([0.0, 0.0]), xf=np.array([10.0, 0.0]), tc=np.array([0.0, 2.0, 8.0, 10.0]), - xc=np.array([[3.0, 7.0], [2.0, 2.0]]), + xc=np.array([[3.0, 7.0], [1.0, 1.0]]), + dt=0.01, + ) + + b, A, p, X, t = traj.solve() + + ############################# + ### Waypoint test + ############################# + + traj = MultiPointSingleAxisPolynomialTrajectoryGenerator( + poly_N=3, + diff_N=5, + con_N=3, + x0=np.array([0.0, 0.0]), + xf=np.array([10.0, 0.0]), + tc=np.array([0.0, 2.0, 8.0, 10.0]), + xc=np.array([[3.0, 7.0]]), + dt=0.01, + ) + + b, A, p, X, t = traj.solve() + + ############################# + ### Waypoint test + ############################# + + traj = MultiPointSingleAxisPolynomialTrajectoryGenerator( + poly_N=4, + diff_N=5, + con_N=4, + x0=np.array([0.0, 0.0, 0.0]), + xf=np.array([10.0, 0.0]), + tc=np.array([0.0, 2.0, 8.0, 10.0]), + xc=np.array([[3.0, 7.0]]), dt=0.01, ) From a21e34df3d0c18138792c8f839b35cac288124b2 Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Tue, 26 Mar 2024 16:09:48 -0400 Subject: [PATCH 87/93] waypoint version works with optimization --- .../droneminsnap_waypoints.py | 159 ++++++++++++++++++ pyro/planning/trajectorygeneration.py | 99 ++++++++--- 2 files changed, 236 insertions(+), 22 deletions(-) create mode 100644 dev/differentialflatness/droneminsnap_waypoints.py diff --git a/dev/differentialflatness/droneminsnap_waypoints.py b/dev/differentialflatness/droneminsnap_waypoints.py new file mode 100644 index 00000000..6fcd2060 --- /dev/null +++ b/dev/differentialflatness/droneminsnap_waypoints.py @@ -0,0 +1,159 @@ +# -*- coding: utf-8 -*- + +############################################################################### +import numpy as np +import matplotlib.pyplot as plt + +from pyro.analysis import graphical +from pyro.planning import trajectorygeneration +from pyro.dynamic import drone +from pyro.planning import plan + +############################################################################### +# Define the c.g. trajectory +############################################################################### + +traj = trajectorygeneration.MultiPointSingleAxisPolynomialTrajectoryGenerator( + poly_N=5, + diff_N=5, + con_N=4, + x0=np.array([0.0, 0.0, 0.0, 0.0]), + xf=np.array([10.0, 0.0, 0.0, 0.0]), + tc=np.array([0.0, 2.0, 8.0, 10.0]), + xc=np.array([[1.0, 9.0]]), + dt=0.01, + ) + +b, A, p, X, t = traj.solve() + +x = X[0, :] +dx = X[1, :] +ax = X[2, :] +dax = X[3, :] +ddax = X[4, :] + +traj = trajectorygeneration.MultiPointSingleAxisPolynomialTrajectoryGenerator( + poly_N=5, + diff_N=5, + con_N=4, + x0=np.array([0.0, 0.0, 0.0, 0.0]), + xf=np.array([0.0, 0.0, 0.0, 0.0]), + tc=np.array([0.0, 2.0, 8.0, 10.0]), + xc=np.array([[1.0, 1.0]]), + dt=0.01, + ) + +b, A, p, Y, t = traj.solve() + +y = Y[0, :] +dy = Y[1, :] +ay = Y[2, :] + 9.81 # added gravity +day = Y[3, :] +dday = Y[4, :] + +############################################################################### +# Compute angular trajectory +############################################################################### + +theta = np.arctan2(ax, ay) +# theta = np.arctan( (ay/ax)) +s = np.sin(theta) +c = np.cos(theta) +dtheta = (dax * c - day * s) / (ax * s + ay * c) +ddtheta = ( + s * (-dday + ay * dtheta**2 - 2 * dax * dtheta) + + c * (ddax - ax * dtheta**2 - 2 * day * dtheta) +) / (ay * c + ax * s) + +############################################################################### +# Compute Forces +############################################################################### + +sys = drone.Drone2D() + +m = sys.mass +J = sys.inertia +r = sys.truster_offset + +F_tot = m * ax / np.sin(theta) +F_del = J * ddtheta / r + +T1 = 0.5 * (F_tot + F_del) +T2 = 0.5 * (F_tot - F_del) + +############################################################################### +# Plots +############################################################################### + +# Create traj +steps = len(t) +us = np.zeros((steps, 2)) # control inputs +xs = np.zeros((steps, 6)) +ys = np.zeros((steps, 6)) +dxs = np.zeros((steps, 6)) + +xs[:, 0] = x +xs[:, 1] = y +xs[:, 2] = -theta +xs[:, 3] = dx +xs[:, 4] = dy +xs[:, 5] = -dtheta + +us[:, 0] = T1 +us[:, 1] = T2 + +traj = plan.Trajectory(xs, us, t, dxs, ys) + + +fig, axes = plt.subplots( + 3, figsize=graphical.default_figsize, dpi=graphical.default_dpi, frameon=True +) + +axes[0].plot(t, theta, "b") +axes[0].set_ylabel("Theta", fontsize=graphical.default_fontsize) +axes[0].set_xlabel("v", fontsize=graphical.default_fontsize) +axes[0].tick_params(labelsize=graphical.default_fontsize) +axes[0].grid(True) + +axes[1].plot(t, dtheta, "b") +# axes[1].plot(t, dtheta_num, "r") +axes[1].set_ylabel("w", fontsize=graphical.default_fontsize) +axes[1].set_xlabel("t", fontsize=graphical.default_fontsize) +axes[1].tick_params(labelsize=graphical.default_fontsize) +axes[1].grid(True) + +axes[2].plot(t, ddtheta, "b") +# axes[2].plot(t, ddtheta_num, "r") +axes[2].set_ylabel("dw", fontsize=graphical.default_fontsize) +axes[2].set_xlabel("t", fontsize=graphical.default_fontsize) +axes[2].tick_params(labelsize=graphical.default_fontsize) +axes[2].grid(True) + +fig.tight_layout() +fig.canvas.draw() + +plt.show() + + +fig, axes = plt.subplots( + 1, figsize=graphical.default_figsize, dpi=graphical.default_dpi, frameon=True +) + +axes.plot(x, y, "r") +axes.set_ylabel("y", fontsize=graphical.default_fontsize) +axes.set_xlabel("x", fontsize=graphical.default_fontsize) +axes.axis("equal") +axes.set(xlim=(-25, 25), ylim=(-25, 25)) +axes.tick_params(labelsize=graphical.default_fontsize) +axes.grid(True) + +fig.tight_layout() +fig.canvas.draw() + +plt.show() + + +sys.traj = traj + +sys.plot_trajectory("xu") +sys.animate_simulation() diff --git a/pyro/planning/trajectorygeneration.py b/pyro/planning/trajectorygeneration.py index 261da346..e2e33b3c 100644 --- a/pyro/planning/trajectorygeneration.py +++ b/pyro/planning/trajectorygeneration.py @@ -475,6 +475,8 @@ def compute_b(self, x0, xf, xc, N0, Nf, Nw, Nc): print("Constraints vector b = \n", b) + print("Constraints vector b shape = ",b.shape) + return b ################################################ @@ -534,10 +536,39 @@ def compute_A(self, tc, N0, Nf, Nw, Nc, Np): print("Constraints matrix: \n", A) + print("Constraints matrix A shape = ",A.shape) + return A + + ################################################ + def compute_Q(self, poly_N, diff_N, tc, Ws, Rs): + + # + Kp1 = tc.shape[0]-1 # number of segments + N = poly_N + 1 # number of polynomial parameters per segments + N_params = Kp1 * N + + # Times on segments + dt = np.diff(tc) # time between waypoints + + # Quadratic global cost matrix + N_params = (poly_N + 1) * Kp1 # number of polynomial parameters + Q = np.zeros((N_params, N_params)) + + # Waypoint conditions + for k in range(Kp1): + Qk = self.compute_segment_Q(poly_N, diff_N, dt[k], Ws, Rs) + Q[N * k : N * (k + 1), N * k : N * (k + 1)] = Qk + + print("Quadratic cost matrix: \n", Q) + + print("Quadratic cost matrix shape = ",Q.shape) + + return Q + ################################################ - def compute_Q(self, poly_N, diff_N, tf, Ws, Rs): + def compute_segment_Q(self, poly_N, diff_N, tf, Ws, Rs): """Compute the cost function matrix Q, only used if the boundary conditions do not fully specify the parameters of the polynomial""" # Quadratic cost matrix @@ -627,9 +658,11 @@ def solve(self, show=True): b = self.compute_b(x0, xf, xc, N0, Nf, Nw, Nc) A = self.compute_A(tc, N0, Nf, Nw, Nc, Np) - # Q = self.compute_Q(Np, Nd, tf, Ws, Rs) # TODO + Q = self.compute_Q(Np, Nd, tc, Ws, Rs) - p = self.solve_for_polynomial_parameters(A, b, None) + print(Q.shape) + + p = self.solve_for_polynomial_parameters(A, b, Q) X, t = self.generate_trajectory(tc, p, Np, Nd, dt) @@ -734,28 +767,45 @@ def solve(self, show=True): ### Waypoint simple fully constraint test ############################# - traj = MultiPointSingleAxisPolynomialTrajectoryGenerator( - poly_N=3, - diff_N=5, - con_N=2, - x0=np.array([0.0, 0.0]), - xf=np.array([10.0, 0.0]), - tc=np.array([0.0, 2.0, 8.0, 10.0]), - xc=np.array([[3.0, 7.0], [1.0, 1.0]]), - dt=0.01, - ) + # traj = MultiPointSingleAxisPolynomialTrajectoryGenerator( + # poly_N=3, + # diff_N=5, + # con_N=2, + # x0=np.array([0.0, 0.0]), + # xf=np.array([10.0, 0.0]), + # tc=np.array([0.0, 2.0, 8.0, 10.0]), + # xc=np.array([[3.0, 7.0], [1.0, 1.0]]), + # dt=0.01, + # ) - b, A, p, X, t = traj.solve() + # b, A, p, X, t = traj.solve() + + ############################# + ### Waypoint test + ############################# + + # traj = MultiPointSingleAxisPolynomialTrajectoryGenerator( + # poly_N=3, + # diff_N=5, + # con_N=3, + # x0=np.array([0.0, 0.0]), + # xf=np.array([10.0, 0.0]), + # tc=np.array([0.0, 2.0, 8.0, 10.0]), + # xc=np.array([[3.0, 7.0]]), + # dt=0.01, + # ) + + # b, A, p, X, t = traj.solve() ############################# ### Waypoint test ############################# traj = MultiPointSingleAxisPolynomialTrajectoryGenerator( - poly_N=3, + poly_N=4, diff_N=5, - con_N=3, - x0=np.array([0.0, 0.0]), + con_N=4, + x0=np.array([0.0, 0.0, 0.0]), xf=np.array([10.0, 0.0]), tc=np.array([0.0, 2.0, 8.0, 10.0]), xc=np.array([[3.0, 7.0]]), @@ -765,18 +815,23 @@ def solve(self, show=True): b, A, p, X, t = traj.solve() ############################# - ### Waypoint test + ### Waypoint simple fully constraint test ############################# traj = MultiPointSingleAxisPolynomialTrajectoryGenerator( - poly_N=4, - diff_N=5, + poly_N=9, + diff_N=7, con_N=4, x0=np.array([0.0, 0.0, 0.0]), - xf=np.array([10.0, 0.0]), + xf=np.array([10.0, 0.0, 0.0]), tc=np.array([0.0, 2.0, 8.0, 10.0]), - xc=np.array([[3.0, 7.0]]), + xc=np.array([[5.0, 12.0]]), dt=0.01, ) + traj.Ws[1]= 1.0 + traj.Ws[2]= 1.0 + traj.Ws[3]= 1.0 + traj.Ws[4]= 1.0 + b, A, p, X, t = traj.solve() From 99f339d4f81d93e447c37463d30754898f3faf71 Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Tue, 26 Mar 2024 22:10:46 -0400 Subject: [PATCH 88/93] waypoints examples --- .../droneminsnap_waypoints.py | 41 ++-- .../rigidbodytest_waypoints.py | 200 ++++++++++++++++++ pyro/planning/trajectorygeneration.py | 8 +- 3 files changed, 232 insertions(+), 17 deletions(-) create mode 100644 dev/differentialflatness/rigidbodytest_waypoints.py diff --git a/dev/differentialflatness/droneminsnap_waypoints.py b/dev/differentialflatness/droneminsnap_waypoints.py index 6fcd2060..4eafa603 100644 --- a/dev/differentialflatness/droneminsnap_waypoints.py +++ b/dev/differentialflatness/droneminsnap_waypoints.py @@ -13,17 +13,30 @@ # Define the c.g. trajectory ############################################################################### +# Waypoints +xyt = np.array([[ 0.0, 0.0, 0.0], + [10.0, 0.0, 5.0], + [10.0, 5.0, 10.0], + [ 0.0, 5.0, 15.0]]).T + + + traj = trajectorygeneration.MultiPointSingleAxisPolynomialTrajectoryGenerator( - poly_N=5, - diff_N=5, + poly_N=9, + diff_N=7, con_N=4, - x0=np.array([0.0, 0.0, 0.0, 0.0]), - xf=np.array([10.0, 0.0, 0.0, 0.0]), - tc=np.array([0.0, 2.0, 8.0, 10.0]), - xc=np.array([[1.0, 9.0]]), + x0=np.array([xyt[0,0], 0.0, 0.0, 0.0]), + xf=np.array([xyt[0,3], 0.0, 0.0, 0.0]), + tc=np.array([xyt[2,0], xyt[2,1], xyt[2,2], xyt[2,3]]), + xc=np.array([[xyt[0,1], xyt[0,2]]]), dt=0.01, ) +traj.Ws[1]= 1.0 +traj.Ws[2]= 1.0 +traj.Ws[3]= 1.0 +traj.Ws[4]= 1.0 + b, A, p, X, t = traj.solve() x = X[0, :] @@ -33,15 +46,19 @@ ddax = X[4, :] traj = trajectorygeneration.MultiPointSingleAxisPolynomialTrajectoryGenerator( - poly_N=5, - diff_N=5, + poly_N=9, + diff_N=7, con_N=4, - x0=np.array([0.0, 0.0, 0.0, 0.0]), - xf=np.array([0.0, 0.0, 0.0, 0.0]), - tc=np.array([0.0, 2.0, 8.0, 10.0]), - xc=np.array([[1.0, 1.0]]), + x0=np.array([xyt[1,0], 0.0, 0.0, 0.0]), + xf=np.array([xyt[1,3], 0.0, 0.0, 0.0]), + tc=np.array([xyt[2,0], xyt[2,1], xyt[2,2], xyt[2,3]]), + xc=np.array([[xyt[1,1], xyt[1,2]]]), dt=0.01, ) +traj.Ws[1]= 1.0 +traj.Ws[2]= 1.0 +traj.Ws[3]= 1.0 +traj.Ws[4]= 1.0 b, A, p, Y, t = traj.solve() diff --git a/dev/differentialflatness/rigidbodytest_waypoints.py b/dev/differentialflatness/rigidbodytest_waypoints.py new file mode 100644 index 00000000..c7efda4f --- /dev/null +++ b/dev/differentialflatness/rigidbodytest_waypoints.py @@ -0,0 +1,200 @@ +# -*- coding: utf-8 -*- + +############################################################################### +import numpy as np +import matplotlib.pyplot as plt + + +from pyro.analysis import graphical +from pyro.planning import trajectorygeneration +from pyro.dynamic import rigidbody +from pyro.planning import plan + +############################################################################### +# Waypoints +xyt = np.array([[ 0.0, 0.0, 0.0], + [10.0, 0.0, 5.0], + [10.0, 10.0, 10.0], + [ 0.0, 10.0, 15.0]]).T + + + +traj = trajectorygeneration.MultiPointSingleAxisPolynomialTrajectoryGenerator( + poly_N=9, + diff_N=7, + con_N=4, + x0=np.array([xyt[0,0], 0.0, 0.0, 0.0]), + xf=np.array([xyt[0,3], 0.0, 0.0, 0.0]), + tc=np.array([xyt[2,0], xyt[2,1], xyt[2,2], xyt[2,3]]), + xc=np.array([[xyt[0,1], xyt[0,2]]]), + dt=0.01, + ) + +traj.Ws[1]= 1.0 +traj.Ws[2]= 1.0 +traj.Ws[3]= 1.0 +traj.Ws[4]= 1.0 + +b, A, p, X, t = traj.solve() + +x = X[0, :] +dx = X[1, :] +ax = X[2, :] +dax = X[3, :] +ddax = X[4, :] + +traj = trajectorygeneration.MultiPointSingleAxisPolynomialTrajectoryGenerator( + poly_N=9, + diff_N=7, + con_N=4, + x0=np.array([xyt[1,0], 0.0, 0.0, 0.0]), + xf=np.array([xyt[1,3], 0.0, 0.0, 0.0]), + tc=np.array([xyt[2,0], xyt[2,1], xyt[2,2], xyt[2,3]]), + xc=np.array([[xyt[1,1], xyt[1,2]]]), + dt=0.01, + ) +traj.Ws[1]= 1.0 +traj.Ws[2]= 1.0 +traj.Ws[3]= 1.0 +traj.Ws[4]= 1.0 + +b, A, p, Y, t = traj.solve() + +y = Y[0, :] +dy = Y[1, :] +ay = Y[2, :] +day = Y[3, :] +dday = Y[4, :] + +# Position theta +theta = np.arctan2(ay, ax) +# theta = np.arctan( (ay/ax)) +s = np.sin(theta) +c = np.cos(theta) +dtheta = (day * c - dax * s) / (ay * s + ax * c) # TODO check analytical equation, seems wrong +ddtheta = ( + s * (-ddax + ax * dtheta**2 - 2 * day * dtheta) + + c * (dday - ay * dtheta**2 - 2 * dax * dtheta) +) / ( + ax * c + ay * s +) # TODO check analytical equation, seems wrong + +dtheta_num = np.diff(theta, n=1, prepend=0.0) +ddtheta_num = np.diff(dtheta, n=1, prepend=0.0) + +# dtheta = dtheta_num +# ddtheta = ddtheta_num + +# Create traj +steps = len(t) +xs = np.zeros((steps, 6)) +ys = np.zeros((steps, 6)) +us = np.zeros((steps, 2)) +dxs = np.zeros((steps, 6)) + +sys = rigidbody.RigidBody2D() + +sys.mass = 0.8 +sys.inertia = 1.0 +sys.l_t = 1.0 + +m = sys.mass +J = sys.inertia +r = sys.l_t + +x_cg = x - J / (m * r) * np.cos(theta) +y_cg = y - J / (m * r) * np.sin(theta) + +xs[:, 0] = x_cg +xs[:, 1] = y_cg +xs[:, 2] = theta + +M = np.array([[m, 0], [0, m]]) + +ax_cg = ( + ax + J / (m * r) * np.sin(theta) * ddtheta + J / (m * r) * np.cos(theta) * dtheta**2 +) +ay_cg = ( + ay - J / (m * r) * np.cos(theta) * ddtheta + J / (m * r) * np.sin(theta) * dtheta**2 +) + +# COmpute forces +for i in range(steps): + R = np.array( + [[np.cos(theta[i]), -np.sin(theta[i])], [np.sin(theta[i]), np.cos(theta[i])]] + ) + a_cg = np.array([ax_cg[i], ay_cg[i]]) + us[i, :] = np.linalg.inv(R) @ M @ a_cg + + +traj = plan.Trajectory(xs, us, t, dxs, ys) + +fig, axes = plt.subplots( + 2, figsize=graphical.default_figsize, dpi=graphical.default_dpi, frameon=True +) + +axes[0].plot(t, ax_cg, "b") +axes[0].set_ylabel("ax_cg", fontsize=graphical.default_fontsize) +axes[0].set_xlabel("t", fontsize=graphical.default_fontsize) +axes[0].tick_params(labelsize=graphical.default_fontsize) +axes[0].grid(True) + +axes[1].plot(t, ay_cg, "b") +axes[1].set_ylabel("ay_cg", fontsize=graphical.default_fontsize) +axes[1].set_xlabel("t", fontsize=graphical.default_fontsize) +axes[1].tick_params(labelsize=graphical.default_fontsize) +axes[1].grid(True) + + +fig, axes = plt.subplots( + 3, figsize=graphical.default_figsize, dpi=graphical.default_dpi, frameon=True +) + +axes[0].plot(t, theta, "b") +axes[0].set_ylabel("Theta", fontsize=graphical.default_fontsize) +axes[0].set_xlabel("v", fontsize=graphical.default_fontsize) +axes[0].tick_params(labelsize=graphical.default_fontsize) +axes[0].grid(True) + +axes[1].plot(t, dtheta, "b") +# axes[1].plot(t, dtheta_num, "r") +axes[1].set_ylabel("w", fontsize=graphical.default_fontsize) +axes[1].set_xlabel("t", fontsize=graphical.default_fontsize) +axes[1].tick_params(labelsize=graphical.default_fontsize) +axes[1].grid(True) + +axes[2].plot(t, ddtheta, "b") +# axes[2].plot(t, ddtheta_num, "r") +axes[2].set_ylabel("dw", fontsize=graphical.default_fontsize) +axes[2].set_xlabel("t", fontsize=graphical.default_fontsize) +axes[2].tick_params(labelsize=graphical.default_fontsize) +axes[2].grid(True) + +fig.tight_layout() +fig.canvas.draw() + +plt.show() + + +fig, axes = plt.subplots( + 1, figsize=graphical.default_figsize, dpi=graphical.default_dpi, frameon=True +) + +axes.plot(x, y, "r") +axes.plot(x_cg, y_cg, "b") +axes.set_ylabel("y", fontsize=graphical.default_fontsize) +axes.set_xlabel("x", fontsize=graphical.default_fontsize) +axes.axis("equal") +axes.set(xlim=(-5, 25), ylim=(-5, 25)) +axes.tick_params(labelsize=graphical.default_fontsize) +axes.grid(True) + +fig.tight_layout() +fig.canvas.draw() + +plt.show() + + +sys.traj = traj + +sys.animate_simulation() diff --git a/pyro/planning/trajectorygeneration.py b/pyro/planning/trajectorygeneration.py index e2e33b3c..113f43e1 100644 --- a/pyro/planning/trajectorygeneration.py +++ b/pyro/planning/trajectorygeneration.py @@ -473,7 +473,7 @@ def compute_b(self, x0, xf, xc, N0, Nf, Nw, Nc): for k in range(K): b = np.hstack((b, np.zeros(Nc))) # continuity conditions - print("Constraints vector b = \n", b) + #print("Constraints vector b = \n", b) print("Constraints vector b shape = ",b.shape) @@ -534,7 +534,7 @@ def compute_A(self, tc, N0, Nf, Nw, Nc, Np): A[N0 + Nw * K + Nf + Nc * k : N0 + Nw * K + Nf + Nc * (k + 1), N * k : N * (k + 1)] = self.A_t(dt[k], Np, Nc) A[N0 + Nw * K + Nf + Nc * k : N0 + Nw * K + Nf + Nc * (k + 1), N * (k + 1) : N * (k + 2)] = -self.A_t(0, Np, Nc) - print("Constraints matrix: \n", A) + #print("Constraints matrix: \n", A) print("Constraints matrix A shape = ",A.shape) @@ -560,7 +560,7 @@ def compute_Q(self, poly_N, diff_N, tc, Ws, Rs): Qk = self.compute_segment_Q(poly_N, diff_N, dt[k], Ws, Rs) Q[N * k : N * (k + 1), N * k : N * (k + 1)] = Qk - print("Quadratic cost matrix: \n", Q) + #print("Quadratic cost matrix: \n", Q) print("Quadratic cost matrix shape = ",Q.shape) @@ -660,8 +660,6 @@ def solve(self, show=True): A = self.compute_A(tc, N0, Nf, Nw, Nc, Np) Q = self.compute_Q(Np, Nd, tc, Ws, Rs) - print(Q.shape) - p = self.solve_for_polynomial_parameters(A, b, Q) X, t = self.generate_trajectory(tc, p, Np, Nd, dt) From e783f35bec566ed6089b1e7e200eb60f80905d2a Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Wed, 27 Mar 2024 15:30:11 -0400 Subject: [PATCH 89/93] updated waypoitns --- dev/differentialflatness/droneminsnap_waypoints.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dev/differentialflatness/droneminsnap_waypoints.py b/dev/differentialflatness/droneminsnap_waypoints.py index 4eafa603..4144f619 100644 --- a/dev/differentialflatness/droneminsnap_waypoints.py +++ b/dev/differentialflatness/droneminsnap_waypoints.py @@ -13,10 +13,10 @@ # Define the c.g. trajectory ############################################################################### -# Waypoints +# Waypoints x_i y_i t_i xyt = np.array([[ 0.0, 0.0, 0.0], [10.0, 0.0, 5.0], - [10.0, 5.0, 10.0], + [10.0, 15.0, 10.0], [ 0.0, 5.0, 15.0]]).T From 970e79e4abdf25a7327702e33f8adaa6d0614869 Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Thu, 28 Mar 2024 09:34:25 -0400 Subject: [PATCH 90/93] added direct method based on lagrange multiplier when Q is not singular --- pyro/planning/trajectorygeneration.py | 91 ++++++++++++++++----------- 1 file changed, 54 insertions(+), 37 deletions(-) diff --git a/pyro/planning/trajectorygeneration.py b/pyro/planning/trajectorygeneration.py index 113f43e1..697ae9a2 100644 --- a/pyro/planning/trajectorygeneration.py +++ b/pyro/planning/trajectorygeneration.py @@ -119,7 +119,7 @@ def compute_b(self, x0, xf, N0, Nf): b = np.hstack((x0[:N0], xf[:Nf])) - print("Boundary condition vector b = [x0;xf]: \n", b) + print("Boundary condition vector b shape: ", b.shape) return b @@ -150,7 +150,7 @@ def compute_A(self, tf, N0, Nf, poly_N): mul = mul * (n - k) A[N0 + j, n] = mul * tf**exp - print("Boundary condition matrix: \n", A) + print("Boundary condition matrix A shape: ", A.shape) return A @@ -227,27 +227,42 @@ def solve_for_polynomial_parameters(self, A, b, Q): print("Optimization over free decision variables") - p0 = np.zeros(A.shape[1]) - - constraints = {"type": "eq", "fun": lambda p: A @ p - b} - cost = lambda p: p.T @ Q @ p - grad = lambda p: 2 * p.T @ Q - hess = lambda p: 2 * Q - - # TODO: Change to a solver specifc to quadratic optimization - res = minimize( - cost, - p0, - method="SLSQP", - jac=grad, - hess=hess, - constraints=constraints, - options={"disp": True, "maxiter": 5000}, - ) + try: + + Q_inv = np.linalg.inv(Q) + AQA = A @ Q_inv @ A.T + lam = np.linalg.solve(AQA, b) + p = Q_inv @ A.T @ lam + + except: + + print(" Q is not invertible, using optimization solver") + + p0 = np.zeros(A.shape[1]) + + constraints = {"type": "eq", "fun": lambda p: A @ p - b} + cost = lambda p: p.T @ Q @ p + grad = lambda p: 2 * p.T @ Q + hess = lambda p: 2 * Q + + # TODO: Change to a solver specifc to quadratic optimization + res = minimize( + cost, + p0, + method="SLSQP", + jac=grad, + hess=hess, + constraints=constraints, + options={"disp": True, "maxiter": 5000}, + ) - p = res.x + p = res.x + + else: - print("Computed polynomial parameters: \n", p) + print(" Q is invertible, using direct solver") + + print("Computed polynomial parameters shape: ", p.shape) return p @@ -680,12 +695,12 @@ def solve(self, show=True): if __name__ == "__main__": """MAIN TEST""" - # x0 = np.array([0, 0, 0, 0, 0, 0, 0, 0]) - # xf = np.array([10, 0, 0, 0, 0, 0, 0, 0]) + x0 = np.array([0, 0, 0, 0, 0, 0, 0, 0]) + xf = np.array([10, 0, 0, 0, 0, 0, 0, 0]) - # ge = SingleAxisPolynomialTrajectoryGenerator( - # x0=x0, xf=xf, tf=10, poly_N=12, diff_N=7, dt=0.01 - # ) + ge = SingleAxisPolynomialTrajectoryGenerator( + x0=x0, xf=xf, tf=10, poly_N=12, diff_N=7, dt=0.01 + ) ############################# ### Fully constrained order 3 @@ -719,6 +734,7 @@ def solve(self, show=True): # ge.Rs = 0.0 * np.ones(ge.poly_N + 1) # # ge.Ws = np.array([0, 0.0, 10.0, 1.0, 1.0, 1.0, 1.0]) # ge.Ws = np.array([0, 1.0, 0.0, 0, 0.0, 0, 0]) + # # ge.Ws = np.array([0.01, 10.0, 0.01, 0.01, 0.01, 0.01, 0.01]) # p, X, t = ge.solve() # order 12 with optimization on polynomial parameters @@ -799,18 +815,18 @@ def solve(self, show=True): ### Waypoint test ############################# - traj = MultiPointSingleAxisPolynomialTrajectoryGenerator( - poly_N=4, - diff_N=5, - con_N=4, - x0=np.array([0.0, 0.0, 0.0]), - xf=np.array([10.0, 0.0]), - tc=np.array([0.0, 2.0, 8.0, 10.0]), - xc=np.array([[3.0, 7.0]]), - dt=0.01, - ) + # traj = MultiPointSingleAxisPolynomialTrajectoryGenerator( + # poly_N=4, + # diff_N=5, + # con_N=4, + # x0=np.array([0.0, 0.0, 0.0]), + # xf=np.array([10.0, 0.0]), + # tc=np.array([0.0, 2.0, 8.0, 10.0]), + # xc=np.array([[3.0, 7.0]]), + # dt=0.01, + # ) - b, A, p, X, t = traj.solve() + # b, A, p, X, t = traj.solve() ############################# ### Waypoint simple fully constraint test @@ -827,6 +843,7 @@ def solve(self, show=True): dt=0.01, ) + traj.Ws[0]= 0.01 traj.Ws[1]= 1.0 traj.Ws[2]= 1.0 traj.Ws[3]= 1.0 From d93d808678f6825ab43df6bbb585ed22095b2152 Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Thu, 28 Mar 2024 11:43:23 -0400 Subject: [PATCH 91/93] playing with the exemples --- .../droneminsnap_waypoints.py | 3 +++ .../rigidbodytest_waypoints.py | 27 ++++++++++--------- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/dev/differentialflatness/droneminsnap_waypoints.py b/dev/differentialflatness/droneminsnap_waypoints.py index 4144f619..6e76f183 100644 --- a/dev/differentialflatness/droneminsnap_waypoints.py +++ b/dev/differentialflatness/droneminsnap_waypoints.py @@ -32,6 +32,7 @@ dt=0.01, ) +traj.Ws[0]= 0.01 traj.Ws[1]= 1.0 traj.Ws[2]= 1.0 traj.Ws[3]= 1.0 @@ -55,6 +56,8 @@ xc=np.array([[xyt[1,1], xyt[1,2]]]), dt=0.01, ) + +traj.Ws[0]= 0.01 traj.Ws[1]= 1.0 traj.Ws[2]= 1.0 traj.Ws[3]= 1.0 diff --git a/dev/differentialflatness/rigidbodytest_waypoints.py b/dev/differentialflatness/rigidbodytest_waypoints.py index c7efda4f..5f9e7f83 100644 --- a/dev/differentialflatness/rigidbodytest_waypoints.py +++ b/dev/differentialflatness/rigidbodytest_waypoints.py @@ -19,7 +19,7 @@ -traj = trajectorygeneration.MultiPointSingleAxisPolynomialTrajectoryGenerator( +xtraj = trajectorygeneration.MultiPointSingleAxisPolynomialTrajectoryGenerator( poly_N=9, diff_N=7, con_N=4, @@ -30,12 +30,13 @@ dt=0.01, ) -traj.Ws[1]= 1.0 -traj.Ws[2]= 1.0 -traj.Ws[3]= 1.0 -traj.Ws[4]= 1.0 +xtraj.Ws[0]= 0.01 +xtraj.Ws[1]= 1.0 +xtraj.Ws[2]= 1.0 +xtraj.Ws[3]= 1.0 +xtraj.Ws[4]= 1.0 -b, A, p, X, t = traj.solve() +b, A, p, X, t = xtraj.solve() x = X[0, :] dx = X[1, :] @@ -43,7 +44,7 @@ dax = X[3, :] ddax = X[4, :] -traj = trajectorygeneration.MultiPointSingleAxisPolynomialTrajectoryGenerator( +ytraj = trajectorygeneration.MultiPointSingleAxisPolynomialTrajectoryGenerator( poly_N=9, diff_N=7, con_N=4, @@ -53,12 +54,14 @@ xc=np.array([[xyt[1,1], xyt[1,2]]]), dt=0.01, ) -traj.Ws[1]= 1.0 -traj.Ws[2]= 1.0 -traj.Ws[3]= 1.0 -traj.Ws[4]= 1.0 -b, A, p, Y, t = traj.solve() +ytraj.Ws[0]= 0.01 +ytraj.Ws[1]= 1.0 +ytraj.Ws[2]= 1.0 +ytraj.Ws[3]= 1.0 +ytraj.Ws[4]= 1.0 + +b, A, p, Y, t = ytraj.solve() y = Y[0, :] dy = Y[1, :] From c2400e62126d07a528eec1cefb2393cd63c7a1e7 Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Wed, 10 Apr 2024 10:53:26 -0400 Subject: [PATCH 92/93] diff flat dev --- dev/differentialflatness/rigidbodytest.py | 6 +++--- dev/differentialflatness/rigidbodytest_waypoints.py | 4 ++-- .../dynamicprogramming/pendulum_optimal_swingup_demo.py | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/dev/differentialflatness/rigidbodytest.py b/dev/differentialflatness/rigidbodytest.py index 44e8910b..41407eb4 100644 --- a/dev/differentialflatness/rigidbodytest.py +++ b/dev/differentialflatness/rigidbodytest.py @@ -37,7 +37,7 @@ gex.x0 = np.array([x0, 0, ddx0, 0, 0, 0]) gex.xf = np.array([xf, 0, ddxf, 0, 0, 0]) gex.poly_N = 7 -#gex.Ws = np.array([0, 1.0, 1.0, 1.0, 1.0, 1.0, .0]) +gex.Ws = np.array([0.01, 1.0, 1.0, 1.0, 1.0, 1.0, .0]) px, X, t = gex.solve() x = X[0, :] dx = X[1, :] @@ -51,7 +51,7 @@ gey.x0 = np.array([y0, 0, ddy0, 0, 0, 0]) gey.xf = np.array([yf, 0, ddyf, 0, 0, 0]) gey.poly_N = 7 -#gey.Ws = np.array([0, 1.0, 1.0, 1.0, 1.0, 1.0, .0]) +gey.Ws = np.array([0.01, 1.0, 1.0, 1.0, 1.0, 1.0, .0]) py, Y, t = gey.solve() y = Y[0, :] dy = Y[1, :] @@ -190,4 +190,4 @@ sys.traj = traj -sys.animate_simulation() +ani = sys.animate_simulation() diff --git a/dev/differentialflatness/rigidbodytest_waypoints.py b/dev/differentialflatness/rigidbodytest_waypoints.py index 5f9e7f83..643730e8 100644 --- a/dev/differentialflatness/rigidbodytest_waypoints.py +++ b/dev/differentialflatness/rigidbodytest_waypoints.py @@ -188,7 +188,7 @@ axes.set_ylabel("y", fontsize=graphical.default_fontsize) axes.set_xlabel("x", fontsize=graphical.default_fontsize) axes.axis("equal") -axes.set(xlim=(-5, 25), ylim=(-5, 25)) +axes.set(xlim=(-15, 25), ylim=(-15, 25)) axes.tick_params(labelsize=graphical.default_fontsize) axes.grid(True) @@ -200,4 +200,4 @@ sys.traj = traj -sys.animate_simulation() +ani = sys.animate_simulation() diff --git a/examples/demos_by_tool/dynamicprogramming/pendulum_optimal_swingup_demo.py b/examples/demos_by_tool/dynamicprogramming/pendulum_optimal_swingup_demo.py index 916ad22a..cee002f8 100644 --- a/examples/demos_by_tool/dynamicprogramming/pendulum_optimal_swingup_demo.py +++ b/examples/demos_by_tool/dynamicprogramming/pendulum_optimal_swingup_demo.py @@ -43,8 +43,8 @@ # dp.plot_policy() #dp.solve_bellman_equation( tol = 1) -# dp.solve_bellman_equation( tol = 0.1 , animate_cost2go = True ) -dp.solve_bellman_equation( tol = 1 , animate_policy = True ) +dp.solve_bellman_equation( tol = 0.1 , animate_cost2go = True ) +# dp.solve_bellman_equation( tol = 1 , animate_policy = True ) #dp.plot_cost2go(150) #dp.animate_cost2go( show = False , save = True ) From bb1820b89b098878913b797d5f78f3a0869dc650 Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Wed, 10 Apr 2024 14:53:14 -0400 Subject: [PATCH 93/93] clean-up before master pull --- dev/2Ddrone/2Ddrone_trajopt | 183 ----- dev/2Ddrone/2Ddrone_trajopt_simple_cost_test | 158 ----- dev/2Ddrone/2Ddrone_trajopt_succes_v1 | 183 ----- dev/2Ddrone/2Ddrone_vi | 208 ------ dev/active_suspension/test_2d_curve.py | 46 -- dev/boat/boat_with_lqr_controllability.py | 70 -- dev/differentialflatness/droneminsnap.py | 160 ----- .../droneminsnap_waypoints.py | 179 ----- .../polynomialtrajectory.py | 75 -- dev/differentialflatness/rigidbodytest.py | 193 ----- .../rigidbodytest_waypoints.py | 203 ------ dev/dimensionless/car1_dimpolicy.pdf | Bin 131345 -> 0 bytes dev/dimensionless/car1_policy.pdf | Bin 54698 -> 0 bytes dev/dimensionless/car1_traj.pdf | Bin 15380 -> 0 bytes dev/dimensionless/car1_traj2.pdf | Bin 15686 -> 0 bytes dev/gym/pyro_gym_tests.py | 50 -- dev/hybrid_systems/hybrid.py | 69 -- dev/hybrid_systems/hybrid_mechanical.py | 462 ------------ dev/hybrid_systems/rmincontroller | 260 ------- dev/hybrid_systems/test_hybrid_vi.py | 59 -- dev/kinematic/__init__.py | 0 dev/kinematic/kinematic.py | 659 ------------------ dev/markov/markov.py | 291 -------- dev/mpc/mpc.py | 121 ---- .../Charles_Khazoom_tests/trajopt.py | 279 -------- ...jopt_double_pendulum_based_on_rrt_guess.py | 304 -------- ...jopt_single_pendulum_based_on_rrt_guess.py | 302 -------- .../direct_collocation/block_minimal_force.py | 128 ---- .../double_pendulum_2sec_swing_up.py | 174 ----- .../double_pendulum_4sec_swing_up.py | 174 ----- .../double_pendulum_8sec_swing_up.py | 174 ----- .../direct_collocation/double_stage.py | 69 -- .../direct_collocation/pendulum_from_RRT.py | 125 ---- .../pendulum_from_sratch.py | 129 ---- ...iew_doublependulum_optimized_trajectory.py | 17 - .../ipopt_tests/test_ipopt_install.py | 23 - .../ipopt_tests/test_ipopt_simple.py | 35 - .../bicycle_parking_with_valueiteration.py | 59 -- .../bicycle_parking_with_valueiteration.py | 58 -- .../car_with_valueiteration.py | 70 -- .../car_with_valueiteration_computation.py | 70 -- .../double_pendulum_with_valueiteration_nd.py | 63 -- ...mic_3d_with_obstacles_valueiteration_nd.py | 48 -- ...mic_mobile_robot_with_valueiteration_nd.py | 49 -- .../simple_pendulum_with_valueiteration.py | 49 -- .../udes_racecar_with_valueiteration.py | 68 -- dev/plane/plane_controller.py | 86 --- dev/plane/plane_trajectory_optimisation.py | 48 -- .../approximatedynamicprogramming.py | 234 ------- .../approximate_dp_tests/cost2go_lqr_vs_vi.py | 81 --- .../fitted_vi_pendulum_21x21_working.py | 53 -- .../fitted_vi_pendulum_lowdef_convergence.py | 53 -- .../fitted_vi_pendulum_test.py | 53 -- .../functionapproximation.py | 335 --------- .../pendulum_cost2go_guassian_approx.py | 91 --- .../pendulum_cost2go_guassian_approx_test.py | 92 --- .../pendulum_cost2go_quadratic_approx.py | 69 -- .../dp_tests/dev_test_double_pendulum.py | 78 --- .../dp_tests/test_hidef.py | 61 -- .../dp_tests/test_lowdef.py | 58 -- .../dp_tests/test_policy_evaluator.py | 56 -- .../test_policy_evaluator_with_lookuptable.py | 68 -- .../dp_tests/test_speed.py | 37 - ...dation_double_pendulum_local_controller.py | 77 -- .../validation_dp_4D_with_two_pendulum.py | 57 -- .../pytorch_tests/pytorch_nn_test.py | 100 --- .../pytorch_tests/pytorch_polynomial_test.py | 107 --- .../pytorch_polynomial_vs_nn_fit.py | 143 ---- .../pytorch_tests/pytorch_rl_demo.py | 258 ------- .../pytorch_tests/pytorch_rl_demo_2.py | 258 ------- .../rl_tests/pendulum_test_rl.py | 50 -- .../rl_tests/reinforcementlearning.py | 1 - dev/rigidbody/rocket2.py | 252 ------- dev/tests/old_tests/saveloadtest.py | 18 - dev/tests/old_tests/test_all_examples.py | 128 ---- dev/tests/old_tests/test_all_pendulums.py | 94 --- dev/tests/old_tests/test_integrators.py | 121 ---- dev/tests/old_tests/test_pidcontrol.py | 187 ----- dev/tests/old_tests/test_state_space.py | 204 ------ dev/tests/old_tests/test_stateobserver.py | 200 ------ dev/tests/old_tests/test_sys | 31 - dev/tests/old_tests/test_utils.py | 13 - dev/tests/old_tests/test_value_iteration.py | 87 --- dev/tests/old_tests/traj.npz | Bin 1362186 -> 0 bytes dev/tests/test_graphical_settings.py | 35 - ...et_landing_with_trajectory_optimization.py | 62 -- ...le_pendulum_multiple_controller_options.py | 4 +- .../prob/demo_crash_commande_en_position.py | 2 +- ...ongitudinal_car_braking_value_iteration.py | 4 +- .../longitudinal_car_with_torque_input.py | 4 +- .../demos_by_system/car_steering/bicycle.py | 4 +- .../bicycle_exploration_with_rrt.py | 4 +- .../bicycle_parallel_parking_with_rrt.py | 4 +- examples/demos_by_system/car_steering/car.py | 4 +- .../car_trajectory_optimisation.py | 2 +- .../car_steering/car_trajectory_with_rrt.py | 4 +- .../car_with_custom_lateral_controller.py | 4 +- .../car_with_valueiteration_minimum_time.py | 4 +- .../car_with_valueiteration_quadratic_cost.py | 4 +- ...bot_exploration_with_obstacles_with_rrt.py | 4 +- ...nomic_mobile_robot_exploration_with_rrt.py | 4 +- ...onomic_mobile_robot_with_valueiteration.py | 4 +- .../demos_by_system/suspension/suspension.py | 3 - .../dynamicprogramming/2D_navigation.py | 4 +- .../braking_reachability.py | 4 +- .../dynamicprogramming/car_braking.py | 4 +- .../dynamicprogramming/car_parking.py | 4 +- ...e_pendulum_with_trajectory_optimization.py | 33 + ...lum_with_trajectory_optimization_CUSTOM.py | 174 ----- .../linear_trajectory_optimisation.py | 30 - ...near_trajectory_optimisation_vectorized.py | 34 - ...pendulum_trajectory_optimization_CUSTOM.py | 120 ---- .../vehicle_modeling/advanced_vehicles.py | 4 +- .../vehicle_modeling/kinematic_VI_bicycle.py | 4 +- pyro/analysis/costfunction.py | 3 - pyro/analysis/costfunction_legacy.py | 261 ------- pyro/analysis/graphical.py | 4 +- pyro/dynamic/drone.py | 4 +- pyro/dynamic/manipulator.py | 24 +- ...inal_vehicule.py => vehicle_propulsion.py} | 1 - .../{vehicle.py => vehicle_steering.py} | 26 +- pyro/planning/trajectorygeneration.py | 28 +- 122 files changed, 122 insertions(+), 10839 deletions(-) delete mode 100644 dev/2Ddrone/2Ddrone_trajopt delete mode 100644 dev/2Ddrone/2Ddrone_trajopt_simple_cost_test delete mode 100644 dev/2Ddrone/2Ddrone_trajopt_succes_v1 delete mode 100644 dev/2Ddrone/2Ddrone_vi delete mode 100644 dev/active_suspension/test_2d_curve.py delete mode 100644 dev/boat/boat_with_lqr_controllability.py delete mode 100644 dev/differentialflatness/droneminsnap.py delete mode 100644 dev/differentialflatness/droneminsnap_waypoints.py delete mode 100644 dev/differentialflatness/polynomialtrajectory.py delete mode 100644 dev/differentialflatness/rigidbodytest.py delete mode 100644 dev/differentialflatness/rigidbodytest_waypoints.py delete mode 100644 dev/dimensionless/car1_dimpolicy.pdf delete mode 100644 dev/dimensionless/car1_policy.pdf delete mode 100644 dev/dimensionless/car1_traj.pdf delete mode 100644 dev/dimensionless/car1_traj2.pdf delete mode 100644 dev/gym/pyro_gym_tests.py delete mode 100755 dev/hybrid_systems/hybrid.py delete mode 100644 dev/hybrid_systems/hybrid_mechanical.py delete mode 100644 dev/hybrid_systems/rmincontroller delete mode 100644 dev/hybrid_systems/test_hybrid_vi.py delete mode 100644 dev/kinematic/__init__.py delete mode 100644 dev/kinematic/kinematic.py delete mode 100644 dev/markov/markov.py delete mode 100644 dev/mpc/mpc.py delete mode 100644 dev/old_to_remove/trajectory_optimisation/Charles_Khazoom_tests/trajopt.py delete mode 100755 dev/old_to_remove/trajectory_optimisation/Charles_Khazoom_tests/trajopt_double_pendulum_based_on_rrt_guess.py delete mode 100755 dev/old_to_remove/trajectory_optimisation/Charles_Khazoom_tests/trajopt_single_pendulum_based_on_rrt_guess.py delete mode 100644 dev/old_to_remove/trajectory_optimisation/direct_collocation/block_minimal_force.py delete mode 100644 dev/old_to_remove/trajectory_optimisation/direct_collocation/double_pendulum_2sec_swing_up.py delete mode 100644 dev/old_to_remove/trajectory_optimisation/direct_collocation/double_pendulum_4sec_swing_up.py delete mode 100644 dev/old_to_remove/trajectory_optimisation/direct_collocation/double_pendulum_8sec_swing_up.py delete mode 100644 dev/old_to_remove/trajectory_optimisation/direct_collocation/double_stage.py delete mode 100644 dev/old_to_remove/trajectory_optimisation/direct_collocation/pendulum_from_RRT.py delete mode 100644 dev/old_to_remove/trajectory_optimisation/direct_collocation/pendulum_from_sratch.py delete mode 100644 dev/old_to_remove/trajectory_optimisation/direct_collocation/view_doublependulum_optimized_trajectory.py delete mode 100644 dev/old_to_remove/trajectory_optimisation/ipopt_tests/test_ipopt_install.py delete mode 100644 dev/old_to_remove/trajectory_optimisation/ipopt_tests/test_ipopt_simple.py delete mode 100644 dev/old_to_remove/value_iteration/3d-vi-test/bicycle_parking_with_valueiteration.py delete mode 100644 dev/old_to_remove/value_iteration/bicycle_parking_with_valueiteration.py delete mode 100644 dev/old_to_remove/value_iteration/car_with_valueiteration.py delete mode 100644 dev/old_to_remove/value_iteration/car_with_valueiteration_computation.py delete mode 100644 dev/old_to_remove/value_iteration/double_pendulum_with_valueiteration_nd.py delete mode 100644 dev/old_to_remove/value_iteration/holonomic_3d_with_obstacles_valueiteration_nd.py delete mode 100644 dev/old_to_remove/value_iteration/holonomic_mobile_robot_with_valueiteration_nd.py delete mode 100644 dev/old_to_remove/value_iteration/simple_pendulum_with_valueiteration.py delete mode 100644 dev/old_to_remove/value_iteration/udes_racecar_with_valueiteration.py delete mode 100644 dev/plane/plane_controller.py delete mode 100644 dev/plane/plane_trajectory_optimisation.py delete mode 100644 dev/reinforcement_learning/approximate_dp_tests/approximatedynamicprogramming.py delete mode 100644 dev/reinforcement_learning/approximate_dp_tests/cost2go_lqr_vs_vi.py delete mode 100644 dev/reinforcement_learning/approximate_dp_tests/fitted_vi_pendulum_21x21_working.py delete mode 100644 dev/reinforcement_learning/approximate_dp_tests/fitted_vi_pendulum_lowdef_convergence.py delete mode 100644 dev/reinforcement_learning/approximate_dp_tests/fitted_vi_pendulum_test.py delete mode 100644 dev/reinforcement_learning/approximate_dp_tests/functionapproximation.py delete mode 100644 dev/reinforcement_learning/approximate_dp_tests/pendulum_cost2go_guassian_approx.py delete mode 100644 dev/reinforcement_learning/approximate_dp_tests/pendulum_cost2go_guassian_approx_test.py delete mode 100644 dev/reinforcement_learning/approximate_dp_tests/pendulum_cost2go_quadratic_approx.py delete mode 100644 dev/reinforcement_learning/dp_tests/dev_test_double_pendulum.py delete mode 100644 dev/reinforcement_learning/dp_tests/test_hidef.py delete mode 100644 dev/reinforcement_learning/dp_tests/test_lowdef.py delete mode 100644 dev/reinforcement_learning/dp_tests/test_policy_evaluator.py delete mode 100644 dev/reinforcement_learning/dp_tests/test_policy_evaluator_with_lookuptable.py delete mode 100644 dev/reinforcement_learning/dp_tests/test_speed.py delete mode 100644 dev/reinforcement_learning/dp_tests/validation_double_pendulum_local_controller.py delete mode 100644 dev/reinforcement_learning/dp_tests/validation_dp_4D_with_two_pendulum.py delete mode 100644 dev/reinforcement_learning/pytorch_tests/pytorch_nn_test.py delete mode 100644 dev/reinforcement_learning/pytorch_tests/pytorch_polynomial_test.py delete mode 100644 dev/reinforcement_learning/pytorch_tests/pytorch_polynomial_vs_nn_fit.py delete mode 100644 dev/reinforcement_learning/pytorch_tests/pytorch_rl_demo.py delete mode 100644 dev/reinforcement_learning/pytorch_tests/pytorch_rl_demo_2.py delete mode 100644 dev/reinforcement_learning/rl_tests/pendulum_test_rl.py delete mode 100644 dev/reinforcement_learning/rl_tests/reinforcementlearning.py delete mode 100644 dev/rigidbody/rocket2.py delete mode 100755 dev/tests/old_tests/saveloadtest.py delete mode 100644 dev/tests/old_tests/test_all_examples.py delete mode 100644 dev/tests/old_tests/test_all_pendulums.py delete mode 100644 dev/tests/old_tests/test_integrators.py delete mode 100644 dev/tests/old_tests/test_pidcontrol.py delete mode 100644 dev/tests/old_tests/test_state_space.py delete mode 100644 dev/tests/old_tests/test_stateobserver.py delete mode 100644 dev/tests/old_tests/test_sys delete mode 100644 dev/tests/old_tests/test_utils.py delete mode 100644 dev/tests/old_tests/test_value_iteration.py delete mode 100644 dev/tests/old_tests/traj.npz delete mode 100644 dev/tests/test_graphical_settings.py delete mode 100644 dev/trajectory_optim_test/rocket_landing_with_trajectory_optimization.py create mode 100644 examples/demos_by_tool/trajectory_planning/double_pendulum_with_trajectory_optimization.py delete mode 100644 examples/demos_by_tool/trajectory_planning/double_pendulum_with_trajectory_optimization_CUSTOM.py delete mode 100644 examples/demos_by_tool/trajectory_planning/linear_trajectory_optimisation.py delete mode 100644 examples/demos_by_tool/trajectory_planning/linear_trajectory_optimisation_vectorized.py delete mode 100644 examples/demos_by_tool/trajectory_planning/simple_pendulum_trajectory_optimization_CUSTOM.py delete mode 100644 pyro/analysis/costfunction_legacy.py rename pyro/dynamic/{longitudinal_vehicule.py => vehicle_propulsion.py} (99%) rename pyro/dynamic/{vehicle.py => vehicle_steering.py} (98%) diff --git a/dev/2Ddrone/2Ddrone_trajopt b/dev/2Ddrone/2Ddrone_trajopt deleted file mode 100644 index 64179237..00000000 --- a/dev/2Ddrone/2Ddrone_trajopt +++ /dev/null @@ -1,183 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Created on Thu May 18 12:30:17 2023 - -@author: alex -""" - -import numpy as np - -import matplotlib.pyplot as plt - -from pyro.dynamic import drone -from pyro.analysis import costfunction -from pyro.planning import trajectoryoptimisation - -# from pyro.planning import dynamicprogramming - -############################################################################# - -class drone2Dcostfunction( costfunction.CostFunction ): - """ - - - """ - - ############################ - def __init__(self, obstacles ): - - costfunction.CostFunction.__init__(self) - - # dimensions - self.n = 2 - self.m = 2 - - # Quadratic cost weights - self.Q = np.diag( np.ones(self.n) ) - self.R = np.diag( np.ones(self.m) ) - self.S = np.diag( np.zeros(self.n) ) - - self.obstacles = obstacles - - self.d_tol = 2.0 - self.nab = 10.0 - self.buffer = 1.0 - - self.is_vectorized = False - - - ############################ - @classmethod - def from_sys(cls, sys): - """ From ContinuousDynamicSystem instance """ - - instance = cls( sys.n , sys.m ) - - return instance - - - ############################# - def h(self, x , t = 0): - """ Final cost function with zero value """ - - if x.ndim == 1 : - - J_f = x.T @ self.S @ x - - - else: - - # Quadratic terminal cost - J_f = np.diag( x.T @ self.S @ x ) - - return J_f - - - ############################# - def g(self, x, u, t): - """ Quadratic additive cost """ - - if x.ndim == 1 : - - dJ = x.T @ self.Q @ x + u.T @ self.R @ u - - deltas = self.obstacles - x - - distances = np.linalg.norm( deltas , axis = 1 ) - self.buffer - - distances[ distances < 0 ] = 0 - - costs = self.nab * np.exp( - ( distances / self.d_tol ) ** 2 ) - - dJ = dJ + costs.sum() - - else: - - dJ = np.diag( x.T @ self.Q @ x ) + np.diag( u.T @ self.R @ u ) - - deltas = self.obstacles.T[:,:,np.newaxis] - x[:,np.newaxis,:] - - distances = np.linalg.norm( deltas , axis = 1 ) - self.buffer - - distances[ distances < 0 ] = 0 - - # d_min = distances.min( axis = 0 ) - - # costs = self.nab * ( 1 / ( distances ) - 1. / self.d_tol ) ** 2 - # costs = self.nab * ( 1 / d_min ) ** 2 - costs = self.nab * np.exp( - ( distances / self.d_tol ) ** 2 ) - - # costs[ distances > self.d_tol ] = 0 - - dJ = dJ + costs.sum( axis = 0 ) - - - return dJ - -sys = drone.SpeedControlledDrone2D() - - -obstacles = np.array([[ 0. , 20. ] , - [ 0. , 25. ] , - [ 0. , 30. ] , - [ 5. , 20. ] , - [ 5. , 25. ] , - [ 5. , 30. ] , - [ 10. , 20. ] , - [ 10. , 25. ] , - [ 10. , 30. ] , - [ 2.5 , 35 ] , - [ 7.5 , 35 ] ] ) - -sys.obstacles = obstacles - -sys.x_ub = np.array([+100,+100]) -sys.x_lb = np.array([-10,-10]) - -sys.u_ub = np.array([+5,+5]) -sys.u_lb = np.array([-5,-5]) - - - -cf = drone2Dcostfunction( obstacles ) - -cf.Q[0,0] = 1.0 -cf.Q[1,1] = 1.0 -cf.R[0,0] = 100.0 -cf.R[1,1] = 100.0 - -cf.buffer = 1.0 -cf.d_tol = 1.0 -cf.nab = 10000.0 - - -x0 = 0.0 -y0 = 16.0 -xf = 0.2 -yf = 27.5 -tf = 10.0 -vx = (xf-x0)/tf -vy = (yf-y0)/tf - -grid = 20 -dt = tf / grid - -planner = trajectoryoptimisation.DirectCollocationTrajectoryOptimisation( sys , dt , grid , cf) - -planner.dec_init[ 0 * grid : 1 * grid ] = np.linspace( x0 , xf , grid ) -planner.dec_init[ 1 * grid : 2 * grid ] = np.linspace( y0 , yf , grid ) -planner.dec_init[ 2 * grid : 3 * grid ] = vx -planner.dec_init[ 3 * grid : 4 * grid ] = vy - - -planner.x_start = np.array([x0,y0]) -planner.x_goal = np.array([xf,yf]) - -planner.maxiter = 200 - -planner.init_dynamic_plot() -planner.compute_solution() -planner.show_solution('j') -planner.animate_solution() - diff --git a/dev/2Ddrone/2Ddrone_trajopt_simple_cost_test b/dev/2Ddrone/2Ddrone_trajopt_simple_cost_test deleted file mode 100644 index 97736de9..00000000 --- a/dev/2Ddrone/2Ddrone_trajopt_simple_cost_test +++ /dev/null @@ -1,158 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Created on Thu May 18 12:30:17 2023 - -@author: alex -""" - -import numpy as np - -import matplotlib.pyplot as plt - -from pyro.dynamic import drone -from pyro.analysis import costfunction -from pyro.planning import trajectoryoptimisation - -# from pyro.planning import dynamicprogramming - -############################################################################# - -class drone2Dcostfunction( costfunction.CostFunction ): - """ - - - """ - - ############################ - def __init__(self, obstacles ): - - costfunction.CostFunction.__init__(self) - - # dimensions - self.n = 2 - self.m = 2 - - # Quadratic cost weights - self.Q = np.diag( np.ones(self.n) ) - self.R = np.diag( np.ones(self.m) ) - self.S = np.diag( np.zeros(self.n) ) - - self.obstacles = obstacles - - self.is_vectorized = False - - - ############################ - @classmethod - def from_sys(cls, sys): - """ From ContinuousDynamicSystem instance """ - - instance = cls( sys.n , sys.m ) - - return instance - - - ############################# - def h(self, x , t = 0): - """ Final cost function with zero value """ - - if x.ndim == 1 : - - J_f = x.T @ self.S @ x - - - else: - - # Quadratic terminal cost - J_f = np.diag( x.T @ self.S @ x ) - - return J_f - - - ############################# - def g(self, x, u, t): - """ Quadratic additive cost """ - - if x.ndim == 1 : - - dJ = x.T @ self.Q @ x + u.T @ self.R @ u - - deltas = self.obstacles - x - - distances = np.linalg.norm( deltas , axis = 1 ) - - costs = -distances - - dJ = dJ + costs.sum() - - else: - - dJ = np.diag( x.T @ self.Q @ x ) - - # plt.pause( 0.01 ) - # input("Press Enter to continue...") - - - return dJ - -sys = drone.SpeedControlledDrone2D() - - -obstacles = np.array([[ 0. , 20. ] , - [ 0. , 25. ] , - [ 0. , 30. ] , - [ 5. , 20. ] , - [ 5. , 25. ] , - [ 5. , 30. ] , - [ 10. , 20. ] , - [ 10. , 25. ] , - [ 10. , 30. ] , - [ 2.5 , 35 ] , - [ 7.5 , 35 ] ] ) - -sys.obstacles = obstacles - -sys.x_ub = np.array([+100,+100]) -sys.x_lb = np.array([-10,-10]) - -sys.u_ub = np.array([+5,+5]) -sys.u_lb = np.array([-5,-5]) - - - -cf = drone2Dcostfunction( obstacles ) - -cf.Q[0,0] = 0.0 -cf.Q[1,1] = 0.0 -cf.R[0,0] = 1.0 -cf.R[1,1] = 1.0 - - - -x0 = 0.0 -y0 = 16.0 -xf = 10.0 -yf = 27.5 -tf = 10.0 -vx = (xf-x0)/tf -vy = (yf-y0)/tf - -grid = 20 -dt = tf / grid - -planner = trajectoryoptimisation.DirectCollocationTrajectoryOptimisation( sys , dt , grid , cf) - -planner.dec_init[ 0 * grid : 1 * grid ] = np.linspace( x0 , xf , grid ) -planner.dec_init[ 1 * grid : 2 * grid ] = np.linspace( y0 , yf , grid ) -planner.dec_init[ 2 * grid : 3 * grid ] = vx -planner.dec_init[ 3 * grid : 4 * grid ] = vy - - -planner.x_start = np.array([x0,y0]) -planner.x_goal = np.array([xf,yf]) - -planner.init_dynamic_plot() -planner.compute_solution() -planner.animate_solution() - diff --git a/dev/2Ddrone/2Ddrone_trajopt_succes_v1 b/dev/2Ddrone/2Ddrone_trajopt_succes_v1 deleted file mode 100644 index 41412508..00000000 --- a/dev/2Ddrone/2Ddrone_trajopt_succes_v1 +++ /dev/null @@ -1,183 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Created on Thu May 18 12:30:17 2023 - -@author: alex -""" - -import numpy as np - -import matplotlib.pyplot as plt - -from pyro.dynamic import drone -from pyro.analysis import costfunction -from pyro.planning import trajectoryoptimisation - -# from pyro.planning import dynamicprogramming - -############################################################################# - -class drone2Dcostfunction( costfunction.CostFunction ): - """ - - - """ - - ############################ - def __init__(self, obstacles ): - - costfunction.CostFunction.__init__(self) - - # dimensions - self.n = 2 - self.m = 2 - - # Quadratic cost weights - self.Q = np.diag( np.ones(self.n) ) - self.R = np.diag( np.ones(self.m) ) - self.S = np.diag( np.zeros(self.n) ) - - self.obstacles = obstacles - - self.d_tol = 2.0 - self.nab = 10.0 - self.buffer = 1.0 - - self.is_vectorized = False - - - ############################ - @classmethod - def from_sys(cls, sys): - """ From ContinuousDynamicSystem instance """ - - instance = cls( sys.n , sys.m ) - - return instance - - - ############################# - def h(self, x , t = 0): - """ Final cost function with zero value """ - - if x.ndim == 1 : - - J_f = x.T @ self.S @ x - - - else: - - # Quadratic terminal cost - J_f = np.diag( x.T @ self.S @ x ) - - return J_f - - - ############################# - def g(self, x, u, t): - """ Quadratic additive cost """ - - if x.ndim == 1 : - - dJ = x.T @ self.Q @ x + u.T @ self.R @ u - - deltas = self.obstacles - x - - distances = np.linalg.norm( deltas , axis = 1 ) - self.buffer - - distances[ distances < 0 ] = 0 - - costs = self.nab * np.exp( - ( distances / self.d_tol ) ** 2 ) - - dJ = dJ + costs.sum() - - else: - - dJ = np.diag( x.T @ self.Q @ x ) + np.diag( u.T @ self.R @ u ) - - deltas = self.obstacles.T[:,:,np.newaxis] - x[:,np.newaxis,:] - - distances = np.linalg.norm( deltas , axis = 1 ) - self.buffer - - distances[ distances < 0 ] = 0 - - # d_min = distances.min( axis = 0 ) - - # costs = self.nab * ( 1 / ( distances ) - 1. / self.d_tol ) ** 2 - # costs = self.nab * ( 1 / d_min ) ** 2 - costs = self.nab * np.exp( - ( distances / self.d_tol ) ** 2 ) - - # costs[ distances > self.d_tol ] = 0 - - dJ = dJ + costs.sum() - - - return dJ - -sys = drone.SpeedControlledDrone2D() - - -obstacles = np.array([[ 0. , 20. ] , - [ 0. , 25. ] , - [ 0. , 30. ] , - [ 5. , 20. ] , - [ 5. , 25. ] , - [ 5. , 30. ] , - [ 10. , 20. ] , - [ 10. , 25. ] , - [ 10. , 30. ] , - [ 2.5 , 35 ] , - [ 7.5 , 35 ] ] ) - -sys.obstacles = obstacles - -sys.x_ub = np.array([+100,+100]) -sys.x_lb = np.array([-10,-10]) - -sys.u_ub = np.array([+5,+5]) -sys.u_lb = np.array([-5,-5]) - - - -cf = drone2Dcostfunction( obstacles ) - -cf.Q[0,0] = 1.0 -cf.Q[1,1] = 1.0 -cf.R[0,0] = 100.0 -cf.R[1,1] = 100.0 - -cf.buffer = 1.0 -cf.d_tol = 1.0 -cf.nab = 10000.0 - - -x0 = 0.0 -y0 = 16.0 -xf = 10.2 -yf = 27.5 -tf = 10.0 -vx = (xf-x0)/tf -vy = (yf-y0)/tf - -grid = 20 -dt = tf / grid - -planner = trajectoryoptimisation.DirectCollocationTrajectoryOptimisation( sys , dt , grid , cf) - -planner.dec_init[ 0 * grid : 1 * grid ] = np.linspace( x0 , xf , grid ) -planner.dec_init[ 1 * grid : 2 * grid ] = np.linspace( y0 , yf , grid ) -planner.dec_init[ 2 * grid : 3 * grid ] = vx -planner.dec_init[ 3 * grid : 4 * grid ] = vy - - -planner.x_start = np.array([x0,y0]) -planner.x_goal = np.array([xf,yf]) - -planner.maxiter = 200 - -planner.init_dynamic_plot() -planner.compute_solution() -planner.show_solution('j') -planner.animate_solution() - diff --git a/dev/2Ddrone/2Ddrone_vi b/dev/2Ddrone/2Ddrone_vi deleted file mode 100644 index e7c6a265..00000000 --- a/dev/2Ddrone/2Ddrone_vi +++ /dev/null @@ -1,208 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Created on Thu May 18 12:30:17 2023 - -@author: alex -""" - -import numpy as np - -import matplotlib.pyplot as plt - -from pyro.dynamic import drone -from pyro.analysis import costfunction -# from pyro.planning import trajectoryoptimisation - -from pyro.planning import dynamicprogramming -from pyro.planning import discretizer - -############################################################################# - -class drone2Dcostfunction( costfunction.CostFunction ): - """ - - - """ - - ############################ - def __init__(self, obstacles ): - - costfunction.CostFunction.__init__(self) - - # dimensions - self.n = 2 - self.m = 2 - - # Quadratic cost weights - self.Q = np.diag( np.ones(self.n) ) - self.R = np.diag( np.ones(self.m) ) - self.S = np.diag( np.zeros(self.n) ) - - self.obstacles = obstacles - - self.d_tol = 2.0 - self.nab = 10.0 - self.buffer = 1.0 - - self.xbar = np.array([0,0]) - - self.is_vectorized = False - - - ############################ - @classmethod - def from_sys(cls, sys): - """ From ContinuousDynamicSystem instance """ - - instance = cls( sys.n , sys.m ) - - return instance - - - ############################# - def h(self, x , t = 0): - """ Final cost function with zero value """ - - dx = x - self.xbar - - if x.ndim == 1 : - - J_f = dx.T @ self.S @ dx - - - else: - - # Quadratic terminal cost - J_f = np.diag( dx.T @ self.S @ dx ) - - return J_f - - - ############################# - def g(self, x, u, t): - """ Quadratic additive cost """ - - dx = x - self.xbar - - if x.ndim == 1 : - - dJ = dx.T @ self.Q @ dx + u.T @ self.R @ u - - deltas = self.obstacles - x - - distances = np.linalg.norm( deltas , axis = 1 ) - self.buffer - - distances[ distances < 0 ] = 0 - - costs = self.nab * np.exp( - ( distances / self.d_tol ) ** 2 ) - - dJ = dJ + costs.sum() - - else: - - dJ = np.diag( dx.T @ self.Q @ dx ) + np.diag( u.T @ self.R @ u ) - - deltas = self.obstacles.T[:,:,np.newaxis] - x[:,np.newaxis,:] - - distances = np.linalg.norm( deltas , axis = 1 ) - self.buffer - - distances[ distances < 0 ] = 0 - - # d_min = distances.min( axis = 0 ) - - # costs = self.nab * ( 1 / ( distances ) - 1. / self.d_tol ) ** 2 - # costs = self.nab * ( 1 / d_min ) ** 2 - costs = self.nab * np.exp( - ( distances / self.d_tol ) ** 2 ) - - # costs[ distances > self.d_tol ] = 0 - - dJ = dJ + costs.sum( axis = 0 ) - - - return dJ - -sys = drone.SpeedControlledDrone2D() - - -obstacles = np.array([[ 0. , 20. ] , - [ 0. , 25. ] , - [ 0. , 30. ] , - [ 5. , 20. ] , - [ 5. , 25. ] , - [ 5. , 30. ] , - [ 10. , 20. ] , - [ 10. , 25. ] , - [ 10. , 30. ] , - [ 2.5 , 35 ] , - [ 7.5 , 35 ] ] ) - -sys.obstacles = obstacles - -sys.x_ub = np.array([+20,+35]) -sys.x_lb = np.array([-10,10]) - -sys.u_ub = np.array([+5,+5]) -sys.u_lb = np.array([-5,-5]) - - - -cf = drone2Dcostfunction( obstacles ) - -cf.Q[0,0] = 1.0 -cf.Q[1,1] = 1.0 -cf.R[0,0] = 100.0 -cf.R[1,1] = 100.0 - -cf.buffer = 1.0 -cf.d_tol = 1.0 -cf.nab = 5000.0 - -cf.xbar = np.array([0.0,15.0]) - - -# sys.x_ub = np.array([+15,+35]) -# sys.x_lb = np.array([-5,15]) -# sys.u_ub = np.array([+2,+2]) -# sys.u_lb = np.array([-2,-2]) -# cf.d_tol = 0.5 -# cf.Q[0,0] = 5.0 -# cf.Q[1,1] = 5.0 -# # cf.xbar = np.array([0.0,22.0]) -# cf.xbar = np.array([5.0,22.0]) -# # cf.xbar = np.array([10.0,22.0]) - -cf.INF = 20000 - -# Discrete world -grid_sys = discretizer.GridDynamicSystem( sys , [201,201] , [5,5] , dt = 0.1 ) - - -# DP alg - -dp = dynamicprogramming.DynamicProgrammingWithLookUpTable( grid_sys, cf) - -# dp.solve_bellman_equation( tol = 0.1 ) - -dp.compute_steps( 400 , animate_cost2go= False ) - -dp.plot_cost2go_3D() - -dp.plot_policy( k = 0 ) -dp.plot_policy( k = 1 ) - - -#asign controller -ctl = dp.get_lookup_table_controller() -cl_sys = ctl + sys - -# Simulation and animation -cl_sys.x0 = np.array([7.5,27.5]) -cl_sys.x0 = np.array([5.0,32.0]) -cl_sys.compute_trajectory( 10, 10001, 'euler') -cl_sys.plot_trajectory('xu') -cl_sys.plot_phase_plane_trajectory() -cl_sys.animate_simulation() - - - diff --git a/dev/active_suspension/test_2d_curve.py b/dev/active_suspension/test_2d_curve.py deleted file mode 100644 index b89f2ec3..00000000 --- a/dev/active_suspension/test_2d_curve.py +++ /dev/null @@ -1,46 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Created on Mon Oct 24 20:24:05 2022 - -@author: alex -""" - -import numpy as np - -import matplotlib -import matplotlib.pyplot as plt - -fig , ax = plt.subplots(2, sharex=True, figsize=(6,4), dpi=200, frameon=True) - -fig.canvas.manager.set_window_title('Figure Name') - -n = 200 - -x = np.linspace(0,10,n) - -a = np.array([ 0.5 , 0.3 , 0.7 , 0.2 , 0.2 , 0.1 ]) -w = np.array([ 0.2 , 0.4 , 0.5 , 1.0 , 2.0 , 3.0 ]) -phi = np.array([ 3.0 , 2.0 , 0.0 , 0.0 , 0.0 , 0.0 ]) - -y = np.zeros(n) -dy = np.zeros(n) - -for i in range(a.size): - y = y + a[i] * np.sin( w[i] * ( x - phi[i] )) - dy = dy + a[i] * w[i] * np.cos( w[i] * ( x - phi[i] )) - - -ax[0].plot( x , y , 'b') -ax[0].set_ylabel('y') -ax[0].axis('equal') -ax[0].grid(True) -ax[0].tick_params( labelsize = 8 ) - -ax[1].plot( x , dy , 'r') -ax[1].set_ylabel('dy') -ax[1].axis('equal') -ax[1].grid(True) -ax[1].tick_params( labelsize = 8 ) - -fig.show() \ No newline at end of file diff --git a/dev/boat/boat_with_lqr_controllability.py b/dev/boat/boat_with_lqr_controllability.py deleted file mode 100644 index b5c8d3d2..00000000 --- a/dev/boat/boat_with_lqr_controllability.py +++ /dev/null @@ -1,70 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Created on Sun Oct 3 08:27:06 2021 - -@author: alex -""" - -import numpy as np - -from pyro.dynamic.boat import Boat2D -from pyro.analysis.costfunction import QuadraticCostFunction -from pyro.dynamic.statespace import linearize -from pyro.control.lqr import synthesize_lqr_controller - - -# Non-linear model -sys = Boat2D() - -sys.mass = 1000.0 -sys.inertia = 2000.0 -sys.l_t = 2.0 -sys.damping_coef = np.array([ 200.0, 2000.0, 1000.0 ]) - -sys.xbar[3] = 0.0 -sys.ubar[0] = 0.0 - -# Linear model -ss = linearize( sys , 0.01 ) - -ss.A[ abs(ss.A) < 0.00001 ] = 0.0 -ss.B[ abs(ss.B) < 0.00001 ] = 0.0 -print(ss.A) -print(ss.B) - -CA = np.hstack( (ss.B, - ss.A @ ss.B, - ss.A @ ss.A @ ss.B, - ss.A @ ss.A @ ss.A @ ss.B, - ss.A @ ss.A @ ss.A @ ss.A @ ss.B, - ss.A @ ss.A @ ss.A @ ss.A @ ss.A @ ss.B) ) - -rank = np.linalg.matrix_rank(CA) -print('Controllability rank = ',rank) -# Linearized boat is never controllable arround zero velocity - -# Cost function -cf = QuadraticCostFunction.from_sys( sys ) -cf.Q[0,0] = 1E3 -cf.Q[1,1] = 1E3 -cf.Q[2,2] = 1E3 -cf.Q[3,3] = 1E3 -cf.Q[4,4] = 1E3 -cf.Q[5,5] = 1E3 - -cf.R[0,0] = 1E-6 -cf.R[1,1] = 1E-6 - -# LQR controller -ctl = synthesize_lqr_controller( ss , cf , sys.xbar , sys.ubar ) - -ctl.K[ abs(ctl.K) < 0.00001 ] = 0.0 -print(ctl.K) - -# Simulation Closed-Loop Non-linear with LQR controller -# cl_sys = ctl + sys -# cl_sys.x0 = np.array([+1.0,+0.2,-0.2,0,0,0]) -# cl_sys.compute_trajectory( tf = 1000 , n = 10001 ) -# cl_sys.plot_trajectory('xu') -# cl_sys.animate_simulation( time_factor_video = 100.0 ) \ No newline at end of file diff --git a/dev/differentialflatness/droneminsnap.py b/dev/differentialflatness/droneminsnap.py deleted file mode 100644 index d496eefd..00000000 --- a/dev/differentialflatness/droneminsnap.py +++ /dev/null @@ -1,160 +0,0 @@ -# -*- coding: utf-8 -*- - -############################################################################### -import numpy as np -import matplotlib.pyplot as plt - -from pyro.analysis import graphical -from pyro.planning import trajectorygeneration -from pyro.dynamic import drone -from pyro.planning import plan - -############################################################################### -# Define the c.g. trajectory -############################################################################### - -# initial position -x0 = -10.0 -y0 = 0.0 - -# final position -xf = 20.0 -yf = 1.0 - -# time of flight -tf = 4.0 - -traj_x = trajectorygeneration.SingleAxisPolynomialTrajectoryGenerator( - poly_N=9, tf=tf, x0=np.array([x0, 0, 0]), xf=np.array([xf, 0, 0]) -) - -traj_x.Ws[4] = 1.0 # set the weight of the snap term to 1 - -px, X, t = traj_x.solve() # find the min snap trajectory - -x = X[0, :] -dx = X[1, :] -ax = X[2, :] -dax = X[3, :] -ddax = X[4, :] - -traj_y = trajectorygeneration.SingleAxisPolynomialTrajectoryGenerator( - poly_N=9, tf=tf, x0=np.array([y0, 0, 0]), xf=np.array([yf, 0, 0]) -) - -traj_y.Ws[4] = 1.0 # set the weight of the snap term to 1 - -py, Y, t = traj_y.solve() # find the min snap trajectory - -y = Y[0, :] -dy = Y[1, :] -ay = Y[2, :] + 9.81 # added gravity -day = Y[3, :] -dday = Y[4, :] - -############################################################################### -# Compute angular trajectory -############################################################################### - -theta = np.arctan2(ax, ay) -# theta = np.arctan( (ay/ax)) -s = np.sin(theta) -c = np.cos(theta) -dtheta = (dax * c - day * s) / (ax * s + ay * c) -ddtheta = ( - s * (-dday + ay * dtheta**2 - 2 * dax * dtheta) - + c * (ddax - ax * dtheta**2 - 2 * day * dtheta) -) / (ay * c + ax * s) - -############################################################################### -# Compute Forces -############################################################################### - -sys = drone.Drone2D() - -m = sys.mass -J = sys.inertia -r = sys.truster_offset - -F_tot = m * ax / np.sin(theta) -F_del = J * ddtheta / r - -T1 = 0.5 * (F_tot + F_del) -T2 = 0.5 * (F_tot - F_del) - -############################################################################### -# Plots -############################################################################### - -# Create traj -steps = len(t) -us = np.zeros((steps, 2)) # control inputs -xs = np.zeros((steps, 6)) -ys = np.zeros((steps, 6)) -dxs = np.zeros((steps, 6)) - -xs[:, 0] = x -xs[:, 1] = y -xs[:, 2] = -theta -xs[:, 3] = dx -xs[:, 4] = dy -xs[:, 5] = -dtheta - -us[:, 0] = T1 -us[:, 1] = T2 - -traj = plan.Trajectory(xs, us, t, dxs, ys) - - -fig, axes = plt.subplots( - 3, figsize=graphical.default_figsize, dpi=graphical.default_dpi, frameon=True -) - -axes[0].plot(t, theta, "b") -axes[0].set_ylabel("Theta", fontsize=graphical.default_fontsize) -axes[0].set_xlabel("v", fontsize=graphical.default_fontsize) -axes[0].tick_params(labelsize=graphical.default_fontsize) -axes[0].grid(True) - -axes[1].plot(t, dtheta, "b") -# axes[1].plot(t, dtheta_num, "r") -axes[1].set_ylabel("w", fontsize=graphical.default_fontsize) -axes[1].set_xlabel("t", fontsize=graphical.default_fontsize) -axes[1].tick_params(labelsize=graphical.default_fontsize) -axes[1].grid(True) - -axes[2].plot(t, ddtheta, "b") -# axes[2].plot(t, ddtheta_num, "r") -axes[2].set_ylabel("dw", fontsize=graphical.default_fontsize) -axes[2].set_xlabel("t", fontsize=graphical.default_fontsize) -axes[2].tick_params(labelsize=graphical.default_fontsize) -axes[2].grid(True) - -fig.tight_layout() -fig.canvas.draw() - -plt.show() - - -fig, axes = plt.subplots( - 1, figsize=graphical.default_figsize, dpi=graphical.default_dpi, frameon=True -) - -axes.plot(x, y, "r") -axes.set_ylabel("y", fontsize=graphical.default_fontsize) -axes.set_xlabel("x", fontsize=graphical.default_fontsize) -axes.axis("equal") -axes.set(xlim=(-25, 25), ylim=(-25, 25)) -axes.tick_params(labelsize=graphical.default_fontsize) -axes.grid(True) - -fig.tight_layout() -fig.canvas.draw() - -plt.show() - - -sys.traj = traj - -sys.plot_trajectory("xu") -sys.animate_simulation() diff --git a/dev/differentialflatness/droneminsnap_waypoints.py b/dev/differentialflatness/droneminsnap_waypoints.py deleted file mode 100644 index 6e76f183..00000000 --- a/dev/differentialflatness/droneminsnap_waypoints.py +++ /dev/null @@ -1,179 +0,0 @@ -# -*- coding: utf-8 -*- - -############################################################################### -import numpy as np -import matplotlib.pyplot as plt - -from pyro.analysis import graphical -from pyro.planning import trajectorygeneration -from pyro.dynamic import drone -from pyro.planning import plan - -############################################################################### -# Define the c.g. trajectory -############################################################################### - -# Waypoints x_i y_i t_i -xyt = np.array([[ 0.0, 0.0, 0.0], - [10.0, 0.0, 5.0], - [10.0, 15.0, 10.0], - [ 0.0, 5.0, 15.0]]).T - - - -traj = trajectorygeneration.MultiPointSingleAxisPolynomialTrajectoryGenerator( - poly_N=9, - diff_N=7, - con_N=4, - x0=np.array([xyt[0,0], 0.0, 0.0, 0.0]), - xf=np.array([xyt[0,3], 0.0, 0.0, 0.0]), - tc=np.array([xyt[2,0], xyt[2,1], xyt[2,2], xyt[2,3]]), - xc=np.array([[xyt[0,1], xyt[0,2]]]), - dt=0.01, - ) - -traj.Ws[0]= 0.01 -traj.Ws[1]= 1.0 -traj.Ws[2]= 1.0 -traj.Ws[3]= 1.0 -traj.Ws[4]= 1.0 - -b, A, p, X, t = traj.solve() - -x = X[0, :] -dx = X[1, :] -ax = X[2, :] -dax = X[3, :] -ddax = X[4, :] - -traj = trajectorygeneration.MultiPointSingleAxisPolynomialTrajectoryGenerator( - poly_N=9, - diff_N=7, - con_N=4, - x0=np.array([xyt[1,0], 0.0, 0.0, 0.0]), - xf=np.array([xyt[1,3], 0.0, 0.0, 0.0]), - tc=np.array([xyt[2,0], xyt[2,1], xyt[2,2], xyt[2,3]]), - xc=np.array([[xyt[1,1], xyt[1,2]]]), - dt=0.01, - ) - -traj.Ws[0]= 0.01 -traj.Ws[1]= 1.0 -traj.Ws[2]= 1.0 -traj.Ws[3]= 1.0 -traj.Ws[4]= 1.0 - -b, A, p, Y, t = traj.solve() - -y = Y[0, :] -dy = Y[1, :] -ay = Y[2, :] + 9.81 # added gravity -day = Y[3, :] -dday = Y[4, :] - -############################################################################### -# Compute angular trajectory -############################################################################### - -theta = np.arctan2(ax, ay) -# theta = np.arctan( (ay/ax)) -s = np.sin(theta) -c = np.cos(theta) -dtheta = (dax * c - day * s) / (ax * s + ay * c) -ddtheta = ( - s * (-dday + ay * dtheta**2 - 2 * dax * dtheta) - + c * (ddax - ax * dtheta**2 - 2 * day * dtheta) -) / (ay * c + ax * s) - -############################################################################### -# Compute Forces -############################################################################### - -sys = drone.Drone2D() - -m = sys.mass -J = sys.inertia -r = sys.truster_offset - -F_tot = m * ax / np.sin(theta) -F_del = J * ddtheta / r - -T1 = 0.5 * (F_tot + F_del) -T2 = 0.5 * (F_tot - F_del) - -############################################################################### -# Plots -############################################################################### - -# Create traj -steps = len(t) -us = np.zeros((steps, 2)) # control inputs -xs = np.zeros((steps, 6)) -ys = np.zeros((steps, 6)) -dxs = np.zeros((steps, 6)) - -xs[:, 0] = x -xs[:, 1] = y -xs[:, 2] = -theta -xs[:, 3] = dx -xs[:, 4] = dy -xs[:, 5] = -dtheta - -us[:, 0] = T1 -us[:, 1] = T2 - -traj = plan.Trajectory(xs, us, t, dxs, ys) - - -fig, axes = plt.subplots( - 3, figsize=graphical.default_figsize, dpi=graphical.default_dpi, frameon=True -) - -axes[0].plot(t, theta, "b") -axes[0].set_ylabel("Theta", fontsize=graphical.default_fontsize) -axes[0].set_xlabel("v", fontsize=graphical.default_fontsize) -axes[0].tick_params(labelsize=graphical.default_fontsize) -axes[0].grid(True) - -axes[1].plot(t, dtheta, "b") -# axes[1].plot(t, dtheta_num, "r") -axes[1].set_ylabel("w", fontsize=graphical.default_fontsize) -axes[1].set_xlabel("t", fontsize=graphical.default_fontsize) -axes[1].tick_params(labelsize=graphical.default_fontsize) -axes[1].grid(True) - -axes[2].plot(t, ddtheta, "b") -# axes[2].plot(t, ddtheta_num, "r") -axes[2].set_ylabel("dw", fontsize=graphical.default_fontsize) -axes[2].set_xlabel("t", fontsize=graphical.default_fontsize) -axes[2].tick_params(labelsize=graphical.default_fontsize) -axes[2].grid(True) - -fig.tight_layout() -fig.canvas.draw() - -plt.show() - - -fig, axes = plt.subplots( - 1, figsize=graphical.default_figsize, dpi=graphical.default_dpi, frameon=True -) - -axes.plot(x, y, "r") -axes.set_ylabel("y", fontsize=graphical.default_fontsize) -axes.set_xlabel("x", fontsize=graphical.default_fontsize) -axes.axis("equal") -axes.set(xlim=(-25, 25), ylim=(-25, 25)) -axes.tick_params(labelsize=graphical.default_fontsize) -axes.grid(True) - -fig.tight_layout() -fig.canvas.draw() - -plt.show() - - -sys.traj = traj - -sys.plot_trajectory("xu") -sys.animate_simulation() diff --git a/dev/differentialflatness/polynomialtrajectory.py b/dev/differentialflatness/polynomialtrajectory.py deleted file mode 100644 index ab05278c..00000000 --- a/dev/differentialflatness/polynomialtrajectory.py +++ /dev/null @@ -1,75 +0,0 @@ -# -*- coding: utf-8 -*- - -############################################################################### -import numpy as np -import matplotlib.pyplot as plt -from pyro.analysis import graphical -############################################################################### - -boundary_conditions = np.array([0, 0, 0, 1, 0, 0]) - -tf = 10 - -A = np.array( - [ - [1, 0, 0, 0, 0, 0], - [0, 1, 0, 0, 0, 0], - [0, 0, 2, 0, 0, 0], - [1, tf, tf**2, tf**3, tf**4, tf**5], - [0, 1, 2 * tf, 3 * tf**2, 4 * tf**3, 5 * tf**4], - [0, 0, 2, 6 * tf, 12 * tf**2, 20 * tf**3], - ] -) - -p = np.linalg.solve(A, boundary_conditions) - -# Time -t = np.linspace(0, tf, 100) - -# Position -x = p[0] + p[1] * t + p[2] * t**2 + p[3] * t**3 + p[4] * t**4 + p[5] * t**5 -dx = p[1] + 2 * p[2] * t + 3 * p[3] * t**2 + 4 * p[4] * t**3 + 5 * p[5] * t**4 -ddx = 2 * p[2] + 6 * p[3] * t + 12 * p[4] * t**2 + 20 * p[5] * t**3 -dddx = 6 * p[3] + 24 * p[4] * t + 60 * p[5] * t**2 -ddddx = 24 * p[4] + 120 * p[5] * t - - -fig, ax = plt.subplots( - 5, figsize=graphical.default_figsize, dpi=graphical.default_dpi, frameon=True -) - - -ax[0].plot(t, x, "b") -ax[0].set_ylabel("Pos", fontsize=graphical.default_fontsize) -ax[0].set_xlabel("v", fontsize=graphical.default_fontsize) -ax[0].tick_params(labelsize=graphical.default_fontsize) -ax[0].grid(True) - -ax[1].plot(t, dx, "b") -ax[1].set_ylabel("Vel", fontsize=graphical.default_fontsize) -ax[1].set_xlabel("t", fontsize=graphical.default_fontsize) -ax[1].tick_params(labelsize=graphical.default_fontsize) -ax[1].grid(True) - -ax[2].plot(t, ddx, "b") -ax[2].set_ylabel("Acc", fontsize=graphical.default_fontsize) -ax[2].set_xlabel("t", fontsize=graphical.default_fontsize) -ax[2].tick_params(labelsize=graphical.default_fontsize) -ax[2].grid(True) - -ax[3].plot(t, dddx, "b") -ax[3].set_ylabel("Jerk", fontsize=graphical.default_fontsize) -ax[3].set_xlabel("t", fontsize=graphical.default_fontsize) -ax[3].tick_params(labelsize=graphical.default_fontsize) -ax[3].grid(True) - -ax[4].plot(t, ddddx, "b") -ax[4].set_ylabel("Snap", fontsize=graphical.default_fontsize) -ax[4].set_xlabel("t", fontsize=graphical.default_fontsize) -ax[4].tick_params(labelsize=graphical.default_fontsize) -ax[4].grid(True) - -fig.tight_layout() -fig.canvas.draw() - -plt.show() diff --git a/dev/differentialflatness/rigidbodytest.py b/dev/differentialflatness/rigidbodytest.py deleted file mode 100644 index 41407eb4..00000000 --- a/dev/differentialflatness/rigidbodytest.py +++ /dev/null @@ -1,193 +0,0 @@ -# -*- coding: utf-8 -*- - -############################################################################### -import numpy as np -import matplotlib.pyplot as plt - - -from pyro.analysis import graphical -from pyro.planning import trajectorygeneration -from pyro.dynamic import rigidbody -from pyro.planning import plan - -############################################################################### - -# fixed initial position for now -# initial angular velocity is related to jerk of trajectory -x0 = 0.0 -y0 = 0.0 -z0 = 0.0 - -# fixed final position for now -xf = 10.0 -yf = 10.0 -zf = np.pi -tf = 5 - -ddx0 = xf * 0.1 -ddy0 = 0.0# ddx0 * np.tan(z0) - -ddxf = 0.0 -ddyf = yf * -0.1#-ddxf * np.tan(zf) - - -gex = trajectorygeneration.SingleAxisPolynomialTrajectoryGenerator(poly_N=9) -gex.x0_N = 3 -gex.xf_N = 3 -gex.x0 = np.array([x0, 0, ddx0, 0, 0, 0]) -gex.xf = np.array([xf, 0, ddxf, 0, 0, 0]) -gex.poly_N = 7 -gex.Ws = np.array([0.01, 1.0, 1.0, 1.0, 1.0, 1.0, .0]) -px, X, t = gex.solve() -x = X[0, :] -dx = X[1, :] -ax = X[2, :] -dax = X[3, :] -ddax = X[4, :] - -gey = trajectorygeneration.SingleAxisPolynomialTrajectoryGenerator(poly_N=9) -gey.x0_N = 3 -gey.xf_N = 3 -gey.x0 = np.array([y0, 0, ddy0, 0, 0, 0]) -gey.xf = np.array([yf, 0, ddyf, 0, 0, 0]) -gey.poly_N = 7 -gey.Ws = np.array([0.01, 1.0, 1.0, 1.0, 1.0, 1.0, .0]) -py, Y, t = gey.solve() -y = Y[0, :] -dy = Y[1, :] -ay = Y[2, :] -day = Y[3, :] -dday = Y[4, :] - -# Position theta -theta = np.arctan2(ay, ax) -# theta = np.arctan( (ay/ax)) -s = np.sin(theta) -c = np.cos(theta) -dtheta = (day * c - dax * s) / (ay * s + ax * c) # TODO check analytical equation, seems wrong -ddtheta = ( - s * (-ddax + ax * dtheta**2 - 2 * day * dtheta) - + c * (dday - ay * dtheta**2 - 2 * dax * dtheta) -) / ( - ax * c + ay * s -) # TODO check analytical equation, seems wrong - -dtheta_num = np.diff(theta, n=1, prepend=0.0) -ddtheta_num = np.diff(dtheta, n=1, prepend=0.0) - -# dtheta = dtheta_num -# ddtheta = ddtheta_num - -# Create traj -steps = len(t) -xs = np.zeros((steps, 6)) -ys = np.zeros((steps, 6)) -us = np.zeros((steps, 2)) -dxs = np.zeros((steps, 6)) - -sys = rigidbody.RigidBody2D() - -sys.mass = 0.8 -sys.inertia = 1.0 -sys.l_t = 1.0 - -m = sys.mass -J = sys.inertia -r = sys.l_t - -x_cg = x - J / (m * r) * np.cos(theta) -y_cg = y - J / (m * r) * np.sin(theta) - -xs[:, 0] = x_cg -xs[:, 1] = y_cg -xs[:, 2] = theta - -M = np.array([[m, 0], [0, m]]) - -ax_cg = ( - ax + J / (m * r) * np.sin(theta) * ddtheta + J / (m * r) * np.cos(theta) * dtheta**2 -) -ay_cg = ( - ay - J / (m * r) * np.cos(theta) * ddtheta + J / (m * r) * np.sin(theta) * dtheta**2 -) - -# COmpute forces -for i in range(steps): - R = np.array( - [[np.cos(theta[i]), -np.sin(theta[i])], [np.sin(theta[i]), np.cos(theta[i])]] - ) - a_cg = np.array([ax_cg[i], ay_cg[i]]) - us[i, :] = np.linalg.inv(R) @ M @ a_cg - - -traj = plan.Trajectory(xs, us, t, dxs, ys) - -fig, axes = plt.subplots( - 2, figsize=graphical.default_figsize, dpi=graphical.default_dpi, frameon=True -) - -axes[0].plot(t, ax_cg, "b") -axes[0].set_ylabel("ax_cg", fontsize=graphical.default_fontsize) -axes[0].set_xlabel("t", fontsize=graphical.default_fontsize) -axes[0].tick_params(labelsize=graphical.default_fontsize) -axes[0].grid(True) - -axes[1].plot(t, ay_cg, "b") -axes[1].set_ylabel("ay_cg", fontsize=graphical.default_fontsize) -axes[1].set_xlabel("t", fontsize=graphical.default_fontsize) -axes[1].tick_params(labelsize=graphical.default_fontsize) -axes[1].grid(True) - - -fig, axes = plt.subplots( - 3, figsize=graphical.default_figsize, dpi=graphical.default_dpi, frameon=True -) - -axes[0].plot(t, theta, "b") -axes[0].set_ylabel("Theta", fontsize=graphical.default_fontsize) -axes[0].set_xlabel("v", fontsize=graphical.default_fontsize) -axes[0].tick_params(labelsize=graphical.default_fontsize) -axes[0].grid(True) - -axes[1].plot(t, dtheta, "b") -# axes[1].plot(t, dtheta_num, "r") -axes[1].set_ylabel("w", fontsize=graphical.default_fontsize) -axes[1].set_xlabel("t", fontsize=graphical.default_fontsize) -axes[1].tick_params(labelsize=graphical.default_fontsize) -axes[1].grid(True) - -axes[2].plot(t, ddtheta, "b") -# axes[2].plot(t, ddtheta_num, "r") -axes[2].set_ylabel("dw", fontsize=graphical.default_fontsize) -axes[2].set_xlabel("t", fontsize=graphical.default_fontsize) -axes[2].tick_params(labelsize=graphical.default_fontsize) -axes[2].grid(True) - -fig.tight_layout() -fig.canvas.draw() - -plt.show() - - -fig, axes = plt.subplots( - 1, figsize=graphical.default_figsize, dpi=graphical.default_dpi, frameon=True -) - -axes.plot(x, y, "r") -axes.plot(x_cg, y_cg, "b") -axes.set_ylabel("y", fontsize=graphical.default_fontsize) -axes.set_xlabel("x", fontsize=graphical.default_fontsize) -axes.axis("equal") -axes.set(xlim=(-5, 25), ylim=(-5, 25)) -axes.tick_params(labelsize=graphical.default_fontsize) -axes.grid(True) - -fig.tight_layout() -fig.canvas.draw() - -plt.show() - - -sys.traj = traj - -ani = sys.animate_simulation() diff --git a/dev/differentialflatness/rigidbodytest_waypoints.py b/dev/differentialflatness/rigidbodytest_waypoints.py deleted file mode 100644 index 643730e8..00000000 --- a/dev/differentialflatness/rigidbodytest_waypoints.py +++ /dev/null @@ -1,203 +0,0 @@ -# -*- coding: utf-8 -*- - -############################################################################### -import numpy as np -import matplotlib.pyplot as plt - - -from pyro.analysis import graphical -from pyro.planning import trajectorygeneration -from pyro.dynamic import rigidbody -from pyro.planning import plan - -############################################################################### -# Waypoints -xyt = np.array([[ 0.0, 0.0, 0.0], - [10.0, 0.0, 5.0], - [10.0, 10.0, 10.0], - [ 0.0, 10.0, 15.0]]).T - - - -xtraj = trajectorygeneration.MultiPointSingleAxisPolynomialTrajectoryGenerator( - poly_N=9, - diff_N=7, - con_N=4, - x0=np.array([xyt[0,0], 0.0, 0.0, 0.0]), - xf=np.array([xyt[0,3], 0.0, 0.0, 0.0]), - tc=np.array([xyt[2,0], xyt[2,1], xyt[2,2], xyt[2,3]]), - xc=np.array([[xyt[0,1], xyt[0,2]]]), - dt=0.01, - ) - -xtraj.Ws[0]= 0.01 -xtraj.Ws[1]= 1.0 -xtraj.Ws[2]= 1.0 -xtraj.Ws[3]= 1.0 -xtraj.Ws[4]= 1.0 - -b, A, p, X, t = xtraj.solve() - -x = X[0, :] -dx = X[1, :] -ax = X[2, :] -dax = X[3, :] -ddax = X[4, :] - -ytraj = trajectorygeneration.MultiPointSingleAxisPolynomialTrajectoryGenerator( - poly_N=9, - diff_N=7, - con_N=4, - x0=np.array([xyt[1,0], 0.0, 0.0, 0.0]), - xf=np.array([xyt[1,3], 0.0, 0.0, 0.0]), - tc=np.array([xyt[2,0], xyt[2,1], xyt[2,2], xyt[2,3]]), - xc=np.array([[xyt[1,1], xyt[1,2]]]), - dt=0.01, - ) - -ytraj.Ws[0]= 0.01 -ytraj.Ws[1]= 1.0 -ytraj.Ws[2]= 1.0 -ytraj.Ws[3]= 1.0 -ytraj.Ws[4]= 1.0 - -b, A, p, Y, t = ytraj.solve() - -y = Y[0, :] -dy = Y[1, :] -ay = Y[2, :] -day = Y[3, :] -dday = Y[4, :] - -# Position theta -theta = np.arctan2(ay, ax) -# theta = np.arctan( (ay/ax)) -s = np.sin(theta) -c = np.cos(theta) -dtheta = (day * c - dax * s) / (ay * s + ax * c) # TODO check analytical equation, seems wrong -ddtheta = ( - s * (-ddax + ax * dtheta**2 - 2 * day * dtheta) - + c * (dday - ay * dtheta**2 - 2 * dax * dtheta) -) / ( - ax * c + ay * s -) # TODO check analytical equation, seems wrong - -dtheta_num = np.diff(theta, n=1, prepend=0.0) -ddtheta_num = np.diff(dtheta, n=1, prepend=0.0) - -# dtheta = dtheta_num -# ddtheta = ddtheta_num - -# Create traj -steps = len(t) -xs = np.zeros((steps, 6)) -ys = np.zeros((steps, 6)) -us = np.zeros((steps, 2)) -dxs = np.zeros((steps, 6)) - -sys = rigidbody.RigidBody2D() - -sys.mass = 0.8 -sys.inertia = 1.0 -sys.l_t = 1.0 - -m = sys.mass -J = sys.inertia -r = sys.l_t - -x_cg = x - J / (m * r) * np.cos(theta) -y_cg = y - J / (m * r) * np.sin(theta) - -xs[:, 0] = x_cg -xs[:, 1] = y_cg -xs[:, 2] = theta - -M = np.array([[m, 0], [0, m]]) - -ax_cg = ( - ax + J / (m * r) * np.sin(theta) * ddtheta + J / (m * r) * np.cos(theta) * dtheta**2 -) -ay_cg = ( - ay - J / (m * r) * np.cos(theta) * ddtheta + J / (m * r) * np.sin(theta) * dtheta**2 -) - -# COmpute forces -for i in range(steps): - R = np.array( - [[np.cos(theta[i]), -np.sin(theta[i])], [np.sin(theta[i]), np.cos(theta[i])]] - ) - a_cg = np.array([ax_cg[i], ay_cg[i]]) - us[i, :] = np.linalg.inv(R) @ M @ a_cg - - -traj = plan.Trajectory(xs, us, t, dxs, ys) - -fig, axes = plt.subplots( - 2, figsize=graphical.default_figsize, dpi=graphical.default_dpi, frameon=True -) - -axes[0].plot(t, ax_cg, "b") -axes[0].set_ylabel("ax_cg", fontsize=graphical.default_fontsize) -axes[0].set_xlabel("t", fontsize=graphical.default_fontsize) -axes[0].tick_params(labelsize=graphical.default_fontsize) -axes[0].grid(True) - -axes[1].plot(t, ay_cg, "b") -axes[1].set_ylabel("ay_cg", fontsize=graphical.default_fontsize) -axes[1].set_xlabel("t", fontsize=graphical.default_fontsize) -axes[1].tick_params(labelsize=graphical.default_fontsize) -axes[1].grid(True) - - -fig, axes = plt.subplots( - 3, figsize=graphical.default_figsize, dpi=graphical.default_dpi, frameon=True -) - -axes[0].plot(t, theta, "b") -axes[0].set_ylabel("Theta", fontsize=graphical.default_fontsize) -axes[0].set_xlabel("v", fontsize=graphical.default_fontsize) -axes[0].tick_params(labelsize=graphical.default_fontsize) -axes[0].grid(True) - -axes[1].plot(t, dtheta, "b") -# axes[1].plot(t, dtheta_num, "r") -axes[1].set_ylabel("w", fontsize=graphical.default_fontsize) -axes[1].set_xlabel("t", fontsize=graphical.default_fontsize) -axes[1].tick_params(labelsize=graphical.default_fontsize) -axes[1].grid(True) - -axes[2].plot(t, ddtheta, "b") -# axes[2].plot(t, ddtheta_num, "r") -axes[2].set_ylabel("dw", fontsize=graphical.default_fontsize) -axes[2].set_xlabel("t", fontsize=graphical.default_fontsize) -axes[2].tick_params(labelsize=graphical.default_fontsize) -axes[2].grid(True) - -fig.tight_layout() -fig.canvas.draw() - -plt.show() - - -fig, axes = plt.subplots( - 1, figsize=graphical.default_figsize, dpi=graphical.default_dpi, frameon=True -) - -axes.plot(x, y, "r") -axes.plot(x_cg, y_cg, "b") -axes.set_ylabel("y", fontsize=graphical.default_fontsize) -axes.set_xlabel("x", fontsize=graphical.default_fontsize) -axes.axis("equal") -axes.set(xlim=(-15, 25), ylim=(-15, 25)) -axes.tick_params(labelsize=graphical.default_fontsize) -axes.grid(True) - -fig.tight_layout() -fig.canvas.draw() - -plt.show() - - -sys.traj = traj - -ani = sys.animate_simulation() diff --git a/dev/dimensionless/car1_dimpolicy.pdf b/dev/dimensionless/car1_dimpolicy.pdf deleted file mode 100644 index 6fe4bd43399ccfe865306f0b03bc418eeb61902c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 131345 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