Austin Schuh | f173eb8 | 2018-01-20 23:32:30 -0800 | [diff] [blame] | 1 | #!/usr/bin/python3 |
| 2 | |
| 3 | # This code was used to select the gear ratio for the intake. |
| 4 | # Run it from the command line and it displays the time required |
| 5 | # to rotate the intake 180 degrees. |
| 6 | # |
| 7 | # Michael Schuh |
| 8 | # January 20, 2018 |
| 9 | |
| 10 | import math |
| 11 | import numpy |
| 12 | import scipy.integrate |
| 13 | |
| 14 | pi = math.pi |
| 15 | pi2 = 2.0 * pi |
| 16 | rad_to_deg = 180.0 / pi |
| 17 | inches_to_meters = 0.0254 |
| 18 | lbs_to_kg = 1.0 / 2.2 |
| 19 | newton_to_lbf = 0.224809 |
| 20 | newton_meters_to_ft_lbs = 0.73756 |
| 21 | run_count = 0 |
| 22 | theta_travel = 0.0 |
| 23 | |
Ravago Jones | 5127ccc | 2022-07-31 16:32:45 -0700 | [diff] [blame^] | 24 | |
Austin Schuh | f173eb8 | 2018-01-20 23:32:30 -0800 | [diff] [blame] | 25 | def to_deg(angle): |
Ravago Jones | 5127ccc | 2022-07-31 16:32:45 -0700 | [diff] [blame^] | 26 | return angle * rad_to_deg |
| 27 | |
Austin Schuh | f173eb8 | 2018-01-20 23:32:30 -0800 | [diff] [blame] | 28 | |
| 29 | def to_rad(angle): |
Ravago Jones | 5127ccc | 2022-07-31 16:32:45 -0700 | [diff] [blame^] | 30 | return angle / rad_to_deg |
| 31 | |
Austin Schuh | f173eb8 | 2018-01-20 23:32:30 -0800 | [diff] [blame] | 32 | |
| 33 | def to_rotations(angle): |
Ravago Jones | 5127ccc | 2022-07-31 16:32:45 -0700 | [diff] [blame^] | 34 | return angle / pi2 |
| 35 | |
Austin Schuh | f173eb8 | 2018-01-20 23:32:30 -0800 | [diff] [blame] | 36 | |
| 37 | def time_derivative(x, t, voltage, c1, c2, c3): |
Ravago Jones | 5127ccc | 2022-07-31 16:32:45 -0700 | [diff] [blame^] | 38 | global run_count |
| 39 | theta, omega = x |
| 40 | dxdt = [omega, -c1 * omega + c3 * math.sin(theta) + c2 * voltage] |
| 41 | run_count = run_count + 1 |
Austin Schuh | f173eb8 | 2018-01-20 23:32:30 -0800 | [diff] [blame] | 42 | |
Ravago Jones | 5127ccc | 2022-07-31 16:32:45 -0700 | [diff] [blame^] | 43 | return dxdt |
| 44 | |
Austin Schuh | f173eb8 | 2018-01-20 23:32:30 -0800 | [diff] [blame] | 45 | |
| 46 | def get_distal_angle(theta_proximal): |
Ravago Jones | 5127ccc | 2022-07-31 16:32:45 -0700 | [diff] [blame^] | 47 | # For the proximal angle = -50 degrees, the distal angle is -180 degrees |
| 48 | # For the proximal angle = 10 degrees, the distal angle is -90 degrees |
| 49 | distal_angle = to_rad(-180.0 - (-50.0 - to_deg(theta_proximal)) * \ |
| 50 | (180.0 - 90.0) / (50.0 + 10.0)) |
| 51 | return distal_angle |
Austin Schuh | f173eb8 | 2018-01-20 23:32:30 -0800 | [diff] [blame] | 52 | |
| 53 | |
| 54 | def get_180_degree_time(c1, c2, c3, voltage, gear_ratio, motor_free_speed): |
Ravago Jones | 5127ccc | 2022-07-31 16:32:45 -0700 | [diff] [blame^] | 55 | global run_count |
| 56 | global theta_travel |
Austin Schuh | f173eb8 | 2018-01-20 23:32:30 -0800 | [diff] [blame] | 57 | |
Ravago Jones | 5127ccc | 2022-07-31 16:32:45 -0700 | [diff] [blame^] | 58 | if (True): |
| 59 | # Gravity is assisting the motion. |
| 60 | theta_start = 0.0 |
| 61 | theta_target = pi |
| 62 | elif (False): |
| 63 | # Gravity is assisting the motion. |
| 64 | theta_start = 0.0 |
| 65 | theta_target = -pi |
| 66 | elif (False): |
| 67 | # Gravity is slowing the motion. |
| 68 | theta_start = pi |
| 69 | theta_target = 0.0 |
| 70 | elif (False): |
| 71 | # Gravity is slowing the motion. |
| 72 | theta_start = -pi |
| 73 | theta_target = 0.0 |
| 74 | elif (False): |
| 75 | # This is for the proximal arm motion. |
| 76 | theta_start = to_rad(-50.0) |
| 77 | theta_target = to_rad(10.0) |
Austin Schuh | f173eb8 | 2018-01-20 23:32:30 -0800 | [diff] [blame] | 78 | |
Ravago Jones | 5127ccc | 2022-07-31 16:32:45 -0700 | [diff] [blame^] | 79 | theta_half = 0.5 * (theta_start + theta_target) |
| 80 | if theta_start > theta_target: |
| 81 | voltage = -voltage |
| 82 | theta = theta_start |
| 83 | theta_travel = theta_start - theta_target |
| 84 | if run_count == 0: |
| 85 | print("# Theta Start = %.2f radians End = %.2f Theta travel %.2f " |
| 86 | "Theta half = %.2f Voltage = %.2f" % |
| 87 | (theta_start, theta_target, theta_travel, theta_half, voltage)) |
| 88 | print("# Theta Start = %.2f degrees End = %.2f Theta travel %.2f " |
| 89 | "Theta half = %.2f Voltage = %.2f" % |
| 90 | (to_deg(theta_start), to_deg(theta_target), to_deg(theta_travel), |
| 91 | to_deg(theta_half), voltage)) |
| 92 | omega = 0.0 |
| 93 | time = 0.0 |
| 94 | delta_time = 0.01 # time step in seconds |
| 95 | for step in range(1, 5000): |
| 96 | t = numpy.array([time, time + delta_time]) |
| 97 | time = time + delta_time |
| 98 | x = [theta, omega] |
| 99 | angular_acceleration = -c1 * omega + c2 * voltage |
| 100 | x_n_plus_1 = scipy.integrate.odeint(time_derivative, |
| 101 | x, |
| 102 | t, |
| 103 | args=(voltage, c1, c2, c3)) |
| 104 | theta, omega = x_n_plus_1[1] |
Austin Schuh | f173eb8 | 2018-01-20 23:32:30 -0800 | [diff] [blame] | 105 | |
Ravago Jones | 5127ccc | 2022-07-31 16:32:45 -0700 | [diff] [blame^] | 106 | if (False): |
| 107 | print( |
| 108 | "%4d %8.4f %8.2f %8.4f %8.4f %8.3f " |
| 109 | "%8.3f %8.3f %8.3f" % |
| 110 | (step, time, theta, omega, angular_acceleration, |
| 111 | to_rotations(theta), to_rotations(omega), omega * gear_ratio * |
| 112 | 60.0 / pi2, omega * gear_ratio / motor_free_speed)) |
| 113 | if theta_start < theta_target: |
| 114 | # Angle is increasing through the motion. |
| 115 | if theta > theta_half: |
| 116 | break |
| 117 | else: |
| 118 | # Angle is decreasing through the motion. |
| 119 | if (theta < theta_half): |
| 120 | break |
Austin Schuh | f173eb8 | 2018-01-20 23:32:30 -0800 | [diff] [blame] | 121 | |
Ravago Jones | 5127ccc | 2022-07-31 16:32:45 -0700 | [diff] [blame^] | 122 | return 2.0 * time |
| 123 | |
Austin Schuh | f173eb8 | 2018-01-20 23:32:30 -0800 | [diff] [blame] | 124 | |
| 125 | def main(): |
Ravago Jones | 5127ccc | 2022-07-31 16:32:45 -0700 | [diff] [blame^] | 126 | # m/sec^2 Gravity Constant |
| 127 | gravity = 9.8 |
| 128 | # m/sec^2 Gravity Constant - Use 0.0 for the intake. It is horizontal. |
| 129 | gravity = 0.0 |
| 130 | # Volts |
| 131 | voltage_nominal = 12 |
Austin Schuh | f173eb8 | 2018-01-20 23:32:30 -0800 | [diff] [blame] | 132 | |
Ravago Jones | 5127ccc | 2022-07-31 16:32:45 -0700 | [diff] [blame^] | 133 | # Vex 775 Pro motor specs from http://banebots.com/p/M2-RS550-120 |
| 134 | motor_name = "Vex 775 Pro motor specs from http://banebots.com/p/M2-RS550-120" |
| 135 | current_stall = 134 # amps stall current |
| 136 | current_no_load = 0.7 # amps no load current |
| 137 | torque_stall = 710 / 1000.0 # N-m Stall Torque |
| 138 | speed_no_load_rpm = 18730 # RPM no load speed |
Austin Schuh | f173eb8 | 2018-01-20 23:32:30 -0800 | [diff] [blame] | 139 | |
Ravago Jones | 5127ccc | 2022-07-31 16:32:45 -0700 | [diff] [blame^] | 140 | if (True): |
| 141 | # Bag motor from https://www.vexrobotics.com/217-3351.html |
| 142 | motor_name = "Bag motor from https://www.vexrobotics.com/217-3351.html" |
| 143 | current_stall = 53.0 # amps stall current |
| 144 | current_no_load = 1.8 # amps no load current |
| 145 | torque_stall = 0.4 # N-m Stall Torque |
| 146 | speed_no_load_rpm = 13180.0 # RPM no load speed |
Austin Schuh | f173eb8 | 2018-01-20 23:32:30 -0800 | [diff] [blame] | 147 | |
Ravago Jones | 5127ccc | 2022-07-31 16:32:45 -0700 | [diff] [blame^] | 148 | if (False): |
| 149 | # Mini CIM motor from https://www.vexrobotics.com/217-3371.html |
| 150 | motor_name = "Mini CIM motor from https://www.vexrobotics.com/217-3371.html" |
| 151 | current_stall = 89.0 # amps stall current |
| 152 | current_no_load = 3.0 # amps no load current |
| 153 | torque_stall = 1.4 # N-m Stall Torque |
| 154 | speed_no_load_rpm = 5840.0 # RPM no load speed |
Austin Schuh | f173eb8 | 2018-01-20 23:32:30 -0800 | [diff] [blame] | 155 | |
Ravago Jones | 5127ccc | 2022-07-31 16:32:45 -0700 | [diff] [blame^] | 156 | # How many motors are we using? |
| 157 | num_motors = 1 |
Austin Schuh | f173eb8 | 2018-01-20 23:32:30 -0800 | [diff] [blame] | 158 | |
Ravago Jones | 5127ccc | 2022-07-31 16:32:45 -0700 | [diff] [blame^] | 159 | # Motor values |
| 160 | print("# Motor: %s" % (motor_name)) |
| 161 | print("# Number of motors: %d" % (num_motors)) |
| 162 | print("# Stall torque: %.1f n-m" % (torque_stall)) |
| 163 | print("# Stall current: %.1f amps" % (current_stall)) |
| 164 | print("# No load current: %.1f amps" % (current_no_load)) |
| 165 | print("# No load speed: %.0f rpm" % (speed_no_load_rpm)) |
Austin Schuh | f173eb8 | 2018-01-20 23:32:30 -0800 | [diff] [blame] | 166 | |
Ravago Jones | 5127ccc | 2022-07-31 16:32:45 -0700 | [diff] [blame^] | 167 | # Constants from motor values |
| 168 | resistance_motor = voltage_nominal / current_stall |
| 169 | speed_no_load_rps = speed_no_load_rpm / 60.0 # Revolutions per second no load speed |
| 170 | speed_no_load = speed_no_load_rps * 2.0 * pi |
| 171 | Kt = num_motors * torque_stall / current_stall # N-m/A torque constant |
| 172 | Kv_rpm = speed_no_load_rpm / ( |
| 173 | voltage_nominal - resistance_motor * current_no_load) # rpm/V |
| 174 | Kv = Kv_rpm * 2.0 * pi / 60.0 # rpm/V |
Austin Schuh | f173eb8 | 2018-01-20 23:32:30 -0800 | [diff] [blame] | 175 | |
Ravago Jones | 5127ccc | 2022-07-31 16:32:45 -0700 | [diff] [blame^] | 176 | # Robot Geometry and physics |
| 177 | # m Length of arm connected to the robot base |
| 178 | length_proximal_arm = inches_to_meters * 47.34 |
| 179 | # m Length of arm that holds the cube |
| 180 | length_distal_arm = inches_to_meters * 44.0 |
| 181 | # m Length of intake arm from the pivot point to where the big roller contacts a cube. |
| 182 | length_intake_arm = inches_to_meters * 9.0 |
| 183 | mass_cube = 6.0 * lbs_to_kg # Weight of the cube in Kgrams |
| 184 | mass_proximal_arm = 5.5 * lbs_to_kg # Weight of proximal arm |
| 185 | mass_distal_arm = 3.5 * lbs_to_kg # Weight of distal arm |
| 186 | mass_distal = mass_cube + mass_distal_arm |
| 187 | mass_proximal = mass_proximal_arm + mass_distal |
| 188 | # m Length from arm pivot point to arm CG |
| 189 | radius_to_proximal_arm_cg = 22.0 * inches_to_meters |
| 190 | # m Length from arm pivot point to arm CG |
| 191 | radius_to_distal_arm_cg = 10.0 * inches_to_meters |
Austin Schuh | f173eb8 | 2018-01-20 23:32:30 -0800 | [diff] [blame] | 192 | |
Ravago Jones | 5127ccc | 2022-07-31 16:32:45 -0700 | [diff] [blame^] | 193 | radius_to_distal_cg = (length_distal_arm * mass_cube + |
| 194 | radius_to_distal_arm_cg * mass_distal_arm) / \ |
| 195 | mass_distal |
| 196 | radius_to_proximal_cg = (length_proximal_arm * mass_distal + |
| 197 | radius_to_proximal_arm_cg * mass_proximal_arm) / \ |
| 198 | mass_proximal |
| 199 | J_cube = length_distal_arm * length_distal_arm * mass_cube |
| 200 | # Kg m^2 Moment of inertia of the proximal arm |
| 201 | J_proximal_arm = radius_to_proximal_arm_cg * radius_to_proximal_arm_cg * \ |
| 202 | mass_distal_arm |
| 203 | # Kg m^2 Moment of inertia distal arm and cube at end of proximal arm. |
| 204 | J_distal_arm_and_cube_at_end_of_proximal_arm = length_proximal_arm * \ |
| 205 | length_proximal_arm * mass_distal |
| 206 | # Kg m^2 Moment of inertia of the distal arm |
| 207 | J_distal_arm = radius_to_distal_arm_cg * radius_to_distal_arm_cg * mass_distal_arm |
| 208 | # Moment of inertia of the arm with the cube on the end |
| 209 | J = J_distal_arm_and_cube_at_end_of_proximal_arm + J_proximal_arm |
| 210 | # Intake claw |
| 211 | J_intake = 0.295 # Kg m^2 Moment of inertia of intake |
| 212 | J = J_intake |
Austin Schuh | f173eb8 | 2018-01-20 23:32:30 -0800 | [diff] [blame] | 213 | |
Ravago Jones | 5127ccc | 2022-07-31 16:32:45 -0700 | [diff] [blame^] | 214 | gear_ratio = 140.0 # Guess at the gear ratio |
| 215 | gear_ratio = 100.0 # Guess at the gear ratio |
| 216 | gear_ratio = 90.0 # Guess at the gear ratio |
Austin Schuh | f173eb8 | 2018-01-20 23:32:30 -0800 | [diff] [blame] | 217 | |
Ravago Jones | 5127ccc | 2022-07-31 16:32:45 -0700 | [diff] [blame^] | 218 | error_margine = 1.0 |
| 219 | voltage = 10.0 # voltage for the motor. Assuming a loaded robot so not using 12 V. |
| 220 | # It might make sense to use a lower motor frees peed when the voltage is not a full 12 Volts. |
| 221 | # motor_free_speed = Kv * voltage |
| 222 | motor_free_speed = speed_no_load |
Austin Schuh | f173eb8 | 2018-01-20 23:32:30 -0800 | [diff] [blame] | 223 | |
Ravago Jones | 5127ccc | 2022-07-31 16:32:45 -0700 | [diff] [blame^] | 224 | print("# Kt = %f N-m/A\n# Kv_rpm = %f rpm/V\n# Kv = %f radians/V" % |
| 225 | (Kt, Kv_rpm, Kv)) |
| 226 | print("# %.2f Ohms Resistance of the motor " % (resistance_motor)) |
| 227 | print("# %.2f kg Cube weight" % (mass_cube)) |
| 228 | print("# %.2f kg Proximal Arm mass" % (mass_proximal_arm)) |
| 229 | print("# %.2f kg Distal Arm mass" % (mass_distal_arm)) |
| 230 | print("# %.2f kg Distal Arm and Cube weight" % (mass_distal)) |
| 231 | print("# %.2f m Length from distal arm pivot point to arm CG" % |
| 232 | (radius_to_distal_arm_cg)) |
| 233 | print("# %.2f m Length from distal arm pivot point to arm and cube cg" % |
| 234 | (radius_to_distal_cg)) |
| 235 | print( |
| 236 | "# %.2f kg-m^2 Moment of inertia of the cube about the arm pivot point" |
| 237 | % (J_cube)) |
| 238 | print("# %.2f m Length from proximal arm pivot point to arm CG" % |
| 239 | (radius_to_proximal_arm_cg)) |
| 240 | print("# %.2f m Length from proximal arm pivot point to arm and cube cg" % |
| 241 | (radius_to_proximal_cg)) |
| 242 | print("# %.2f m Proximal arm length" % (length_proximal_arm)) |
| 243 | print("# %.2f m Distal arm length" % (length_distal_arm)) |
Austin Schuh | f173eb8 | 2018-01-20 23:32:30 -0800 | [diff] [blame] | 244 | |
Ravago Jones | 5127ccc | 2022-07-31 16:32:45 -0700 | [diff] [blame^] | 245 | print( |
| 246 | "# %.2f kg-m^2 Moment of inertia of the intake about the intake pivot point" |
| 247 | % (J_intake)) |
| 248 | print( |
| 249 | "# %.2f kg-m^2 Moment of inertia of the distal arm about the arm pivot point" |
| 250 | % (J_distal_arm)) |
| 251 | print( |
| 252 | "# %.2f kg-m^2 Moment of inertia of the proximal arm about the arm pivot point" |
| 253 | % (J_proximal_arm)) |
| 254 | print( |
| 255 | "# %.2f kg-m^2 Moment of inertia of the distal arm and cube mass about " |
| 256 | "the proximal arm pivot point" % |
| 257 | (J_distal_arm_and_cube_at_end_of_proximal_arm)) |
| 258 | print( |
| 259 | "# %.2f kg-m^2 Moment of inertia of the intake the intake pivot point " |
Austin Schuh | f173eb8 | 2018-01-20 23:32:30 -0800 | [diff] [blame] | 260 | "(J value used in simulation)" % (J)) |
Ravago Jones | 5127ccc | 2022-07-31 16:32:45 -0700 | [diff] [blame^] | 261 | print("# %d Number of motors" % (num_motors)) |
Austin Schuh | f173eb8 | 2018-01-20 23:32:30 -0800 | [diff] [blame] | 262 | |
Ravago Jones | 5127ccc | 2022-07-31 16:32:45 -0700 | [diff] [blame^] | 263 | print("# %.2f V Motor voltage" % (voltage)) |
| 264 | for gear_ratio in range(60, 241, 10): |
| 265 | c1 = Kt * gear_ratio * gear_ratio / (Kv * resistance_motor * J) |
| 266 | c2 = gear_ratio * Kt / (J * resistance_motor) |
| 267 | c3 = radius_to_proximal_cg * mass_proximal * gravity / J |
Austin Schuh | f173eb8 | 2018-01-20 23:32:30 -0800 | [diff] [blame] | 268 | |
Ravago Jones | 5127ccc | 2022-07-31 16:32:45 -0700 | [diff] [blame^] | 269 | if (False): |
| 270 | print("# %.8f 1/sec C1 constant" % (c1)) |
| 271 | print("# %.2f 1/sec C2 constant" % (c2)) |
| 272 | print("# %.2f 1/(V sec^2) C3 constant" % (c3)) |
| 273 | print("# %.2f RPM Free speed at motor voltage" % |
| 274 | (voltage * Kv_rpm)) |
Austin Schuh | f173eb8 | 2018-01-20 23:32:30 -0800 | [diff] [blame] | 275 | |
Ravago Jones | 5127ccc | 2022-07-31 16:32:45 -0700 | [diff] [blame^] | 276 | torque_90_degrees = radius_to_distal_cg * mass_distal * gravity |
| 277 | voltage_90_degrees = resistance_motor * torque_90_degrees / ( |
| 278 | gear_ratio * Kt) |
| 279 | torque_peak = gear_ratio * num_motors * torque_stall |
| 280 | torque_peak_ft_lbs = torque_peak * newton_meters_to_ft_lbs |
| 281 | normal_force = torque_peak / length_intake_arm |
| 282 | normal_force_lbf = newton_to_lbf * normal_force |
| 283 | time_required = get_180_degree_time(c1, c2, c3, voltage, gear_ratio, |
| 284 | motor_free_speed) |
| 285 | print("Time for %.1f degrees for gear ratio %3.0f is %.2f seconds. " |
| 286 | "Peak (stall) torque %3.0f nm %3.0f ft-lb Normal force at intake " |
| 287 | "end %3.0f N %2.0f lbf" % \ |
| 288 | (to_deg(theta_travel), gear_ratio, time_required, |
| 289 | torque_peak, torque_peak_ft_lbs, normal_force, normal_force_lbf)) |
| 290 | |
Austin Schuh | f173eb8 | 2018-01-20 23:32:30 -0800 | [diff] [blame] | 291 | |
| 292 | if __name__ == '__main__': |
Ravago Jones | 5127ccc | 2022-07-31 16:32:45 -0700 | [diff] [blame^] | 293 | main() |