Achieving float speeds for a motor speed control setup

Hello everyone,

I am working on a project where I need to control the motor speed of a DC motor with an encoder. I am using this motor, an Arduino UNO, an L298N motor driver, and a 12V power supply. The circuit diagram is attached below.

My goal is to input a goal speed in RPM and use the PID control formula to correct the error between the goal speed and the actual speed measured by an accelerometer. I accomplished doing that for all integer speeds, but am struggling to write my code in a way to achieve float speeds. For example, if I want my motor to run at 5.5rpm, the output speed is 6rpm every time.

From my understanding, a float will occur if you set all values run through calculations to have the same number of values after the decimal, but when I do that, I still get an integer. I've attached my code below. If there is any way I can modify my code to achieve float speeds, please let me know. Any and all help is greatly appreciated! Thank you!!

#define ENCA 2 // ORANGE
#define ENCB 3 // YELLOW 
#define IN2 8 // GREY
#define IN1 9 // PINK
#define PWM 10 // BLUE

float ipos = 0.0;
float prevT = 0.0;
float eprev = 0.0;
float eintegral = 0.0;
float actualRPM = 0.0;

void setup() {
  Serial.begin(9600);
  pinMode(ENCA,INPUT);
  pinMode(ENCB,INPUT);
  attachInterrupt(digitalPinToInterrupt(ENCA),readEncoder,RISING);
  
  pinMode(PWM,OUTPUT);
  pinMode(IN1,OUTPUT);
  pinMode(IN2,OUTPUT);
}

void loop() {

  // set target position
  float desiredRPM = 0.00; //CHANGE THIS LINE ONLY
  float target = 20.9124*desiredRPM *(prevT/1.0e3); // Calculated based on accelerometer output
  
  
  // PID constants (set before running device -different for each motor)
  float kp = 15.0; 
  float kd = 0.75;
  float ki = 0.75;

  // time difference
  float currT = millis();
  float deltaT = ((currT - prevT))/(1.0e3);
  prevT = currT;


  float pos = 0.0; 
  {
    pos = ipos;
  }

  // error
  float e = pos - target;

  // derivative
  float dedt = (e-eprev)/(deltaT);

  // integral
  eintegral = eintegral + e*deltaT;

  // control signal
  float u = kp*e + kd*dedt + ki*eintegral;

  // motor power
  float pwr = fabs(u);
  if( pwr > 255){
    pwr = 255;
  }

  // motor direction
  float dir = 1;
  if(u<0){
    dir = -1;
  }

  // actual RPM
  float actualRPM = (ipos*1.0e3)/(20.9124*prevT);

  // signal the motor
  setMotor(dir,pwr,PWM,IN1,IN2);


  // store previous error
  eprev = e;

  Serial.print(" Speed: ");
  Serial.print(actualRPM);
  Serial.print(" RPM");
  Serial.println();
}

void setMotor(int dir, int pwmVal, int pwm, int in1, int in2){
  analogWrite(pwm,pwmVal);
  if(dir == 1){
    digitalWrite(in1,HIGH);
    digitalWrite(in2,LOW);
  }
  else if(dir == -1){
    digitalWrite(in1,LOW);
    digitalWrite(in2,HIGH);
  }
  else{
    digitalWrite(in1,LOW);
    digitalWrite(in2,LOW);
  }  
}

void readEncoder(){
  int b = digitalRead(ENCB);
  if(b > 0){
    ipos++;
  }
  else{
    ipos--;
  }
}

What is this? Are you using the breadboard as a junction point?
image

Please take a pen and paper, and draw a complete, fully annotated schematic. Also provide links to all your actual hardware devices, or else post complete specifications.

Sorry, but your diagram is not useful. For example, there are rainbow wires from the motor, but no labels to tell what they are.

I've never heard that terminology before. What do you mean, exactly?

From my understanding, a float will occur if you set all values run through calculations to have the same number of values after the decimal

That is a misunderstanding. Please consult the Arduino language reference page and see what it tells you about 'int' and 'float' data types.

An encoder position can take on only integer values. It is pointless to use a float variable.

  // time difference
  float currT = millis();

Likewise, elapsed and current times are reported as integer values of milliseconds, and must be declared unsigned long (integer).

1 Like

MCU's execute millions of instructions in one second. So it's not restricted to counting seconds. Every expression like 5.5RPM can be stated using integers, e.g. 55 revolutions per 10 seconds.

Or better, can be expressed as a period in milliseconds for example, (60 x 60 x 1000) / 5.5 and rounded to an integer value.

1 Like

My apologies, this is my first time posting. I changed the diagram to hopefully give more information and linked the hardware I am using.

Thanks for the updates. However, in future don't edit previous posts, it makes replies into nonsense. just add information in new posts.

1 Like

Are the digital and motor grounds common on the driver board?

I am not sure what you mean by this. Common as in shared on the driver board? If so, yes.

A commonly used technique is "scaled integer arithmetic". If you are interested in speed precision of 0.1 RPM then scale so that 1 = 0.1 RPM. Then a value of 55 gives you 5.5 RPM.

Floating point is very slow and really should be avoided in a microcontroller. Which makes this amusing as "float speeds" aren't speedy at all!

2 Likes

I want to work to millimeters, my working unit is 1 micrometer so that when I divide I have 3 extra places to lose to round-offs before it matters.

unsigned long is good for 9 places 0 to 9 where Arduino float is good to 6.

Anything you can pre-compile and table into flash can speed your code insanely, that is how flight sims ran on 6502's at all!

If you really want FP then get a Teensy 4.1 that has an AMD M4 core with FPU running at 600MHz. You might have to wait.

IMO, an Uno can do this with ease.

1 Like

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.