Code to let Gr20's BioRobotics2017 robot come to live.

Dependencies:   FastPWM MODSERIAL QEI mbed

Revision:
0:caa8ee3bd882
Child:
2:df0c6af898ac
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp	Sun Nov 12 00:14:05 2017 +0000
@@ -0,0 +1,328 @@
+#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);
+}
\ No newline at end of file