PID Motor Control

Hello Forum

This is my first post so please be understanding with me!

I am building a cricket bowling machine which is essentially two motors that rotate in opposite directions and are each fitted with 10inch diameter wheels with a gap the size of a tennis ball between them. A ball is rolled between the wheels and is fired out at varying speeds towards the batsman. As result I would like to be able to measure and control the speed of these motors and adjust the relative speeds to create spin or spin on the ball. I do not need to control the direction of the motors as one will always be clockwise and the other anti-clockwise.

The motors are CIM type motors which require a bit more current than a standard Arduino motor controller can provide so I am using the 43 Amp IBT-2 motor controller. At present I have a small test rig to prove the concept which uses the above motor controllers but instead of the motors I am using old PC cooling fans with an encoder disk attached to the centre to give my rpm measurement. I have attached a photo of the test rig for reference.

I have been working through various examples on motor control and rpm measurement and have successfully got to the stage of being able to control the motor speed via a variable resistor and the above motor controller and measure the rpm and output it to an LCD screen.

My next stage is to be able to enter a speed and use a PID routine to reach that set speed and then maintain it. This is where I am having problems as I cant seem to get the PID routine to work other than do nothing or send the motor to full power instantly. I have researched PID and understand the concept and the maths involved.

I have found some example some code which does exactly what I want to do but I cant get it to work on my rig. I have added comments to it and attached it below and I am hoping someone can give me some guidance. I have been looking at the calculated values and I am not sure it is reading the input from the encoder correctly as the Frequency value always appears to be 0 and the dT value doesn’t seem to change either, although the motor does rotate at various speed.

/*
IBT-2 1 Motor Control Board driven by Arduino with no motor reverse function.
 
Speed controlled by user input via serial monitor.
PID Variables set initially in code but can be modified by user via serial monitor during program run.
 
Connection to the IBT-2 controller:
IBT-2 pin 1 (RPWM) to Arduino pin 6(PWM)
IBT-2 pins 3 (R_EN), 7 (VCC) to Arduino 5V pin
IBT-2 pin 8 (GND) to Arduino GND
IBT-2 pins 2 (LPWM), 4 (L_EN), 5 (R_IS) and 6 (L_IS) not connected

Optical encoder board is FC-02 v2.0 twin encoder version and is connected to Ardiuno pin 2 as only one encoder required
RPM read from optical encoder and use as feedback for PID calculation routine.
*/

int MotorPWM = 6; // ENABLE to digital pin 6
//int MotorIN_1 = 8;
//int MotorIN_2 = 12;
int encoder0PinA = 2;  // Optical encoder attached to this pin

// temp working variables
volatile unsigned long lastTime;
volatile unsigned long duration, CapTime, LastCapTime;
volatile double errSum, lastErr, lastErr_2;
volatile double lastOutput;
volatile double dT;
volatile int count = 0;
double PV, Output;
double kp, ki, kd;
double ref;

//The setup function

void setup (){
  pinMode(encoder0PinA, INPUT); // Optical encoder input
  //pinMode(MotorIN_1, OUTPUT);
  //pinMode(MotorIN_2, OUTPUT);
  pinMode(MotorPWM, OUTPUT);  // PWM value to motor
  attachInterrupt(0, doEncoder, FALLING);
  CapTime = 0;
  LastCapTime = 0;

//Controller Parameter
  kp = 1.00; ki = 0.00; kd = 0.00;  // Only using process part of PID routine at present
  Output = 0;
  Serial.begin (9600);
}
//Interrupt subroutine function

void doEncoder(){
  LastCapTime = CapTime;
  CapTime = millis();
}

void loop (){
  unsigned long now;
  double Freq;
  now = millis();
  dT = (double)(now - lastTime) / 1000; //Our Sampling time
  if(dT>=0.01){ //Do control loop every 10ms
    duration = CapTime - LastCapTime; //Get the period in ms
    LastCapTime = CapTime;
    if(duration==0){
      Freq = 0;
    }
    else{
      Freq = 1000/(double)duration; //in Hz unit
    }
    PV = Freq*60/8; // Rpm unit '8' used in formula as there are 4 interupts per revolution on encoder disk
    compute(ref, PV); // Find controller output
    sendCommand(Output); // Send command to DC motor
    lastTime = now;
  }
  //Receive Command
  byte val;
  if(Serial.available()){
    val = Serial.read();
    switch (val) {
    case 'A': ref = 0; break;
    case 'W': ref += 50; break;
    case 'S': ref -= 50; break;
    case 'O': kp+=0.01;
      Serial.println(kp); break;
    case 'L': kp-=0.01;
      Serial.println(kp); break;
    case 'I': ki+=0.01;
      Serial.println(ki); break;
    case 'K': ki-=0.01;
      Serial.println(ki); break;
    case 'U': kd+=0.001;
      Serial.println(kd); break;
    case 'J': kd-=0.001;
      Serial.println(kd); 
      break;
    }
  }
  // display setpoint and measured value
  if(count>5000){
    Serial.print(ref);
    Serial.print(", ");
    Serial.print(Freq);
    Serial.print(", ");
    Serial.print(dT);
    Serial.print(", ");
    Serial.print(duration);
    Serial.print(", ");
    Serial.print(PV);
    Serial.print(", ");
    Serial.print(Output);
    Serial.println(", ");
    Serial.print(now);
    Serial.println(", ");
    Serial.print(lastTime);
    Serial.println(", ");

    count = 0;
  }
  else{
    count++;
  }
}
//The "sendCommand" function
void sendCommand(int cmd){
  if(cmd>0) {
    analogWrite(MotorPWM, cmd);
    //digitalWrite(MotorIN_1,LOW);
    //digitalWrite(MotorIN_2,HIGH);
  }
  else if(cmd<0){
    analogWrite(MotorPWM, -cmd);
    //digitalWrite(MotorIN_1,HIGH);
    //digitalWrite(MotorIN_2,LOW);
  }
  else{
    analogWrite(MotorPWM, 0);
    //digitalWrite(MotorIN_1,LOW);
    //digitalWrite(MotorIN_2,LOW);
  }
}

//The PID controller algorithm (velocity PID algorithm)
void compute(double Setpoint, double Measured)
{
  double error = Setpoint - Measured;
  /*Compute PID Output*/
  Output = kp*(error - lastErr) + ki*error*dT + ( kd*(error - 2*lastErr + lastErr_2) / dT )
    + lastOutput;
  /*Max 255, Min -255*/
  if(Output>255){
    Output = 255;
  }
  else if(Output <-255){
    Output = -255;
  }
  else{
  }
  /*Remember some variables for next time*/
  lastOutput = Output;
  lastErr_2 = lastErr;
  lastErr = error;
}

I hope someone can help me and I hope I haven’t made any silly errors in the above.

Thanks and I look forward to hearing from you
Chris

I'd be more inclined to use the PID library than debug: PID library

Bump.....can anyone help me please?

Or if anyone has working code that does what I am trying to do that they dont mind sharing?

Thanks

I've looked at your compute function - you're not integrating at all, so that term is all wrong. The integral value is the running sum of the error term.(*)

First set kd = ki = 0, then experiment with kp till you get reasonable behaviour.

Now you can start increasing kp to the point of instability, increasing (negative) kd to quell the instability, repeat until you get the sharpest stable response you can.

Start increasing ki as far as you can without adding oscillations - you also may need to tackle integral wind-up by zeroing the integral value when the output drive is above a threshold (the integral term is only useful when close to desired value, not during transients).

(*) The PID calculations are normally like

  integral += error * dT ;
  derivative = (error - last_error) / dT ;
  last_error = error ;
  result = kp * error + ki * integral + kd * derivative ;
  constrain (result, -limit, +limit) ;

I.

I a working on a tennis ball machine. I use 2 CIM motors like you : Motors and 2 BTS7960B 43A Stepper Motor.

I tried with a L298N shield but i doesn't work., due to 2A limitation. So, I ordered the famous IBT - 2 cards.

I did some tests with my small motor -> work perfectly.

The problem is that it does not work with then FIRST CIM engines, a slight jolt but no more. The motors doesn't start, the make just a little noise.

If I read the Motor specifications I understand that he needs 133A to start ?

However the guy on this video uses the same motors and the same shields !

Do I have to use a PID ??

If you have ideas .....

Hi

I'm glad you are making progress, I am using the same tennis ball machine as the basis for my cricket bowling machine however I can't get the PID to work at all and I am completely lost as to what I am doing wrong.

Would you be able to post your code so that I can see what you have done?

I don't think the motors need 133amps to start as he is only using 7aH batteries.

You will need to use PID if you want to maintain the speed accurately and also be able to enter the rpm you require of each motor. This is exactly what I want to do but can't seem to find any examples to learn from.

Chris

No the motor has a stall current of 133A. It only takes a few amps to turn without load.

You need to ensure that the motor driver circuit can handle the maximum currents involved which is either the stall current or the supply current (whichever is least).

Use a 20A supply and 20A will be the limit - but make sure the supply is designed for temperory overload (without tripping).

If you use big SLA or LiPo battery pack then your motor driver could see the whole stall current of 133A...

BTW if you use flywheels you won't need such powerful motors - store the energy mechanically... (unless this is rapid-fire ?!)

My next stage is to be able to enter a speed and use a PID routine to reach that set speed and then maintain it. This is where I am having problems as I cant seem to get the PID routine to work other than do nothing or send the motor to full power instantly.

Why do you need a PID to do such a simple task? Do you care if the motors speeds change a lot after shooting the ball? Do you care how long it takes for the motor speed to recover? Its free spinning after, it should recover quickly.

Sounds like all you need is the P, Kp*error. Given time they will return back to the originally set speed. My bet is that is a very short amount of time as you don't have any resistance on them. If it takes to long to stabilize then you might want to add a integral.

I have no idea what you've done so far but so many people have the same general problems. They don't test the basics then complain when the complicated doesn't work, check the sensor inputs, check your outputs, can you do a manual speed control on the actual motor your going to use? Once you know it works, implement the code in steps, does each step work, add more, then when something doesn't work its obvious what the problem is.

P.S The start up current for motors with no load is usually 1.5-3 times the no load run current.

edit: On more thought, do you even need anything beside a set value on your motors? You set a PWM or in some cases a voltage to set the speed, because they are free spinning 99% of the time they will always return to the same rpm after they shoot a ball.

If they are slowing down to much then your problem is in the tightness of the ball grip or the motors torque is to low. No PID or other will be able to correct the rpm of a inductive motor in the time between the first contact and the last contact at throwing speed.

I have to agree with harddrive. Simplicity is the order of the day. far too often we tend to over-engineer what is effectively a simple problem.

In any system you have to look at how the system is intended to perform and do some basic maths in order to evaluate system requirements.

An example of which follows :

Your wheels are 10" so their periphery is 31.42", ( 2.62feet ). Say you want a launch speed of 30MPH, that's 158,400 feet per hour or 2640 feet per minute With a 2.62feet periphery, your wheels need to turn at somewhere in the order of 1000RPM or 16.8 revs/second. The wheel therefore revolves once every 60milliseconds With a ball of around 3" diameter, the wheel will "see" a load for say 10% of a revolution (the periphery) or 10% of 60mS ie 6 milliseconds of every ball throw. Assume balls are thrown every minute then the wheel is loaded for 6*100/(1000*60) = 0.01% of the period between balls ie for 99.99% of the time the machine is doing "nothing"

A three term control system can only be tuned for one set of operating conditions - this is a feature of control that many fail to understand. A system that runs slow requires a set of PID tuning parameters and the same system running fast will require a totally different set of tuning parameters.

Your system is effectively a steady state DC driven motor whose speed is governed by the drive voltage overcoming rolling friction and windage. When the occasional short-duration impulse load is applied the tuning parameters controlling steady state speed will be totally incapable of responding to the impulse load. Conversely if you set up tuning parameters to cater for the impulse load they would be useless at controlling the steady state conditions.

Your best bet therefore is to look purely at controlling steady state speed and ignoring the impulse load. To provide some stability of speed, you will need inertial mass (flywheels) and these will also minimise the effect of the impulse. The flywheel mass may simply be the metal-work involved in manufacture of the ball throwing wheels. If you intend to use something like barrow wheels then filling the tyres with a liquid will add considerable mass with minimal engineering.

I think you may experience difficulty creating spin on the ball as friction between ball and wheels will demand that there is no slip and hence no spin. Spin may only be introduced after the ball has left the launch wheels. Perhaps a third wheel to effectively flick on side of the ball to introduce an acceleration or deceleration.

Edit.

Have had a bit of a think on the spin problem.
If there is differential speed on the two wheels, there will be a spin placed into the ball but there will also be a major off-set of direction of throw, since the ball will tend to roll around the periphery of the slower wheel before it is let loose. This directional offset can be countered by aligning the angle the wheels face the target.

Thanks for the replies

harddrive123:

I mention in my post that I have started with a test rig (hence the picture of the test rig) and have worked my way up to being able to control the motor speed via a pot and also display the output on an LCD screen. I have not just started trying to build the entire project without first going through the basics. It is now that I am trying to add PID control that I have encountered problems and the reason for posting to the forum.

I thought that PID was the best method for being able to set and monitor the rpm of a motor but I am new to Arduino so I am seeking the advice of others who are more experienced in this field.

jackrae:

Thank you for your in depth reply as well. I have considered the mechanics of what I am trying to do and my sums agree with yours which is always nice and proves I have calculated correctly!

All I am trying to achieve is to set and measure a motor rpm that relates to a launch speed and display it on an LCD. For example, I set 1000 rpm, wait for the system to achieve that speed and then launch the ball. I want to get this working on my test rig before I build the actual machine. The spin aspect is a whole different ball game and I want to get it launching them straight before I think about imparting any spin.

If PID is not the best way to achieve this can you please explain what method I should use? I don't want to have to work out what every PWM value equates to in terms of rpm of the motor which I assume is one possible method?

If you can post some example code that would be extremely helpful as I am struggling to make any progress on this project now.

Thanks a lot Chris

If you have a display of rotor speed then you have the most basic of control systems available - the human brain - eye monitors speed and hand adjusts controller. Monitor speed of Motor A and adjust its speed pot accordingly. Because of wheel inertia the motor speed, once fully accelerated should remain fairly constant. If you use motor A speed (as measured by its speed sensor) as the control set point you can use the speed sensor on motor B as the measured variable in your PID equation. With reasonably heavy wheels (high inertia) I'd surmise your system time constant to be measured in seconds so a reasonably slow dynamic system. The controller then adjusts Motor B speed PWM until it matches the sensed speed on motor A ie both motors are synchronised. With a slow system your expect intergral time (Ti) to be measured in seconds per repeat. I'd suggest you forget about using differential action (Td) since, once you are at steady speed there should be no need for it and all you are looking at is correcting for minor speed variations.

This is a first step, I don't need to do a manual speed control on the actual motor for my projet. I think that they will always return to the same rpm after they shoot a ball (like you).

I just want to set the PWM (0-255) to give effects at the ball.

My problem is that I do not launch these engines with IBT2 card. Like Chris, I have started with a test rig and have worked my way up to being able to control the motor speed via a PWM signal. It works perfectly with These motors

If I connect them on a PC power supply which provides about 10A I have no problem, the engines start and run. So I think they need a big Current in order to start (more than 43A ?)....

Chris, would it be possible to make the system more simple? I'd try using the pot reading and map it to a range of PWM values that move your motors - low values likely will not. That range would be discovered by experiment.

Set the PWM for both motors and then wait until motor A is showing stable enough RPMs. That might be sufficient. If not, tweak the PWM setting for motor B until its RPM is close enough to that of A. Fire the ball and then wait for motor A to come back up to speed & you're ready to go again.

In other words, I don't think your application needs precision control - good enough may well serve.

Hello jackrae

I really like your idea of using the first motor as the setpoint for the second motor! That is a neat way of getting them synchronised. At a later date I could possibly then introduce an offset factor to slow the second motor if I did want to try an impart any spin to the ball.

Now I just have to get it to work in practice!

Thanks
Chris

Hi Chris.

In you project, do you use 2 BTS7960B 43A Stepper Motor ?

Hello wildbill

That is certainly a possibility but being an engineer and a stubborn type I really wanted to try and use PID and be able to set, measure and home in on the set value automatically! I like a challenge.

I havent started building the actual ball launcher yet as I was trying to get all the control aspects sorted out such as inputs, rpm measurement and display and of course rpm control.

You are correct though that in my test rig using pots the motors (or fans in my test rig) did reach a steady state and hold an rpm within a couple of points, so I am sure I could display the PWM value and then vary the pot and make a note of the rpm and PWM value and build up a map like that.

If I cant get it working with PID that is what I will have to do. I am surprised nobody else has done a project that requires motor speed control using PID. If they have I cant find any examples that would let me see the implementation of the code.

Thanks for the reply. Chris

Hi

Yes I am using two of those controllers and at a couple of PC cooling fans at the moment in my test rig.

Take a look at this: improving the beginners pid introduction

It steps you through the improvements made to the PID library that you can find in the Arduino playground. You can either use the library (I certainly would) or at least compare Brett's code to your own to see if you can figure out what's wrong with your home grown version.

The basic example in the playground is really pretty much what you need except that you would need a separate PID for each motor. You already have desired and actual RPM, each PID's output can be constrained to values between 0 and 255 and there's your PWM value to drive the motors.

Tricky part of course is the tuning of the PID parameters, but that bites you whenever you opt to use PID.

As noted above, the PID will likely be disturbed by the changes to RPM when a ball is thrown - you may find that setting it to manual during the throw helps.

Yes I am using two of those controllers and at a couple of PC cooling fans at the moment in my test rig.

OK, did you try with the CIM motor ? In my case, it works fine with little motors but it's impossible to start CIM motors with these shields