Code to let Gr20's BioRobotics2017 robot come to live.
Dependencies: FastPWM MODSERIAL QEI mbed
main.cpp
- Committer:
- megrootens
- Date:
- 2017-11-12
- Revision:
- 0:caa8ee3bd882
- Child:
- 2:df0c6af898ac
File content as of revision 0:caa8ee3bd882:
#include "main.h" // = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = // UI implementation // = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = void ui::StatusDisplay() { switch (robot::state) { case robot::OFF: // continuous red; rgb_led[0] = LED_ON; rgb_led[1] = !LED_ON; rgb_led[2] = !LED_ON; break; case robot::CALIBRATION: // alternating red-green; busy rgb_led[0] = !rgb_led[0]; rgb_led[1] = !rgb_led[0]; rgb_led[2] = !LED_ON; break; case robot::HOMING: // alternating green-blue; busy rgb_led[0] = !LED_ON; rgb_led[1] = !rgb_led[1]; rgb_led[2] = !rgb_led[1]; break; case robot::READY: // continuous green rgb_led[0] = !LED_ON; rgb_led[1] = LED_ON; rgb_led[2] = !LED_ON; break; case robot::DEMO: // alternating red-blue; busy rgb_led[0] = !rgb_led[0]; rgb_led[1] = !LED_ON; rgb_led[2] = !rgb_led[0]; break; case robot::MANUAL: // alternating green-purple rgb_led[0] = !rgb_led[0]; rgb_led[1] = !rgb_led[0]; rgb_led[2] = rgb_led[0]; break; } } void ui::SwitchState(State new_state) { ui::serial.printf("* Switching Input State to: %s\r\n",StateNames[new_state]); ui::state = new_state; } void robot::SwitchState(State new_state) { ui::serial.printf("* Switching Robot State to: %s\r\n",StateNames[new_state]); state = new_state; } void ui::InterruptSwitch2() { if (robot::has_power()) { robot::Stop(); } else { robot::Start(); } } void ui::InterruptSwitch3() { switch (state) { case STATE_SWITCHING: robot::GoToNextState(); break; } } // = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = // Robot implementation // = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = void robot::GoToNextState() { switch (state) { case OFF: SwitchState(CALIBRATION); ui::SwitchState(ui::IGNORE); Start(); break; case CALIBRATION: SwitchState(HOMING); Start(); break; case HOMING: SwitchState(DEMO); //ui::SwitchState(ui::STATE_SWITCHING); break; case READY: SwitchState(DEMO); break; case DEMO: SwitchState(MANUAL); break; default: SwitchState(OFF); robot::is_calibrated = false; Stop(); } } bool robot::has_power() { return m1.has_power() or m2.has_power(); } double robot::get_angle(int i) { switch(i) { case 1: return m1.get_angle(); case 2: return m2.get_angle(); default: return NULL; } } double robot::ForwardKinematicsX(double theta_1, double theta_2) { return kL1*cos(deg2rad(theta_1)) + kL2*cos(deg2rad(theta_2)); } double robot::ForwardKinematicsY(double theta_1, double theta_2) { return kL1*sin(deg2rad(theta_1)) + kL2*sin(deg2rad(theta_2)); } double robot::get_x() { return ForwardKinematicsX(get_angle(1),get_angle(2)); } double robot::get_y() { return ForwardKinematicsY(get_angle(1),get_angle(2)); } double robot::InverseKinematicsTheta(double x, double y, int i) { double r = sqrt(pow(x,2) + pow(y,2)); double dir_angle = rad2deg( atan2(y,x) ); double int_angle_1 = rad2deg( acos((pow(kL1,2) + pow(r,2) - pow(kL2,2))/(2*kL1*r)) ); double int_angle_2 = rad2deg( acos((pow(kL2,2) + pow(r,2) - pow(kL1,2))/(2*kL2*r)) ); double theta_1 = dir_angle + int_angle_1; double theta_2 = dir_angle - int_angle_2; switch (i) { case 1: return theta_1; case 2: return theta_2; default: return NULL; } } void robot::Start() { ui::serial.printf(" Setting current position as reference.\r\n"); ref::SetPositionAsReference(); ui::serial.printf(" Resetting controllers.\r\n"); c1.Reset(); c2.Reset(); ui::serial.printf(" Starting motors.\r\n"); m1.Start(); m2.Start(); } void robot::Stop() { ui::serial.printf(" Stopping motors.\r\n"); m1.Stop(); m2.Stop(); } void robot::ControlLoop() { m1.set_pwm(c1.Update( ref::theta_1 - get_angle(1) )); m2.set_pwm(c2.Update( ref::theta_2 - get_angle(2) )); } void robot::SetCalibrationAngles() { m1.set_angle(kCalibAngleMotor1); m2.set_angle(kCalibAngleMotor2); is_calibrated = true; } // = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = // Reference implementation // = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = double ref::get_ref_x() { return robot::ForwardKinematicsX(theta_1,theta_2); } double ref::get_ref_y() { return robot::ForwardKinematicsY(theta_1,theta_2); } void ref::UpdateReference() { switch(robot::state) { case robot::CALIBRATION: CalibrationReference(); break; case robot::HOMING: HomingReference(); break; case robot::DEMO: DemoReference(); break; } } void ref::CalibrationReference() { // Calibration process bool is_ready_1 = (theta_1 - robot::get_angle(1) > kCalibrationError); bool is_ready_2 = (theta_2 - robot::get_angle(2) > kCalibrationError); if (is_ready_1) { robot::m1.Stop(); } else { theta_1 += kCalibrationOmegaStep; } if (is_ready_2) { robot::m2.Stop(); } else { theta_2 += kCalibrationOmegaStep; } if (is_ready_1 & is_ready_2) { robot::Stop(); ui::serial.printf("+ Both motors at end-stop.\r\n"); robot::SetCalibrationAngles(); robot::GoToNextState(); } } void ref::HomingReference() { double dist_1 = kOriginTheta1 - theta_1; if (abs(dist_1)<=kCalibrationOmegaStep) { // motor 1 ready theta_1 = kOriginTheta1; double dist_2 = kOriginTheta2 - theta_2; if (abs(dist_2)<=kCalibrationOmegaStep) { // motor 2 also ready theta_2 = kOriginTheta2; ui::serial.printf(" Robot at home position.\r\n"); robot::GoToNextState(); } else { // motor 2 still to move theta_2 += dist_2 / abs(dist_2) * kCalibrationOmegaStep; } } else { // motor 1 still to move theta_1 += dist_1 / abs(dist_1) * kCalibrationOmegaStep; } } void ref::DemoReference() { double x = robot::ForwardKinematicsX(theta_1,theta_2); double y = robot::ForwardKinematicsY(theta_1,theta_2); double x_goal = kDemoCoords[0][i_demo_coord]; double y_goal = kDemoCoords[1][i_demo_coord]; double delta_x = x_goal - x; double delta_y = y_goal - y; double dist = sqrt(pow(delta_x,2) + pow(delta_y,2)); double x_new = x; double y_new = y; if (dist<kMaxStep) { x_new = x_goal; y_new = y_goal; ui::serial.printf(" Reached Demo Coord %d\r\n",i_demo_coord); i_demo_coord = ++i_demo_coord % kNumDemoCoords; if (i_demo_coord == 0) { robot::is_calibrated = false; robot::SwitchState(robot::CALIBRATION); } } else { x_new += delta_x / dist * kMaxStep; y_new += delta_y / dist * kMaxStep; } theta_1 = robot::InverseKinematicsTheta(x_new, y_new, 1); theta_2 = robot::InverseKinematicsTheta(x_new, y_new, 2); } void ref::SetPositionAsReference() { ref::theta_1 = robot::get_angle(1); ref::theta_2 = robot::get_angle(2); }