M2 – steering pid and speed pid

 

/*
* 
* DAMI-M1
* 
* slave-motion-mod1-vx
* 
* Shared PIDs for speed and steering
* Adaptative steering pidKs for start + under move above error limit
* Speed Change (step 1 pulse until target)
* - increase: step 1 pulse until target (wait period on start)
* - decrease: distance to decrease speed (Torricelli equation) 
* 
* LIBRARIES
* Servo (sg90)
* Wire (Arduino I2C slave)
* PID_v1
*
*/
#define outputForPlotter true
#define serialSpeed 115200

#define bodyLeftMotorPwmBase 0
#define bodyRightMotorPwmBase 0

// encoder type
#define bodyEncoderSJ01 true
#define bodyEncoderHC020K false
#define bodyEncoderLM393 false

#if bodyEncoderSJ01
#define bodyEncoderSignal CHANGE
#define bodyEncoderInputSignal INPUT
#endif
#if bodyEncoderHC020K
#define bodyEncoderSignal FALLING
#define bodyEncoderInputSignal INPUT_PULLUP
#endif
#if bodyEncoderLM393
#define bodyEncoderSignal RISING
#define bodyEncoderInputSignal INPUT
#endif

// motor one
#define enA 5
#define in1 4
#define in2 7
// motor two
#define enB 6
#define in3 8
#define in4 12

// LeftMotor
#define bodyLeftMotorEn enA
#define bodyLeftMotorForward in1
#define bodyLeftMotorBackward in2

// RightMotor
#define bodyRightMotorEn enB
#define bodyRightMotorForward in4
#define bodyRightMotorBackward in3

// encoder LeftMotor
#define bodyEncoderLeftInt 0
#define bodyEncoderLeftFunction bodyEncoderLeftCounter
#define bodyEncoderLeftPin 2 // A pin the interrupt pin
#if bodyEncoderSJ01
#define bodyEncoderLeftPinB 9 // B pin: the digital pin
#endif
#define bodyEncoderLeftSignal bodyEncoderSignal
#define bodyEncoderLeftInputSignal bodyEncoderInputSignal

// encoder RightMotor
#define bodyEncoderRightInt 1
#define bodyEncoderRightFunction bodyEncoderRightCounter
#define bodyEncoderRightPin 3 // A pin: the interrupt pin
#if bodyEncoderSJ01
#define bodyEncoderRightPinB 10 // B pin: the digital pin
#endif
#define bodyEncoderRightSignal bodyEncoderSignal
#define bodyEncoderRightInputSignal bodyEncoderInputSignal

// steering PID
#define bodySteeringPidSetPointTarget 0
#define bodySteeringPidSampleTime 2
#define bodySteeringPidMinOutput -100
#define bodySteeringPidMaxOutput 100
// adaptative pidKs
#define bodySteeringPidAdaptativeLimit0 1
#define bodySteeringPidAdaptativeLimit1 3

#define bodySteeringPidKp0 1 //3
#define bodySteeringPidKi0 2 //0
#define bodySteeringPidKd0 0.001

#define bodySteeringPidKp1 1 //6
#define bodySteeringPidKi1 1 //0
#define bodySteeringPidKd1 0

#define bodySteeringPidKp2 2
#define bodySteeringPidKi2 2
#define bodySteeringPidKd2 0

// speed PID
#define bodySpeedPidSetPointBase 15
#define bodySpeedPidSampleTime 25
#define bodySpeedPidMinOutput 50
#define bodySpeedPidMaxOutput 200
// adaptative pidKs
#define bodySpeedPidAdaptativeLimit0 1
#define bodySpeedPidAdaptativeLimit1 5

#define bodySpeedPidKp0 0.3
#define bodySpeedPidKi0 1
#define bodySpeedPidKd0 0

#define bodySpeedPidKp1 0.6
#define bodySpeedPidKi1 2
#define bodySpeedPidKd1 0

#define bodySpeedPidKp2 1.2
#define bodySpeedPidKi2 5
#define bodySpeedPidKd2 0

// speed change acceleration constant
#define bodyMoveAcceleration 10 // used on slow down distance calculation

#include <PID_v1.h>

// odometry / navigation 
#define PI2 6.28318530718
//units: mm for high resolution / cm for low resolution
#define bodyWheelRadius 31.9 // wheel diameter / 2
//#define bodyWheelAxisRadius 67.5 // = lenght of axis / 2
#define bodyWheelAxisRadius 99 // = lenght of axis / 2
#define bodyEncoderRotationPulses 1920 // encoder resolution
#define bodyEncoderPulsesPerUnit 0.104392505884918 // = (2 * PI * bodyWheelRadius) / bodyEncoderRotationPulses

// speedChange
#define bodySpeedPidSpeedChangeWaitingCount 80
#define bodySpeedPidSpeedChangeStepMaxCount 25

// I2C Arduino Slave
#include <Wire.h>
#define I2C_NODE_ADDRESS 8

#include <Servo.h>
Servo servo;
int servoPin = 11;

// I2C
byte requestEventInfoType = 1;
byte requestEventCommand = 0;

#if bodyEncoderSJ01
byte bodyEncoderLeftPinLast;
boolean bodyEncoderLeftDirection; // the rotation direction
byte bodyEncoderRightPinLast;
boolean bodyEncoderRightDirection; //the rotation direction 
#endif

volatile signed long bodyEncoderLeftPulses = 0; // pulses since last PID computation
volatile signed long bodyEncoderLeftTotalPulses = 0; // total pulses since last reset (use: calculate error left-right)
volatile signed long bodyEncoderRightPulses = 0; // pulses since last PID computation
volatile signed long bodyEncoderRightTotalPulses = 0; // total pulses since move start
signed long bodyEncoderLeftTotalPulsesLast = 0;
signed long bodyEncoderRightTotalPulsesLast = 0;

// steering PID
double bodySteeringPidInput; double bodySteeringPidInputTmp = 0; // tmp for report only
double bodySteeringPidOutput; // Power supplied to the motor PWM value.
double bodySteeringPidSetPoint;
double bodySteeringPidKp=bodySteeringPidKp0, bbodySteeringPidKi=bodySteeringPidKi0, bodySteeringPidKd=bodySteeringPidKd0;

// adaptative pidKs
byte bodySteeringPidAdaptativeStatus = 0;

PID bodySteeringPid(&bodySteeringPidInput, &bodySteeringPidOutput, &bodySteeringPidSetPoint, bodySteeringPidKp, bbodySteeringPidKi, bodySteeringPidKd, REVERSE);

// speed PID
double bodySpeedPidInput; double bodySpeedPidInputTmp = 0; // tmp for report only
double bodySpeedPidOutput; // Power supplied to the motor PWM value.
double bodySpeedPidSetPoint;
double bodySpeedPidKp=bodySpeedPidKp0, bbodySpeedPidKi=bodySpeedPidKi0, bodySpeedPidKd=bodySpeedPidKd0;

// adaptative pidKs
byte bodySpeedPidAdaptativeStatus = 0;

PID bodySpeedPid(&bodySpeedPidInput, &bodySpeedPidOutput, &bodySpeedPidSetPoint, bodySpeedPidKp, bbodySpeedPidKi, bodySpeedPidKd, DIRECT);

// PWM to motors
byte bodyLeftMotorPwmOut = 0;
byte bodyRightMotorPwmOut = 0;

// speedChange
double bodySpeedPidSetPointTarget = 0;
//double bodySpeedPidSetPointTargetStart = 0;
double bodySpeedPidSetPointTargetEnd = 0;
boolean bodySpeedPidSpeedChangeFlag = false;
boolean bodySpeedPidSpeedChangeOnStartFlag = false;
boolean bodySpeedPidSpeedChangeOnEndFlag = false;
double bodySpeedPidSpeedChangeStepIncrement = 0;
unsigned int bodySpeedPidSpeedResultCounter = 0;
unsigned int bodySpeedPidSpeedResultLastCounter = 0;
double bodyMoveDistanceRemain = 0;
signed long bodyMoveSlowDownDistance = 0;
double bodyMoveCurrentSpeedUnitPerSec = 0;

byte bodyMoveProcessStatus = 0;

#define bodyMoveCmdSequenceMax 24

// pulses per sample time = pidSetPoint
// pulses per second = 1000/sampleTime * pidSetPoint
// distance per second = 1000/sampleTime * pidSetPoint * distance

typedef struct {
byte cmd;
unsigned long distance; // distance in mm/cm (depends on resolution), angles on degrees
byte setPointStart; // pulses per pidSampleTime
byte setPointTarget; // 5=2,10=4,15=6,20=8,25=10,40=16,60=24,80=32 cm/s
} bodyMoveCmdType;

//bodyMoveCmdType bodyMoveCmdSequence[bodyMoveCmdSequenceMax] = {{41, 1800}, {31, 90}, {41, 300}, {31, 90}, {41, 300}, {31, 90}, {41, 300}, {32, 90}, {41, 900}, {32, 360}, {41, 900}, {31, 180}};
// 2 bodyMoveCmdType bodyMoveCmdSequence[bodyMoveCmdSequenceMax] = {{31, 360},{31, 360}};
// 6 bodyMoveCmdType bodyMoveCmdSequence[bodyMoveCmdSequenceMax] = {{41, 1000, 5, 30}, {42, 1000, 10, 30}, {41, 1000, 0, 0}, {42, 1000, 0, 10}, {41, 1000, 20, 0}, {42, 1000, 0, 0}};
// 6 bodyMoveCmdType bodyMoveCmdSequence[bodyMoveCmdSequenceMax] = {{41, 100, 5, 30}, {42, 200, 5, 20}, {41, 300, 5, 30}, {42, 400, 5, 30}, {41, 500, 5, 20}, {42, 1000, 5, 30}};
//bodyMoveCmdType bodyMoveCmdSequence[bodyMoveCmdSequenceMax] = {{41, 2000, 0, 60}, {31, 90}, {41, 850, 0, 30}, {32, 90}, {41, 500, 0, 30}, {31, 90}, {41, 180}, {32, 90}, {41, 800, 30, 60}, {41, 2500, 0, 60}, {32, 90}, {41, 900, 0, 40}};
//bodyMoveCmdType bodyMoveCmdSequence[bodyMoveCmdSequenceMax] = {{31, 360, 15, 15}, {32, 180, 10, 10}, {32, 90, 0, bodySpeedPidSetPointBase}, {32, 90, 0, 0}, {0, 0, 0, 0}};
bodyMoveCmdType bodyMoveCmdSequence[bodyMoveCmdSequenceMax] = {{41, 1000, 15, 15}, {32, 180, 10, 15}, {41, 1000, 0, bodySpeedPidSetPointBase}, {31, 180, 15, 15}, {0, 0, 0, 0}};


int bodyMoveCurrentCommand = 0;

unsigned long bodyEncoderLeftPulsesTarget = 0;
unsigned long bodyEncoderRightPulsesTarget = 0;

signed int bodyMoveCurrentDirection = 0;
int bodyMoveCurrentMoveCode = 0; // 0=STOP / 41=F / 42=B / 31=L / 32=R

unsigned long bodyMoveLoopCounter = 0;
unsigned long loopTimeStart = 0;
unsigned long loopTime = 0;

// only for display
signed long bodyEncoderLeftTotalPulsesTmp, bodyEncoderRightTotalPulsesTmp;

// odometry
double bodyTheta = 0;
double bodyPosX = 0;
double bodyPosY = 0;

void setup()
{ 
Serial.begin(serialSpeed); //Initialize the serial port

pinMode(bodyLeftMotorEn, OUTPUT);
pinMode(bodyLeftMotorForward, OUTPUT);
pinMode(bodyLeftMotorBackward, OUTPUT);
pinMode(bodyRightMotorEn, OUTPUT);
pinMode(bodyRightMotorForward, OUTPUT);
pinMode(bodyRightMotorBackward, OUTPUT);

// steering PID
bodySteeringPidSetPoint = bodySteeringPidSetPointTarget; 
bodySteeringPid.SetMode(MANUAL); 
bodySteeringPid.SetSampleTime(bodySteeringPidSampleTime); 
bodySteeringPid.SetOutputLimits(bodySteeringPidMinOutput, bodySteeringPidMaxOutput);

// speed PID
bodySpeedPidSetPoint = bodySpeedPidSetPointBase; // speed in pulses per sample time
bodySpeedPid.SetMode(MANUAL); 
bodySpeedPid.SetSampleTime(bodySpeedPidSampleTime); 
bodySpeedPid.SetOutputLimits(bodySpeedPidMinOutput, bodySpeedPidMaxOutput);

#if bodyEncoderSJ01
bodyEncoderLeftDirection = true; //default -> Forward 
pinMode(bodyEncoderLeftPinB, bodyEncoderLeftInputSignal); 
#endif
attachInterrupt(bodyEncoderLeftInt, bodyEncoderLeftFunction, bodyEncoderLeftSignal);

#if bodyEncoderSJ01
bodyEncoderRightDirection = true; // default -> Forward 
pinMode(bodyEncoderRightPinB, bodyEncoderRightInputSignal); 
#endif
attachInterrupt(bodyEncoderRightInt, bodyEncoderRightFunction, bodyEncoderRightSignal);

// I2C Arduino Slave
Wire.begin(I2C_NODE_ADDRESS); // join i2c bus with address #8
Wire.onReceive(receiveEvent); // register event
Wire.onRequest(requestEvent); // register event

// servo
delay(50);
servo.attach(servoPin);
//control the servo's direction and the position of the motor
delay(50); 
servo.write(0);
delay(1000);
servo.write(90);
delay(1000);
servo.detach();

loopTimeStart = millis();
}

void loop()
{
// just move & display
bodyMoveProcess();

// end loop
bodyMoveLoopCounter++;
loopTime = millis() - loopTimeStart; 
loopTimeStart = millis();
}

void bodyMoveProcess() {
boolean bodySteeringPidResult = false;
boolean bodySpeedPidResult = false;

bodyMoveCheck();
if(bodyMoveProcessStatus) {
if(bodyMoveCurrentMoveCode == 0) {
// the moviment end and we are stoped
bodyMoveEnd();
}

// calculate pwm parts for desired speed and steering
bodySpeedPidResult = bodyMoveSpeedPidCompute();
bodySteeringPidResult = bodyMoveSteeringPidCompute();

// set speed
bodyLeftMotorPwmOut = bodyLeftMotorPwmBase + bodySpeedPidOutput - bodySteeringPidOutput;
bodyRightMotorPwmOut = bodyRightMotorPwmBase + bodySpeedPidOutput + bodySteeringPidOutput;

bodyMove(bodyLeftMotorPwmOut, bodyRightMotorPwmOut);

// odometry
updateOdometry();
}

if(bodySteeringPidResult) {
if(bodyEncoderLeftTotalPulses != 0 || bodyEncoderRightTotalPulses != 0 ) {
// display info
Serial.print(abs(bodyEncoderLeftTotalPulses)); Serial.print("\t");
Serial.print(abs(bodyEncoderRightTotalPulses)); Serial.print("\t");
/*
Serial.print(bodySteeringPidOutput); Serial.print("\t");
*/
Serial.print((float)bodyLeftMotorPwmOut); Serial.print("\t"); // divided for scale display
Serial.print((float)bodyRightMotorPwmOut); Serial.print("\t"); // divided for scale display
Serial.print(bodySpeedPidOutput); Serial.print("\t");
Serial.print(bodySteeringPidOutput); Serial.print("\t");
Serial.print(bodySpeedPidSetPoint - bodySpeedPidInputTmp); Serial.print("\t");
Serial.print(bodySteeringPidInputTmp); Serial.print("\t"); // bodySteeringError
Serial.print(bodySpeedPidAdaptativeStatus); Serial.print("\t");
Serial.print(bodySteeringPidAdaptativeStatus); Serial.print("\t");
//Serial.print(bodyMoveDistanceRemain); Serial.print("\t");
//Serial.print(bodyMoveSlowDownDistance); Serial.print("\t");
//Serial.print(bodySpeedPidSetPoint / 10); Serial.print("\t");
//Serial.print(bodyMoveDistanceRemain); Serial.print("\t");
//Serial.print(bodyMoveSlowDownDistance); Serial.print("\t");
Serial.print(bodyTheta * 180 / PI); Serial.print("\t");
Serial.print(bodyPosX); Serial.print("\t");
Serial.print(bodyPosY); Serial.print("\t");
//Serial.print(loopTime); Serial.print("\t");
//Serial.print((bodyEncoderLeftPulsesTarget + bodyEncoderRightPulsesTarget) / 4); Serial.print("\t");
//Serial.print((bodyEncoderLeftPulsesTarget + bodyEncoderRightPulsesTarget - abs(bodyEncoderLeftTotalPulses) - abs(bodyEncoderRightTotalPulses)) / 2); Serial.print("\t");
Serial.println("");
}
}
}

void bodyMoveCheck() {
if(!bodyMoveProcessStatus) {
//
// run ONLY on first loop
//
bodyMoveStart(); 
} else {
//
// run on ALL but NOT on first loop
//

// calculate distance to target
bodyMoveCheckTargetDistance();

// calculate eventual speedChange
bodyMoveSpeedChangeControl();

// one encoder reach the target?
boolean onTarget = false;
if(abs(bodyEncoderLeftTotalPulses) >= bodyEncoderLeftPulsesTarget) onTarget = true;
if(abs(bodyEncoderRightTotalPulses) >= bodyEncoderRightPulsesTarget) onTarget = true;
if(onTarget) bodyMoveCurrentMoveCode = 0;
}
}

void bodyMoveCheckTargetDistance() {
double targetSpeedUnitPerSec = 0;
unsigned long bodyMoveDistanceRemainPulses;

// calculate distanceRemain
bodyMoveDistanceRemainPulses = (bodyEncoderLeftPulsesTarget + bodyEncoderRightPulsesTarget - abs(bodyEncoderLeftTotalPulses) - abs(bodyEncoderRightTotalPulses)) / 2;
bodyMoveDistanceRemain = bodyMoveDistanceRemainPulses * bodyEncoderPulsesPerUnit;

// convert pulses (pulses per sample time) in speed (distanceUnits per second)
bodyMoveCurrentSpeedUnitPerSec = bodySpeedPidSetPoint * bodyEncoderPulsesPerUnit * (1000 / bodySpeedPidSampleTime); // TODO: (1000 / bodySpeedPidSampleTime) is a constant
targetSpeedUnitPerSec = bodySpeedPidSetPointBase * bodyEncoderPulsesPerUnit * (1000 / bodySpeedPidSampleTime); // TODO: (1000 / bodySpeedPidSampleTime) is a constant

// calculate safeDistance based on torricceli formula
bodyMoveSlowDownDistance = abs((targetSpeedUnitPerSec * targetSpeedUnitPerSec) - (bodyMoveCurrentSpeedUnitPerSec * bodyMoveCurrentSpeedUnitPerSec)) / ( 2 * bodyMoveAcceleration);

// doesnt activate if remains more than a quarter of path
if (bodyMoveDistanceRemainPulses > (bodyEncoderLeftPulsesTarget + bodyEncoderRightPulsesTarget) / 2 * 3/4) {
bodyMoveSlowDownDistance = 0;
}

if(bodyMoveSlowDownDistance > 0 && (bodyMoveSlowDownDistance - bodyMoveDistanceRemain) >= 0) {
if(!bodySpeedPidSpeedChangeOnEndFlag) {
bodySpeedPidSpeedChangeOnEndFlag = true;
bodySpeedPidSpeedChangeFlag = true;
bodySpeedPidSetPointTarget = bodySpeedPidSetPointBase;
bodySpeedPidSpeedChangeStepIncrement = -1;
#if !outputForPlotter
Serial.print("SLOWDOWN START"); Serial.print("\t");
#endif
}
}
}

void bodyMoveSpeedChangeControl() {
// we do not accelerate to target speed at begin of the move
// check if its time to start the accelleration increase
if(bodySpeedPidSpeedChangeOnStartFlag) {
if(bodySpeedPidSpeedResultCounter > bodySpeedPidSpeedChangeWaitingCount) {
bodySpeedPidSpeedChangeFlag = true;
bodySpeedPidSpeedChangeOnStartFlag = false;
}
} 
// we are in the middle of an accelleration (increase/decrease)
if(bodySpeedPidSpeedChangeFlag) {
// time to do a speed change step?
if(bodySpeedPidSpeedResultCounter - bodySpeedPidSpeedResultLastCounter > bodySpeedPidSpeedChangeStepMaxCount) {
// update control var
bodySpeedPidSpeedResultLastCounter = bodySpeedPidSpeedResultCounter;
// change speed (update current pidSetPoint)
bodySpeedPidSetPoint += bodySpeedPidSpeedChangeStepIncrement;
bodySpeedPidSetPoint += bodySpeedPidSpeedChangeStepIncrement;
// time to end the speed change?
if(bodySpeedPidSpeedChangeStepIncrement >= 0 ) {
if(bodySpeedPidSetPoint >= bodySpeedPidSetPointTarget) {
bodySpeedPidSetPoint = bodySpeedPidSetPointTarget;
bodySpeedPidSpeedChangeFlag = false;
}
} else {
if(bodySpeedPidSetPoint <= bodySpeedPidSetPointTarget) {
bodySpeedPidSetPoint = bodySpeedPidSetPointTarget;
bodySpeedPidSpeedChangeFlag = false;
}
}
}
}
}

void bodyMoveStart() {
unsigned long tmpVar;
unsigned long tmpLong;
int minSetPoint = 0;
int targetSetPoint = 0;
double minSpeedUnitPerSec;
int targetSpeedUnitPerSec;
int i = 0;

bodyMoveCurrentMoveCode = bodyMoveCmdSequence[bodyMoveCurrentCommand].cmd;
// target distance calculations
switch(bodyMoveCurrentMoveCode) {
case 0:
// stop
break;
case 31:
case 32:
// rotate (left/right)
// set distance : pulses = (((degrees x PI)/180) x axis_radius) x encoderPulsesPerUnit
tmpVar = ((bodyMoveCmdSequence[bodyMoveCurrentCommand].distance * PI) / 180 ) * bodyWheelAxisRadius;
tmpVar = tmpVar / bodyEncoderPulsesPerUnit;
bodyEncoderLeftPulsesTarget = tmpVar;
bodyEncoderRightPulsesTarget = tmpVar; 
break; 
case 41:
case 42:
// forward / backward
// set distance : pulses = distance / distance_per_pulse
tmpVar = bodyMoveCmdSequence[bodyMoveCurrentCommand].distance / bodyEncoderPulsesPerUnit;
bodyEncoderLeftPulsesTarget = tmpVar;
bodyEncoderRightPulsesTarget = tmpVar;
break; 
}

// start, target and end speed setup
minSetPoint = bodyMoveCmdSequence[bodyMoveCurrentCommand].setPointStart;
if(!minSetPoint) {
minSetPoint = bodySpeedPidSetPointBase;
}

targetSetPoint = bodyMoveCmdSequence[bodyMoveCurrentCommand].setPointTarget;
if(!targetSetPoint) {
// calculate good speed for distance with torricelli equation
//f = sqrt(i^2+2ad) 
// convert pulses (pulses per sample time) in speed (distanceUnits per second)
minSpeedUnitPerSec = minSetPoint * bodyEncoderPulsesPerUnit * (1000 / bodySpeedPidSampleTime);
// calculate using torricelli equation
targetSpeedUnitPerSec = (int)sqrt((minSpeedUnitPerSec * minSpeedUnitPerSec) + ( 2 * bodyMoveAcceleration) * (bodyMoveCmdSequence[bodyMoveCurrentCommand].distance / 2)) ;
// convert speed (distanceUnits per second) in pulses (pulses per sample time)
targetSetPoint = targetSpeedUnitPerSec / (bodyEncoderPulsesPerUnit * (1000 / bodySpeedPidSampleTime));
//Serial.print(targetSetPoint); Serial.print("\t");
// distance calculation based on torricceli formula
//int stopDistance = abs((minSpeedUnitPerSec * minSpeedUnitPerSec) - (targetSpeedUnitPerSec * targetSpeedUnitPerSec)) / ( 2 * bodyMoveAcceleration);
//Serial.print(stopDistance); Serial.print("\t");
}

// does not allow target speed bellow minimum speed
if(targetSetPoint < minSetPoint) {
targetSetPoint = minSetPoint;
}

// speedChange speeds
bodySpeedPidSetPointTarget = targetSetPoint;
bodySpeedPidSetPointTargetEnd = minSetPoint;

// pulses increment on speedChange
bodySpeedPidSpeedChangeStepIncrement = 1;

// speedChange flags
bodySpeedPidSpeedResultCounter = 0;
bodySpeedPidSpeedChangeOnEndFlag = false;
bodySpeedPidSpeedChangeFlag = false;

if(targetSetPoint != minSetPoint ) {
bodySpeedPidSpeedChangeOnStartFlag = true;
}

// adaptative steeringPid
bodySteeringPid.SetTunings(bodySteeringPidKp2, bodySteeringPidKi2, bodySteeringPidKd2);
bodySteeringPidAdaptativeStatus = 2;

// adaptative speedPid
bodySpeedPid.SetTunings(bodySpeedPidKp2, bodySpeedPidKi2, bodySpeedPidKd2);
bodySpeedPidAdaptativeStatus = 2;

#if !outputForPlotter
Serial.print("STARTMOVE"); Serial.print("\t");
Serial.print(bodyMoveCurrentCommand); Serial.print("\t");
Serial.print(bodyMoveCurrentMoveCode); Serial.print("\t");
Serial.print(bodyMoveCmdSequence[bodyMoveCurrentCommand].distance); Serial.print("\t");
Serial.print(bodyEncoderLeftPulsesTarget); Serial.print("\t");
Serial.print(bodyEncoderRightPulsesTarget); Serial.print("\t");
Serial.print(minSetPoint); Serial.print("\t");
Serial.print(bodySpeedPidSetPointTarget); Serial.print("\t");
Serial.print(bodySpeedPidSpeedChangeStepIncrement); Serial.print("\t");
Serial.println("");
#endif

// lets prepare to start moving
delay(5000);

// steering pid
bodySteeringPidSetPoint = bodySteeringPidSetPointTarget; 
bodySteeringPid.SetMode(AUTOMATIC);
// speed PID
bodySpeedPidSetPoint = minSetPoint;
bodySpeedPid.SetMode(AUTOMATIC); 
// set move variables
bodyMoveProcessStatus = 1;
bodyMoveLoopCounter = 0;
bodyEncoderLeftTotalPulses = 0;
bodyEncoderRightTotalPulses = 0;
}

void bodyMoveEnd() {
// assure that all motors are stoped and reset vars
bodyMoveStop();

// steering pid
bodySteeringPid.SetMode(MANUAL);
bodySteeringPidOutput = 0; // have last steering
// speed pid
bodySpeedPid.SetMode(MANUAL);
bodySpeedPidOutput = 0; // have last speed error

// wait for last wheel to stop (to get current total pulses)
delay(250);

// some info
// bodyMoveCurrentMoveCode is already 0 (set on bodyMoveEndCheck())
#if !outputForPlotter
Serial.print("ENDMOVE"); Serial.print("\t");
Serial.print(bodyMoveCurrentCommand); Serial.print("\t");
Serial.print(bodyMoveCmdSequence[bodyMoveCurrentCommand].cmd); Serial.print("\t");
Serial.print(bodyMoveCmdSequence[bodyMoveCurrentCommand].distance); Serial.print("\t");
Serial.print(abs(bodyEncoderLeftTotalPulses)); Serial.print("\t");
Serial.print(abs(bodyEncoderRightTotalPulses)); Serial.print("\t");
Serial.print(abs(bodyEncoderLeftTotalPulses) - abs(bodyEncoderRightTotalPulses)); Serial.print("\t");
Serial.print(bodyEncoderLeftPulsesTarget); Serial.print("\t");
Serial.print(bodyEncoderRightPulsesTarget); Serial.print("\t");
Serial.print((signed long)bodyEncoderLeftPulsesTarget - abs(bodyEncoderLeftTotalPulses)); Serial.print("\t");
Serial.print((signed long)bodyEncoderRightPulsesTarget - abs(bodyEncoderRightTotalPulses)); Serial.print("\t");
Serial.println("");
#endif

delay(1000);

// prepare some vars
bodySteeringPidInput = 0;
bodyEncoderLeftTotalPulses = 0;
bodyEncoderRightTotalPulses = 0;
bodyEncoderLeftTotalPulsesLast = 0;
bodyEncoderRightTotalPulsesLast = 0;
bodyMoveCurrentDirection = 0;

// advance command sequence
bodyMoveCurrentCommand++;
if(bodyMoveCurrentCommand == bodyMoveCmdSequenceMax - 1) {
// restart move loop
#if !outputForPlotter
Serial.println("RESTART");
#endif
bodyMoveCurrentCommand = 0;
} else {
if(bodyMoveCmdSequence[bodyMoveCurrentCommand].cmd == 0) {
// restart move loop
#if !outputForPlotter
Serial.println("RESTART");
#endif
bodyMoveCurrentCommand = 0;
}
}

// allow new command processing
bodyMoveProcessStatus = 0;
}

void bodyMove(unsigned int leftPwm, unsigned int rightPwm) {
switch(bodyMoveCurrentMoveCode) {
case 31:
bodyRotateLeft(leftPwm, rightPwm);
break;
case 32:
bodyRotateRight(leftPwm, rightPwm);
break; 
case 41:
bodyMoveForward(leftPwm, rightPwm);
break; 
case 42:
bodyMoveBackward(leftPwm, rightPwm);
break; 
}
}

// calculate pwm wheel diference correction using adaptative pid
boolean bodyMoveSteeringPidCompute() {
boolean pidResult;
byte adaptativeStatus = 0;

// wheel error
bodySteeringPidInput = abs(bodyEncoderLeftTotalPulses) - abs(bodyEncoderRightTotalPulses);

// bof: adaptative ks
unsigned long averagePulses = (abs(bodyEncoderLeftTotalPulses) + abs(bodyEncoderRightTotalPulses)) /2 ;
if(averagePulses > 100) {
if(abs(bodySteeringPidInput) > bodySteeringPidAdaptativeLimit1) {
adaptativeStatus = 2;
} else if(abs(bodySteeringPidInput) > bodySteeringPidAdaptativeLimit0) {
adaptativeStatus = 1;
}

if(bodySteeringPidAdaptativeStatus != adaptativeStatus) {
switch(adaptativeStatus) {
case 0:
bodySteeringPid.SetTunings(bodySteeringPidKp0, bodySteeringPidKi0, bodySteeringPidKd0);
break;
case 1:
bodySteeringPid.SetTunings(bodySteeringPidKp1, bodySteeringPidKi1, bodySteeringPidKd1);
break;
case 2:
bodySteeringPid.SetTunings(bodySteeringPidKp2, bodySteeringPidKi2, bodySteeringPidKd2);
break;
}
bodySteeringPidAdaptativeStatus = adaptativeStatus;
}
}
// eof: adaptative ks

pidResult = bodySteeringPid.Compute();
if(pidResult) {
bodySteeringPidInputTmp = bodySteeringPidInput; // for report only
}
return pidResult;
}

// calculate pwm base for desired speed using adaptative pid
boolean bodyMoveSpeedPidCompute() {
boolean pidResult;
byte adaptativeStatus = 0;

// speed error
//bodySpeedPidInput = (abs(bodyEncoderLeftPulses) + abs(bodyEncoderRightPulses)) / 2; 
bodySpeedPidInput = abs(bodyEncoderLeftPulses);

// bof: adaptative ks
unsigned long averagePulses = (abs(bodyEncoderLeftTotalPulses) + abs(bodyEncoderRightTotalPulses)) /2 ;
if(averagePulses > 20) {
if(abs(bodySpeedPidInput) > bodySpeedPidAdaptativeLimit1) {
//adaptativeStatus = 2;
adaptativeStatus = 1;
} else if(abs(bodySpeedPidInput) > bodySpeedPidAdaptativeLimit0) {
adaptativeStatus = 1;
}

if(bodySpeedPidAdaptativeStatus != adaptativeStatus) {
switch(adaptativeStatus) {
case 0:
bodySpeedPid.SetTunings(bodySpeedPidKp0, bodySpeedPidKi0, bodySpeedPidKd0);
break;
case 1:
bodySpeedPid.SetTunings(bodySpeedPidKp1, bodySpeedPidKi1, bodySpeedPidKd1);
break;
case 2:
bodySpeedPid.SetTunings(bodySpeedPidKp2, bodySpeedPidKi2, bodySpeedPidKd2);
break;
}
bodySpeedPidAdaptativeStatus = adaptativeStatus;
}
}
// eof: adaptative ks

// try to handle strange spikes on bodySpeedPidInput
if(bodySpeedPidInput > 50) bodySpeedPidInput = 50;

pidResult = bodySpeedPid.Compute(); 
if(pidResult) {
bodySpeedPidInputTmp = bodySpeedPidInput; // for report only
bodyEncoderLeftPulses = 0;
bodyEncoderRightPulses = 0;
bodySpeedPidSpeedResultCounter++;
}
return pidResult;
}

void bodyEncoderLeftCounter()
{
#if bodyEncoderSJ01
int Lstate = digitalRead(bodyEncoderLeftPin);
if((bodyEncoderLeftPinLast == LOW) && Lstate==HIGH)
{
int val = digitalRead(bodyEncoderLeftPinB);
if(val == LOW && bodyEncoderLeftDirection)
{
bodyEncoderLeftDirection = false; //Reverse
}
else if(val == HIGH && !bodyEncoderLeftDirection)
{
bodyEncoderLeftDirection = true; //Forward
}
}
bodyEncoderLeftPinLast = Lstate;

if(!bodyEncoderLeftDirection)
{
bodyEncoderLeftPulses++;
bodyEncoderLeftTotalPulses++;
} else {
bodyEncoderLeftPulses--;
bodyEncoderLeftTotalPulses--;
}
#else
bodyEncoderLeftPulses++;
bodyEncoderLeftTotalPulses ++;
#endif
}

void bodyEncoderRightCounter()
{
#if bodyEncoderSJ01
int Lstate = digitalRead(bodyEncoderRightPin);
if((bodyEncoderRightPinLast == LOW) && Lstate==HIGH)
{
int val = digitalRead(bodyEncoderRightPinB);
if(val == LOW && bodyEncoderRightDirection)
{
bodyEncoderRightDirection = false; // Reverse
}
else if(val == HIGH && !bodyEncoderRightDirection)
{
bodyEncoderRightDirection = true; // Forward
}
}
bodyEncoderRightPinLast = Lstate;

if(!bodyEncoderRightDirection)
{
bodyEncoderRightPulses++;
bodyEncoderRightTotalPulses ++; 
} else {
bodyEncoderRightPulses--;
bodyEncoderRightTotalPulses --; 
}
#else
bodyEncoderRightPulses++;
bodyEncoderRightTotalPulses ++; 
#endif
}

void bodyMoveForward( int pmwSpeedLeft, int pmwSpeedRight ){
if(bodyMoveCurrentDirection != 1) {
bodyMoveCurrentDirection = 1;
// set motors direction
setBodyMotorLeftForward();
setBodyMotorRightForward();
}
// set motors speed
analogWrite(bodyLeftMotorEn, pmwSpeedLeft);
analogWrite(bodyRightMotorEn, pmwSpeedRight);
}

void bodyMoveBackward( int pmwSpeedLeft, int pmwSpeedRight ) {
if(bodyMoveCurrentDirection != -1) {
bodyMoveCurrentDirection = -1;
// set motors direction
setBodyMotorLeftBackward();
setBodyMotorRightBackward(); 
}
// set motors speed
analogWrite(bodyLeftMotorEn, pmwSpeedLeft);
analogWrite(bodyRightMotorEn, pmwSpeedRight);
}

void bodyRotateLeft( int pmwSpeedLeft, int pmwSpeedRight ) {
if(bodyMoveCurrentDirection != -2) {
bodyMoveCurrentDirection = -2;
// set motors direction
setBodyMotorLeftBackward();
setBodyMotorRightForward(); 
}
// set motors speed
analogWrite(bodyLeftMotorEn, pmwSpeedLeft);
analogWrite(bodyRightMotorEn, pmwSpeedRight);
}

void bodyRotateRight( int pmwSpeedLeft, int pmwSpeedRight ) {
if(bodyMoveCurrentDirection != 2) {
bodyMoveCurrentDirection = 2;
// set motors direction
setBodyMotorLeftForward();
setBodyMotorRightBackward(); 
}
// set motors speed
analogWrite(bodyLeftMotorEn, pmwSpeedLeft);
analogWrite(bodyRightMotorEn, pmwSpeedRight);
}

void bodyMoveStop() {
bodyMoveCurrentDirection = 0;
bodyMoveStopLeft();
bodyMoveStopRight(); 
}

void bodyMoveStopLeft() {
// set motor direction
setBodyMotorLeftStop();
// set motor speed
analogWrite(bodyLeftMotorEn, 0);
}

void bodyMoveStopRight() {
// set motor direction
setBodyMotorRightStop(); 
// set motor speed
analogWrite(bodyRightMotorEn, 0);
}

void setBodyMotorLeftForward() {
digitalWrite(bodyLeftMotorForward, HIGH);
digitalWrite(bodyLeftMotorBackward, LOW);
}

void setBodyMotorLeftBackward() {
digitalWrite(bodyLeftMotorForward, LOW);
digitalWrite(bodyLeftMotorBackward, HIGH);
}

void setBodyMotorRightForward() {
digitalWrite(bodyRightMotorForward, HIGH);
digitalWrite(bodyRightMotorBackward, LOW);
}

void setBodyMotorRightBackward() {
digitalWrite(bodyRightMotorForward, LOW);
digitalWrite(bodyRightMotorBackward, HIGH);
}

void setBodyMotorLeftStop() {
digitalWrite(bodyLeftMotorForward, LOW);
digitalWrite(bodyLeftMotorBackward, LOW);
}

void setBodyMotorRightStop() {
digitalWrite(bodyRightMotorForward, LOW);
digitalWrite(bodyRightMotorBackward, LOW);
}

// function that executes whenever data is requested by master
// this function is registered as an event, see setup()
// respond with message of N bytesas expected by master
// I2C limit write to 32 bytes
void requestEvent() {
double tmp = 0;
signed int bodySteeringPidOutputTmp;
signed int bodySpeedPidSetPointTmp;
signed int bodySpeedPidInputTmp;
signed int bodySpeedPidOutputTmp;
switch(requestEventInfoType) {
case 1:
// prepare
// bodySteeringPidInput // calculate on master
// bodyMoveDistanceRemain // calculate on master
bodySteeringPidOutputTmp = (signed int) (bodySteeringPidOutput * 1000);
//bodySpeedPidSetPointTmp = (signed int) (bodySpeedPidSetPoint * 1000);
//bodySpeedPidInputTmp = (signed int) (bodySpeedPidInput * 1000);
//bodySpeedPidOutputTmp = (signed int) (bodySpeedPidOutput * 1000);
// send
Wire.write((byte*)&requestEventCommand, 1);
Wire.write((byte*)&loopTime, 1);
Wire.write((byte*)&bodyLeftMotorPwmOut, 1);
Wire.write((byte*)&bodyRightMotorPwmOut, 1);
Wire.write((byte*)&bodyEncoderLeftTotalPulses, 4);
Wire.write((byte*)&bodyEncoderRightTotalPulses, 4);
Wire.write((byte*)&bodySteeringPidOutputTmp, 2);

//Wire.write((byte*)&bodySpeedPidSetPointTmp, 2);
Wire.write((byte*)&bodySteeringPidOutputTmp, 2); // just reserved

//Wire.write((byte*)&bodySpeedPidInputTmp, 2);
//Wire.write((byte*)&bodySpeedPidOutputTmp, 2);
Wire.write((byte*)&bodySteeringPidOutputTmp, 2); // just reserved
Wire.write((byte*)&bodySteeringPidOutputTmp, 2); // just reserved

// Available for 32 bytes
//Wire.write((byte*)&bodySteeringPidOutput, 4);
//Wire.write((byte*)&bodySteeringPidOutput, 4);
//Wire.write((byte*)&bodySteeringPidOutput, 4);
break;
case 2:
tmp = 10;
tmp++; Wire.write((byte*)&tmp, 4);
tmp++; Wire.write((byte*)&tmp, 4);
tmp++; Wire.write((byte*)&tmp, 4);
tmp++; Wire.write((byte*)&tmp, 4);
tmp++; Wire.write((byte*)&tmp, 4);
//tmp++; Wire.write((byte*)&tmp, 4);
//tmp++; Wire.write((byte*)&tmp, 4);
//tmp++; Wire.write((byte*)&tmp, 4);

requestEventInfoType = 1;
break;
}
}

// function that executes whenever data is received from master
// this function is registered as an event, see setup()
void receiveEvent(int howMany) {
byte i2c_byte[32];
int i = 0;
while (Wire.available()) { // slave may send less than requested
char c = Wire.read(); // receive a byte as character
i2c_byte[i] = c;
i++;
}
switch(i2c_byte[0]) {
case 82:
requestEventInfoType = 2;
break;
}
}

void updateOdometry() {
float SL = bodyEncoderPulsesPerUnit * (abs(bodyEncoderLeftTotalPulses) - bodyEncoderLeftTotalPulsesLast);
float SR = bodyEncoderPulsesPerUnit * (abs(bodyEncoderRightTotalPulses) - bodyEncoderRightTotalPulsesLast);
bodyEncoderLeftTotalPulsesLast = abs(bodyEncoderLeftTotalPulses);
bodyEncoderRightTotalPulsesLast = abs(bodyEncoderRightTotalPulses);

double meanDistance = (SL + SR)/2;
//bodyTheta += (SR - SL) / (bodyWheelAxisRadius * 2);
bodyTheta += atan((SR - SL) / (bodyWheelAxisRadius * 2));

if(bodyTheta > PI2)
bodyTheta -= PI2;
else if(bodyTheta < -PI2)
bodyTheta += PI2;

bodyPosX = bodyPosX + meanDistance*cos(bodyTheta);
bodyPosY = bodyPosY + meanDistance*sin(bodyTheta);
}