| 
									
										
										
										
											2022-04-28 20:23:18 +00:00
										 |  |  | #include <cstdio>
 | 
					
						
							| 
									
										
										
										
											2022-04-26 22:15:13 +00:00
										 |  |  | #include "pico/stdlib.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "motor2040.hpp"
 | 
					
						
							| 
									
										
										
										
											2022-04-28 20:23:18 +00:00
										 |  |  | #include "button.hpp"
 | 
					
						
							|  |  |  | #include "pid.hpp"
 | 
					
						
							| 
									
										
										
										
											2022-04-26 22:15:13 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  | An example of how to drive a motor smoothly between random speeds, | 
					
						
							|  |  |  | with the help of it's attached encoder and PID control. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Press "Boot" to exit the program. | 
					
						
							|  |  |  | */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | using namespace motor; | 
					
						
							| 
									
										
										
										
											2022-04-28 20:23:18 +00:00
										 |  |  | using namespace encoder; | 
					
						
							| 
									
										
										
										
											2022-04-26 22:15:13 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-28 20:23:18 +00:00
										 |  |  | // The pins of the motor being profiled
 | 
					
						
							|  |  |  | const pin_pair MOTOR_PINS = motor2040::MOTOR_A; | 
					
						
							| 
									
										
										
										
											2022-04-26 22:15:13 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-28 20:23:18 +00:00
										 |  |  | // The pins of the encoder attached to the profiled motor
 | 
					
						
							|  |  |  | const pin_pair ENCODER_PINS = motor2040::ENCODER_A; | 
					
						
							| 
									
										
										
										
											2022-04-26 22:15:13 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-28 20:23:18 +00:00
										 |  |  | // The gear ratio of the motor
 | 
					
						
							|  |  |  | constexpr float GEAR_RATIO = 50.0f; | 
					
						
							| 
									
										
										
										
											2022-04-26 22:15:13 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-28 20:23:18 +00:00
										 |  |  | // The counts per revolution of the motor's output shaft
 | 
					
						
							|  |  |  | constexpr float COUNTS_PER_REV = MMME_CPR * GEAR_RATIO; | 
					
						
							| 
									
										
										
										
											2022-04-26 22:15:13 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-28 20:23:18 +00:00
										 |  |  | // The direction to spin the motor in. NORMAL_DIR (0), REVERSED_DIR (1)
 | 
					
						
							|  |  |  | const Direction DIRECTION = NORMAL_DIR; | 
					
						
							| 
									
										
										
										
											2022-04-26 22:15:13 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-28 20:23:18 +00:00
										 |  |  | // The scaling to apply to the motor's speed to match its real-world speed
 | 
					
						
							| 
									
										
										
										
											2022-05-12 12:20:52 +00:00
										 |  |  | constexpr float SPEED_SCALE = 5.4f; | 
					
						
							| 
									
										
										
										
											2022-04-28 20:23:18 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | // How many times to update the motor per second
 | 
					
						
							|  |  |  | const uint UPDATES = 100; | 
					
						
							|  |  |  | constexpr float UPDATE_RATE = 1.0f / (float)UPDATES; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // The time to travel between each random value, in seconds
 | 
					
						
							|  |  |  | constexpr float TIME_FOR_EACH_MOVE = 1.0f; | 
					
						
							|  |  |  | const uint UPDATES_PER_MOVE = TIME_FOR_EACH_MOVE * UPDATES; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // How many of the updates should be printed (i.e. 2 would be every other update)
 | 
					
						
							|  |  |  | const uint PRINT_DIVIDER = 4; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Multipliers for the different printed values, so they appear nicely on the Thonny plotter
 | 
					
						
							|  |  |  | constexpr float ACC_PRINT_SCALE = 0.05f;    // Acceleration multiplier
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // How far from zero to drive the motor at, in revolutions per second
 | 
					
						
							|  |  |  | constexpr float VELOCITY_EXTENT = 3.0f; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // The interpolating mode between setpoints. STEP (0), LINEAR (1), COSINE (2)
 | 
					
						
							|  |  |  | const uint INTERP_MODE = 2; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // PID values
 | 
					
						
							|  |  |  | constexpr float VEL_KP = 30.0f;   // Velocity proportional (P) gain
 | 
					
						
							|  |  |  | constexpr float VEL_KI = 0.0f;    // Velocity integral (I) gain
 | 
					
						
							|  |  |  | constexpr float VEL_KD = 0.4f;    // Velocity derivative (D) gain
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Create a motor and set its direction and speed scale
 | 
					
						
							|  |  |  | Motor m = Motor(MOTOR_PINS, DIRECTION, SPEED_SCALE); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Create an encoder and set its direction and counts per rev, using PIO 0 and State Machine 0
 | 
					
						
							|  |  |  | Encoder enc = Encoder(pio0, 0, ENCODER_PINS, PIN_UNUSED, DIRECTION, COUNTS_PER_REV, true); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Create the user button
 | 
					
						
							|  |  |  | Button user_sw(motor2040::USER_SW); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Create PID object for velocity control
 | 
					
						
							|  |  |  | PID vel_pid = PID(VEL_KP, VEL_KI, VEL_KD, UPDATE_RATE); | 
					
						
							| 
									
										
										
										
											2022-04-26 22:15:13 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int main() { | 
					
						
							|  |  |  |   stdio_init_all(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-28 20:23:18 +00:00
										 |  |  |   // Initialise the motor and encoder
 | 
					
						
							| 
									
										
										
										
											2022-04-26 22:15:13 +00:00
										 |  |  |   m.init(); | 
					
						
							| 
									
										
										
										
											2022-04-28 20:23:18 +00:00
										 |  |  |   enc.init(); | 
					
						
							| 
									
										
										
										
											2022-04-26 22:15:13 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |   // Enable the motor
 | 
					
						
							|  |  |  |   m.enable(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-28 20:23:18 +00:00
										 |  |  |   uint update = 0; | 
					
						
							|  |  |  |   uint print_count = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // Set the initial value and create a random end value between the extents
 | 
					
						
							|  |  |  |   float start_value = 0.0f; | 
					
						
							|  |  |  |   float end_value = (((float)rand() / (float)RAND_MAX) * (VELOCITY_EXTENT * 2.0f)) - VELOCITY_EXTENT; | 
					
						
							| 
									
										
										
										
											2022-04-26 22:15:13 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-28 20:23:18 +00:00
										 |  |  |   // Continually move the motor until the user button is pressed
 | 
					
						
							|  |  |  |   while(!user_sw.raw()) { | 
					
						
							| 
									
										
										
										
											2022-04-26 22:15:13 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-28 20:23:18 +00:00
										 |  |  |     // Capture the state of the encoder
 | 
					
						
							|  |  |  |     Encoder::Capture capture = enc.capture(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Calculate how far along this movement to be
 | 
					
						
							|  |  |  |     float percent_along = (float)update / (float)UPDATES_PER_MOVE; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     switch(INTERP_MODE) { | 
					
						
							|  |  |  |       case 0: | 
					
						
							|  |  |  |         // Move the motor instantly to the end value
 | 
					
						
							|  |  |  |         vel_pid.setpoint = end_value; | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       case 2: | 
					
						
							|  |  |  |         // Move the motor between values using cosine
 | 
					
						
							|  |  |  |         vel_pid.setpoint = (((-cosf(percent_along * (float)M_PI) + 1.0) / 2.0) * (end_value - start_value)) + start_value; | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       case 1: | 
					
						
							|  |  |  |       default: | 
					
						
							|  |  |  |         // Move the motor linearly between values
 | 
					
						
							|  |  |  |         vel_pid.setpoint = (percent_along * (end_value - start_value)) + start_value; | 
					
						
							| 
									
										
										
										
											2022-04-26 22:15:13 +00:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-28 20:23:18 +00:00
										 |  |  |     // Calculate the acceleration to apply to the motor to move it closer to the velocity setpoint
 | 
					
						
							|  |  |  |     float accel = vel_pid.calculate(capture.revolutions_per_second()); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Accelerate or decelerate the motor
 | 
					
						
							|  |  |  |     m.speed(m.speed() + (accel * UPDATE_RATE)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Print out the current motor values and their setpoints, but only on every multiple
 | 
					
						
							|  |  |  |     if(print_count == 0) { | 
					
						
							|  |  |  |         printf("Vel = %f, ", capture.revolutions_per_second()); | 
					
						
							|  |  |  |         printf("Vel SP = %f, ", vel_pid.setpoint); | 
					
						
							|  |  |  |         printf("Accel = %f, ", accel * ACC_PRINT_SCALE); | 
					
						
							|  |  |  |         printf("Speed = %f\n", m.speed()); | 
					
						
							| 
									
										
										
										
											2022-04-26 22:15:13 +00:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-04-28 20:23:18 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     // Increment the print count, and wrap it
 | 
					
						
							|  |  |  |     print_count = (print_count + 1) % PRINT_DIVIDER; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     update++;   // Move along in time
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Have we reached the end of this movement?
 | 
					
						
							|  |  |  |     if(update >= UPDATES_PER_MOVE) { | 
					
						
							|  |  |  |         update = 0;  // Reset the counter
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Set the start as the last end and create a new random end value
 | 
					
						
							|  |  |  |         start_value = end_value; | 
					
						
							|  |  |  |         end_value = (((float)rand() / (float)RAND_MAX) * (VELOCITY_EXTENT * 2.0f)) - VELOCITY_EXTENT; | 
					
						
							| 
									
										
										
										
											2022-04-26 22:15:13 +00:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-04-28 20:23:18 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     sleep_ms(UPDATE_RATE * 1000.0f); | 
					
						
							| 
									
										
										
										
											2022-04-26 22:15:13 +00:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // Disable the motor
 | 
					
						
							|  |  |  |   m.disable(); | 
					
						
							|  |  |  | } |