1

I'm currently working on a project where I need to use PID to get the correct speed for a motor when it is angled a certain amount.

The problem is that I'm only getting 0.00 and 255.00 as the speed. Nothing in between. The code is supposed to activate when the angle value is more than 1300 and less than -1300. The ax variable is from an MPU6050 sensor.

int16_t ax, ay, az;

double Setpoint, Input, Output;
//Specify the links and initial tuning parameters
double Kp=2, Ki=5, Kd=1;
PID myPID(&Input, &Output, &Setpoint, Kp,Ki,Kd, DIRECT);

void setup() {
  Input = analogRead(ax);
  Setpoint = 100;
  //turn the PID on
  myPID.SetMode(AUTOMATIC);
}

void loop() {
  accelgyro.getAcceleration(&ax, &ay, &az);
  int a=ax+10900;
  //Here I set the used variable to around 0. ax is actually around -10900
  //This is always true, this won't be in the final code. This is just making it work.
  if(1) {
    int val;
    val = map(a, -1300, 1300, 0, 1023);
    Input = val;
    myPID.Compute();
    Serial.println(Output);
  }
}
  • I suggest adding the symptom to the title as in the rejected edit arduino.stackexchange.com/review/suggested-edits/13084 The PID is working as configured, however these configuration parameters of the PID makes it oversensitive to the inputs, resulting in bang-bang operation and overshoot. This failure mode is distinct from other PID failure modes in the 'Related' PID questions, and with a better title could help a user find or exclude this question and answer from their search. – Dave X Feb 17 '16 at 17:18
  • If the values of 'a' and 'val' aren't used/useful for other purposes, you can control 'ax' directly. If you want 'ax' controlled to the -10900 value, you can use ax as input and Setpoint as -10900 and avoid the offset, and mapping. E.g. PID myPID(&ax, &Setpoint,kP, Ki, Kd, DIRECT); Setpoint=-10900; – Dave X Feb 19 '16 at 16:01
2

How fast is your system compared to the loop() sampling time? Does (100-map((ax+10900),-1300,1300,0,1024)) change appreciably during the sub-millisecond loop()?

With kI=5, an error of only 10 will saturate your 0-255 speed output in 26 iterations of loop(), potentially producing the behavior you see. If you don't have control of your sampling time, the integral error can wind up (or down) to the process limits very quickly.

From https://www.arduino.cc/en/Reference/Map map() only linearly transforms the input data to the output data, but does not constrain() it to those limits. If ax=-9000, then val would be greater than 1024. Still, taking the normal working range of ax+10900 to be (-1300,1300) which is then mapped into (0,1024), the Kp=2 term with a setpoint of 100 would produce a range of (-200,2048) constrained to the default (0,255) limits.

Working backwards from the setpoint of 100, through the 0-1024 map is 0.0975 along the (-1300 to 1300) range, or val = -949, or ax = -11849. And the kP=2 times the mapping factor of 1024/2600 would change the output by 2*1024/2600=0.79 points for each step difference of ax from -11849.

The http://playground.arduino.cc/Main/MPU-6050 pages says the acceleration registers are 16 bit with high sensitivity, and suggests that 50 points of noise is a small variation. If the sensor set for 16 bits for +/-2G into ax, then, each step change 60 micro-G. If that is the source of your ax, then the small variations could easily saturate your control system, making it bang-bang between its output limits with only milli-G variations at the sensor.

Slow down your sampling time, reduce kI, and think of your Kp, Ki, and Kd as conversions of your measured and translated variable ax errors into output space (0-255 speed).

Also, in choosing a sampling time, consider how often you want the PID to make adjustments to the process.

2

Please respect others by presenting code that is readable. By doing that you may also find obvious errors in your code. Let me clean up the loop() and you will see what I mean.

void loop() {
    int a = ax + 10900;
    if (1) {
      int val;
      val = map(ax, -1300, 1300, 0, 1023);
      Input = val;
      myPID.Compute();
      Serial.println(Output);
    }
  }
}

So what is "int a = ax + 10900"? "ax" is not defined but the other use of it is as an analog pin number "Input = analogRead(ax)". And "a" is not used.

There is a condition that is always true "if (1)" and so on.

Cheers!

  • That was my fault when putting in the code but i fixed it. – Marc Feb 15 '16 at 13:53
  • 1
    Try to use indentation so that you code is more readable. This will also help you understand where you are going wrong. Here is a good reference on typical C++ style rules. google.github.io/styleguide/cppguide.html. Please post more of your code. Right now "ax" is not taken from MPU6050 sensor. That is not in your snippet. How could anybody guess that? – Mikael Patel Feb 16 '16 at 8:22
  • 1
    So what do you think is the problem? – Mikael Patel Feb 16 '16 at 8:51
  • 1
    Where did these numbers come from? Estimation? Example code? Guesstimate? – Mikael Patel Feb 16 '16 at 9:11
  • 1
    @Marc - so the code is quite unnecessary... that is quite incorrect... The code is necessary for us to fully understand what it is that is happening. Many times we have seen an OP emphatically stating that his code does "x, y and z" but then the code shows that it is actually doing "a, b and c", i.e., something quite different from what the OP expected and/or stated... – Greenonline Feb 17 '16 at 7:57

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.