C++ class for controlling DC motor with encoder feedback. Dependencies include LS7366LIB, MotCon, and PID.
Dependencies: LS7366LIB MotCon2 PID
Revision 0:d7f0715676f4, committed 2018-11-08
- Comitter:
- jebradshaw
- Date:
- Thu Nov 08 17:54:30 2018 +0000
- Commit message:
- 20181108 - Committed changes
Changed in this revision
diff -r 000000000000 -r d7f0715676f4 Axis.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Axis.cpp Thu Nov 08 17:54:30 2018 +0000 @@ -0,0 +1,320 @@ + +#include "Axis.h" +#include "LS7366.h" +#include "MotCon.h" +#include "PID.h" + +Axis::Axis(SPI& spi, PinName cs, PinName pwm, PinName dir, PinName analog, int* limit): _spi(spi), _cs(cs), _pwm(pwm), _dir(dir) , _analog(analog){ + this->_cs = 1; // Initialize chip select as off (high) + this->_pwm = 0.0; + this->_dir = 0; + this->co = 0.0; + this->Tdelay = .01; + this->Pk = 450.0; //120.0; //rough gains, seem to work well but could use tuning + this->Ik = 105.0; //75.0; + this->Dk = 0.0; + this->set_point = 0.0; + this->set_point_last = 0.0; + this->pos = 0.0; + this->vel = 0.0; + this->acc = 0.0; + this->stat = -1; + this->pos_cmd = 0.0; + this->vel_cmd = 0.0; + this->vel_avg_cmd = 0; + this->acc_cmd = 0.0; + this->vel_max = 2700.0 * Tdelay; //counts * Tdelay + this->acc_max = 1200.0 * Tdelay; //counts/sec/sec * Tdelay + this->p_higher = 0.0; + this->p_lower = 0.0; + this->vel_accum = 0.0; + this->moveTime = 0.0; + this->enc = 0; + this->moveStatus = 0; //status flag to indicate state of profile movement + this->moveState = 0; //used for state machine in movement profiles + this->debug = 0; + this->update.attach(this, &Axis::paramUpdate, Tdelay); + this->axisState = 0; + this->mot_I_lim = .35; + this->dIdT = 0.0; + this->motI = 0.0; + this->motI_last = 0.0; + this->mot_I_max = 0.0; + this->mot_I_max_last = 0.0; + this->motInvert = 0; + this->dataFormat = 'r'; //default is radians +// this->ctsPerDeg = cpd; //update counts per degree passed from constructor + + this->pid = new PID(0.0,0.0,0.0,Tdelay); //Kc, Ti, Td, interval + this->ls7366 = new LS7366(spi, cs); //LS7366 encoder interface IC + this->motcon = new MotCon(pwm, dir); + this->ptr_limit = limit; + + //start at 0 + this->ls7366->LS7366_reset_counter(); + this->ls7366->LS7366_quad_mode_x4(); + this->ls7366->LS7366_write_DTR(0); + + this->set_point = 0.0; + this->pid->setSetPoint(this->set_point); + this->enc = this->ls7366->LS7366_read_counter(); //update class variable +} + +void Axis::init(void){ + //resets the controllers internals + this->pid->reset(); + + //Encoder counts limit + this->pid->setInputLimits(-55000, 55000); + //Pwm output from 0.0 to 1.0 + this->pid->setOutputLimits(-1.0, 1.0); + //If there's a bias. + this->pid->setBias(0.0); + this->pid->setMode(AUTO_MODE); + + this->pid->setInterval(this->Tdelay); + + //start at 0 + this->ls7366->LS7366_reset_counter(); + this->ls7366->LS7366_quad_mode_x4(); + this->ls7366->LS7366_write_DTR(0); + + this->set_point = 0.0; + this->pid->setSetPoint(this->set_point); + this->enc = this->ls7366->LS7366_read_counter(); //update class variable + + //resets the controllers internals + this->pid->reset(); + //start at 0 + this->set_point = 0.0; + this->pid->setSetPoint(0); + + this->pid->setTunings(this->Pk, this->Ik, this->Dk); //turns on controller +} + +void Axis::updatePIDgains(float P, float I, float D){ + this->Pk = P; //120.0; //rough gains, seem to work well but could use tuning + this->Ik = I; //75.0; + this->Dk = D; + this->pid->setTunings(this->Pk, this->Ik, this->Dk); +} + +void Axis::paramUpdate(void){ + //testOut = 1; + this->enc = this->ls7366->LS7366_read_counter(); + this->pos = (float)this->enc; // * this->countsPerDeg * PI/180.0; //times counts/degree and convert to radians + + this->vel = (this->pos - this->pos_last) * this->Tdelay; + this->acc = (this->vel - this->vel_last); + + this->pid->setSetPoint(this->set_point); + + //Update the process variable. + this->pid->setProcessValue(this->pos); + //Set the new output. + this->co = this->pid->compute(); + + if(this->axisState){ + if(this->motInvert==0){ + this->motcon->mot_control(this->co); //send controller output to PWM motor control command + } + else{ + this->motcon->mot_control(this->co, 1); //send controller output to PWM motor control command + } + } + else{ + this->co = 0.0; + this->motcon->mot_control(0.0); //turn off motor command + } + + this->pos_last = this->pos; + this->vel_last = this->vel; + this->set_point_last = this->set_point; +} + +void Axis::center(void){ + while((*this->ptr_limit == 1) && (this->readCurrent() < mot_I_lim)){ //limit switch not pressed and mot current not exceeded + this->set_point += 100; + wait(.05); + if(this->debug) + printf("T=%.2f SP=%.3f co=%.3f pos=%.3f vel=%.3f acc=%.3f limit=%d motI=%.3f\r\n", t.read(), this->set_point, this->co, this->pos, this->vel, this->acc,*this->ptr_limit, this->_analog.read()); + } + wait(.5); + while((*this->ptr_limit == 0)){ //limit switch is pressed + this->set_point -= 10; + wait(.1); + if(this->debug) + printf("T=%.2f SP=%.3f co=%.3f pos=%.3f vel=%.3f acc=%.3f limit=%d motI=%.3f\r\n", t.read(), this->set_point, this->co, this->pos, this->vel, this->acc,*this->ptr_limit, this->_analog.read()); + } + this->zero(); //zero channel + +// this->set_point = -(totalCounts/2.0); + + if(this->debug) + printf("HOME END:T=%.2f SP=%.3f co=%.3f pos=%.3f vel=%.3f acc=%.3f limit=%d motI=%.3f\r\n", t.read(), this->set_point, this->co, this->pos, this->vel, this->acc,*this->ptr_limit, this->_analog.read()); +// pc.printf("End Home\r\n\r\n"); +} + +void Axis::moveUpdate(void){ + +/* if(*this->ptr_limit == 0){ + this->moveState = 4; //terminate the move + printf("\r\nLimit reached on axis!\r\n"); + } +*/ + if(this->debug) + printf("T=%.2f SP=%.3f co=%.3f pos=%.3f vel=%.3f acc=%.3f limit=%d motI=%.3f\r\n", t.read(), this->set_point, this->co, this->pos, this->vel, this->acc,*this->ptr_limit, this->_analog.read()); + + switch(this->moveState){ + case 0: + break; + + //accelerate + case 1: + //testOut = 1; + this->vel_accum += this->acc_cmd * this->Tdelay; //add acceleration to the velocity accumulator + if(this->vel_avg_cmd > 0.0){ //check the sign of the movement + if(this->vel_accum >= this->vel_cmd) //if the accumulator reaches or exceeds the velocity command + this->vel_accum = this->vel_cmd; // only add the velocity command to the accumulator + } + else{ //if the sign was negative + if(this->vel_accum <= this->vel_cmd) + this->vel_accum = this->vel_cmd; + } + //testOut = 0; + + this->set_point += this->vel_accum; + //pc.printf("T=%.2f SP=%.3f co=%.3f enc=%d pos=%.3f vel=%.3f acc=%.3f vel_accum=%.2f accelCnt=%.2f \r\n", t.read(), con0.set_point, con0.co, con0.enc, con0.pos, con0.vel, con0.acc, vel_accum, accelCnt); + //pc.printf("acc_up,%.2f,%.3f,%.1f,%.3f,%.3f,%.2f,%.2f\r\n", this->t.read(), this->set_point, this->pos, this->vel, this->acc, this->vel_accum, this->acc_cmd); + + if(this->t.read()>=(this->moveTime/3.0) || (abs(this->vel_accum) > abs(this->vel_cmd))) + this->moveState = 2; + break; + + //constant velocity + case 2: + //testOut = 1; + //this->vel_accum += this->vel_cmd * this->Tdelay; + this->set_point += this->vel_cmd; + //testOut = 0; + //pc.printf("T=%.2f SP=%.3f co=%.3f enc=%d pos=%.3f vel=%.3f acc=%.3f vel_accum=%.2f accelCnt=%.2f \r\n", t.read(), con0.set_point, con0.co, con0.enc, con0.pos, con0.vel, con0.acc, vel_accum, accelCnt); + //pc.printf("vel_cn,%.2f,%.3f,%.1f,%.3f,%.3f,%.2f,%.2f\r\n", this->t.read(), this->set_point, this->pos, this->vel, this->acc, this->vel_accum, this->acc_cmd); + + if(this->t.read()>=(2.0/3.0 * this->moveTime)) + this->moveState = 3; + break; + + //decelerate + case 3: + this->vel_accum -= this->acc_cmd * this->Tdelay; + + this->set_point += this->vel_accum; //ramp down velocity by acceleration + //pc.printf("T=%.2f SP=%.3f co=%.3f enc=%d pos=%.3f vel=%.3f acc=%.3f vel_accum=%.2f accelCnt=%.2f \r\n", t.read(), con0.set_point, con0.co, con0.enc, con0.pos, con0.vel, con0.acc, vel_accum, accelCnt); + //pc.printf("acc_dn,%.2f,%.3f,%.1f,%.3f,%.3f,%.2f,%.2f\r\n", this->t.read(), this->set_point, this->pos, this->vel, this->acc, this->vel_accum, this->acc_cmd); + + if(this->vel_avg_cmd > 0.0){ + if(this->pos_cmd <= this->pos){ + //finish with position command + this->set_point = this->pos_cmd; + this->moveState = 4; + } + } + else{ + if(this->pos_cmd >= this->pos){ + //finish with position command + //this->set_point = this->pos_cmd; //May be causing jerk after multi-axis movements J Bradshaw 20151124 + this->moveState = 4; + } + } + + if(this->t.read()>=this->moveTime){ + //finish with position command + //this->set_point = this->pos_cmd; //May be causing jerk after multi-axis movements J Bradshaw 20151124 + this->moveState = 4; + } + break; + + case 4: + this->moveProfile.detach(); //turn off the trapazoidal update ticker + this->t.stop(); + this->moveState = 0; + break; + }//switch moveStatus + return; +} + +// position - encoder position to move to +// time - duration of the movement +void Axis::moveTrapezoid(float positionCmd, float time){ + this->pos_cmd = positionCmd; + this->moveTime = time; + float enc_distance = this->pos_cmd - (float)this->enc;// * 1.0/con0.countsPerDeg * 180.0/PI; + + this->vel_avg_cmd = enc_distance / time; + this->vel_cmd = 1.5 * this->vel_avg_cmd * this->Tdelay; + this->acc_cmd = 4.5 * (enc_distance / (this->moveTime * this->moveTime)) * this->Tdelay; + + //pc.printf("tx=%f encdist=%.3f vAvg=%.3f vMax=%.3f Acc=%.3f \r\n", this->moveTime, enc_distance,this->vel_avg_cmd,this->vel_cmd,this->acc_cmd); + + //establish encoder velocities and accelerations for position control per Tdelay + this->vel_accum = 0.0; + +// this->set_point = this->pos; + this->moveState = 1; + this->t.reset(); + this->t.start(); + this->moveProfile.attach(this, &Axis::moveUpdate, this->Tdelay); +} + +float Axis::readCurrent(void){ + this->motI = (this->_analog.read() * 3.3) / .525; //525mV per amp + if(abs(this->motI) > this->mot_I_max){ //update the max motor current measured + this->mot_I_max = this->motI; + } + this->dIdT = motI - motI_last; + this->motI_last = motI; + return this->motI; +} + +void Axis::axisOff(void){ + this->co = 0; + this->axisState = 0; + this->update.detach(); +} + +void Axis::axisOn(void){ + this->co = 0.0; + this->pid->reset(); + //start at 0 if not already homed + if(this->stat != 0){ + this->set_point = 0.0; + this->pid->setSetPoint(0); + } + + this->pid->setTunings(this->Pk, this->Ik, this->Dk); //turns on controller + this->axisState = 1; + this->update.attach(this, &Axis::paramUpdate, this->Tdelay); +} + +void Axis::zero(void){ + + this->ls7366->LS7366_reset_counter(); + this->ls7366->LS7366_quad_mode_x4(); + this->ls7366->LS7366_write_DTR(0); + + this->enc = this->ls7366->LS7366_read_counter(); + + this->pos = 0.0; + this->pid->reset(); //added to avoid possible itegral windup effects on instant position change 20170616 + this->set_point = 0.0; + this->pid->setSetPoint(0); + +} + +void Axis::writeEncoderValue(long value){ + + this->ls7366->LS7366_write_DTR(value); + + this->set_point = (float)value; + this->pid->setSetPoint(this->set_point); +} \ No newline at end of file
diff -r 000000000000 -r d7f0715676f4 Axis.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Axis.h Thu Nov 08 17:54:30 2018 +0000 @@ -0,0 +1,77 @@ + + +#ifndef MBED_AXIS_H +#define MBED_AXIS_H + +#include "mbed.h" +#include "PID.h" //library for software routine PID controller +#include "LS7366.h" //library for quadrature encoder interface IC's +#include "MotCon.h" //simple motor control routines + +class Axis{ +public: + Axis(SPI& _spi, PinName _cs, PinName _pwm, PinName _dir, PinName _analog, int* limit); + void paramUpdate(void); + void center(void); + void init(void); + void moveTrapezoid(float position, float time); + void moveUpdate(void); + float readCurrent(void); + void axisOff(void); + void axisOn(void); + void zero(void); + void writeEncoderValue(long value); + void updatePIDgains(float P, float I, float D); + + long enc; //used to return the data from the LS7366 encoder chip + float co; // = 0.0; + float Tdelay; // = .01; + float Pk; // 120.0 for scorbot + float Ik; // 55.0 for scorbot + float Dk; + float set_point;// = 0.0; + float set_point_last; + float pos, vel, acc; //calculated position, velocity, and acceleration + int stat; //overall axis status + float pos_last, vel_last, acc_last; //history variables used to calculate motion + float pos_cmd, vel_cmd, vel_avg_cmd, acc_cmd; + float vel_max, acc_max; + float vel_accum; + float moveTime; + float p_higher, p_lower; + int moveStatus; + int moveState; + int debug; + int *ptr_limit; + float motI; //motor current read from readCurrent() function + volatile float motI_last; + float mot_I_lim; //max current limit + float dIdT; + float mot_I_max, mot_I_max_last; + int axisState; + int motInvert; + char dataFormat; //'r'=radians (default), 'd'=degrees, 'e'=encoder counts + float pos_rad, vel_rad; //current position measurement in radians + float pos_deg, vel_deg; //current position measurement in degrees + float ctsPerDeg; + int busyflag; + + Ticker update; + Ticker moveProfile; + Timer t; + PID *pid; + LS7366 *ls7366; + MotCon *motcon; + //AnalogIn *motCurrent; + +private: + SPI _spi; + DigitalOut _cs; + PwmOut _pwm; + DigitalOut _dir; + AnalogIn _analog; +}; + +#endif + +
diff -r 000000000000 -r d7f0715676f4 LS7366LIB.lib --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/LS7366LIB.lib Thu Nov 08 17:54:30 2018 +0000 @@ -0,0 +1,1 @@ +http://developer.mbed.org/users/jebradshaw/code/LS7366LIB/#2193b220248b
diff -r 000000000000 -r d7f0715676f4 MotCon.lib --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MotCon.lib Thu Nov 08 17:54:30 2018 +0000 @@ -0,0 +1,1 @@ +https://developer.mbed.org/users/jebradshaw/code/MotCon2/#709761ee0a14
diff -r 000000000000 -r d7f0715676f4 PID.lib --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/PID.lib Thu Nov 08 17:54:30 2018 +0000 @@ -0,0 +1,1 @@ +https://os.mbed.com/users/jebradshaw/code/PID/#d5d6a555cc8c