blob: 4abd1cfa11332106c68d2faebdda9b6f192861b5 [file] [log] [blame]
Michael Schuh10dd1e02018-01-20 13:19:44 -08001#!/usr/bin/python3
2
3# This code was used to select the gear ratio for the proximal arm.
4# Run it from the command line and it displays the time required
5# to move the proximal arm 180 degrees from straight down to straight up.
Ravago Jones5127ccc2022-07-31 16:32:45 -07006#
Michael Schuh10dd1e02018-01-20 13:19:44 -08007# Michael Schuh
8# January 20, 2018
9
10import math
11import numpy
12import scipy.integrate
13
14# apt-get install python-scipy python3-scipy python-numpy python3-numpy
15
16pi = math.pi
Ravago Jones5127ccc2022-07-31 16:32:45 -070017pi2 = 2.0 * pi
18rad_to_deg = 180.0 / pi
Michael Schuh10dd1e02018-01-20 13:19:44 -080019inches_to_meters = 0.0254
Ravago Jones5127ccc2022-07-31 16:32:45 -070020lbs_to_kg = 1.0 / 2.2
Michael Schuh10dd1e02018-01-20 13:19:44 -080021newton_to_lbf = 0.224809
22newton_meters_to_ft_lbs = 0.73756
23run_count = 0
24theta_travel = 0.0
25
Ravago Jones5127ccc2022-07-31 16:32:45 -070026
Michael Schuh10dd1e02018-01-20 13:19:44 -080027def to_deg(angle):
Ravago Jones5127ccc2022-07-31 16:32:45 -070028 return (angle * rad_to_deg)
29
Michael Schuh10dd1e02018-01-20 13:19:44 -080030
31def to_rad(angle):
Ravago Jones5127ccc2022-07-31 16:32:45 -070032 return (angle / rad_to_deg)
33
Michael Schuh10dd1e02018-01-20 13:19:44 -080034
35def to_rotations(angle):
Ravago Jones5127ccc2022-07-31 16:32:45 -070036 return (angle / pi2)
37
Michael Schuh10dd1e02018-01-20 13:19:44 -080038
39def time_derivative(x, t, voltage, c1, c2, c3):
Ravago Jones5127ccc2022-07-31 16:32:45 -070040 global run_count
41 theta, omega = x
42 dxdt = [omega, -c1 * omega + c3 * math.sin(theta) + c2 * voltage]
43 run_count = run_count + 1
Michael Schuh10dd1e02018-01-20 13:19:44 -080044
Ravago Jones5127ccc2022-07-31 16:32:45 -070045 #print ('dxdt = ',dxdt,' repr(dxdt) = ', repr(dxdt))
46 return dxdt
47
Michael Schuh10dd1e02018-01-20 13:19:44 -080048
49def get_distal_angle(theta_proximal):
Ravago Jones5127ccc2022-07-31 16:32:45 -070050 # For the proximal angle = -50 degrees, the distal angle is -180 degrees
51 # For the proximal angle = 10 degrees, the distal angle is -90 degrees
52 distal_angle = to_rad(-180.0 - (-50.0 - to_deg(theta_proximal)) *
53 (180.0 - 90.0) / (50.0 + 10.0))
54 return distal_angle
55
Michael Schuh10dd1e02018-01-20 13:19:44 -080056
57def get_distal_omega(omega_proximal):
Ravago Jones5127ccc2022-07-31 16:32:45 -070058 # For the proximal angle = -50 degrees, the distal angle is -180 degrees
59 # For the proximal angle = 10 degrees, the distal angle is -90 degrees
60 distal_angle = omega_proximal * ((180.0 - 90.0) / (50.0 + 10.0))
61 return distal_angle
Michael Schuh10dd1e02018-01-20 13:19:44 -080062
Michael Schuh10dd1e02018-01-20 13:19:44 -080063
Ravago Jones5127ccc2022-07-31 16:32:45 -070064def get_180_degree_time(c1, c2, c3, voltage, gear_ratio, motor_free_speed):
65 #print ("# step time theta angular_speed angular_acceleration theta angular_speed motor_speed motor_speed_fraction")
66 #print ("# (sec) (rad) (rad/sec) (rad/sec^2) (rotations) (rotations/sec) (rpm) (fraction)")
67 global run_count
68 global theta_travel
Michael Schuh10dd1e02018-01-20 13:19:44 -080069
Ravago Jones5127ccc2022-07-31 16:32:45 -070070 if (False):
71 # Gravity is assisting the motion.
72 theta_start = 0.0
73 theta_target = pi
74 elif (False):
75 # Gravity is assisting the motion.
76 theta_start = 0.0
77 theta_target = -pi
78 elif (False):
79 # Gravity is slowing the motion.
80 theta_start = pi
81 theta_target = 0.0
82 elif (False):
83 # Gravity is slowing the motion.
84 theta_start = -pi
85 theta_target = 0.0
86 elif (True):
87 # This is for the proximal arm motion.
88 theta_start = to_rad(-50.0)
89 theta_target = to_rad(10.0)
90
91 theta_half = 0.5 * (theta_start + theta_target)
92 if (theta_start > theta_target):
93 voltage = -voltage
94 theta = theta_start
95 theta_travel = theta_start - theta_target
96 if (run_count == 0):
97 print(
98 "# Theta Start = %.2f radians End = %.2f Theta travel %.2f Theta half = %.2f Voltage = %.2f"
99 % (theta_start, theta_target, theta_travel, theta_half, voltage))
100 print(
101 "# Theta Start = %.2f degrees End = %.2f Theta travel %.2f Theta half = %.2f Voltage = %.2f"
102 % (to_deg(theta_start), to_deg(theta_target), to_deg(theta_travel),
103 to_deg(theta_half), voltage))
104 omega = 0.0
105 time = 0.0
106 delta_time = 0.01 # time step in seconds
107 for step in range(1, 5000):
108 t = numpy.array([time, time + delta_time])
109 time = time + delta_time
110 x = [theta, omega]
111 angular_acceleration = -c1 * omega + c2 * voltage
112 x_n_plus_1 = scipy.integrate.odeint(time_derivative,
113 x,
114 t,
115 args=(voltage, c1, c2, c3))
116 #print ('x_n_plus_1 = ',x_n_plus_1)
117 #print ('repr(x_n_plus_1) = ',repr(x_n_plus_1))
118 theta, omega = x_n_plus_1[1]
119 #theta= x_n_plus_1[0]
120 #omega = x_n_plus_1[1]
121 if (False):
122 print ("%4d %8.4f %8.2f %8.4f %8.4f %8.3f %8.3f %8.3f %8.3f" % \
123 (step, time, theta, omega, angular_acceleration, to_rotations(theta), \
124 to_rotations(omega), omega*gear_ratio*60.0/pi2, omega*gear_ratio/motor_free_speed ))
125 if (theta_start < theta_target):
126 # Angle is increasing through the motion.
127 if (theta > theta_half):
128 break
129 else:
130 # Angle is decreasing through the motion.
131 if (theta < theta_half):
132 break
133
134 #print ("# step time theta angular_speed angular_acceleration theta angular_speed motor_speed motor_speed_fraction")
135 #print ("# (sec) (rad) (rad/sec) (rad/sec^2) (rotations) (rotations/sec) (rpm) (fraction)")
136 #print ("# Total time for 1/2 rotation of arm is %0.2f" % (time*2))
137 return (2.0 * time)
138
Michael Schuh10dd1e02018-01-20 13:19:44 -0800139
140def main():
Ravago Jones5127ccc2022-07-31 16:32:45 -0700141 global run_count
142 gravity = 9.8 # m/sec^2 Gravity Constant
143 voltage_nominal = 12 # Volts
Michael Schuh10dd1e02018-01-20 13:19:44 -0800144
Ravago Jones5127ccc2022-07-31 16:32:45 -0700145 # Vex 775 Pro motor specs from http://banebots.com/p/M2-RS550-120
146 motor_name = "Vex 775 Pro motor specs from http://banebots.com/p/M2-RS550-120"
147 current_stall = 134 # amps stall current
148 current_no_load = 0.7 # amps no load current
149 torque_stall = 710 / 1000.0 # N-m Stall Torque
150 speed_no_load_rpm = 18730 # RPM no load speed
Michael Schuh10dd1e02018-01-20 13:19:44 -0800151
Ravago Jones5127ccc2022-07-31 16:32:45 -0700152 if (False):
153 # Bag motor from https://www.vexrobotics.com/217-3351.html
154 motor_name = "Bag motor from https://www.vexrobotics.com/217-3351.html"
155 current_stall = 53.0 # amps stall current
156 current_no_load = 1.8 # amps no load current
157 torque_stall = 0.4 # N-m Stall Torque
158 speed_no_load_rpm = 13180.0 # RPM no load speed
Michael Schuh10dd1e02018-01-20 13:19:44 -0800159
Ravago Jones5127ccc2022-07-31 16:32:45 -0700160 if (True):
161 # Mini CIM motor from https://www.vexrobotics.com/217-3371.html
162 motor_name = "Mini CIM motor from https://www.vexrobotics.com/217-3371.html"
163 current_stall = 89.0 # amps stall current
164 current_no_load = 3.0 # amps no load current
165 torque_stall = 1.4 # N-m Stall Torque
166 speed_no_load_rpm = 5840.0 # RPM no load speed
Michael Schuh10dd1e02018-01-20 13:19:44 -0800167
Ravago Jones5127ccc2022-07-31 16:32:45 -0700168 # How many motors are we using?
169 num_motors = 1
Michael Schuh10dd1e02018-01-20 13:19:44 -0800170
Ravago Jones5127ccc2022-07-31 16:32:45 -0700171 # Motor values
172 print("# Motor: %s" % (motor_name))
173 print("# Number of motors: %d" % (num_motors))
174 print("# Stall torque: %.1f n-m" % (torque_stall))
175 print("# Stall current: %.1f amps" % (current_stall))
176 print("# No load current: %.1f amps" % (current_no_load))
177 print("# No load speed: %.0f rpm" % (speed_no_load_rpm))
Michael Schuh10dd1e02018-01-20 13:19:44 -0800178
Ravago Jones5127ccc2022-07-31 16:32:45 -0700179 # Constants from motor values
180 resistance_motor = voltage_nominal / current_stall
181 speed_no_load_rps = speed_no_load_rpm / 60.0 # Revolutions per second no load speed
182 speed_no_load = speed_no_load_rps * 2.0 * pi
183 Kt = num_motors * torque_stall / current_stall # N-m/A torque constant
184 Kv_rpm = speed_no_load_rpm / (
185 voltage_nominal - resistance_motor * current_no_load) # rpm/V
186 Kv = Kv_rpm * 2.0 * pi / 60.0 # rpm/V
Michael Schuh10dd1e02018-01-20 13:19:44 -0800187
Ravago Jones5127ccc2022-07-31 16:32:45 -0700188 # Robot Geometry and physics
189 length_proximal_arm = inches_to_meters * 47.34 # m Length of arm connected to the robot base
190 length_distal_arm = inches_to_meters * 44.0 # m Length of arm that holds the cube
191 mass_cube = 6.0 * lbs_to_kg # Weight of the cube in Kgrams
192 mass_proximal_arm = 5.5 * lbs_to_kg # Weight of proximal arm
193 mass_distal_arm = 3.5 * lbs_to_kg # Weight of distal arm
194 mass_distal = mass_cube + mass_distal_arm
195 mass_proximal = mass_proximal_arm + mass_distal
196 radius_to_proximal_arm_cg = 22.0 * inches_to_meters # m Length from arm pivot point to arm CG
197 radius_to_distal_arm_cg = 10.0 * inches_to_meters # m Length from arm pivot point to arm CG
198
199 radius_to_distal_cg = (
200 length_distal_arm * mass_cube +
201 radius_to_distal_arm_cg * mass_distal_arm) / mass_distal
202 radius_to_proximal_cg = (
203 length_proximal_arm * mass_distal +
204 radius_to_proximal_arm_cg * mass_proximal_arm) / mass_proximal
205 J_cube = length_distal_arm * length_distal_arm * mass_cube
206 # Kg m^2 Moment of inertia of the proximal arm
207 J_proximal_arm = radius_to_proximal_arm_cg * radius_to_proximal_arm_cg * mass_distal_arm
208 # Kg m^2 Moment of inertia distal arm and cube at end of proximal arm.
209 J_distal_arm_and_cube_at_end_of_proximal_arm = length_proximal_arm * length_proximal_arm * mass_distal
210 J_distal_arm = radius_to_distal_arm_cg * radius_to_distal_arm_cg * mass_distal_arm # Kg m^2 Moment of inertia of the distal arm
211 J = J_distal_arm_and_cube_at_end_of_proximal_arm + J_proximal_arm # Moment of inertia of the arm with the cube on the end
212
213 error_margine = 1.0
214 voltage = 10.0 # voltage for the motor. Assuming a loaded robot so not using 12 V.
215 # It might make sense to use a lower motor frees peed when the voltage is not a full 12 Volts.
216 # motor_free_speed = Kv*voltage
217 motor_free_speed = speed_no_load
218
219 print("# Kt = %f N-m/A\n# Kv_rpm = %f rpm/V\n# Kv = %f radians/V" %
220 (Kt, Kv_rpm, Kv))
221 print("# %.2f Ohms Resistance of the motor " % (resistance_motor))
222 print("# %.2f kg Cube weight" % (mass_cube))
223 print("# %.2f kg Proximal Arm mass" % (mass_proximal_arm))
224 print("# %.2f kg Distal Arm mass" % (mass_distal_arm))
225 print("# %.2f kg Distal Arm and Cube weight" % (mass_distal))
226 print("# %.2f m Length from distal arm pivot point to arm CG" %
227 (radius_to_distal_arm_cg))
228 print("# %.2f m Length from distal arm pivot point to arm and cube cg" %
229 (radius_to_distal_cg))
230 print(
231 "# %.2f kg-m^2 Moment of inertia of the cube about the arm pivot point"
232 % (J_cube))
233 print("# %.2f m Length from proximal arm pivot point to arm CG" %
234 (radius_to_proximal_arm_cg))
235 print("# %.2f m Length from proximal arm pivot point to arm and cube cg" %
236 (radius_to_proximal_cg))
237 print("# %.2f m Proximal arm length" % (length_proximal_arm))
238 print("# %.2f m Distal arm length" % (length_distal_arm))
239
240 print(
241 "# %.2f kg-m^2 Moment of inertia of the distal arm about the arm pivot point"
242 % (J_distal_arm))
243 print(
244 "# %.2f kg-m^2 Moment of inertia of the proximal arm about the arm pivot point"
245 % (J_proximal_arm))
246 print(
247 "# %.2f kg-m^2 Moment of inertia of the distal arm and cube mass about the proximal arm pivot point"
248 % (J_distal_arm_and_cube_at_end_of_proximal_arm))
249 print(
250 "# %.2f kg-m^2 Moment of inertia of the proximal arm and distal arm and cube about the arm pivot point"
251 % (J))
252 print("# %d Number of motors" % (num_motors))
253
254 print("# %.2f V Motor voltage" % (voltage))
255
256 print(
257 "\n# Min time is for proximal arm without any forces from distal arm. Max time puts all distal arm mass at the end of proximal arm."
258 )
259
260 for gear_ratio in range(60, 241, 10):
261 c1 = Kt * gear_ratio * gear_ratio / (Kv * resistance_motor * J)
262 c2 = gear_ratio * Kt / (J * resistance_motor)
263 c3 = radius_to_proximal_cg * mass_proximal * gravity / J
264 c1_proximal_only = Kt * gear_ratio * gear_ratio / (
265 Kv * resistance_motor * J_proximal_arm)
266 c2_proximal_only = gear_ratio * Kt / (J_proximal_arm *
267 resistance_motor)
268 c3_proximal_only = radius_to_proximal_arm_cg * mass_proximal_arm * gravity / J_proximal_arm
269
270 if (False and run_count < 1):
271 print("# %.8f 1/sec C1 constant" % (c1))
272 print("# %.2f 1/sec C2 constant" % (c2))
273 print("# %.2f 1/(V sec^2) C3 constant" % (c3))
274 print("# %.8f 1/sec C1 proximal only constant" %
275 (c1_proximal_only))
276 print("# %.2f 1/sec C2 proximal only constant" %
277 (c2_proximal_only))
278 print("# %.2f 1/(V sec^2) C3 proximal only constant" %
279 (c3_proximal_only))
280 print("# %.2f RPM Free speed at motor voltage" %
281 (voltage * Kv_rpm))
282
283 torque_90_degrees = radius_to_proximal_cg * mass_proximal * gravity
284 voltage_90_degrees = resistance_motor * torque_90_degrees / (
285 gear_ratio * Kt)
286 torque_peak = gear_ratio * num_motors * torque_stall
287 torque_peak_ft_lbs = torque_peak * newton_meters_to_ft_lbs
288 normal_force = torque_peak / length_proximal_arm
289 normal_force_lbf = newton_to_lbf * normal_force
290 normal_distal_arm_end_force = torque_peak / (length_proximal_arm +
291 length_distal_arm)
292 normal_distal_arm_end_force_lbf = newton_to_lbf * normal_distal_arm_end_force
293 time_required = get_180_degree_time(c1, c2, c3, voltage, gear_ratio,
294 motor_free_speed)
295 time_required_proximal_only = get_180_degree_time(
296 c1_proximal_only, c2_proximal_only, c3_proximal_only, voltage,
297 gear_ratio, motor_free_speed)
298 print ("Time for %.1f degrees for gear ratio %3.0f is %.2f (min) %.2f (max) seconds. 90 degree torque %.1f N-m and voltage %.1f Volts. Peak torque %3.0f nm %3.0f ft-lb Normal force at proximal end %3.0f N %2.0f lbf distal end %3.0f N %2.0f lbf" % \
299 (to_deg(theta_travel),gear_ratio,time_required_proximal_only,time_required,torque_90_degrees,voltage_90_degrees,
300 torque_peak,torque_peak_ft_lbs,normal_force,normal_force_lbf,normal_distal_arm_end_force,normal_distal_arm_end_force_lbf))
301
302
Michael Schuh10dd1e02018-01-20 13:19:44 -0800303if __name__ == '__main__':
Ravago Jones5127ccc2022-07-31 16:32:45 -0700304 main()