encoder feedback with potentiometer input to create error output to run motor

I need to position control of bldc motor which is having encoder(20000 pulses/rotation) connected with motor shaft.

Setpoint is giving from Potentiometer.

Input is a encoderPos.

error is the (Setpoint-Input) to program the system with PID.

I have a little bit confusion how the encoderPos count will map with Potentiometer setpoint to get the error.

how to program encoderPos count.

by seeing example codes I prepared the following code. this is incomplete.

If error signal code part solved for PID coding Iam trying to write with brettbeauregard PID library.

code]

const int encodeA =2;
const int encodeB=3;

volatile long encoderPos = 0;

double input=0, output=0, setpoint=0;

double error=0;

void setup() {
  pinMode(encodeA, INPUT_PULLUP);
  pinMode(encodeB, INPUT_PULLUP);
  
  attachInterrupt(0, encoder, FALLING);

}

void loop() {
  setpoint= analogRead(A0);

  input=encoderPos;

  error=setpoint-input;
  
}

void encoder()
{
 
}

[/code]

With this code

void loop() {
  setpoint = analogRead(A0);
  input = encoderPos;

  error = setpoint - input;
}

your calculation is comparing apples and oranges.

analogRead() produces values in the range 0 to 1023.

What is the range of values for encoderPos?

You will need to convert one of them so that the ranges match.

...R

if the error != zero, step the motor until it becomes zero.

step in the direction based on the sign of the error.

since the pot range is 0-1023 and there are 20,000 step/rotation, you may want he scale (e.g. x20) the pot value

It depends on what you want the values from the potentiometer to mean. Is it a position? Is it a speed? Is it an acceleration? Pick one.

Encoder datasheet or brand name & part number? I would suspect it's a 5000 PPR.

it is for position control.

encoder is incremental and Make:IVO, Model : G0355, partNo:72 which is a 20000 pulses.
datasheet attached.

Motor is BLDC motor with 200RPM and 3Nm.

Motor drive needs 0-5 Volts from controller output which is an error signal instead of PWM out.

By studying the brett beauregard Arduino PID library I prepared the code which is incomplete.

Iam trying to implement the code using arduino due to get DAC output.

to modify analog output range to 0-5v ,OP amp differential amplifier hardware is used.

seperate Motor brake and Motor direction signals are required.

In this code i need a help to program encoder function and how to map encoder count(feedbackPos) and potentiometer position (potPos/setpoint)to generate error signal.

And also pl.suggest/corrections in my code.
it is grate helpful to me.

[code]
int encoderA = 2;
int encoderB = 3;

int MotBrake = 6;
int MotDirection = 4;

double kp = 0, ki = 0, kd = 0;

double PotPos = 0, feedbackPos = 0, output = 0;

volatile long encoderPos = 0;

unsigned long lastTime;

double Iterm, lastfeedbackPos;

int sampleTime = 1000;

double outMin, outMax;

bool inAuto = false;


#define MANUAL 0
#define AUTOMATIC 1

#define DIRECT 0
#define REVERSE 1

int controllerDirection = DIRECT;


void setup()
{
  pinMode(encoderA, INPUT_PULLUP);
  pinMode(encoderB, INPUT_PULLUP);

  pinMode(DAC0, OUTPUT);
  pinMode(A0, INPUT);
  
  analogWriteResolution(12);
  analogReadResolution(12);
  
  [attachInterrupt(0, encoder, FALLING);

  SetMode(AUTOMATIC);
  SetOutputLimits(0, 4095);
  setSampleTime(1);
  Serial.begin(115200);
}

void loop()

{

PotPos=analogRead(A0);
Serial.println(PotPos);

feedbackPos=encoderPos;
Serial.println(encoderPos);

compute();

analogWrite(DAC0,output);

Serial.println(output);


if (output==0)
digitalWrite(MotBrake, LOW);
else
digitalWrite(MotBrake, HIGH);

}

void compute()
{
  if(!inAuto)
  return;
  unsigned long now=millis();
  int timeChange =(now-lastTime);
  if(timeChange>+sampleTime)
  {
    double error=(PotPos-feedbackPos);

    Iterm+=(ki*error);
   
    if (Iterm>outMax)
    Iterm=outMax;
    else if(Iterm<outMin)
    Iterm=outMin;

    double dfeedbackPos=(feedbackPos-lastfeedbackPos);
    
    output=kp*error+Iterm-kd*dfeedbackPos;
    
    if(output>outMax)
    output=outMax;
    
    else if(output<outMin)
    output=outMin;

    lastfeedbackPos=feedbackPos;
    lastTime=now;
      }
}







void encoder()
{

}


void SetMode(int Mode)
{
  bool newAuto = (Mode == AUTOMATIC);
  if (newAuto && !inAuto)
  {
    initialize();
  }
  inAuto = newAuto;
}

void initialize()
{
  lastfeedbackPos=feedbackPos;
  Iterm=output;
  if(Iterm>outMax)
  Iterm=outMax;
  else if(Iterm<outMin)
  Iterm=outMin;
}


void  SetOutputLimits(double  Min, double Max)
{
if(Min>Max)
return;
outMin=Min;
outMax=Max;

if(output>outMax)
output=outMax;

else if (output<outMin)
output=outMin;

if(Iterm>outMax)
Iterm=outMax;

else if (Iterm<outMin)
Iterm=outMin;
}

void setSampleTime(int NewSampleTime)
{
  if(NewSampleTime>0)
  {
    double ratio = (double)NewSampleTime/(double)sampleTime;
    ki*=ratio;
    kd/=ratio;
    sampleTime=(unsigned long)NewSampleTime;
  }
}

void setTuning(double Kp,double Ki, double Kd)
{
  if (kp<0 || ki<0 ||kd<0)
  return;
  double SampleTimeinSec = ((double)sampleTime)/1000;
  kp=Kp;
  ki=Ki*SampleTimeinSec;
  kd=Kd/SampleTimeinSec;
  if(controllerDirection==REVERSE)
  {kp=(0-kp);
   ki=(0-ki);
   kd=(0-kd);
   }
}

[/code]

incremental encoder.pdf (280 KB)

as a start, looks like you need code to read the encoder -- Rotary Encoder : How to use the Keys KY-040 Encoder on the Arduino

It looks like much of your sketch is an attempt to duplicate the behavior of the PID_v1 library without using the PID_v1 library. That is something you can do later if you want to learn the internals of PID but will slow you way down. Try using existing libraries before writing code to replace them.

Here is an attempt at your desired sketch using the Encoder and PID_v1 libraries. Of course I don't know if this works on your hardware since I don't have access to it.

You will need to decide what positions of the encoder the positions of the pot represent. Since you have not said, I put in a range of 0 to 10 revolutions forward.

#include <Encoder.h>
#include <PID_v1.h>


// Encoder pins
const byte encoderA = 2;
const byte encoderB = 3;
const long EncoderStepsPerRevoltion = 20000L;
Encoder encoder(encoderA, encoderB);


// Motor pins
const byte MotBrake = 6;
const byte MotDirection = 4;
const byte MotSpeed = DAC0;


// PID Variables
double Setpoint, Input, Output;
const byte InputPin = A0;


// initial tuning parameters
double Kp = 2, Ki = 5, Kd = 1;


PID pid(&Input, &Output, &Setpoint, Kp, Ki, Kd, DIRECT);


void setup()
{
  Serial.begin(115200);
  while (!Serial);


  analogWriteResolution(12); // 4096 steps
  // pinMode(MotSpeed, OUTPUT);  // DO NOT USE WITH analogWrite(). Only digitalWrite()


  analogReadResolution(12); // 4096 steps
  // pinMode(InputPin, INPUT);  // DO NOT USE WITH analogRead(),  Only digitalRead()


  pid.SetMode(AUTOMATIC);
  pid.SetOutputLimits(-4095, 4095);
}


void loop()
{
  // The 'Setpoint' is where you want the motor to be
  Setpoint = analogRead(InputPin) * (10 * EncoderStepsPerRevoltion) / 4096.0;


  Input = encoder.read();  // Current encoder position


  pid.Compute();
  Serial.println(Output);


  if (Output < -0.1)
  {
    digitalWrite(MotBrake, HIGH);
    digitalWrite(MotDirection, LOW);
    analogWrite(DAC0, -Output);
  }
  else if (Output > 0.1)
  {
    digitalWrite(MotBrake, HIGH);
    digitalWrite(MotDirection, HIGH);
    analogWrite(DAC0, Output);
  }
  else
  {
    analogWrite(DAC0, 0);
    digitalWrite(MotBrake, LOW);
  }
}

Hi,
How many degrees of rotation do you want to obtain.
How are you controlling the BLDC motor.
What is its encoder.
What is the overall application?
It looks like you are trying to make a position servo.

Will a 200RPM BLDC motor be happy stalled in one position as you set the angle of the shaft?
Is the encoder connected to the 200RPM output shaft.

Thanks.. Tom.. :slight_smile:

200 * 20000 / 60 = 66666.7 pulses per second, can your Arduino read that without missing pulses?

Hi,
Have you got the hardware parts and assembled?
Have you got code that makes sure you are able to JUST read the encoder?
Have you got code that makes sure you are able to JUST control the BLDC motor?

What is your projects application?

Thanks.. Tom... :slight_smile:

Motor Drive available which needs input of 0-5V analog voltage.

Using Arduino Due the output may be 0.55 - 2.75.

There is a circuit of differential OPAMP , to raise the analog voltage to 0-5V

Motor drive needs seperate Motor brake and direction signals from controller.

BLDC motor & encoder available.

BLDC Motor : Speed 200 RPM, 3Nm

Encoder : 20,000 pulses./ rotation (Make:IVO, Model;G0355, partNo.72, datasheet enclosed )

Now I made code by duplicating the PID library code instead of using library to use the same code in any platform and also to learn things being a new to this area.

Attaching the code.

Now I want read the Potentiometer Value(setpoint/potPos) and encoderPos(feedbackPos) to create error signal.

I have not understood to relate the PotPos and encoder counts.

How to program the encoderPos (counts as change, rising or falling) and relate with Potentiometer Value
to generate Error signal.

The computed PID output should be Analog signal.

Application is servo position control of elevation.

int encoderA = 2;
int encoderB = 3;
int MotBrake = 6;
int MotDirection = 4;

double kp = 0, ki = 0, kd = 0;

double PotPos = 0, feedbackPos = 0, output = 0;
volatile long encoderPos = 0;

unsigned long lastTime;
double Iterm, lastfeedbackPos;

int sampleTime = 1000;
double outMin, outMax;
bool inAuto = false;


#define MANUAL 0
#define AUTOMATIC 1

#define DIRECT 0
#define REVERSE 1
int controllerDirection = DIRECT;


void setup()
{
  pinMode(encoderA, INPUT_PULLUP);
  pinMode(encoderB, INPUT_PULLUP);
  pinMode(DAC0, OUTPUT);
  pinMode(A0, INPUT);
  
  analogWriteResolution(12);
  analogReadResolution(12);
  
  attachInterrupt(0, encoder, FALLING);

  SetMode(AUTOMATIC);
  SetOutputLimits(0, 4095);
  setSampleTime(1);
  Serial.begin(115200);
}
void loop()
{
PotPos=analogRead(A0);
Serial.println(PotPos);

feedbackPos=encoderPos;
Serial.println(encoderPos);

compute();

analogWrite(DAC0,output);

Serial.println(output);

if (output==0)
digitalWrite(MotBrake, LOW);
else
digitalWrite(MotBrake,HIGH);
}

void compute()
{
  if(!inAuto)
  return;
  unsigned long now=millis();
  int timeChange =(now-lastTime);
  if(timeChange>+sampleTime)
  {
    double error=(PotPos-feedbackPos);
    Iterm+=(ki*error);
   
    if (Iterm>outMax)
    Iterm=outMax;
    else if(Iterm<outMin)
    Iterm=outMin;
    double dfeedbackPos=(feedbackPos-lastfeedbackPos);
    
    output=kp*error+Iterm-kd*dfeedbackPos;
    
    if(output>outMax)
    output=outMax;
    
    else if(output<outMin)
    output=outMin;

    lastfeedbackPos=feedbackPos;
    lastTime=now;
      }
}
void encoder()
{

}

void SetMode(int Mode)
{
  bool newAuto = (Mode == AUTOMATIC);
  if (newAuto && !inAuto)
  {
    initialize();
  }
  inAuto = newAuto;
}

void initialize()
{
  lastfeedbackPos=feedbackPos;
  Iterm=output;
  if(Iterm>outMax)
  Iterm=outMax;
  else if(Iterm<outMin)
  Iterm=outMin;
}


void  SetOutputLimits(double  Min, double Max)
{
if(Min>Max)
return;
outMin=Min;
outMax=Max;

if(output>outMax)
output=outMax;

else if (output<outMin)
output=outMin;

if(Iterm>outMax)
Iterm=outMax;

else if (Iterm<outMin)
Iterm=outMin;
}

void setSampleTime(int NewSampleTime)
{
  if(NewSampleTime>0)
  {
    double ratio = (double)NewSampleTime/(double)sampleTime;
    ki*=ratio;
    kd/=ratio;
    sampleTime=(unsigned long)NewSampleTime;
  }
}

void setTuning(double Kp,double Ki, double Kd)
{
  if (kp<0 || ki<0 ||kd<0)
  return;
  double SampleTimeinSec = ((double)sampleTime)/1000;
  kp=Kp;
  ki=Ki*SampleTimeinSec;
  kd=Kd/SampleTimeinSec;
  if(controllerDirection==REVERSE)
  {kp=(0-kp);
   ki=(0-ki);
   kd=(0-kd);
   }
}

TomGeorge:
Hi,
Have you got the hardware parts and assembled?
Have you got code that makes sure you are able to JUST read the encoder?
Have you got code that makes sure you are able to JUST control the BLDC motor?

What is your projects application?

Thanks.. Tom... :slight_smile:

TomGeorge:
Hi,
Have you got the hardware parts and assembled?
Have you got code that makes sure you are able to JUST read the encoder?
Have you got code that makes sure you are able to JUST control the BLDC motor?

What is your projects application?

Thanks.. Tom... :slight_smile:

TomGeorge:
Hi,
Have you got the hardware parts and assembled?
Have you got code that makes sure you are able to JUST read the encoder?
Have you got code that makes sure you are able to JUST control the BLDC motor?

What is your projects application?

Thanks.. Tom... :slight_smile:

incremental encoder.pdf (131 KB)

Hi,
Please answer these questions.

  • HAVE YOU GOT ANY HARDWARE?
  • HAVE YOU CONNECTED IT TO THE DUE?
  • What are the specs of the BLDC controller, post link to data/specs.
  • Have you got the encoder connected to the DUE and reading the encoder output?
  • What output configuration is the encoder, what is its FULL part number?

FORGET THE PID for the moment.
Writing massive amounts of untried code without getting the basic I/O working first is courting disaster.

Lets get the basic I/O working.

If you have a schematic, PLEASE post it.

Can you please tell us your electronics, programming, arduino, hardware experience?

Thanks.. Tom... :slight_smile:

odces:
I have not understood to relate the PotPos and encoder counts.

The PID controller needs to know the current position (Input) and the desired position (Setpoint). Those two numbers must be in the same units of measure, like Inches, Centimeters, Degrees, Radians. Pick a unit. Convert both measurements to that unit. To get the Error term, subtract Setpoint from Input.