dsa
Dependencies: MatrixMath Matrix ExperimentServer QEI_pmw MotorShield
Diff: main.cpp
- Revision:
- 30:1dd3ef6acde6
- Parent:
- 29:8b4fd3d36882
--- a/main.cpp Tue Oct 06 01:02:31 2020 +0000 +++ b/main.cpp Wed Nov 18 20:44:40 2020 +0000 @@ -9,33 +9,32 @@ #include "Matrix.h" #include "MatrixMath.h" -#define BEZIER_ORDER_FOOT 7 -#define NUM_INPUTS (12 + 2*(BEZIER_ORDER_FOOT+1)) -#define NUM_OUTPUTS 19 +#define PULSE_TO_RAD (2.0f*3.14159f / 1200.0f) -#define PULSE_TO_RAD (2.0f*3.14159f / 1200.0f) +#define size_torqueTraj 20 +#define size_jointTraj 20 +#define size_inputs 14+9*size_torqueTraj +#define size_outputs 30 // Initializations Serial pc(USBTX, USBRX); // USB Serial Terminal ExperimentServer server; // Object that lets us communicate with MATLAB Timer t; // Timer to measure elapsed time of experiment +// Leg motors: QEI encoderA(PE_9,PE_11, NC, 1200, QEI::X4_ENCODING); // MOTOR A ENCODER (no index, 1200 counts/rev, Quadrature encoding) QEI encoderB(PA_5, PB_3, NC, 1200, QEI::X4_ENCODING); // MOTOR B ENCODER (no index, 1200 counts/rev, Quadrature encoding) + +// Arm motor: QEI encoderC(PC_6, PC_7, NC, 1200, QEI::X4_ENCODING); // MOTOR C ENCODER (no index, 1200 counts/rev, Quadrature encoding) + +// Extra QEI encoderD(PD_12, PD_13, NC, 1200, QEI::X4_ENCODING);// MOTOR D ENCODER (no index, 1200 counts/rev, Quadrature encoding) MotorShield motorShield(12000); //initialize the motor shield with a period of 12000 ticks or ~20kHZ Ticker currentLoop; -Matrix MassMatrix(2,2); -Matrix Jacobian(2,2); -Matrix JacobianT(2,2); -Matrix InverseMassMatrix(2,2); -Matrix temp_product(2,2); -Matrix Lambda(2,2); - -// Variables for q1 +// Variables for q1 (hip) float current1; float current_des1 = 0; float prev_current_des1 = 0; @@ -45,7 +44,7 @@ float duty_cycle1; float angle1_init; -// Variables for q2 +// Variables for q2 (knee) float current2; float current_des2 = 0; float prev_current_des2 = 0; @@ -55,6 +54,16 @@ float duty_cycle2; float angle2_init; +// Variables for q3 (arm) +float current3; +float current_des3 = 0; +float prev_current_des3 = 0; +float current_int3 = 0; +float angle3; +float velocity3; +float duty_cycle3; +float angle3_init; + // Fixed kinematic parameters const float l_OA=.011; const float l_OB=.042; @@ -75,22 +84,31 @@ const float N = 18.75; const float Ir = 0.0035/pow(N,2); +// Design parameters +const float m_body = 0.186+0.211; +const float l_body = 0.04; +const float l_arm = 0.10; +const float l_cm_arm = 0.8*l_arm; +const float m_arm = 0.1; +const float I_arm = 0.00064; // calculated with I = ml^2, not from CAD + // Timing parameters float current_control_period_us = 200.0f; // 5kHz current control loop -float impedance_control_period_us = 1000.0f; // 1kHz impedance control loop +float impedance_control_period_us = 10000.0f; // 100 Hz impedance control loop float start_period, traj_period, end_period; // Control parameters float current_Kp = 4.0f; float current_Ki = 0.4f; -float current_int_max = 3.0f; +float current_int_max = 3.0f; +float K_q1; +float K_q2; +float K_q3; +float D_qd1; +float D_qd2; +float D_qd3; +int control_method = 0; // defaults to using just ff torque float duty_max; -float K_xx; -float K_yy; -float K_xy; -float D_xx; -float D_xy; -float D_yy; // Model parameters float supply_voltage = 12; // motor supply voltage @@ -102,10 +120,11 @@ void CurrentLoop() { // This loop sets the motor voltage commands using PI current controllers with feedforward terms. - //use the motor shield as follows: //motorShield.motorAWrite(DUTY CYCLE, DIRECTION), DIRECTION = 0 is forward, DIRECTION =1 is backwards. - + + //*************************************************************************** + //HIP MOTOR current1 = -(((float(motorShield.readCurrentA())/65536.0f)*30.0f)-15.0f); // measure current velocity1 = encoderA.getVelocity() * PULSE_TO_RAD; // measure velocity float err_c1 = current_des1 - current1; // current errror @@ -126,6 +145,8 @@ } prev_current_des1 = current_des1; + //**************************************************************************** + //KNEE MOTOR current2 = -(((float(motorShield.readCurrentB())/65536.0f)*30.0f)-15.0f); // measure current velocity2 = encoderB.getVelocity() * PULSE_TO_RAD; // measure velocity float err_c2 = current_des2 - current2; // current error @@ -146,50 +167,102 @@ } prev_current_des2 = current_des2; + //*************************************************************************** + //ARM MOTOR + current3 = -(((float(motorShield.readCurrentC())/65536.0f)*30.0f)-15.0f); // measure current + velocity3 = encoderC.getVelocity() * PULSE_TO_RAD; // measure velocity + float err_c3 = current_des3 - current3; // current error + current_int3 += err_c3; // integrate error + current_int3 = fmaxf( fminf(current_int3, current_int_max), -current_int_max); // anti-windup + float ff3 = R*current_des3 + k_t*velocity3; // feedforward terms + duty_cycle3 = (ff3 + current_Kp*err_c3 + current_Ki*current_int3)/supply_voltage; // PI current controller + + float absDuty3 = abs(duty_cycle3); + if (absDuty3 > duty_max) { + duty_cycle3 *= duty_max / absDuty3; + absDuty3 = duty_max; + } + if (duty_cycle3 < 0) { // backwards + motorShield.motorCWrite(absDuty3, 1); + } else { // forwards + motorShield.motorCWrite(absDuty3, 0); + } + prev_current_des3 = current_des3; } int main (void) { + // Object for torque profile + //BezierCurve tauDes_bez(3,size_torqueTraj-1); - // Object for 7th order Cartesian foot trajectory - BezierCurve rDesFoot_bez(2,BEZIER_ORDER_FOOT); + // Object for joint position profile + //BezierCurve qDes_bez(3,size_jointTraj-1); + + // Object for joint velocity profile + //BezierCurve qdDes_bez(3,size_jointTraj-1); // Link the terminal with our server and start it up server.attachTerminal(pc); server.init(); // Continually get input from MATLAB and run experiments - float input_params[NUM_INPUTS]; + float input_params[size_inputs]; pc.printf("%f",input_params[0]); while(1) { - // If there are new inputs, this code will run - if (server.getParams(input_params,NUM_INPUTS)) { - - + if (server.getParams(input_params,size_inputs)) { + // Get inputs from MATLAB start_period = input_params[0]; // First buffer time, before trajectory - traj_period = input_params[1]; // Trajectory time/length - end_period = input_params[2]; // Second buffer time, after trajectory - - angle1_init = input_params[3]; // Initial angle for q1 (rad) - angle2_init = input_params[4]; // Initial angle for q2 (rad) - - K_xx = input_params[5]; // Foot stiffness N/m - K_yy = input_params[6]; // Foot stiffness N/m - K_xy = input_params[7]; // Foot stiffness N/m - D_xx = input_params[8]; // Foot damping N/(m/s) - D_yy = input_params[9]; // Foot damping N/(m/s) - D_xy = input_params[10]; // Foot damping N/(m/s) - duty_max = input_params[11]; // Maximum duty factor - - // Get foot trajectory points - float foot_pts[2*(BEZIER_ORDER_FOOT+1)]; - for(int i = 0; i<2*(BEZIER_ORDER_FOOT+1);i++) { - foot_pts[i] = input_params[12+i]; + end_period = input_params[1]; // Second buffer time, after trajectory + traj_period = input_params[2]; // Total trajectory time + + angle1_init = input_params[3]; // Initial angle for q1 (hip, rad) + angle2_init = input_params[4]; // Initial angle for q2 (knee, rad) + angle3_init = input_params[5]; // Initial angle for q3 (arm, rad) + + K_q1 = input_params[6]; // Joint space stiffness for hip (N/rad) + K_q2 = input_params[7]; // Joint space stiffness for knee (N/rad) + K_q3 = input_params[8]; // Joint space stiffness for arm (N/rad) + D_qd1 = input_params[9]; // Joint space damping for arm (Ns/rad) + D_qd2 = input_params[10]; // Joint space damping for knee (Ns/rad) + D_qd3 = input_params[11]; // Joint space damping for hip (Ns/rad) + + control_method = int(input_params[12]); // Controller choices: feedfwd torque = 0, PD control = 1, both = 2 + + duty_max = input_params[13]; // Maximum duty factor + + //************************************************************** + // LOADING OPTIMIZED PROFILES AND GENERATING BEZIER TRAJECTORIES + + int last_index = 14; // index to track where profiles begin and end + + // Load torque profile: + float torque_profile[3*(size_torqueTraj)]; + for(int i = 0; i < 3*(size_torqueTraj); i++) { + torque_profile[i] = input_params[last_index+i]; } - rDesFoot_bez.setPoints(foot_pts); + //tauDes_bez.setPoints(torque_profile); + last_index = last_index + 3*(size_torqueTraj); + + + // Load joint angle profile: + float q_profile[3*(size_jointTraj)]; + for(int i = 0; i < 3*(size_jointTraj); i++) { + q_profile[i] = input_params[last_index+i]; + } + // qDes_bez.setPoints(q_profile); + last_index = last_index + 3*(size_jointTraj); + + + // Load joint velocity profile: + float qd_profile[3*(size_jointTraj)]; + for(int i = 0; i < 3*(size_jointTraj); i++) { + qd_profile[i] = input_params[last_index+i]; + } + //qdDes_bez.setPoints(qd_profile); + // Attach current loop interrupt currentLoop.attach_us(CurrentLoop,current_control_period_us); @@ -204,8 +277,12 @@ motorShield.motorAWrite(0, 0); //turn motor A off motorShield.motorBWrite(0, 0); //turn motor B off - + motorShield.motorCWrite(0, 0); //turn motor C off + // Run experiment + + int iter = 0; + while( t.read() < start_period + traj_period + end_period) { // Read encoders to get motor states @@ -213,47 +290,37 @@ velocity1 = encoderA.getVelocity() * PULSE_TO_RAD; angle2 = encoderB.getPulses() * PULSE_TO_RAD + angle2_init; - velocity2 = encoderB.getVelocity() * PULSE_TO_RAD; + velocity2 = encoderB.getVelocity() * PULSE_TO_RAD; + + angle3 = encoderC.getPulses() * PULSE_TO_RAD + angle3_init; + velocity3 = encoderC.getVelocity() * PULSE_TO_RAD; const float th1 = angle1; const float th2 = angle2; + const float th3 = angle3; const float dth1= velocity1; const float dth2= velocity2; - - // Calculate the Jacobian - float Jx_th1 = 0; - float Jx_th2 = 0; - float Jy_th1 = 0; - float Jy_th2 = 0; - - // Calculate the forward kinematics (position and velocity) - float xFoot = 0; - float yFoot = 0; - float dxFoot = 0; - float dyFoot = 0; + const float dth3 = velocity3; // Set gains based on buffer and traj times, then calculate desired x,y from Bezier trajectory at current time if necessary float teff = 0; float vMult = 0; if( t < start_period) { - if (K_xx > 0 || K_yy > 0) { - K_xx = 100; - K_yy = 100; - D_xx = 5; - D_yy = 5; - K_xy = 0; - D_xy = 0; - } - teff = 0; + K_q1 = 0.5; + K_q2 = 0.5; + K_q3 = 0.5; + D_qd1 = 0.01; + D_qd2 = 0.01; + D_qd3 = 0.01; } else if (t < start_period + traj_period) { - K_xx = input_params[5]; // Foot stiffness N/m - K_yy = input_params[6]; // Foot stiffness N/m - K_xy = input_params[7]; // Foot stiffness N/m - D_xx = input_params[8]; // Foot damping N/(m/s) - D_yy = input_params[9]; // Foot damping N/(m/s) - D_xy = input_params[10]; // Foot damping N/(m/s) + K_q1 = input_params[7]; // Hip stiffness N/rad + K_q2 = input_params[8]; // Knee stiffness N/rad + K_q3 = input_params[9]; // Arm stiffness N/rad + D_qd1 = input_params[9]; // Hip damping N/(rad/s) + D_qd2 = input_params[10]; // Knee damping N/(rad/s) + D_qd3 = input_params[11]; // Arm damping N/(rad/s) teff = (t-start_period); vMult = 1; } @@ -263,78 +330,118 @@ vMult = 0; } - // Get desired foot positions and velocities - float rDesFoot[2] , vDesFoot[2]; - rDesFoot_bez.evaluate(teff/traj_period,rDesFoot); - rDesFoot_bez.evaluateDerivative(teff/traj_period,vDesFoot); - vDesFoot[0]/=traj_period; - vDesFoot[1]/=traj_period; - vDesFoot[0]*=vMult; - vDesFoot[1]*=vMult; + // desired values + float tau_des[3], q_des[3], qd_des[3]; + for (int i = 0; i < 3; i++){ + if (t < start_period){ + tau_des[i] = 0; + q_des[i] = q_profile[i]; + qd_des[i] = 0; + } else if (t < start_period + traj_period){ + tau_des[i] = torque_profile[3*iter+i]; + q_des[i] = q_profile[3*iter+i]; + qd_des[i] = qd_profile[3*iter+i];} + else{ + tau_des[i] = 0; + q_des[i] = 0; + qd_des[i] = 0; + } + } - // Calculate the inverse kinematics (joint positions and velocities) for desired joint angles - float xFoot_inv = -rDesFoot[0]; - float yFoot_inv = rDesFoot[1]; - float l_OE = sqrt( (pow(xFoot_inv,2) + pow(yFoot_inv,2)) ); - float alpha = abs(acos( (pow(l_OE,2) - pow(l_AC,2) - pow((l_OB+l_DE),2))/(-2.0f*l_AC*(l_OB+l_DE)) )); - float th2_des = -(3.14159f - alpha); - float th1_des = -((3.14159f/2.0f) + atan2(yFoot_inv,xFoot_inv) - abs(asin( (l_AC/l_OE)*sin(alpha) ))); + + //tauDes_bez.evaluate(teff/traj_period,tau_des); + //qDes_bez.evaluate(teff/traj_period,q_des); + //qdDes_bez.evaluateDerivative(teff/traj_period,qd_des); // get qdDes from derivative of Bezier of qDes + //qdDes_bez.evaluate(teff/traj_period,qd_des); // alternatively, get qdDes directly from optimized profile. Potential error? - float dd = (Jx_th1*Jy_th2 - Jx_th2*Jy_th1); - float dth1_des = (1.0f/dd) * ( Jy_th2*vDesFoot[0] - Jx_th2*vDesFoot[1] ); - float dth2_des = (1.0f/dd) * ( -Jy_th1*vDesFoot[0] + Jx_th1*vDesFoot[1] ); - - // Calculate error variables - float e_x = 0; - float e_y = 0; - float de_x = 0; - float de_y = 0; - - // Calculate virtual force on foot - float fx = 0; - float fy = 0; + // From old code -> not sure why velocities need to be scaled wrt traj. time. Don't think it's needed. + // qd_des[0]/=traj_period; + // qd_des[1]/=traj_period; + // qd_des[2]/=traj_period; + + qd_des[0]*=vMult; // ensures zero velocity when moving to starting configuration + qd_des[1]*=vMult; + qd_des[2]*=vMult; + - // Calculate mass matrix elements - float M11 = 0; - float M12 = 0; - float M22 = 0; + // Calculate the forward kinematics (position and velocity) + float xFoot = sin(th1)*(l_DE - l_OA + l_OB) + l_AC*sin(th1 + th2) + l_OA*sin(th1); + float yFoot = - cos(th1)*(l_DE - l_OA + l_OB) - l_AC*cos(th1 + th2) - l_OA*cos(th1); + float xArm = l_arm*sin(th3); // assuming th3 defined relative to line coincident with body pointing down + float yArm = l_body - cos(th3); + float dxFoot = dth1*(cos(th1)*(l_DE - l_OA + l_OB) + l_AC*cos(th1 + th2) + l_OA*cos(th1)) + dth2*l_AC*cos(th1 + th2); + float dyFoot = dth1*(sin(th1)*(l_DE - l_OA + l_OB) + l_AC*sin(th1 + th2) + l_OA*sin(th1)) + dth2*l_AC*sin(th1 + th2); + float dxArm = dth3*l_arm*cos(th3); + float dyArm = dth3*sin(th3); + + // Calculate the desired forward kinematics + float xFootDes = sin(q_des[0])*(l_DE - l_OA + l_OB) + l_AC*sin(q_des[0] + q_des[1]) + l_OA*sin(q_des[0]); + float yFootDes = - cos(q_des[0])*(l_DE - l_OA + l_OB) - l_AC*cos(q_des[0] + q_des[1]) - l_OA*cos(q_des[0]); + float xArmDes = l_arm*sin(q_des[2]); // assuming th3 defined relative to line coincident with body pointing down + float yArmDes = l_body - cos(q_des[2]); + float dxFootDes = qd_des[0]*(cos(q_des[0])*(l_DE - l_OA + l_OB) + l_AC*cos(q_des[0] + q_des[1]) + l_OA*cos(q_des[0])) + qd_des[1]*l_AC*cos(q_des[0] + q_des[1]); + float dyFootDes = qd_des[0]*(sin(q_des[0])*(l_DE - l_OA + l_OB) + l_AC*sin(q_des[0] + q_des[1]) + l_OA*sin(q_des[0])) + qd_des[1]*l_AC*sin(q_des[0] + q_des[1]); + float dxArmDes = qd_des[2]*l_arm*cos(q_des[2]); + float dyArmDes = qd_des[2]*sin(q_des[2]); - - // Populate mass matrix - MassMatrix.Clear(); - MassMatrix << M11 << M12 - << M12 << M22; + // Calculate error variables + float e_th1 = q_des[0] - th1; + float e_th2 = q_des[1] - th2; + float e_th3 = q_des[2] - th3; + float de_th1 = qd_des[0] - dth1; + float de_th2 = qd_des[1] - dth2; + float de_th3 = qd_des[2] - dth3; + + // Set desired currents + float current_ff[3], current_PD[3]; + for(int i = 0; i < 3; i++){ // set feedforward currents + current_ff[i] = tau_des[i]/k_t; + } + + current_PD[0] = (K_q1*(q_des[0] - th1)+D_qd1*(qd_des[0]-dth1))/k_t; // set PD currents + current_PD[1] = (K_q2*(q_des[1] - th2)+D_qd2*(qd_des[1]-dth2))/k_t; + current_PD[2] = (K_q3*(q_des[2] - th3)+D_qd3*(qd_des[2]-dth3))/k_t; - // Populate Jacobian matrix - Jacobian.Clear(); - Jacobian << Jx_th1 << Jx_th2 - << Jy_th1 << Jy_th2; + if( t < start_period) { + control_method = 1;} + else{ + control_method = int(input_params[12]); + } - // Once you have copied the elements of the mass matrix, uncomment the following section - // Calculate Lambda matrix -// JacobianT = MatrixMath::Transpose(Jacobian); -// InverseMassMatrix = MatrixMath::Inv(MassMatrix); -// temp_product = Jacobian*InverseMassMatrix*JacobianT; -// Lambda = MatrixMath::Inv(temp_product); - - // Pull elements of Lambda matrix -// float L11 = Lambda.getNumber(1,1); -// float L12 = Lambda.getNumber(1,2); -// float L21 = Lambda.getNumber(2,1); -// float L22 = Lambda.getNumber(2,2); - - - - // Set desired currents - current_des1 = 0; - current_des2 = 0; - - + // ************** + // CONTROL CHOICE (there may be issues with setting current_des inside a switch statement with the current loop interrupt, make sure to check) + switch(control_method){ + case 0: // feedforward torque only + pc.printf("TIME: %f \n FFWD CURRENT \n",t.read()); + current_des1 = current_ff[0]; + current_des2 = current_ff[1]; + current_des3 = current_ff[2]; + break; + + case 1: // Joint PD control only + pc.printf("TIME: %f \n PD CURRENT \n",t.read()); + current_des1 = current_PD[0]; + current_des2 = current_PD[1]; + current_des3 = current_PD[2]; + break; + + case 2: // both combined + pc.printf("TIME: %f \n BOTH CURRENT \n",t.read()); + current_des1 = current_ff[0] + current_PD[0]; + current_des2 = current_ff[1] + current_PD[1]; + current_des3 = current_ff[2] + current_PD[2]; + break; + + default: + pc.printf("Invalid control method selector.\n"); + exit(-100); + break; + } // Form output to send to MATLAB - float output_data[NUM_OUTPUTS]; + float output_data[size_outputs]; // current time output_data[0] = t.read(); // motor 1 state @@ -349,20 +456,37 @@ output_data[8] = current2; output_data[9] = current_des2; output_data[10]= duty_cycle2; - // foot state - output_data[11] = xFoot; - output_data[12] = yFoot; - output_data[13] = dxFoot; - output_data[14] = dyFoot; - output_data[15] = rDesFoot[0]; - output_data[16] = rDesFoot[1]; - output_data[17] = vDesFoot[0]; - output_data[18] = vDesFoot[1]; + // motor 3 state + output_data[11] = angle3; + output_data[12] = velocity3; + output_data[13] = current3; + output_data[14] = current_des3; + output_data[15]= duty_cycle3; + // foot and arm state + output_data[16] = xFoot; + output_data[17] = yFoot; + output_data[18] = dxFoot; + output_data[19] = dyFoot; + output_data[20] = xArm; + output_data[21] = yArm; + output_data[22] = dxArm; + output_data[23] = dyArm; + + output_data[24] = q_des[0]; + output_data[25] = q_des[1]; + output_data[26] = q_des[2]; + output_data[27] = qd_des[0]; + output_data[28] = qd_des[1]; + output_data[29] = qd_des[2]; + + // can add calculations for more outputs as needed, currently not outputting desired position of arm and foot // Send data to MATLAB - server.sendData(output_data,NUM_OUTPUTS); + server.sendData(output_data,size_outputs); wait_us(impedance_control_period_us); + if((t > start_period)&&(t<start_period+traj_period)){ + iter++;} } // Cleanup after experiment @@ -370,6 +494,7 @@ currentLoop.detach(); motorShield.motorAWrite(0, 0); //turn motor A off motorShield.motorBWrite(0, 0); //turn motor B off + motorShield.motorCWrite(0, 0); //turn motor B off } // end if