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: MatrixMath Matrix ExperimentServer QEI_pmw MotorShield
Revision 30:1dd3ef6acde6, committed 2020-11-18
- Comitter:
- sehwan
- Date:
- Wed Nov 18 20:44:40 2020 +0000
- Parent:
- 29:8b4fd3d36882
- Commit message:
- adfs
Changed in this revision
| main.cpp | Show annotated file Show diff for this revision Revisions of this file |
--- 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