arduino-pid-autotune-library's Introduction
/********************************************************************************************** * Arduino PID AutoTune Library - Version 0.0.1 * by Brett Beauregard <[email protected]> brettbeauregard.com * * This Library is ported from the AutotunerPID Toolkit by William Spinelli * (http://www.mathworks.com/matlabcentral/fileexchange/4652) * Copyright (c) 2004 * * This Library is licensed under the BSD License: * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the distribution * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. **********************************************************************************************/ Note: I'd really hoped to have this more polished before release, but with the osPID coming out I felt that this needed to be out there NOW. if you encounter any issues please contact me, or post to the diy-pid-control google group.
arduino-pid-autotune-library's People
Forkers
johndorian t0mpr1c3 mcu786 gausscheng samacumen rcstarttofinish virginia-chen akadamson konyham numericoverflow akshay009 ferrysko penghou620 koson sizaphitz2 mksab2007 cris8259 zargoun demirturk54 gelay3d rekingwang silvioso enorms yoelperalta jc- jbinkleyj sproutorc ivankravets prussiap acemu matsstaff kiwironnie pi19404 rtorresca bvernham brucetsao shangma mayman99 bdpcvit nategeorge meshane rubensmachado drpjk trsigg linuxfor bspratt wc789 cnsapril cyberang3l tom131313 amano92861 dark-guan electronics sartymlg colossus212 keyurrakholiya zhu04303661 konglingchun jchtt jjjonas liqingqiya cybermatatu weera00 drieswillemsens zendevelopmentsystems waspinator tomatlab blue-design quekuan ferstar georgines msh2050 cnc4less danak6jq quenaz forever3000 rinksrides2013 dadongdong kiyoshi7 theoutfield jnosky lesthersk huleg shentqlf hooper2chile zhijieliu029 edgsc shannon-alan solertis antoinehng flaviohernan edgary777 erolca nandyy josetorres1961 hankerbit ptz1986 hkybupt rafalfigura sycomixarduino-pid-autotune-library's Issues
Reverse type of controller
This two lines of code are made for DIRECT type of pid controller. What if user wants reverse type? Output will never step in other direction. We have to swap +'s and -'s.
//oscillate the output base on the input's relation to the setpoint
if(refVal>setpoint+noiseBand) *output = outputStart-oStep;
else if (refVal<setpoint-noiseBand) *output = outputStart+oStep;
To this:
//oscillate the output base on the input's relation to the setpoint
if(refVal>setpoint+noiseBand) *output = outputStart+oStep;
else if (refVal<setpoint-noiseBand) *output = outputStart-oStep;
getting wrong gains for dc motor pid
Hi,
I'm using the autotune PID library for finding the gains of the dc motors of my differential drive. I am using a teensy 3.2 microcontroller, a sabertooth 2x12 motor driver, HC-05 Bluetooth module, two dc motors, and two external rotary/incremental encoders having 360ppr. I was unable to find any example to use the autotune library except for the library example so I used it as a reference and made the changes to suit my code. Here it is
#include <PID_v1.h>
#include <PID_AutoTune_v0.h>
#include <SoftwareSerial.h>
#include <SabertoothSimplified.h>
//library for sabertooth motordriver
SoftwareSerial SWSerial1(0,2);
SabertoothSimplified ST1(SWSerial1);
int pinA1 = 11;
int pinB1 = 12;
int pinA2 = 7;
int pinB2 = 8;
int count1 = 0, count2 = 0;
// encoder counts
int prevCount1=0, prevCount2 = 0;
// last count
unsigned long prevTime1=0,prevTime2=0,prevTime3=0,prevTime4=0,prevTime5=0;
double reqRPM1 = 150,reqRPM2 = 150,reqTheta = 0 ;
// required RPM
double actRPM1 =0,actRPM2= 0,motorInput2=0;
// actual RPM
byte ATuneModeRemember=2;
double kp=0.43793,ki=0.06332,kd=0.75718;
// parameters obtained from autotuner
double aTuneStep=45, aTuneNoise=10, aTuneStartValue=55;
unsigned int aTuneLookBack=1;
unsigned long modelTime, serialTime;
double kpmodel=1.5, taup=100, theta[50];
double outputStart=5;
boolean tuning = false;
//changed to false after tuning
PID myPID(&actRPM2, &motorInput2, &reqRPM2,kp,ki,kd, DIRECT);
PID_ATune aTune(&actRPM2, &motorInput2);
boolean useSimulation = true;
//set to false to connect to the real world
void Check2()
// interrupt function to get encoder readings
{
if(digitalRead(pinB2) == digitalRead(pinA2))
count2--;
else
count2++;
}
void setup()
{
myPID.SetMode(AUTOMATIC);
aTune.SetControlType(1);
ST1.motor(1,0);
// running only one motor for now
delay(1000);
// if(tuning)
//commented this code otherwise the autotuner was not starting
// {
// tuning=false;
// changeAutoTune();
//this functions sets output to atuneStartValue and was not letting
//autotuner to start, had to comment this for it to run
// tuning=true;
// }
serialTime = 0;
if(useSimulation)
{
for(byte i=0;i<50;i++)
{
theta[i]=outputStart;
}
modelTime = 0;
}
//encoder pins
pinMode(pinA2,INPUT)
;
pinMode(pinB2,INPUT);
//interrupt
attachInterrupt(digitalPinToInterrupt(pinA2),Check2,RISING);
//keeping track of time from start
prevTime1 = micros();
prevTime4 = micros();
prevTime5 = micros();
Serial.begin(9600);
SWSerial1.begin(9600);
//serial for sabertooth motor driver
}
void loop()
{
unsigned long now = micros();
ST1.motor(1,0);
// running only one motor
if((micros()-prevTime1)>10000)
// run every 10 ms
{
if(tuning)
{
byte val = (aTune.Runtime());
if (val!=0)
{
tuning = false;
}
if(!tuning)
{ //we're done, set the tuning parameters
kp = aTune.GetKp();
ki = aTune.GetKi();
kd = aTune.GetKd();
myPID.SetTunings(kp,ki,kd);
AutoTuneHelper(false);
}
}
else {
myPID.Compute();
}
if(useSimulation)
{
theta[30]=motorInput2;
if(now>=modelTime)
{
modelTime +=100;
actRPM2 = getRPM2();
// instead of DoModel() in order to update input value I placed my
// code to update input in this portion
ST1.motor(2 ,motorInput2);
// sending values to motor
}
}
if(micros()>serialTime)
{
SerialReceive();
SerialSend();
serialTime+=500;
}
prevTime1 = micros();
}
}
int getRPM2()
// calculating RPM
{
long pps2 = long(count2 - prevCount2);
float ppr2 = 360;
prevTime4 = micros() - prevTime5;
double tt = prevTime4 / 6000.0 ;
double actRPM2 = ((pps2*10000.0)/(tt*ppr2));
prevTime5 = micros();
// SWSerial2.print(" rpm2= " );
// SWSerial2.println(actRPM2);
prevCount2 = count2;
return actRPM2;
}
void changeAutoTune()
{
if(!tuning)
{
//Set the output to the desired starting frequency.
//removed one line of code that sets the output to aTuneStartValue
aTune.SetNoiseBand(aTuneNoise);
aTune.SetOutputStep(aTuneStep);
aTune.SetLookbackSec((int)aTuneLookBack);
AutoTuneHelper(true);
tuning = true;
}
else
{ //cancel autotune
aTune.Cancel();
tuning = false;
AutoTuneHelper(false);
Serial.print("Adad222");
}
}
void AutoTuneHelper(boolean start)
{
if(start)
ATuneModeRemember = myPID.GetMode();
else
myPID.SetMode(ATuneModeRemember);
}
void SerialSend()
{
Serial.print("setpoint: ");Serial.print(reqRPM2); Serial.print(" ");
Serial.print("input: ");Serial.print(actRPM2); Serial.print(" ");
Serial.print("output: ");Serial.print(motorInput2); Serial.print(" ");
if(tuning){
Serial.println("tuning mode");
} else {
Serial.print("kp: ");Serial.print(myPID.GetKp(),5);Serial.print(" ");
Serial.print("ki: ");Serial.print(myPID.GetKi(),5);Serial.print(" ");
Serial.print("kd: ");Serial.print(myPID.GetKd(),5);Serial.println();
}
}
void SerialReceive()
{
if(Serial.available())
{
char b = Serial.read();
Serial.flush();
if((b=='1' && !tuning) || (b!='1' && tuning))changeAutoTune();
}
}
Even after I commented the changeAutoTune and AutoTuneHelper the autotuner was taking forever to end. So I looked at the library .cpp file and tweaked with LookBackSec, Noise, and some other values, getting no success. Finally, when I reduced the value of nLookBack in RunTime function to a value lower than 9 (worked good till 5 for me) the tuner came to an end after oscillating for a while. But the problem is the values that I am getting are too high that it makes the pid unstable.
Kp both for PonE and PonM?
I have a doubt that others may have too. The PID library got an update for choosing Kp applied on actual error (Proportional on Error, PonE) or Kp applied on deltaInput (Proportional on Measurement).
Does the Autotune PID library calculated Kp is useful for both or only PonE? Thanks!
Links to read about "The Theory"
How can a library work?
We need to strive for setpoint. And in the code at the start of tuning, this is what input was like that - it will be instead of setpoint.
And all the time it will float near input.
int PID_ATune::Runtime()
{
justevaled=false;
if(peakCount>9 && running)
{
running = false;
FinishUp();
return 1;
}
unsigned long now = millis();
if((now-lastTime)<sampleTime) return false;
lastTime = now;
double refVal = *input; --------------------------------------------------------------?
justevaled=true;
if(!running) ------------------------------------------------ first start
{ //initialize working variables the first time around
peakType = 0;
peakCount=0;
justchanged=false;
absMax=refVal;
absMin=refVal;
setpoint = refVal; ----------------------------------first start setpoint = input ?
running = true;
outputStart = *output;
*output = outputStart+oStep;
}
else
{
if(refVal>absMax)absMax=refVal;
if(refVal<absMin)absMin=refVal;
}
if(refVal>setpoint+noiseBand) *output = outputStart-oStep; -------------------? input and input ?
else if (refVal<setpoint-noiseBand) *output = outputStart+oStep; ----------- ? input and input ?
"corrected" output float near input? or maybe near setpoint ???????????
Very low gains for a position control PID tuning
Hi Brett,
It kind of works https://www.youtube.com/watch?v=WlZKYZCA7k8
And after a while, I get a Kp of 0.01 but if I tune it manually Kp=3.0 works ok (and it is preferred).
My process is definitely faster than an oven and I am not sure whether any assumption about time is made in the library.
Any help is appreciated,
misan
Checking wrong variable
Checking a value that can be either value*4 or 100 (only assignment I can find is in SetLookbackSec(value)) to see if it is smaller than 9 does not seem right to me? Only occation that could be true is if SetLookbackSec(1) has been called by the user...
Based on the comment I would do something like this:
if (lastInputs[nLookBack] == 0)
justevaled doesn't do anything
Seems redundant given that Runtime() returns a value anyway.
Ziegler Nichols 2nd Method - Isn't it for Series Algorithm? (PID library uses ISA form)
Hi Guys,
Isn't Ziegler-Nichols 2nd Method (Frequency Response Method or Oscillation Method or Ku/Pu Method etc) for Series PID Algorithm?
From https://www.pidtuning.net/pid-algorithm.php
From Autotuning of PID Controllers A Relay Feedback Approach_, 2nd edition, Cheng-Ching Yu (2007), page 30?
Bret's PID library uses ISA (Ideal) PID Algorithm, right?
So, shouldn't we transform Kp, Ki, Kd from Series to Parallel form?
from http://www.acsysteme.com/en/serial-or-parallel-pid
from ISA Effective Use of PID Controllers 3-7-2013, https://pt.slideshare.net/sarodp/isa-effective-use-of-pid-controllers-372013
@br3ttb
Thanks!
update code
distribute in wIki
20221205 by embedream
initCount - what does it do?
its value is not ever looked at within the algorithm as far as I can tell.
Minimum value for nLookBack
SetLookbackSec(2) will give nLookBack=8 but with nLookBack<9 Runtime() will always return 0 and never call a peak.
Autotune should be a method for class PID
It seems desirable to me to have the entire PID object passed to the auto tune function rather than the individual variables. Probably not possible since I don't see a way to do this that would not break existing programs.
Swapped constants used for Ki calculation
I think you accidently swapped the 1.2 and 0.48 in the Ki calculation:
double PID_ATune::GetKi()
{
return controlType==1? 1.2*Ku / Pu : 0.48 * Ku / Pu; // Ki = Kc/Ti
}
Should be like this for Ziegler & Nichols (closed loop):
double PID_ATune::GetKi()
{
return controlType==1? 0.48 * Ku / Pu : 1.2 * Ku / Pu; // Ki = Kc/Ti
}
/Thanks
Runtime() boolean rather than int
Runtime() returns 0 or 1 currently. I suppose int might be preferable if there are plans to extend the range of return codes to include other values. If not then it might be tidier to call it boolean.
Install Troubles
Hello!
I'm having a hard time even getting this library to install. It's not showing up in the Library Manager so I came here to install the .zip and that's not working out either. It always just says that:
"Specified folder/zip file does not contain a valid library"
What can I even attempt do with that? I know that my IDE is installing .zip files fine otherwise--I just installed via .zip for the Adafruit Motor Shield V2 Library and it's working just fine.
Tuning plot doesn't seems like Bret's Blog Example
Hi Guys,
This is what what the graph Setpoint (S), Input (I) (both left axis), and Output (O - right axis) looks like.
for Lookback =10 Step =30 Noise = 2.0, it gets Ku=152 Pu=2699
kp: | 91,67 |
ki: | 0,68 |
kd: | 3085,77
It doesn't looks like Bret's example on his blog:
Obs: time is in seconds (horizontal axis)
But with the obtained parameters (kp, ki, kd) I can get the input to be 31.95 - 32.05 (setpoint 32), so it seems fine.
But I wonder why we don't see the output as HIGH ad LOW pulses, neither the input as clear peaks.
SampleTime of data is 0,96 seconds, +-0,024 seconds.
It's tuned after 1178 seconds; as you can see, the perfomance is fine.
Thanks!
===================
Sorry, my mistake, there was a PID.comput() being run while tuning. Closed issue.
possible lastInputs overrun
Question: Wont the following code run off the end of the lastInputs Array?
nLookBack could potentially be 100, in which case 'i' starts at 99, the end of the array, only for lastInputs[100] to eventually be updated. I would think the array should be declared of size 101 to fix this, yes?
for(int8_t i=nLookBack-1;i>=0;i--)
{
double val = lastInputs[i];
if(isMax) isMax = refVal>val;
if(isMin) isMin = refVal<val;
lastInputs[i+1] = lastInputs[i];
}
Output limits
PID_ATune::Runtime() does not check that outputStart-oStep is greater than or equal to the minimum possible value for *output, nor that outputStart+oStep is less than or equal to the maximum possible value for *output.
Where these boundaries are exceeded it is likely that Ku is overestimated because the actual changes in *output are less than +/- oStep.
use sampleTime from PID
Wouldn't it make sense to have sampleTime in the auto tuner as some multiple (>=1) of the sampleTime used in the PID.
Simulated input equation - Where does it come from?
Hello, from where does the simulated input equation comes from?
input = (kpmodel / taup) (theta[0]-outputStart) + input(1-1/taup) + ((float)random(-10,10))/100;
What kind of dynamics? How was it linearized?
Thanks!!!
"LIBRARY_VERSION" Warning
Library version clash with supporting script PID_v1
In file included from eThrottleV2.ino:3:0:
C:\Program Files (x86)\Arduino\libraries\PID_AutoTune_v0/PID_AutoTune_v0.h:3:0: warning: "LIBRARY_VERSION" redefined [enabled by default]
define LIBRARY_VERSION 0.0.1
^
In file included from eThrottleV2.ino:2:0:
C:\Program Files (x86)\Arduino\libraries\br3ttb-Arduino-PID-Library-d46dded/PID_v1.h:3:0: note: this is the location of the previous definition
define LIBRARY_VERSION 1.1.1
^
Can we implement this to working system?
Can we implement this to working system? I want to add this to find constants of quadrotor control.
peak-to-peak amplitude for relay is 2*oStep
In the formula for the ultimate gain Ku, oStep is used as the measure of amplitude for the relay which is half the peak-to-peak amplitude. The amplitude of the induced oscillation is measured by ( absMax - absMin ) i.e. the peak-to-peak amplitude. This would appear to underestimate Ku by a factor of 2.
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. ๐๐๐
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google โค๏ธ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.