30
30
import numpy as np
31
31
import datetime # for unique filenames
32
32
33
+ # Import the ODE solver
34
+ from scipy .integrate import solve_ivp
35
+
33
36
logger = logging .getLogger (__name__ )
34
37
35
38
@@ -57,6 +60,12 @@ def __init__(self):
57
60
# Define thesholds for trial limits, penalized heavily for exceeding these
58
61
self .mass_pos_threshold = 4.0 # max mass position (m)
59
62
self .mass_vel_threshold = 0.5 # max mass velocity (m/s)
63
+
64
+ # Set up solver parameters
65
+ # ODE solver parameters
66
+ self .abserr = 1.0e-9
67
+ self .relerr = 1.0e-9
68
+ self .max_step = 0.1
60
69
61
70
# This action space is the range of acceleration of the trolley
62
71
self .action_space = spaces .Box (low = - self .max_force ,
@@ -77,43 +86,82 @@ def __init__(self):
77
86
self .state = None
78
87
self .done = False
79
88
self .force = 0.0
89
+
90
+ def eq_of_motion (self , t , w ):
91
+ """
92
+ Defines the differential equations for the coupled spring-mass system.
93
+
94
+ Arguments:
95
+ w : vector of the state variables:
96
+ t : time
97
+ """
98
+
99
+ x1 = w [0 ]
100
+ x1_dot = w [1 ]
101
+ x2 = w [2 ]
102
+ x2_dot = w [3 ]
103
+
104
+ # Create sysODE = (x', x_dot', y', y_dot')
105
+ sysODE = np .array ([x1_dot ,
106
+ 1 / self .m1 * (self .k * (x2 - x1 - self .spring_equil ) + self .c * (x2_dot - x1_dot ) + self .force ),
107
+ x2_dot ,
108
+ 1 / self .m2 * (- self .k * (x2 - x1 - self .spring_equil ) + - self .c * (x2_dot - x1_dot ))])
109
+
110
+ return sysODE
80
111
81
112
def _seed (self , seed = None ):
82
113
self .np_random , seed = seeding .np_random (seed )
83
114
return [seed ]
84
115
116
+
85
117
def _step (self , action ):
86
118
x1 , x1_dot , x2 , x2_dot = self .state
87
119
self .counter = self .counter + 1
88
120
89
121
# Get the action and clip it to the min/max trolley accel
90
122
self .force = np .clip (action [0 ], - self .max_force , self .max_force )
91
123
92
-
93
- # Update m1 states
94
- x1_accel = 1 / self .m1 * (self .k * (x2 - x1 - self .spring_equil ) +
95
- self .c * (x2_dot - x1_dot ) +
96
- self .force )
97
-
98
-
99
- x1_dot = x1_dot + self .tau * x1_accel
100
-
101
- # Get the action and clip it to the min/max m1 vel
102
- x1_dot = np .clip (x1_dot , - self .mass_vel_threshold , self .mass_vel_threshold )
103
-
104
- x1 = x1 + self .tau * x1_dot
124
+ x0 = [x1 , x1_dot , x2 , x2_dot ]
105
125
106
- # Update m2 states
107
- x2_accel = 1 / self .m2 * (- self .k * (x2 - x1 - self .spring_equil ) +
108
- - self .c * (x2_dot - x1_dot ))
109
-
110
-
111
- x2_dot = x2_dot + self .tau * x2_accel
126
+ # Call the ODE solver.
127
+ solution = solve_ivp (self .eq_of_motion ,
128
+ [0 , self .tau ],
129
+ x0 ,
130
+ max_step = self .max_step ,
131
+ atol = self .abserr ,
132
+ rtol = self .relerr )
133
+
134
+ resp = solution .y
112
135
113
- # Get the action and clip it to the min/max m2 accel
114
- x2_dot = np .clip (x2_dot , - self .mass_vel_threshold , self .mass_vel_threshold )
136
+ # Update m1 states
137
+ # x1_accel = 1 / self.m1 * (self.k * (x2 - x1 - self.spring_equil) +
138
+ # self.c * (x2_dot - x1_dot) +
139
+ # self.force)
140
+ #
141
+ #
142
+ # x1_dot = x1_dot + self.tau * x1_accel
143
+ #
144
+ # # Get the action and clip it to the min/max m1 vel
145
+ # x1_dot = np.clip(x1_dot, -self.mass_vel_threshold, self.mass_vel_threshold)
146
+ #
147
+ # x1 = x1 + self.tau * x1_dot
148
+ #
149
+ # # Update m2 states
150
+ # x2_accel = 1 / self.m2 * (-self.k * (x2 - x1 - self.spring_equil) +
151
+ # -self.c * (x2_dot - x1_dot))
152
+ #
153
+ #
154
+ # x2_dot = x2_dot + self.tau * x2_accel
155
+ #
156
+ # # Get the action and clip it to the min/max m2 accel
157
+ # x2_dot = np.clip(x2_dot, -self.mass_vel_threshold, self.mass_vel_threshold)
158
+ #
159
+ # x2 = x2 + self.tau * x2_dot
115
160
116
- x2 = x2 + self .tau * x2_dot
161
+ x1 = resp [0 , - 1 ]
162
+ x1_dot = resp [1 , - 1 ]
163
+ x2 = resp [2 , - 1 ]
164
+ x2_dot = resp [3 , - 1 ]
117
165
118
166
self .state = (x1 , x1_dot , x2 , x2_dot )
119
167
0 commit comments