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
MeArm.cpp
- Committer:
- eencae
- Date:
- 2014-11-04
- Revision:
- 0:49b8e971fa83
- Child:
- 1:d2b7780ca6b3
File content as of revision 0:49b8e971fa83:
/* @file MeArm.cpp @brief Member functions implementations */ #include "mbed.h" #include "MeArm.h" MeArm::MeArm(PinName basePin, PinName shoulderPin, PinName elbowPin, PinName gripperPin) { // set up pins as required base = new PwmOut(basePin); shoulder = new PwmOut(shoulderPin); elbow = new PwmOut(elbowPin); gripper = new PwmOut(gripperPin); // PWM objects have 50 Hz frequency by default so shouldn't need to set servo frequency // can do it anyway to be sure. All channels share same period so just need to set one of them base->period(1.0/50.0); // servos are typically 50 Hz // setup USB serial for debug/comms serial = new Serial(USBTX,USBRX); // leds used for debug leds = new BusOut(LED4,LED3,LED2,LED1); } void MeArm::setTarget(float x,float y,float z) { target.x = x; target.y = y; target.z = z; // TODO - add checks for valid target } /// TODO - add checks for valid angles void MeArm::moveBaseServo(float angle) { // arm facing forward is defined as 90 degrees, angle increases counter-clockwise int pulseWidthMicroSeconds = 575.0 + 985.0*angle/90.0; serial->printf("Base Pulse = %d us\n",pulseWidthMicroSeconds); // equation to convert angle to pulsewidth in us base->pulsewidth_us(pulseWidthMicroSeconds); // move servo } void MeArm::moveShoulderServo(float angle) { // angle relative to horizontal int pulseWidthMicroSeconds = 2705.0 - 1097.0*angle/90.0; serial->printf("Shoulder Pulse = %d us\n",pulseWidthMicroSeconds); // equation to convert angle to pulsewidth in us shoulder->pulsewidth_us(pulseWidthMicroSeconds); // move servo } void MeArm::moveElbowServo(float angle) { // angle relative to horizontal int pulseWidthMicroSeconds = 1910.0 - 1095.0*angle/90.0; serial->printf("Elbow Pulse = %d us\n",pulseWidthMicroSeconds); // equation to convert angle to pulsewidth in us elbow->pulsewidth_us(pulseWidthMicroSeconds); // move servo } void MeArm::moveServos() { moveBaseServo(thetaBase); moveShoulderServo(thetaShoulder); moveElbowServo(thetaElbow); } // calculate angle in DEGREES float MeArm::cosineRule(float adj1, float adj2, float opp) { // cosine rule float tmp = (adj1*adj1 + adj2*adj2 - opp*opp) / (2*adj1*adj2); //println("tmp = " + tmp); // check for valid angle if ( fabs(tmp) <= 1.0 ) { return RAD2DEG*acos(tmp); } else { error(1); } return -1.0; // will never get here due to infinite loop } void MeArm::error(int code) { // flash error code in infinite loop while(1) { leds->write(code); wait_ms(100); leds->write(0); wait_ms(100); } } void MeArm::solveInverseKinematics() { serial->printf("#################################\n"); serial->printf("\nx = %f y = %f z = %f\n\n",target.x,target.y,target.z); float maxReach = 220.0; // distance when arm outstretched (mm) // rudimentary checks if target is within reach - won't cover all possibilities - need to catch more later if (target.z > 140.0 || target.z < 25.0 || target.y < 0 || target.y > maxReach || target.x < -maxReach || target.x > maxReach ) { error(2); // hang and flash error code } // distance to target in x-y plane float rTarget = sqrt(target.x*target.x + target.y*target.y); serial->printf("Distance to target = %f mm\n",rTarget); // if target out of reach if (rTarget > maxReach ) { error(3); // hang and flash error code } // distance to wrist position in x-y plane float rWrist = rTarget - L3; serial->printf("Distance to wrist = %f mm\n",rWrist); // shoulder offset from centre of rotation of base float rShoulder = L0; serial->printf("Distance to shoulder = %f mm\n",rShoulder); // direct distance from shoulder to wrist float Dsw = sqrt( (rWrist - rShoulder)*(rWrist - rShoulder) + (target.z-BASE_HEIGHT)*(target.z-BASE_HEIGHT) ); serial->printf("Direct distance from shoulder to wrist = %f mm\n",Dsw); // angle from shoulder to wrist float thetaSw; // angle from shoulder to wrist - with check if (fabs((rWrist - rShoulder)/Dsw) <= 1.0) { thetaSw = RAD2DEG*acos((rWrist - rShoulder)/Dsw); } else { error(4); } // if target is below base, then reflect the angle if (target.z < BASE_HEIGHT) thetaSw*=-1.0; serial->printf("Angle from shoulder to wrist = %f degree\n" , thetaSw); // elbow internal angle float beta = cosineRule(L1, L2, Dsw); serial->printf("Elbow internal angle = %f degrees\n",beta); // shoulder angle from horizontal thetaShoulder = thetaSw + cosineRule(Dsw, L1, L2); serial->printf("Shoulder servo angle = %f degrees\n",thetaShoulder); // convert to servo angle thetaElbow = 180.0 - thetaShoulder - beta; serial->printf("Elbow servo angle = %f\n",thetaElbow); // calculate angle from top view thetaBase = RAD2DEG*atan2(target.y, target.x); serial->printf("Base servo angle = %f degrees\n",thetaBase); }