Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependencies: FastPWM MODSERIAL mbed QEI
Diff: main.cpp
- Revision:
- 0:9cf3d3dc621b
- Child:
- 1:08d285e33eea
- Child:
- 4:e8b3fba77dd0
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main.cpp Wed May 31 20:30:48 2017 +0000 @@ -0,0 +1,457 @@ +//#includes-------------------------------------------------------------------------------------------------------- +#include "mbed.h" +#include "MODSERIAL.h" //for more efficient serial comm +#include "FastPWM.h" //To fix a bug in the standard mbed lib concerning pwm + +//#efines-------------------------------------------------------------------------------------------------------- +#define PWMCYCLETIMEUS 50 //20 kHz PWM cycle time +#define MAX_ENCODER_POS_RIGHT -1453 //Encoder counts at right end-stop +#define MAX_ENCODER_POS_LEFT 1333 //Encoder counts at left end-stop +#define LOOPDURATIONUS 200 //microsec +#define LOOPDURATION ((float)LOOPDURATIONUS*0.000001f) //sec +#define LEDPERIODS 1000 //How many loops before changing the LED state +#define CALIBPWM 0.085f //12.5% of max PWM is the calibration PWM +#define ENCODER_CPR 1024.0f //Counts per revolution for the encode (1x quad, to avoid missing counts) +#define TWO_PI 6.283185307179586476925286766559f //rad +#define WORKSPACEBOUND 7.0f //workspace bound for admittane model +#define MAXVEL 100.0f //maximal admittance control velocity in rad/s +#define FORCESENSORGAIN 15.134f //N per measured unit + +//Low pass filter filter coeffs, 2nd order, 200 Hz +double b[3] = {0.003621681514929, 0.007243363029857, 0.003621681514929}; +double a[3] = {1.00000000000000, -1.822694925196308, 0.837181651256023}; + +//Pins and interrupts-------------------------------------------------------------------------------------------------------- +// Remember that pins D4,D5,D6,D7 are internally connected to the drivers! Do not use D6 and D7. +Ticker mainLoop; //Main loop runs at fixed fequency + +//Inputs +AnalogIn measuredForce(A5); //Pin that measures analog force +InterruptIn EncoderA(D2); //Encoder interrupt for Channel A +DigitalIn EncoderB(D3); //Encoder Channel B + +//Outputs +DigitalOut motorDir(D4); //Motor Direction Pin +FastPWM motorPWM(D5); //Motor PWM out pin + +//Communication for teleoperation +//Ethernet eth; + +//For DBG +DigitalOut dbgPin(D1); //Possible debug pin to check with scope +MODSERIAL pc(USBTX, USBRX); //Serial communication with host PC +DigitalOut ledRed(LED_RED); //LED RED +DigitalOut ledBlue(LED_BLUE); //LED BLUE +DigitalOut ledGreen(LED_GREEN);//LED GREEN + +//Enums-------------------------------------------------------------------------------------------------------- +enum States { stateIdle, stateCalibration, stateHoming, stateOPImpedance, + stateOPAdmittance, stateOPTeleOpPosPos, stateOPTeleOpImpedance, + stateOPTeleOpAdmittance, stateFailed=99 + }; //System states +enum LedColors { colorCyan, colorMagenta, colorBlue, colorYellow, colorGreen, colorRed, colorBlack, colorWhite}; //Possible LED colors + +//Global variables-------------------------------------------------------------------------------------------------------- +//General +long loopCounter = 0; //How many loops have passed. Will wrap around after 2^32-1 +int currentState = stateIdle; //Current state of state machine +int statePrev = -1; //Previous state of state machine +float stateTime = 0.0f; //How long have we been in this state? +int ledCnt = 0; //Loops for LED state +bool ledState = false; //Current LED state (on/off = true/false) +Timer t; // Main timer to measure time + +//Controller references +float refPos = 0.0f; //System reference position +float refVel = 0.0f; //System reference velocity +float refAcc = 0.0f; //System reference acceleration +float u = 0.0f; //Control signal (PWM duty cycle) + +//Controller gains +const float K_p = 4.5f; //Proportional position gain +const float K_d = 0.03f; //Differential position gain +const float K_i = 0.000f; //Integral position gain +const float I_ff = 0.0000; //Feed-forward mass +float posError = 0.0f; +float velError = 0.0f; +float integratedError = 0.0f; + +//Virtual model properties +const float virtualMass = 0.0015f; +const float virtualStiffness = 50.0f; +const float virtualDamping = 3.0f; +const float springPos = 0.0f; + +//Measured variables +float motorPos = 0.0f; +float motorPos_prev = 0.0f; +float motorVel = 0.0f; +float force = 0.0f; +long encoderPos = 0; //Current encoder position +long encoderPos_prev = 0; //Previous sample encoder position +float encoderVel = 0.0f; // Estimated encoder velocity + +//DEbug and communication variables (unused?) +char buffer[0x80]; //128 byte buffer (necessary?) + +//Functions-------------------------------------------------------------------------------------------------------- +//Encoder Interrupt Routine +void encoderFunctionA() +{ + if ((bool)EncoderA == (bool)EncoderB) { //Increment or decrement encoder position during interrupt + encoderPos++; + } else { + encoderPos--; + } +} + +//Velocity filtering +double velocityFilter(float x) +{ + double y = 0.0; //Output + static double y_1 = 0.0; //Output last loop + static double y_2 = 0.0; //Output 2 loops ago + static double x_1 = 0.0; //Input last loop + static double x_2 = 0.0; //Input two loops ago + + //Finite difference equation for 2nd order Butterworth low-pass + y = -a[1]*y_1 - a[2]*y_2 + x*b[0] + x_1*b[1] + x_2*b[2]; + y_2 = y_1; + y_1 = y; + x_2 = x_1; + x_1 = x; + return (float)y; +} + +//Limit a function, passed by reference +void limit(float &x, float lower, float upper) +{ + if (x > upper) + x = upper; + if (x < lower) + x = lower; +} + +//Set led colors +void setLed(int ledColorArg) +{ + switch(ledColorArg) { + case colorCyan: + ledRed = 1; + ledGreen = 0; + ledBlue = 0; + break; + case colorMagenta: + ledRed = 0; + ledGreen = 1; + ledBlue = 0; + break; + case colorBlue: + ledRed = 1; + ledGreen = 1; + ledBlue = 0; + break; + case colorYellow: + ledRed = 0; + ledGreen = 0; + ledBlue = 1; + break; + case colorGreen: + ledRed = 1; + ledGreen = 0; + ledBlue = 1; + break; + case colorRed: + ledRed = 0; + ledGreen = 1; + ledBlue = 1; + break; + case colorBlack: + ledRed = 1; + ledGreen = 1; + ledBlue = 1; + break; + case colorWhite: + ledRed = 0; + ledGreen = 0; + ledBlue = 0; + break; + } +} + +void setPWM(float u) +{ + //Possibly limit, if required + limit(u,-1.0f,1.0f); + //limit(u,-0.3f,0.3f); //DEBUG + //Set proper PWM + if (u >= 0.0f) { + motorPWM = u; + motorDir = false; + } else { + motorPWM = -u; + motorDir = true; + } +} + +void doNetwork() +{ + //int size = eth.receive(); + //if(size > 0) + //{ + //eth.read(buf, size); + + //} +} + +void mapIn() +{ + //Measure relevant signals + motorPos = (float)encoderPos / ENCODER_CPR * TWO_PI; //motor position in rad + encoderVel = (float)(encoderPos - encoderPos_prev) / LOOPDURATION; //encoder velocity in counts/s + encoderPos_prev = encoderPos; //encoder position in counts + motorVel = encoderVel / ENCODER_CPR * TWO_PI; //motor velocity in rad/s + motorVel = velocityFilter(motorVel); //filtered motor velocity in rad/s + motorPos_prev = motorPos; //Previous motor position in rad + force = -FORCESENSORGAIN*2.0f*(measuredForce - 0.5f); //Measured force + doNetwork(); //Receive inputs over network (if available) +} + +void mapOut() +{ + //Apply control + setPWM(u); + //setPWM(0); //DEBUG ONLY + //Send network packets if required +} + +//State functions-------------------------------------------------------------------------------------------------------- + +//During the wait/idle state +inline void doStateIdle() +{ + //Control + u = 0.0f; + + //Handle LED + ledCnt++; + if (ledCnt > LEDPERIODS) { + ledState = !ledState; + if (ledState) + setLed(colorYellow); + else + setLed(colorBlack); + ledCnt = 0; + } + + //State Guard: + if (t.read() > 3.0f) { + currentState = stateCalibration; + stateTime = t.read(); + } +} + +inline void doStateCalib() +{ + //Control + //Send out calibration PWM + u = CALIBPWM; + + //Handle LED + ledCnt++; + if (ledCnt > LEDPERIODS) { + ledState = !ledState; + if (ledState) + setLed(colorBlue); + else + setLed(colorBlack); + ledCnt = 0; + } + + //State Guard + if ((abs(motorVel) < 0.001f) && (t.read()-stateTime > 1.0f)) { + currentState = stateHoming; + encoderPos = MAX_ENCODER_POS_LEFT; //When calibrated, set encoder pos to the known value + stateTime = t.read(); + refPos = (float)MAX_ENCODER_POS_LEFT / ENCODER_CPR * TWO_PI; //Start at the left calibration point + } +} + +inline void doStateHoming() +{ + //Calculate a ramp to location 0 (center) with -2 rad/s + refVel = -2.00f; + if (refPos > 0.0f) { + refPos += refVel*LOOPDURATION;//refVel*LOOPDURATION; + } + else { + refPos = 0.0f; + refVel = 0.0f; + } + + //Control + posError = refPos - motorPos; + integratedError += posError*LOOPDURATION; + velError = refVel - motorVel; + u = K_p * posError + K_d * velError + 0*K_i*integratedError; + + //Handle LED + ledCnt++; + if (ledCnt > LEDPERIODS) { + ledState = !ledState; + if (ledState) + setLed(colorGreen); + else + setLed(colorBlack); + ledCnt = 0; + } + + //State Guard + if ((abs(encoderPos) < 6) && (abs(velError) < 0.002f) && (t.read()-stateTime > 1.0f)) { + integratedError = 0.0f; + setLed(colorGreen); + stateTime = t.read(); + currentState = stateOPAdmittance;//stateOPImpedance; + /*if (doAdmittance) { + currentState = stateOPAdmittance; + } else { + currentState = stateOPImpedance; + }*/ + } +} + +inline void doStateOPAdmittance() +{ + //Virtual Model + refAcc = force / virtualMass - virtualStiffness * (refPos - springPos) - virtualDamping * refVel; + refVel += refAcc * LOOPDURATION; + + if (refVel > MAXVEL) + { + refVel = MAXVEL; + refAcc = 0.0f; + } else if (refVel < -MAXVEL) + { + refVel = -MAXVEL; + refAcc = 0.0f; + } + + refPos += refVel * LOOPDURATION; + if (refPos > WORKSPACEBOUND) + { + refPos = WORKSPACEBOUND; + refVel = 0.0f; + refAcc = 0.0f; + } else if (refPos < -WORKSPACEBOUND) + { + refPos = -WORKSPACEBOUND; + refVel = 0.0f; + refAcc = 0.0f; + } + + //Controller + posError = refPos - motorPos; + velError = refVel - motorVel; + integratedError += posError*LOOPDURATION; + u = refAcc*I_ff + K_p * posError + K_d * velError + 0*K_i * integratedError; + + //StateGuard + //if... +} + +inline void doStateOPImpedance() +{ + //Controller is the virtual model in OPEN LOOP impedance + u = - virtualStiffness * (motorPos - springPos) - virtualDamping * motorVel; + //StateGuard + //if... +} + +inline void doStateFailed() +{ + //Control + u = 0.0f; + + //Handle LED + ledCnt++; + if (ledCnt > LEDPERIODS) { + ledState = !ledState; + if (ledState) + setLed(colorRed); + else + setLed(colorBlack); + ledCnt = 0; + } + + //No state guard. +} + +//Loop Function-------------------------------------------------------------------------------------------------------- +void loopFunction() +{ + //This code is run every loop + loopCounter++; + + //Measure relevant inputs + mapIn(); + + //Set Defaults + u = 0.0f; + + //Do State Machine + switch(currentState) { + case stateIdle: + doStateIdle(); + break; + case stateCalibration: + doStateCalib(); + break; + case stateHoming: + doStateHoming(); + break; + case stateOPImpedance: + doStateOPImpedance(); + break; + case stateOPAdmittance: + doStateOPAdmittance(); + break; + case stateFailed: //fall through + doStateFailed(); + break; + } + + mapOut(); //Set all relevant output pins +} + +//Main-------------------------------------------------------------------------------------------------------- +int main() +{ + setLed(colorBlack); //Set LED to black + pc.baud(115200); //set Serial comm + t.start(); + + //Set all interrupt requests to 2nd place priority + for(int n = 0; n < 86; n++) { + NVIC_SetPriority((IRQn)n,1); + } + //Set out motor encoder interrupt to 1st place priority + NVIC_SetPriority(PORTB_IRQn,0); + + + EncoderA.rise(&encoderFunctionA); //Attach interrupts only to rising edge encoder channel A + motorPWM.period_us(PWMCYCLETIMEUS); //50 us = 20 kHz + mainLoop.attach_us(&loopFunction,LOOPDURATIONUS); //200 us = 5 kHz + + //Infinite while loop + while (true) { + //Print relevant stuff to terminal here, do not print in the in the loopFunction ticker!! + if (currentState != statePrev) { //When state changes + pc.printf(">>>>>State Changed to State: %d\r\n",currentState); + statePrev = currentState; //Update the prev state var + } + wait(0.01); + float millisecondsPassed = (float)loopCounter*(((float)LOOPDURATIONUS) / 1000.0f); + pc.printf("t:%f;State:%d;EncPos:%d;MotPos:%f;MotVel:%f;Force:%f;RefPos:%f;PosErr:%f;MotPWM:%f\r\n",millisecondsPassed,currentState,encoderPos,motorPos,motorVel,force,refPos,posError,u); + } +} +//EOF-------------------------------------------------------------------------------------------------------- \ No newline at end of file