/********************************************************************************************** * Arduino PID AutoTune Library - Version 0.0.0 * by Brett Beauregard 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. **********************************************************************************************/ #if ARDUINO >= 100 #include "Arduino.h" #else #include "WProgram.h" #endif #include "PID_AutoTune_v0_local.h" //renamed to avoid conflict if Autotune library is installed on IDE PID_ATune::PID_ATune(double *Input, double *Output) { input = Input; output = Output; controlType = 0; //default to PI noiseBand = 0.5; running = false; oStep = 30; SetLookbackSec(10); lastTime = millis(); } void PID_ATune::Cancel() { running = false; } int PID_ATune::Runtime() { justevaled = false; if (peakCount > 9 && running) { running = false; FinishUp(); return 1; } unsigned long now = millis(); if ((now - lastTime) < (unsigned long)sampleTime) return false; lastTime = now; double refVal = *input; justevaled = true; if (!running) { //initialize working variables the first time around peakType = 0; peakCount = 0; justchanged = false; absMax = refVal; absMin = refVal; setpoint = refVal; running = true; initCount = 0; outputStart = *output; *output = outputStart + oStep; } else { if (refVal > absMax) absMax = refVal; if (refVal < absMin) absMin = refVal; } //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; //bool isMax=true, isMin=true; isMax = true; isMin = true; //id peaks for (int 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]; } lastInputs[0] = refVal; if (nLookBack < 9) { //we don't want to trust the maxes or mins until the inputs array has been filled initCount++; return 0; } if (isMax) { if (peakType == 0) peakType = 1; if (peakType == -1) { peakType = 1; justchanged = true; peak2 = peak1; } peak1 = now; peaks[peakCount] = refVal; } else if (isMin) { if (peakType == 0) peakType = -1; if (peakType == 1) { peakType = -1; peakCount++; justchanged = true; } if (peakCount < 10) peaks[peakCount] = refVal; } if (justchanged && peakCount > 2) { //we've transitioned. check if we can autotune based on the last peaks double avgSeparation = (abs(peaks[peakCount - 1] - peaks[peakCount - 2]) + abs(peaks[peakCount - 2] - peaks[peakCount - 3])) / 2; if (avgSeparation < 0.05 * (absMax - absMin)) { FinishUp(); running = false; return 1; } } justchanged = false; return 0; } void PID_ATune::FinishUp() { *output = outputStart; //we can generate tuning parameters! Ku = 4 * oStep / ((absMax - absMin) * 3.14159); Pu = (double)(peak1 - peak2) / 1000; } double PID_ATune::GetKp() { return controlType == 1 ? 0.6 * Ku : 0.4 * Ku; } double PID_ATune::GetKi() { return controlType == 1 ? 1.2 * Ku / Pu : 0.48 * Ku / Pu; // Ki = Kc/Ti } double PID_ATune::GetKd() { return controlType == 1 ? 0.075 * Ku * Pu : 0; //Kd = Kc * Td } void PID_ATune::SetOutputStep(double Step) { oStep = Step; } double PID_ATune::GetOutputStep() { return oStep; } void PID_ATune::SetControlType(int Type) //0=PI, 1=PID { controlType = Type; } int PID_ATune::GetControlType() { return controlType; } void PID_ATune::SetNoiseBand(double Band) { noiseBand = Band; } double PID_ATune::GetNoiseBand() { return noiseBand; } void PID_ATune::SetLookbackSec(int value) { if (value < 1) value = 1; if (value < 25) { nLookBack = value * 4; sampleTime = 250; } else { nLookBack = 100; sampleTime = value * 10; } } int PID_ATune::GetLookbackSec() { return nLookBack * sampleTime / 1000; }