Cobots-Arduino/baseline_dev/baseline_dev.ino
2022-05-02 10:45:08 -04:00

557 lines
18 KiB
C++

#include <Encoder.h>
#include <avr/pgmspace.h>
#include <AccelStepper.h>
// Firmware version
const char* VERSION = "0.0.1";
const int NUM_JOINTS = 6;
/*
MOTOR DIRECTION - motor directions can be changed on the caibration page in the software but can also
be changed here: example using DM542T driver(CW) set to 1 - if using ST6600 or DM320T driver(CCW) set to 0
DEFAULT = 111011 */
const int J1rotdir = 1;
const int J2rotdir = 1;
const int J3rotdir = 1;
const int J4rotdir = 0;
const int J5rotdir = 1;
const int J6rotdir = 1;
const int ROT_DIRS[] = { J1rotdir, J2rotdir, J3rotdir, J4rotdir, J5rotdir, J6rotdir };
// approx encoder counts at rest position, 0 degree joint angle
// DEFAULTS 38681, 11264, 0, 36652, 5839, 15671
// J2 and J6 joint enc { 38681, 902, 0, 36652, 2198, 15671 }
const int REST_ENC_POSITIONS[] = { 38681, 11264, 0, 36652, 5839, 15671 };
const int J1stepPin = 0;
const int J1dirPin = 1;
const int J2stepPin = 2;
const int J2dirPin = 3;
const int J3stepPin = 4;
const int J3dirPin = 5;
const int J4stepPin = 6;
const int J4dirPin = 7;
const int J5stepPin = 8;
const int J5dirPin = 9;
const int J6stepPin = 10;
const int J6dirPin = 11;
const int TRstepPin = 12;
const int TRdirPin = 13;
const int STEP_PINS[] = { J1stepPin, J2stepPin, J3stepPin, J4stepPin, J5stepPin, J6stepPin };
const int DIR_PINS[] = { J1dirPin, J2dirPin, J3dirPin, J4dirPin, J5dirPin, J6dirPin };
// set encoder pins
Encoder J1encPos(14, 15);
Encoder J2encPos(16, 17);
Encoder J3encPos(18, 19);
Encoder J4encPos(33, 34);
Encoder J5encPos(35, 36);
Encoder J6encPos(37, 38);
Encoder JOINT_ENCODERS[] = { J1encPos, J2encPos, J3encPos, J4encPos, J5encPos, J6encPos };
// DEFAULTS { 1, 1, 1, 1, 1, 1 }
// J2 joint enc { 1, -1, 1, 1, 1, 1 }
int ENC_DIR[] = { 1, 1, 1, 1, 1, 1 }; // +1 if encoder direction matches motor direction
// set calibration limit switch pins
const int J1calPin = 27;
const int J2calPin = 28;
const int J3calPin = 29;
const int J4calPin = 30;
const int J5calPin = 31;
const int J6calPin = 32;
const int CAL_PINS[] = { J1calPin, J2calPin, J3calPin, J4calPin, J5calPin, J6calPin };
const int LIMIT_SWITCH_HIGH[] = { 1, 1, 1, 1, 1, 1 }; // to account for both NC and NO limit switches
const int CAL_DIR[] = { -1, -1, 1, -1, -1, 1 }; // joint rotation direction to limit switch
const int CAL_SPEED = 600; // motor steps per second
// DEFAULT { 1, 1, 1, 2, 1, 1 }
// J2 joint enc { 1, 10, 1, 2, 1, 1 }
const int CAL_SPEED_MULT[] = { 1, 1, 1, 2, 1, 1 }; // multiplier to account for transmission ratios
// motor and encoder steps per revolution
// DEFAULTS { 400, 400, 400, 400, 800, 400 }
// J2 motor 4000 steps per rev { 400, 4000, 400, 400, 800, 400 }
// const int MOTOR_STEPS_PER_REV[] = { 200, 200, 200, 200, 200, 200 };
const int MOTOR_STEPS_PER_REV[] = { 400, 400, 400, 400, 800, 400 };
// num of steps in range of motion of joint, may vary depending on your limit switch
// DEFAULT { 77363, 36854, 40878, 71967, 23347, 32358 }
// J2 joint enc { 77363, 2949, 40878, 71967, 4740, 32358 }
const int ENC_RANGE_STEPS[] = { 77363, 36854, 40878, 71967, 23347, 32358 };
//set encoder multiplier, encoder steps per motor step
// DEFAULT { 5.12, 5.12, 5.12, 5.12, 2.56, 5.12 }
// J2 and J5 joint enc { 5.12, 0.04096, 5.12, 5.12, 0.5197, 5.12 }
const float ENC_MULT[] = { 5.12, 5.12, 5.12, 5.12, 5.12, 5.12 };
// const float ENC_MULT[] = { 1.0, 1.0, 1.0, 1.0, 1.0, 1.0 };
// DEFAULT { 227.5555555555556, 284.4444444444444, 284.4444444444444, 223.0044444444444, 56.04224675948152, 108.0888888888889 }
// J2 and J5 joint enc { 227.5555555555556, 22.75555555555556, 284.4444444444444, 223.0044444444444, 22.75555555555556, 108.0888888888889 }
const float ENC_STEPS_PER_DEG[] = { 227.5555555555556, 284.4444444444444, 284.4444444444444, 223.0044444444444, 56.04224675948152, 108.0888888888889 };
// speed and acceleration settings
float JOINT_MAX_SPEED[] = { 2.0, 2.0, 2.0, 2.0, 2.0, 2.0 }; // deg/s
float JOINT_MAX_ACCEL[] = { 5.0, 5.0, 5.0, 5.0, 5.0, 5.0 }; // deg/s^2
// DEFAULT { 1500, 1500, 1500, 2000, 1500, 1500 }
// J2 4000 steps/rev { 1500, 15000, 1500, 2000, 1500, 1500 }
int MOTOR_MAX_SPEED[] = { 5500, 1500, 1500, 1500, 1500, 1500 }; // motor steps per sec
// DEFAULT { 250, 250, 250, 250, 250, 250 }
// J2 4000 steps/rev { 250, 2500, 250, 250, 250, 250 }
int MOTOR_MAX_ACCEL[] = { 25000, 25000, 25000, 25000, 25000, 25000 }; // motor steps per sec^2
// played around with this a little but I don't see a need to use this at the moment
float MOTOR_SPEED_MULT[] = { 1.0, 1.0, 1.0, 1.0, 1.0, 1.0 }; // for tuning position control
float MOTOR_ACCEL_MULT[] = { 1.0, 1.0, 1.0, 1.0, 1.0, 1.0 }; // for tuning position control
AccelStepper stepperJoints[NUM_JOINTS];
enum SM { STATE_INIT, STATE_TRAJ, STATE_ERR };
SM STATE = STATE_INIT;
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//MAIN
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void setup() {
// run once:
Serial.begin(115200);
for (int i = 0; i < NUM_JOINTS; ++i)
{
JOINT_ENCODERS[i].write(REST_ENC_POSITIONS[i]);
}
pinMode(TRstepPin, OUTPUT);
pinMode(TRdirPin, OUTPUT);
pinMode(J1stepPin, OUTPUT);
pinMode(J1dirPin, OUTPUT);
pinMode(J2stepPin, OUTPUT);
pinMode(J2dirPin, OUTPUT);
pinMode(J3stepPin, OUTPUT);
pinMode(J3dirPin, OUTPUT);
pinMode(J4stepPin, OUTPUT);
pinMode(J4dirPin, OUTPUT);
pinMode(J5stepPin, OUTPUT);
pinMode(J5dirPin, OUTPUT);
pinMode(J6stepPin, OUTPUT);
pinMode(J6dirPin, OUTPUT);
pinMode(J1calPin, INPUT_PULLUP);
pinMode(J2calPin, INPUT_PULLUP);
pinMode(J3calPin, INPUT_PULLUP);
pinMode(J4calPin, INPUT_PULLUP);
pinMode(J5calPin, INPUT_PULLUP);
pinMode(J6calPin, INPUT_PULLUP);
digitalWrite(TRstepPin, HIGH);
digitalWrite(J1stepPin, HIGH);
digitalWrite(J2stepPin, HIGH);
digitalWrite(J3stepPin, HIGH);
digitalWrite(J4stepPin, HIGH);
digitalWrite(J5stepPin, HIGH);
digitalWrite(J6stepPin, HIGH);
}
bool initStateTraj(String inData)
{
// parse initialisation message
int idxVersion = inData.indexOf('A');
String softwareVersion = inData.substring(idxVersion + 1, inData.length() - 1);
int versionMatches = (softwareVersion == VERSION);
// return acknowledgement with result
String msg = String("ST") + String("A") + String(versionMatches) + String("B") + String(VERSION) + String("\n");
Serial.print(msg);
return versionMatches ? true : false;
}
void readEncPos(int* encPos)
{
/*
encPos[0] = J1encPos.read() * ENC_DIR[0];
encPos[1] = J2encPos.read() * ENC_DIR[1];
encPos[2] = J3encPos.read() * ENC_DIR[2];
encPos[3] = J4encPos.read() * ENC_DIR[3];
encPos[4] = J5encPos.read() * ENC_DIR[4];
encPos[5] = J6encPos.read() * ENC_DIR[5];
*/
encPos[0] = stepperJoints[0].currentPosition();
encPos[1] = stepperJoints[1].currentPosition();
encPos[2] = stepperJoints[2].currentPosition();
encPos[3] = stepperJoints[3].currentPosition();
encPos[4] = stepperJoints[4].currentPosition();
encPos[5] = stepperJoints[5].currentPosition();
}
void updateStepperSpeed(String inData)
{
int idxSpeedJ1 = inData.indexOf('A');
int idxAccelJ1 = inData.indexOf('B');
int idxSpeedJ2 = inData.indexOf('C');
int idxAccelJ2 = inData.indexOf('D');
int idxSpeedJ3 = inData.indexOf('E');
int idxAccelJ3 = inData.indexOf('F');
int idxSpeedJ4 = inData.indexOf('G');
int idxAccelJ4 = inData.indexOf('H');
int idxSpeedJ5 = inData.indexOf('I');
int idxAccelJ5 = inData.indexOf('J');
int idxSpeedJ6 = inData.indexOf('K');
int idxAccelJ6 = inData.indexOf('L');
JOINT_MAX_SPEED[0] = inData.substring(idxSpeedJ1 + 1, idxAccelJ1).toFloat();
JOINT_MAX_ACCEL[0] = inData.substring(idxAccelJ1 + 1, idxSpeedJ2).toFloat();
JOINT_MAX_SPEED[1] = inData.substring(idxSpeedJ2 + 1, idxAccelJ2).toFloat();
JOINT_MAX_ACCEL[1] = inData.substring(idxAccelJ2 + 1, idxSpeedJ3).toFloat();
JOINT_MAX_SPEED[2] = inData.substring(idxSpeedJ3 + 1, idxAccelJ3).toFloat();
JOINT_MAX_ACCEL[2] = inData.substring(idxAccelJ3 + 1, idxSpeedJ4).toFloat();
JOINT_MAX_SPEED[3] = inData.substring(idxSpeedJ4 + 1, idxAccelJ4).toFloat();
JOINT_MAX_ACCEL[3] = inData.substring(idxAccelJ4 + 1, idxSpeedJ5).toFloat();
JOINT_MAX_SPEED[4] = inData.substring(idxSpeedJ5 + 1, idxAccelJ5).toFloat();
JOINT_MAX_ACCEL[4] = inData.substring(idxAccelJ5 + 1, idxSpeedJ6).toFloat();
JOINT_MAX_SPEED[5] = inData.substring(idxSpeedJ6 + 1, idxAccelJ6).toFloat();
JOINT_MAX_ACCEL[5] = inData.substring(idxAccelJ6 + 1).toFloat();
}
void calibrateJoints(int* calJoints)
{
// check which joints to calibrate
bool calAllDone = false;
bool calJointsDone[NUM_JOINTS];
for (int i = 0; i < NUM_JOINTS; ++i)
{
calJointsDone[i] = !calJoints[i];
}
// first pass of calibration, fast speed
for (int i = 0; i < NUM_JOINTS; i++)
{
stepperJoints[i].setSpeed(CAL_SPEED * CAL_SPEED_MULT[i] * CAL_DIR[i]);
}
while (!calAllDone)
{
calAllDone = true;
for (int i = 0; i < NUM_JOINTS; ++i)
{
// if joint is not calibrated yet
if (!calJointsDone[i])
{
// check limit switches
if (!reachedLimitSwitch(i))
{
// limit switch not reached, continue moving
stepperJoints[i].runSpeed();
calAllDone = false;
}
else
{
// limit switch reached
stepperJoints[i].setSpeed(0); // redundancy
calJointsDone[i] = true;
}
}
}
}
delay(2000);
return;
}
bool reachedLimitSwitch(int joint)
{
int pin = CAL_PINS[joint];
// check multiple times to deal with noise
// possibly EMI from motor cables?
if (digitalRead(pin) == LIMIT_SWITCH_HIGH[joint])
{
if (digitalRead(pin) == LIMIT_SWITCH_HIGH[joint])
{
if (digitalRead(pin) == LIMIT_SWITCH_HIGH[joint])
{
if (digitalRead(pin) == LIMIT_SWITCH_HIGH[joint])
{
if (digitalRead(pin) == LIMIT_SWITCH_HIGH[joint])
{
return false;
}
}
}
}
}
return false;
}
void stateTRAJ()
{
// initialise joint steps
int curEncSteps[NUM_JOINTS];
readEncPos(curEncSteps);
int cmdEncSteps[NUM_JOINTS];
for (int i = 0; i < NUM_JOINTS; ++i)
{
cmdEncSteps[i] = curEncSteps[i];
}
// initialise AccelStepper instance
for (int i = 0; i < NUM_JOINTS; ++i)
{
stepperJoints[i] = AccelStepper(1, STEP_PINS[i], DIR_PINS[i]);
stepperJoints[i].setPinsInverted(true, false, false); // DM542T CW
stepperJoints[i].setAcceleration(MOTOR_MAX_ACCEL[i]);
stepperJoints[i].setMaxSpeed(MOTOR_MAX_SPEED[i]);
}
stepperJoints[3].setPinsInverted(false, false, false); // J4 DM320T CCW
stepperJoints[3].setMinPulseWidth(7.5);
// message
String inData = "";
// start loop
while (STATE == STATE_TRAJ)
{
char received = '\0';
// check for message from host
if (Serial.available())
{
received = Serial.read();
inData += received;
}
// process message when new line character is received
if (received == '\n')
{
String function = inData.substring(0, 2);
// update trajectory information
if (function == "MT")
{
// read current joint positions
readEncPos(curEncSteps);
// update host with joint positions
String msg = String("JP") + String("A") + String(curEncSteps[0]) + String("B") + String(curEncSteps[1]) + String("C") + String(curEncSteps[2])
+ String("D") + String(curEncSteps[3]) + String("E") + String(curEncSteps[4]) + String("F") + String(curEncSteps[5]) + String("\n");
Serial.print(msg);
// get new position commands
int msgIdxJ1 = inData.indexOf('A');
int msgIdxJ2 = inData.indexOf('B');
int msgIdxJ3 = inData.indexOf('C');
int msgIdxJ4 = inData.indexOf('D');
int msgIdxJ5 = inData.indexOf('E');
int msgIdxJ6 = inData.indexOf('F');
cmdEncSteps[0] = inData.substring(msgIdxJ1 + 1, msgIdxJ2).toInt();
cmdEncSteps[1] = inData.substring(msgIdxJ2 + 1, msgIdxJ3).toInt();
cmdEncSteps[2] = inData.substring(msgIdxJ3 + 1, msgIdxJ4).toInt();
cmdEncSteps[3] = inData.substring(msgIdxJ4 + 1, msgIdxJ5).toInt();
cmdEncSteps[4] = inData.substring(msgIdxJ5 + 1, msgIdxJ6).toInt();
cmdEncSteps[5] = inData.substring(msgIdxJ6 + 1).toInt();
// update target joint positions
readEncPos(curEncSteps);
for (int i = 0; i < NUM_JOINTS; ++i)
{
//curEncSteps[1] = J2encPos.read() * ENC_DIR[1];
int diffEncSteps = cmdEncSteps[i] - curEncSteps[i];
//if (abs(diffEncSteps) > 2)
//{
int diffMotSteps = diffEncSteps / ENC_MULT[i];
/*
if (diffMotSteps < MOTOR_STEPS_PER_REV[i])
{
// for the last rev of motor, introduce artificial decceleration
// to help prevent overshoot
diffMotSteps = diffMotSteps / 2;
}
*/
//stepperJoints[i].move(diffMotSteps);
stepperJoints[i].moveTo(cmdEncSteps[i]);
stepperJoints[i].run();
//}
}
}
else if (function == "JC")
{
// calibrate joint 6
int calJoint6[] = { 0, 0, 0, 0, 0, 1 }; // 000001
calibrateJoints(calJoint6);
// record encoder steps
int calStepJ6 = J6encPos.read() * ENC_DIR[5];
// calibrate joints 1 to 5
int calJoints[] = { 1, 1, 1, 1, 1, 0 }; // 111110
calibrateJoints(calJoints);
// record encoder steps
int calStepJ1 = J1encPos.read() * ENC_DIR[0];
int calStepJ2 = J2encPos.read() * ENC_DIR[1];
int calStepJ3 = J3encPos.read() * ENC_DIR[2];
int calStepJ4 = J4encPos.read() * ENC_DIR[3];
int calStepJ5 = J5encPos.read() * ENC_DIR[4];
// if limit switch at lower end, set encoder to 0
// otherwise set to encoder upper limit
J1encPos.write(0);
J2encPos.write(0);
J3encPos.write(ENC_RANGE_STEPS[2]);
J4encPos.write(0);
J5encPos.write(0);
J6encPos.write(ENC_RANGE_STEPS[5]);
// read current joint positions
readEncPos(curEncSteps);
// return to original position
for (int i = 0; i < NUM_JOINTS; ++i)
{
stepperJoints[i].setAcceleration(MOTOR_MAX_ACCEL[i]);
stepperJoints[i].setMaxSpeed(MOTOR_MAX_SPEED[i]);
cmdEncSteps[i] = REST_ENC_POSITIONS[i];
stepperJoints[i].move((REST_ENC_POSITIONS[i] - curEncSteps[i]) / ENC_MULT[i]);
}
bool restPosReached = false;
while (!restPosReached)
{
restPosReached = true;
// read current joint positions
readEncPos(curEncSteps);
for (int i = 0; i < NUM_JOINTS; ++i)
{
if (abs(REST_ENC_POSITIONS[i] - curEncSteps[i]) > 5)
{
restPosReached = false;
stepperJoints[i].move((REST_ENC_POSITIONS[i] - curEncSteps[i]) / ENC_MULT[i]);
stepperJoints[i].run();
}
}
}
// calibration done, send calibration values
String msg = String("JC") + String("A") + String(calStepJ1) + String("B") + String(calStepJ2) + String("C") + String(calStepJ3)
+ String("D") + String(calStepJ4) + String("E") + String(calStepJ5) + String("F") + String(calStepJ6) + String("\n");
Serial.print(msg);
for (int i = 0; i < NUM_JOINTS; ++i)
{
stepperJoints[i].setAcceleration(MOTOR_MAX_ACCEL[i]);
stepperJoints[i].setMaxSpeed(MOTOR_MAX_SPEED[i]);
}
}
else if (function == "JP")
{
// read current joint positions
readEncPos(curEncSteps);
// update host with joint positions
String msg = String("JP") + String("A") + String(curEncSteps[0]) + String("B") + String(curEncSteps[1]) + String("C") + String(curEncSteps[2])
+ String("D") + String(curEncSteps[3]) + String("E") + String(curEncSteps[4]) + String("F") + String(curEncSteps[5]) + String("\n");
Serial.print(msg);
}
else if (function == "SS")
{
updateStepperSpeed(inData);
// set motor speed and acceleration
for (int i = 0; i < NUM_JOINTS; ++i)
{
MOTOR_MAX_SPEED[i] = JOINT_MAX_SPEED[i] * ENC_STEPS_PER_DEG[i] / ENC_MULT[i];
MOTOR_MAX_ACCEL[i] = JOINT_MAX_ACCEL[i] * ENC_STEPS_PER_DEG[i] / ENC_MULT[i];
stepperJoints[i].setAcceleration(MOTOR_MAX_ACCEL[i] * MOTOR_ACCEL_MULT[i]);
stepperJoints[i].setMaxSpeed(MOTOR_MAX_SPEED[i] * MOTOR_SPEED_MULT[i]);
}
// read current joint positions
readEncPos(curEncSteps);
// update host with joint positions
String msg = String("JP") + String("A") + String(curEncSteps[0]) + String("B") + String(curEncSteps[1]) + String("C") + String(curEncSteps[2])
+ String("D") + String(curEncSteps[3]) + String("E") + String(curEncSteps[4]) + String("F") + String(curEncSteps[5]) + String("\n");
Serial.print(msg);
}
else if (function == "ST")
{
if (!initStateTraj(inData))
{
STATE = STATE_INIT;
return;
}
}
// clear message
inData = "";
}
// execute motor commands
for (int i = 0; i < NUM_JOINTS; ++i)
{
// target joint positions are already updated, just call run()
stepperJoints[i].run();
}
}
}
void stateINIT()
{
// message
String inData = "";
// start loop
while (STATE == STATE_INIT)
{
char received = '\0';
// check for message from host
if (Serial.available())
{
received = Serial.read();
inData += received;
}
// process message when new line character is received
if (received == '\n')
{
String function = inData.substring(0, 2);
// update trajectory information
if (function == "ST")
{
if (initStateTraj(inData))
{
STATE = STATE_TRAJ;
return;
}
}
inData = "";
}
}
}
void stateERR()
{
// enter holding state
digitalWrite(J1stepPin, LOW);
digitalWrite(J2stepPin, LOW);
digitalWrite(J3stepPin, LOW);
digitalWrite(J4stepPin, LOW);
digitalWrite(J5stepPin, LOW);
digitalWrite(J6stepPin, LOW);
// do recovery @TODO
while (STATE == STATE_ERR) {}
}
void loop()
{
// state control
switch (STATE)
{
case STATE_TRAJ:
stateTRAJ();
break;
case STATE_ERR:
stateERR();
break;
default:
stateINIT();
break;
}
}