{"id":767,"date":"2011-04-15T15:03:08","date_gmt":"2011-04-15T19:03:08","guid":{"rendered":"http:\/\/brettbeauregard.com\/blog\/?p=767"},"modified":"2025-03-22T06:51:16","modified_gmt":"2025-03-22T10:51:16","slug":"improving-the-beginners-pid-tuning-changes","status":"publish","type":"post","link":"http:\/\/brettbeauregard.com\/blog\/2011\/04\/improving-the-beginners-pid-tuning-changes\/","title":{"rendered":"Improving the Beginner\u2019s PID: Tuning Changes"},"content":{"rendered":"<p><small>(This is Modification #3 in a <a href=\"http:\/\/brettbeauregard.com\/blog\/2011\/04\/improving-the-beginners-pid-introduction\">larger series<\/a> on writing a solid PID algorithm)<\/small><\/p>\n<h3>The Problem<\/h3>\n<p>The ability to change tuning parameters while the system is running is a must for any respectable PID algorithm.<\/p>\n<p><a href=\"http:\/\/brettbeauregard.com\/blog\/wp-content\/uploads\/2011\/03\/BadIntegral.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-971\" src=\"http:\/\/brettbeauregard.com\/blog\/wp-content\/uploads\/2011\/03\/BadIntegral.png\" alt=\"\" width=\"336\" height=\"348\" srcset=\"http:\/\/brettbeauregard.com\/blog\/wp-content\/uploads\/2011\/03\/BadIntegral.png 336w, http:\/\/brettbeauregard.com\/blog\/wp-content\/uploads\/2011\/03\/BadIntegral-289x300.png 289w\" sizes=\"auto, (max-width: 336px) 100vw, 336px\" \/><\/a><\/p>\n<p>The Beginner&#8217;s PID acts a little crazy if you try to change the tunings while it&#8217;s running. Let&#8217;s see why.  Here is the state of the beginner&#8217;s PID before and after the parameter change above:<\/p>\n<p><a href=\"http:\/\/brettbeauregard.com\/blog\/wp-content\/uploads\/2011\/03\/BadIntegralCode.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-972\" src=\"http:\/\/brettbeauregard.com\/blog\/wp-content\/uploads\/2011\/03\/BadIntegralCode.png\" alt=\"\" width=\"442\" height=\"144\" srcset=\"http:\/\/brettbeauregard.com\/blog\/wp-content\/uploads\/2011\/03\/BadIntegralCode.png 442w, http:\/\/brettbeauregard.com\/blog\/wp-content\/uploads\/2011\/03\/BadIntegralCode-300x97.png 300w\" sizes=\"auto, (max-width: 442px) 100vw, 442px\" \/><\/a><\/p>\n<p>So we can immediately blame this bump on the Integral Term (or &#8220;I Term&#8221;).  It&#8217;s the only thing that changes drastically when the parameters change.  Why did this happen?  It has to do with the beginner&#8217;s interpretation of the Integral:<\/p>\n<p><a href=\"http:\/\/brettbeauregard.com\/blog\/wp-content\/uploads\/2011\/03\/BadIntegralEqn1.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-983\" src=\"http:\/\/brettbeauregard.com\/blog\/wp-content\/uploads\/2011\/03\/BadIntegralEqn1.png\" alt=\"\" width=\"191\" height=\"31\" \/><\/a><\/p>\n<p>This interpretation works fine until the Ki is changed.  Then, all of a sudden, you multiply this new Ki times the entire error sum that you have accumulated.  That&#8217;s not what we wanted!  We only wanted to affect things moving forward!<\/p>\n<h3>The Solution<\/h3>\n<p>There are a couple ways I know of to deal with this problem.  The method  I used in the last library was to rescale errSum.  Ki doubled?  Cut errSum in Half.  That keeps the I Term from bumping, and it works.  It&#8217;s kind of clunky though, and I&#8217;ve come up with something more elegant. (There&#8217;s no way I&#8217;m the first to have thought of this, but I did think of it on my own.  That counts damnit!)<\/p>\n<p>The solution requires a little basic algebra (or is it calculus?)<\/p>\n<p><a href=\"http:\/\/brettbeauregard.com\/blog\/wp-content\/uploads\/2011\/03\/GoodIntegralEqn.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-982\" src=\"http:\/\/brettbeauregard.com\/blog\/wp-content\/uploads\/2011\/03\/GoodIntegralEqn.png\" alt=\"\" width=\"207\" height=\"62\" \/><\/a><\/p>\n<p>Instead of having the Ki live outside the integral, we bring it inside.  It looks like we haven&#8217;t done anything,  but we&#8217;ll see that in practice this makes a big difference.<\/p>\n<p>Now, we take the error and multiply it by whatever the Ki is at that time.  We then store the sum of THAT.  When the Ki changes, there&#8217;s no bump because all the old Ki&#8217;s are already &#8220;in the bank&#8221; so to speak.  We get a smooth transfer with no additional math operations.  It may make me a geek but I think that&#8217;s pretty sexy.<\/p>\n<h3>The Code<\/h3>\n<pre class=\"brush: css; gutter: true; highlight: [4,15,19]; title: ; notranslate\" title=\"\">\n\/*working variables*\/\nunsigned long lastTime;\ndouble Input, Output, Setpoint;\ndouble ITerm, lastInput;\ndouble kp, ki, kd;\nint SampleTime = 1000; \/\/1 sec\nvoid Compute()\n{\n   unsigned long now = millis();\n   int timeChange = (now - lastTime);\n   if(timeChange&gt;=SampleTime)\n   {\n      \/*Compute all the working error variables*\/\n      double error = Setpoint - Input;\n      ITerm += (ki * error);\n      double dInput = (Input - lastInput);\n\n      \/*Compute PID Output*\/\n      Output = kp * error + ITerm - kd * dInput;\n\n      \/*Remember some variables for next time*\/\n      lastInput = Input;\n      lastTime = now;\n   }\n}\n\nvoid SetTunings(double Kp, double Ki, double Kd)\n{\n  double SampleTimeInSec = ((double)SampleTime)\/1000;\n   kp = Kp;\n   ki = Ki * SampleTimeInSec;\n   kd = Kd \/ SampleTimeInSec;\n}\n\nvoid SetSampleTime(int NewSampleTime)\n{\n   if (NewSampleTime &gt; 0)\n   {\n      double ratio  = (double)NewSampleTime\n                      \/ (double)SampleTime;\n      ki *= ratio;\n      kd \/= ratio;\n      SampleTime = (unsigned long)NewSampleTime;\n   }\n}\n\n<\/pre>\n<p>So we replaced the errSum variable with a composite ITerm variable [Line 4].  It sums Ki*error, rather than just error [Line 15].  Also, because Ki is now buried in ITerm, it&#8217;s removed from the main PID calculation [Line 19].<\/p>\n<h3>The Result<\/h3>\n<p><a href=\"http:\/\/brettbeauregard.com\/blog\/wp-content\/uploads\/2011\/03\/GoodIntegral.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-973\" src=\"http:\/\/brettbeauregard.com\/blog\/wp-content\/uploads\/2011\/03\/GoodIntegral.png\" alt=\"\" width=\"336\" height=\"348\" srcset=\"http:\/\/brettbeauregard.com\/blog\/wp-content\/uploads\/2011\/03\/GoodIntegral.png 336w, http:\/\/brettbeauregard.com\/blog\/wp-content\/uploads\/2011\/03\/GoodIntegral-289x300.png 289w\" sizes=\"auto, (max-width: 336px) 100vw, 336px\" \/><\/a><br \/>\n<a href=\"http:\/\/brettbeauregard.com\/blog\/wp-content\/uploads\/2011\/03\/GoodIntegralCode.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-974\" src=\"http:\/\/brettbeauregard.com\/blog\/wp-content\/uploads\/2011\/03\/GoodIntegralCode.png\" alt=\"\" width=\"440\" height=\"140\" srcset=\"http:\/\/brettbeauregard.com\/blog\/wp-content\/uploads\/2011\/03\/GoodIntegralCode.png 440w, http:\/\/brettbeauregard.com\/blog\/wp-content\/uploads\/2011\/03\/GoodIntegralCode-300x95.png 300w\" sizes=\"auto, (max-width: 440px) 100vw, 440px\" \/><\/a><br \/>\nSo how does this fix things.  Before when ki was changed, it rescaled the entire sum of the error; every error value we had seen.  With this code, the previous error remains untouched, and the new ki only affects things moving forward, which is exactly what we want.<br \/>\n<a href=\"improving-the-beginner\u2019s-pid-reset-windup\">Next >><\/a><br \/>\n<br \/>\n<a rel=\"license\" href=\"http:\/\/creativecommons.org\/licenses\/by-sa\/3.0\/\"><img decoding=\"async\" alt=\"Creative Commons License\" style=\"border-width:0\" src=\"http:\/\/i.creativecommons.org\/l\/by-sa\/3.0\/80x15.png\" \/><\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>(This is Modification #3 in a larger series on writing a solid PID algorithm) The Problem The ability to change tuning parameters while the system is running is a must for any respectable PID algorithm. The Beginner&#8217;s PID acts a &hellip; <a href=\"http:\/\/brettbeauregard.com\/blog\/2011\/04\/improving-the-beginners-pid-tuning-changes\/\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"footnotes":""},"categories":[31,7],"tags":[6,30,41],"class_list":["post-767","post","type-post","status-publish","format-standard","hentry","category-coding","category-pid","tag-arduino","tag-beginners-pid","tag-pid"],"_links":{"self":[{"href":"http:\/\/brettbeauregard.com\/blog\/wp-json\/wp\/v2\/posts\/767","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/brettbeauregard.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/brettbeauregard.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/brettbeauregard.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/brettbeauregard.com\/blog\/wp-json\/wp\/v2\/comments?post=767"}],"version-history":[{"count":64,"href":"http:\/\/brettbeauregard.com\/blog\/wp-json\/wp\/v2\/posts\/767\/revisions"}],"predecessor-version":[{"id":7246,"href":"http:\/\/brettbeauregard.com\/blog\/wp-json\/wp\/v2\/posts\/767\/revisions\/7246"}],"wp:attachment":[{"href":"http:\/\/brettbeauregard.com\/blog\/wp-json\/wp\/v2\/media?parent=767"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/brettbeauregard.com\/blog\/wp-json\/wp\/v2\/categories?post=767"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/brettbeauregard.com\/blog\/wp-json\/wp\/v2\/tags?post=767"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}