Library for controlling #MeArm from phenoptix (http://www.phenoptix.com/collections/mearm). Solves inverse kinematics of the arm to position gripper in 3D space.

Dependents:   wizArm_WIZwiki-W7500ECO wizArm_WIZwiki-W7500ECO MeArm_Lab2_Task1 MeArm_Lab3_Task1 ... more

Committer:
eencae
Date:
Tue Nov 04 20:54:49 2014 +0000
Revision:
0:49b8e971fa83
Child:
1:d2b7780ca6b3
Initial commit.

Who changed what in which revision?

UserRevisionLine numberNew contents of line
eencae 0:49b8e971fa83 1 /*
eencae 0:49b8e971fa83 2 @file MeArm.cpp
eencae 0:49b8e971fa83 3
eencae 0:49b8e971fa83 4 @brief Member functions implementations
eencae 0:49b8e971fa83 5
eencae 0:49b8e971fa83 6 */
eencae 0:49b8e971fa83 7 #include "mbed.h"
eencae 0:49b8e971fa83 8 #include "MeArm.h"
eencae 0:49b8e971fa83 9
eencae 0:49b8e971fa83 10 MeArm::MeArm(PinName basePin, PinName shoulderPin, PinName elbowPin, PinName gripperPin)
eencae 0:49b8e971fa83 11 {
eencae 0:49b8e971fa83 12 // set up pins as required
eencae 0:49b8e971fa83 13 base = new PwmOut(basePin);
eencae 0:49b8e971fa83 14 shoulder = new PwmOut(shoulderPin);
eencae 0:49b8e971fa83 15 elbow = new PwmOut(elbowPin);
eencae 0:49b8e971fa83 16 gripper = new PwmOut(gripperPin);
eencae 0:49b8e971fa83 17 // PWM objects have 50 Hz frequency by default so shouldn't need to set servo frequency
eencae 0:49b8e971fa83 18 // can do it anyway to be sure. All channels share same period so just need to set one of them
eencae 0:49b8e971fa83 19 base->period(1.0/50.0); // servos are typically 50 Hz
eencae 0:49b8e971fa83 20 // setup USB serial for debug/comms
eencae 0:49b8e971fa83 21 serial = new Serial(USBTX,USBRX);
eencae 0:49b8e971fa83 22 // leds used for debug
eencae 0:49b8e971fa83 23 leds = new BusOut(LED4,LED3,LED2,LED1);
eencae 0:49b8e971fa83 24 }
eencae 0:49b8e971fa83 25
eencae 0:49b8e971fa83 26 void MeArm::setTarget(float x,float y,float z)
eencae 0:49b8e971fa83 27 {
eencae 0:49b8e971fa83 28 target.x = x;
eencae 0:49b8e971fa83 29 target.y = y;
eencae 0:49b8e971fa83 30 target.z = z;
eencae 0:49b8e971fa83 31
eencae 0:49b8e971fa83 32 // TODO - add checks for valid target
eencae 0:49b8e971fa83 33 }
eencae 0:49b8e971fa83 34
eencae 0:49b8e971fa83 35 /// TODO - add checks for valid angles
eencae 0:49b8e971fa83 36 void MeArm::moveBaseServo(float angle)
eencae 0:49b8e971fa83 37 {
eencae 0:49b8e971fa83 38 // arm facing forward is defined as 90 degrees, angle increases counter-clockwise
eencae 0:49b8e971fa83 39 int pulseWidthMicroSeconds = 575.0 + 985.0*angle/90.0;
eencae 0:49b8e971fa83 40 serial->printf("Base Pulse = %d us\n",pulseWidthMicroSeconds);
eencae 0:49b8e971fa83 41 // equation to convert angle to pulsewidth in us
eencae 0:49b8e971fa83 42 base->pulsewidth_us(pulseWidthMicroSeconds);
eencae 0:49b8e971fa83 43 // move servo
eencae 0:49b8e971fa83 44 }
eencae 0:49b8e971fa83 45
eencae 0:49b8e971fa83 46 void MeArm::moveShoulderServo(float angle)
eencae 0:49b8e971fa83 47 {
eencae 0:49b8e971fa83 48 // angle relative to horizontal
eencae 0:49b8e971fa83 49 int pulseWidthMicroSeconds = 2705.0 - 1097.0*angle/90.0;
eencae 0:49b8e971fa83 50 serial->printf("Shoulder Pulse = %d us\n",pulseWidthMicroSeconds);
eencae 0:49b8e971fa83 51 // equation to convert angle to pulsewidth in us
eencae 0:49b8e971fa83 52 shoulder->pulsewidth_us(pulseWidthMicroSeconds);
eencae 0:49b8e971fa83 53 // move servo
eencae 0:49b8e971fa83 54 }
eencae 0:49b8e971fa83 55
eencae 0:49b8e971fa83 56 void MeArm::moveElbowServo(float angle)
eencae 0:49b8e971fa83 57 {
eencae 0:49b8e971fa83 58 // angle relative to horizontal
eencae 0:49b8e971fa83 59 int pulseWidthMicroSeconds = 1910.0 - 1095.0*angle/90.0;
eencae 0:49b8e971fa83 60 serial->printf("Elbow Pulse = %d us\n",pulseWidthMicroSeconds);
eencae 0:49b8e971fa83 61 // equation to convert angle to pulsewidth in us
eencae 0:49b8e971fa83 62 elbow->pulsewidth_us(pulseWidthMicroSeconds);
eencae 0:49b8e971fa83 63 // move servo
eencae 0:49b8e971fa83 64 }
eencae 0:49b8e971fa83 65
eencae 0:49b8e971fa83 66 void MeArm::moveServos()
eencae 0:49b8e971fa83 67 {
eencae 0:49b8e971fa83 68 moveBaseServo(thetaBase);
eencae 0:49b8e971fa83 69 moveShoulderServo(thetaShoulder);
eencae 0:49b8e971fa83 70 moveElbowServo(thetaElbow);
eencae 0:49b8e971fa83 71 }
eencae 0:49b8e971fa83 72
eencae 0:49b8e971fa83 73 // calculate angle in DEGREES
eencae 0:49b8e971fa83 74 float MeArm::cosineRule(float adj1, float adj2, float opp)
eencae 0:49b8e971fa83 75 {
eencae 0:49b8e971fa83 76 // cosine rule
eencae 0:49b8e971fa83 77 float tmp = (adj1*adj1 + adj2*adj2 - opp*opp) / (2*adj1*adj2);
eencae 0:49b8e971fa83 78 //println("tmp = " + tmp);
eencae 0:49b8e971fa83 79
eencae 0:49b8e971fa83 80 // check for valid angle
eencae 0:49b8e971fa83 81 if ( fabs(tmp) <= 1.0 ) {
eencae 0:49b8e971fa83 82 return RAD2DEG*acos(tmp);
eencae 0:49b8e971fa83 83 } else {
eencae 0:49b8e971fa83 84 error(1);
eencae 0:49b8e971fa83 85 }
eencae 0:49b8e971fa83 86
eencae 0:49b8e971fa83 87 return -1.0; // will never get here due to infinite loop
eencae 0:49b8e971fa83 88 }
eencae 0:49b8e971fa83 89
eencae 0:49b8e971fa83 90 void MeArm::error(int code)
eencae 0:49b8e971fa83 91 {
eencae 0:49b8e971fa83 92 // flash error code in infinite loop
eencae 0:49b8e971fa83 93 while(1) {
eencae 0:49b8e971fa83 94 leds->write(code);
eencae 0:49b8e971fa83 95 wait_ms(100);
eencae 0:49b8e971fa83 96 leds->write(0);
eencae 0:49b8e971fa83 97 wait_ms(100);
eencae 0:49b8e971fa83 98 }
eencae 0:49b8e971fa83 99 }
eencae 0:49b8e971fa83 100
eencae 0:49b8e971fa83 101 void MeArm::solveInverseKinematics()
eencae 0:49b8e971fa83 102 {
eencae 0:49b8e971fa83 103 serial->printf("#################################\n");
eencae 0:49b8e971fa83 104 serial->printf("\nx = %f y = %f z = %f\n\n",target.x,target.y,target.z);
eencae 0:49b8e971fa83 105
eencae 0:49b8e971fa83 106 float maxReach = 220.0; // distance when arm outstretched (mm)
eencae 0:49b8e971fa83 107
eencae 0:49b8e971fa83 108 // rudimentary checks if target is within reach - won't cover all possibilities - need to catch more later
eencae 0:49b8e971fa83 109 if (target.z > 140.0 || target.z < 25.0 || target.y < 0 || target.y > maxReach || target.x < -maxReach || target.x > maxReach ) {
eencae 0:49b8e971fa83 110 error(2); // hang and flash error code
eencae 0:49b8e971fa83 111 }
eencae 0:49b8e971fa83 112
eencae 0:49b8e971fa83 113 // distance to target in x-y plane
eencae 0:49b8e971fa83 114 float rTarget = sqrt(target.x*target.x + target.y*target.y);
eencae 0:49b8e971fa83 115 serial->printf("Distance to target = %f mm\n",rTarget);
eencae 0:49b8e971fa83 116
eencae 0:49b8e971fa83 117 // if target out of reach
eencae 0:49b8e971fa83 118 if (rTarget > maxReach ) {
eencae 0:49b8e971fa83 119 error(3); // hang and flash error code
eencae 0:49b8e971fa83 120 }
eencae 0:49b8e971fa83 121
eencae 0:49b8e971fa83 122 // distance to wrist position in x-y plane
eencae 0:49b8e971fa83 123 float rWrist = rTarget - L3;
eencae 0:49b8e971fa83 124 serial->printf("Distance to wrist = %f mm\n",rWrist);
eencae 0:49b8e971fa83 125
eencae 0:49b8e971fa83 126 // shoulder offset from centre of rotation of base
eencae 0:49b8e971fa83 127 float rShoulder = L0;
eencae 0:49b8e971fa83 128 serial->printf("Distance to shoulder = %f mm\n",rShoulder);
eencae 0:49b8e971fa83 129
eencae 0:49b8e971fa83 130 // direct distance from shoulder to wrist
eencae 0:49b8e971fa83 131 float Dsw = sqrt( (rWrist - rShoulder)*(rWrist - rShoulder) + (target.z-BASE_HEIGHT)*(target.z-BASE_HEIGHT) );
eencae 0:49b8e971fa83 132 serial->printf("Direct distance from shoulder to wrist = %f mm\n",Dsw);
eencae 0:49b8e971fa83 133
eencae 0:49b8e971fa83 134 // angle from shoulder to wrist
eencae 0:49b8e971fa83 135 float thetaSw;
eencae 0:49b8e971fa83 136 // angle from shoulder to wrist - with check
eencae 0:49b8e971fa83 137 if (fabs((rWrist - rShoulder)/Dsw) <= 1.0) {
eencae 0:49b8e971fa83 138 thetaSw = RAD2DEG*acos((rWrist - rShoulder)/Dsw);
eencae 0:49b8e971fa83 139 } else {
eencae 0:49b8e971fa83 140 error(4);
eencae 0:49b8e971fa83 141 }
eencae 0:49b8e971fa83 142
eencae 0:49b8e971fa83 143 // if target is below base, then reflect the angle
eencae 0:49b8e971fa83 144 if (target.z < BASE_HEIGHT)
eencae 0:49b8e971fa83 145 thetaSw*=-1.0;
eencae 0:49b8e971fa83 146
eencae 0:49b8e971fa83 147 serial->printf("Angle from shoulder to wrist = %f degree\n" , thetaSw);
eencae 0:49b8e971fa83 148
eencae 0:49b8e971fa83 149 // elbow internal angle
eencae 0:49b8e971fa83 150 float beta = cosineRule(L1, L2, Dsw);
eencae 0:49b8e971fa83 151 serial->printf("Elbow internal angle = %f degrees\n",beta);
eencae 0:49b8e971fa83 152
eencae 0:49b8e971fa83 153 // shoulder angle from horizontal
eencae 0:49b8e971fa83 154 thetaShoulder = thetaSw + cosineRule(Dsw, L1, L2);
eencae 0:49b8e971fa83 155 serial->printf("Shoulder servo angle = %f degrees\n",thetaShoulder);
eencae 0:49b8e971fa83 156
eencae 0:49b8e971fa83 157 // convert to servo angle
eencae 0:49b8e971fa83 158 thetaElbow = 180.0 - thetaShoulder - beta;
eencae 0:49b8e971fa83 159 serial->printf("Elbow servo angle = %f\n",thetaElbow);
eencae 0:49b8e971fa83 160
eencae 0:49b8e971fa83 161 // calculate angle from top view
eencae 0:49b8e971fa83 162 thetaBase = RAD2DEG*atan2(target.y, target.x);
eencae 0:49b8e971fa83 163 serial->printf("Base servo angle = %f degrees\n",thetaBase);
eencae 0:49b8e971fa83 164 }