Turning a motor into a servo

Hello,

I bought two Pololu 131:1 metal 37Dx57 motors with a dual MC33926 motor driver like two months ago. This week I had some time to test my investment. I had some linear potentiometers so I thought of making my own servo’s with the motor driver, pots and motors. But After some tweaking I realised that the accuracy and ‘responsiveness’ was lower than my expactations. I’m a beginner so did not know what to expect but it was not ‘servo’ like to say the least. I use a Arduino Uno controller to control the setup and used some simple code to control the motor’s position with a potentiometer and with potentiometer feedback (same pots so I compared both values for precise positioning).

My question is how I should best attain ‘servo-like’ attitude with the hardware I have. I want precise and powerfull positioning. The 131:1 motors are very powerfull so that is OK but it looks like I need a JRK 21V3 controller because the Invenscience servo’s use them (and it is that responsivenss that I would like to have). Can I reach the same accuracy like the Invenscience servo’s if I control my motors with a JRK controller (with my linear continuous pots)? Or maybe a good code on the Arduino Uno is enough (is a cheaper alternative…)?

Thanks a lot,
Fons Jena

How does your Arduino code work? Are you using PID? What’s the update rate? Have you tuned your PID constants?

Here’s a page about how to tune your PID constants. It’s for the jrk, but the general principles apply to any PID feedback system: pololu.com/docs/0J38/5

The ATmega328 on the Arduino Uno is just about as powerful as the PIC on the jrk 21v3, and the jrk 21v3 uses an MC33926, so in principle you should be able to get the same performance out of the Arduino + MC33926 carrier that you can get out of the jrk. However, doing the feedback with Arduino will require you to write some sophisticated code, it will occupy a lot of the Arduino’s IO lines, and it will potentially interfere with other tasks that you want the Arduino to perform. You said you were a beginner, so I think using the jrk 21v3 would be easier for you.

–David

Thank you for the quick and helpfull advise,

The code I wrote for the Arduino is very simple. I’ve posted it here below. I used three ‘if’ cases where I compare the value of both potentiometers. If the controlling pot value is smaller than the one that is mounted on the motor axis the motor should go left (or right) and vice versa. That didn’t work out well cause the motor overshot the position and started to get loud so I used a speeding up and speeding down phase when the two values got close to each other. I tried different lengths of acceleration and deceleration phases but could not fine tune it. This resulted in a somewhat better response but still not servo-like. I don’t mind using some I/O lines cause it is the only thing the controller should do (for max 3 motors).
But it looks that I should use PID feedback. We saw it in class but didn’t think about it for servo control. I’ll try to find a PID code and read about it. If this turns out too complicated I might use a jrk but first I would like to try it with what I got and it is a good way to learn I guess!

(I used the serial monitor to monitor the difference, it never got to 0 but more like 40 or so from the 1024 possible values)

Fons Jena

/*
  Motor Position Control
  Control a motor's position with two potentiometers.
  */
void setup()
{
  Serial.begin(9600);
  pinMode(8, OUTPUT);
  pinMode(9, OUTPUT);
  pinMode(10, OUTPUT);
}
void loop()
{
  int sensorposition = analogRead(0);
  int motorposition = analogRead(1);
  int difference = sensorposition - motorposition;
  Serial.println(difference, DEC);
  if (difference > 0)
  {
    digitalWrite(8, HIGH);
    digitalWrite(9, LOW);
    if (difference > 255)
    { analogWrite(10, 255); }
    else { analogWrite(10, difference); }
  }
  if (difference < 0)  
  {
    digitalWrite(8, LOW);
    digitalWrite(9, HIGH);
    if (abs(difference) < 255)
    { analogWrite(10, abs(difference)); }
    else { analogWrite(10, 255); }
  }
  if (difference == 0)  
  {
    digitalWrite(8, HIGH);
    digitalWrite(9, HIGH);
    analogWrite(10, 0);
  }
}

Hello.

Please use [code] tags when posting code. For some example PID code, you could look at this section of the 3pi user’s guide on PID control. It is code for controlling two motors, but the PID calculation part should be the same as for your custom servo.

- Ryan

Seconding the advice. Your motor can’t stop on a dime: if it’s going full speed clockwise when the potentiometer reports you’ve hit your mark, it will still take some time for the motor to slow down and stop–and by then you’ve overshot your mark.

That kind of control problem can best be addressed by using a PID algorithm. Effectively the control algorithm will run your motor full speed when it’s far from the desired endpoint, and as it approaches the motor will slow down; if it overshoots (and it will) the overshoot will be minimal, and the algorithm will automatically backtrack the motor slightly to make it stop on the right position.

You can imagine why the same basic control mechanism is used for line-following robots, as they have a similar problem: if they turn to the right and accidentally pass over the line, they need to turn left and compensate. The same system is used for controlling thermostats, mechanical devices–even rudder control for ships. For something as responsive as a high-torque motor and a potentiometer, it should be a perfect fit.

Thank you for the link and explanation.

The overshoot will indeed always be there a bit. I tried with the PID code for the 3pi. I retouched the code with my understanding of it (I posted my code below). If I use the I and D part of PID feedback it doesn’t work. Probably I must tune those constants, I will try different values but is it possible that the gear ratio is too large for quick changes in direction? At higher PWM frequencies (like 5-10kHz) the motor makes a little noise without moving. Strange.

The 3pi robot is really amazing. The speed and accuracy is far above my expectations. I’ll keep trying with PID but in the future I will probably try a jrk.

In the code I tried with the standard PWM command (for arduino) but I use a custom code so I can change the PWM frequency (but then I don’t use the ‘max’ value).

void setup()
{
  Serial.begin(9600);
  pinMode(8, OUTPUT);
  pinMode(9, OUTPUT);
  pinMode(10, OUTPUT);
}

void loop()
{
  int motorposition = analogRead(0);
  int sensorposition = analogRead(1);
  int proportional = motorposition - sensorposition;
  int last_proportional = proportional;
  int derivative = proportional - last_proportional;
  int integral = integral + proportional;  
  int power_difference = proportional/1; // + integral/10000 + derivative/10000  + 3; when adding this part the motor keeps on turning in one direction
  Serial.println(power_difference, DEC);

const int max = 80;
if(power_difference > max)
    power_difference = max;
if(power_difference < -max)
    power_difference = -max;
 
int pulse = 50;
int freq = 80;
if(power_difference < 0)
{
    digitalWrite(8, HIGH);
    digitalWrite(9, LOW);
    digitalWrite(10, HIGH);
    delayMicroseconds(pulse);
    digitalWrite(10, LOW);
    delayMicroseconds(freq-pulse);
    //analogWrite(10, max); 
}
else
{
    digitalWrite(8, LOW);
    digitalWrite(9, HIGH);
    digitalWrite(10, HIGH);
    delayMicroseconds(pulse);
    digitalWrite(10, LOW);
    delayMicroseconds(freq-pulse);
    //analogWrite(10,max);
}
}

Your last_proportional and integral variables are not declared as static, so every time you enter the loop() method they’re being reinitialized. It’s the “+3” part that was making the thing runaway.

Hello.

You are not doing PID control there. With PID control you vary the power you send to the motor with how far you are away from the target. You are still just doing a bang-bang controller. In your case, that means you need to vary the duty cycle of the PWM as some function of power_difference.

- Ryan

Thanks for those reactions. I’m realising that it is pretty complex to program a good PID code (not speaking about positioning a motor where you want it to be) for a beginner. We have a PID course at school this semester so maybe I can get something out of it.
Because I need fast and accurate positioning the temptation of using a Jrk grows. Will have to make a decision the coming weeks.

I feel like given what you’ve already written, you are about 98% of the way to having PID control.

All you need to do is when power_difference is less than zero set pulse equal to -1* power_difference otherwise set pulse equal to power_difference.

- Ryan

That is good to know. I made the necessary adjustments and got some results. After playing with the numbers I still had overshoot problems and in higher PWM frequency my motors stop turning and make a vibrating noice. But it has been better than my simple code in the beginning.
At school they recommend me using a stepper motor setup for my application. I think I will further experiment with it (A4983 driver with 42x48mm stepper motor probably). Many thanks for the help. I have learned about PID and it will come in handy in future projects (my motors plus driver too)!