Motor smoother speed changes

Hi everyone,

I was recently able to control the speed of my 24V geared electric motor, with a driver, an arduino UNO and a joystick controller.
However, when I do a quick deceleration of the motor from full speed to nothing (e.g duty cycle=250 -> duty cycle=0), I cannot understand why he does a little "jump"..

  if(sensor <200) {
      
       analogWrite (pin1, 0); 
       digitalWrite(pin2,LOW);
       digitalWrite(pinEN,HIGH);
    
    }

      if(sensor >200 && sensor < 400) {
      
        analogWrite(pin1,80); 
        digitalWrite(pin2,LOW);
        digitalWrite(pinEN,HIGH);
       }
       
        if(sensor > 400 && sensor <600) {
           
       analogWrite(pin1, 127); 
       digitalWrite(pin2,LOW);
       digitalWrite(pinEN,HIGH);
       }
       
        if(sensor > 600 && sensor < 800) {
         
        analogWrite(pin1,180);
        digitalWrite(pin2,LOW);
        digitalWrite(pinEN,HIGH);
        }
      
     if(sensor > 800) {
      
        analogWrite(pin1,250);
        digitalWrite(pin2,LOW);
        digitalWrite(pinEN,HIGH);
     }

This is the code I'm using just to change the speed of the motor.

My question is: if there's a way that I can obtain smoother speed changes using my arduino UNO to avoid those motor "jumps"? I hope I can do that with some algorithm.

Thanks in advanced!

You seem to be controlling the output PWM value using a small number of discrete steps.

You would get a smoother response if you made the output PWM value vary continuously with the input value so that you had a completely proportional control. The map() function provides a convenient way to convert an input value e.g. an analogRead() input value in the range 0 .. 1023 to an output value e.g. an analogWrite() value in the range 0 .. 255.

Thanks PeterH,

I don't know if the map() function will work with my joystick.
http://media.digikey.com/pdf/Data%20Sheets/APEM%20Components%20PDFs/BF%20Series.pdf

But in case it does, will the code be something like this?

void loop () {

 int val = A0;   
 int sensor = 0;
  
  // read the value from the sensor:
  sensor = analogRead(val);   

 if(sensor <200) {
      
       val = map(val, 0, 1023, 0, 255);
       analogWrite (pin1, val); 
       digitalWrite(pin2,LOW);
       digitalWrite(pinEN,HIGH);
    
    }

      if(sensor >200 && sensor < 400) {

       val = map(val, 0, 1023, 0, 80);
        analogWrite(pin1,val); 
        digitalWrite(pin2,LOW);
        digitalWrite(pinEN,HIGH);
       }
       
        if(sensor > 400 && sensor <600) {

       val = map(val, 0, 1023, 0, 127);    
       analogWrite(pin1, val); 
       digitalWrite(pin2,LOW);
       digitalWrite(pinEN,HIGH);
       }
       
        if(sensor > 600 && sensor < 800) {

        val = map(val, 0, 1023, 0, 180);
        analogWrite(pin1,val);
        digitalWrite(pin2,LOW);
        digitalWrite(pinEN,HIGH);
        }
      
     if(sensor > 800) {

        val = map(val, 0, 1023, 0, 250);
        analogWrite(pin1,val);
        digitalWrite(pin2,LOW);
        digitalWrite(pinEN,HIGH);
     }
}

Serge_Brien:
But in case it does, will the code be something like this?

Unlikely. The range of possible return values from analogRead() is 0 .. 1023. What values you actually get in your case will depend on the external hardware you're connecting, and you need to know what range of return values correspond to the range of joystick positions. Then you need to decide how you want to relate the motor speed to those positions.

In your original code, you just tested the position against four fixed ranges and had a fixed speed for each range, so the motor ran at one of four speeds. This doesn't give a smooth progression and I assume this is why it isn't working smoothly. I suggest that instead of using four fixed speeds, you use the joystick position to calculate a proportional speed. The map() function would be an easy way to do that.

int joystickPosition = analogRead(JOYSTICK_PIN);
int motorSpeed = map(joystickPosition, JOYSTICK_MIN, JOYSTICK_MAX, MOTOR_MIN, MOTOR_MAX);
analogWrite(MOTOR_SPEED_PIN, motorSpeed);

JOYSTICK_MIN and JOYSTICK_MAX are two constants you would define which record the analogRead() values corresponding to the extreme positions of the joystick.
MOTOR_MIN and MOTOR_MAX are two constants you would define which record the motor speed you want at those extreme positions, probably 0 and 255.

  • with if then else if you can test less (sort of chain of tests)
  • you probably wanted to map the sensor value not the pin number.
  • consistent indentation makes code readable (use CTRL-T in the IDE)
void loop () 
{
  int val = A0;   
  int sensor = 0;

  // read the value from the sensor:
  sensor = analogRead(val);   

  if(sensor < 200) 
  {
    val = map(sensor, 0, 200, 0, 255);
    analogWrite (pin1, val); 
    digitalWrite(pin2,LOW);
    digitalWrite(pinEN,HIGH);
  }
  else if (sensor < 400)
  {
    val = map(sensor, 200, 400, 255, 200);
    analogWrite(pin1,val); 
    digitalWrite(pin2,LOW);
    digitalWrite(pinEN,HIGH);
  }
  else if(sensor < 600) 
  {
    val = map(sensor, 400, 600, 200, 65);    
    analogWrite(pin1, val); 
    digitalWrite(pin2,LOW);
    digitalWrite(pinEN,HIGH);
  }
  else if(sensor < 800) 
  {
    val = map(sensor, 600, 800, 65, 20);
    analogWrite(pin1,val);
    digitalWrite(pin2,LOW);
    digitalWrite(pinEN,HIGH);
  }
  else 
  {
    val = map(sensor, 800, 1023, 20, 255);
    analogWrite(pin1,val);
    digitalWrite(pin2,LOW);
    digitalWrite(pinEN,HIGH);
  }
}

Note mapping is a bit of random but the 'segments' match in their values - check this.

Sorry for only being able to reply today, but I had some problems with driver I'm using..
Nonetheless, your comments helped me a lot :slight_smile:

In your original code, you just tested the position against four fixed ranges and had a fixed speed for each range, so the motor ran at one of four speeds. This doesn't give a smooth progression and I assume this is why it isn't working smoothly. I suggest that instead of using four fixed speeds, you use the joystick position to calculate a proportional speed. The map() function would be an easy way to do that.

I did that, and by the time I have the code working perfectly I will also use the global variables as you told me.

  • with if then else if you can test less (sort of chain of tests)
  • you probably wanted to map the sensor value not the pin number.
  • consistent indentation makes code readable (use CTRL-T in the IDE)

I agree, thanks!

Note mapping is a bit of random but the 'segments' match in their values

I didn't understand this sentence. However, I think you're telling me that in order to adjust the values I want, I have to follow a "trial and error" approach.. At least I did that with the code below.

void loop () 
{
  int val = A0;   
  int sensor = 0;

  // read the value from the sensor:
  sensor = analogRead(val);   

  if(sensor < 200) 
  {
    val = map(sensor, 0, 200, 0, 0);
    analogWrite (pin1, val); 
    digitalWrite(pin2,LOW);
    digitalWrite(pinEN,HIGH);
    Serial.println(val);
  }
  else if (sensor < 400)
  {
    val = map(sensor, 200, 400, 0, 160);
    analogWrite(pin1,val); 
    digitalWrite(pin2,LOW);
    digitalWrite(pinEN,HIGH);
    Serial.println(val);
  }
  else if(sensor < 600) 
  {
    val = map(sensor, 400, 600, 160, 100);    
    analogWrite(pin1, val); 
    digitalWrite(pin2,LOW);
    digitalWrite(pinEN,HIGH);
    Serial.println(val);
  }
  else if(sensor < 800) 
  {
    val = map(sensor, 600, 800, 100, 236); 
    analogWrite(pin1,val);
    digitalWrite(pin2,LOW);
    digitalWrite(pinEN,HIGH);
    Serial.println(val);
  }
  else 
  {
    val = map(sensor, 800, 1023, 236, 252);  
    analogWrite(pin1,val);
    digitalWrite(pin2,LOW);
    digitalWrite(pinEN,HIGH);
    Serial.println(val);
  }
}

I have attached 3 images, they represent the transition between the 3 middle speeds.
Is that what I'm supposed to get, from map function() ?
Is it possible to get an even more smoother change of speed, or this is the limit with the joystick I'm using?

Serge_Brien:
I did that, and by the time I have the code working perfectly I will also use the global variables as you told me.

I'm puzzled that you seem to agree with me but then completely ignore what I wrote.

What your original code does is work out which of five ranges the input value is and output one of five fixed speeds. Obvious this gives you very coarse control with abrupt changes between the five speeds.

You can throw all those if statements away and replace it with a single call to map(), as I showed you. That is ONE line of code giving a smooth progression from no power to full power.

Sorry PeterH, I should have been more clear in my previous comment.

I already tried the code with your suggestion, but the result is similar with the last code I posted. The change of speed isn't more smoother..

int pin1 = 9; 

int JOYSTICK_MIN = 0;
int JOYSTICK_MAX = 1023;

int MOTOR_MIN = 0;
int MOTOR_MAX = 250;

void setup () 
{ 

 pinMode(pin1,OUTPUT) ; // define the interface to the output interface  
 
 Serial.begin(9600);
} 

void loop () 
{
  //int val = A0;  
  //int sensor = 0; 
  int JOYSTICK_PIN = A0;
  
  // read the value from the sensor:
  //sensor = analogRead(val);   
  int joystickPosition = analogRead(JOYSTICK_PIN);
  
  int motorSpeed = map(joystickPosition, JOYSTICK_MIN, JOYSTICK_MAX, MOTOR_MIN, MOTOR_MAX);
  
  analogWrite(pin1, motorSpeed);
  Serial.println(motorSpeed);

}

It's by far more presentable, but at the same time using this code as it is, when the joystick is placed on the first position the motor is already running (the serial monitor shows 25), instead of beginning with 0 like I had before.
Am I doing something wrong?

Looks like the joystick doesn't actually ever give you zero volts. Add a serial print to show what it gives and adjust your two joystick parameters accordingly.

Just like you said wildbill! That problem is resolved.

I just tested the code with map function(), the dc motor and the driver. Unfortunately I think this function it's not suited for this application.. the result was the same. It's executed way too fast, so the motor receives the signal with fixed values and when I do a quick deceleration of the motor it still "jumps".

Also as I mentioned before, I'm not sure if the map() function will works with this joystick.
http://media.digikey.com/pdf/Data%20Sheets/APEM%20Components%20PDFs/BF%20Series.pdf
It's not completely linear, it has 5 different positions..

Is the problem related with the joystick?

It's not completely linear, it has 5 different positions..

The Paddle operates from 5V and
provides two proportional outputs.

I'm sorry, but one of you is wrong.