{"id":762,"date":"2011-04-15T15:01:41","date_gmt":"2011-04-15T19:01:41","guid":{"rendered":"http:\/\/brettbeauregard.com\/blog\/?p=762"},"modified":"2025-03-22T06:50:32","modified_gmt":"2025-03-22T10:50:32","slug":"improving-the-beginners-pid-sample-time","status":"publish","type":"post","link":"http:\/\/brettbeauregard.com\/blog\/2011\/04\/improving-the-beginners-pid-sample-time\/","title":{"rendered":"Improving the Beginner\u2019s PID &#8211; Sample Time"},"content":{"rendered":"<p><small>(This is Modification #1 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 Beginner&#8217;s PID is designed to be called irregularly.  This causes 2 issues:<\/p>\n<ul>\n<li>You don&#8217;t get consistent behavior from the PID, since sometimes it&#8217;s called frequently and sometimes it&#8217;s not.<\/li>\n<li>You need to do extra math computing the derivative and integral, since they&#8217;re both dependent on the change in time.<\/li>\n<\/ul>\n<h3>The Solution<\/h3>\n<p>Ensure that the PID is called at a regular interval.  The way I&#8217;ve decided to do this is to specify that the compute function get called every cycle.  based on a pre-determined Sample Time, the PID decides if it should compute or return immediately.<\/p>\n<p>Once we know that the PID is being evaluated at a constant interval, the derivative and integral calculations can also be simplified.  Bonus!<\/p>\n<h3>The Code<\/h3>\n<pre class=\"brush: css; gutter: true; highlight: [6,10,11,12,15,16,24,29,31,32,35,36,37,38,39,40,41,42,43,44,45]; title: ; notranslate\" title=\"\">\n\/*working variables*\/\nunsigned long lastTime;\ndouble Input, Output, Setpoint;\ndouble errSum, lastErr;\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      errSum += error;\n      double dErr = (error - lastErr);\n\n      \/*Compute PID Output*\/\n      Output = kp * error + ki * errSum + kd * dErr;\n\n      \/*Remember some variables for next time*\/\n      lastErr = error;\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<\/pre>\n<p>On lines 10&amp;11, the algorithm now decides for itself if it&#8217;s time to calculate.  Also, because we now KNOW that it&#8217;s going to be the same time between samples, we don&#8217;t need to constantly multiply by time change.  We can merely adjust the Ki and Kd appropriately (lines 31 &#038; 32) and result is mathematically equivalent, but more efficient.<\/p>\n<p>one little wrinkle with doing it this way though though.  if the user decides to change the sample time during operation, the Ki and Kd will need to be re-tweaked to reflect this new change.  that&#8217;s what lines 39-42 are all about.<\/p>\n<p>Also Note that I convert the sample time to Seconds on line 29.  Strictly speaking this isn&#8217;t necessary,  but allows the user to enter Ki and Kd in units of 1\/sec and s, rather than 1\/mS and mS.<\/p>\n<h3>The Results<\/h3>\n<p>the changes above do 3 things for us<\/p>\n<ol>\n<li>Regardless of how frequently Compute() is called, the PID algorithm will be evaluated at a regular interval [Line 11]<\/li>\n<li>Because of the time subtraction [Line 10] there will be no issues when millis() wraps back to 0.  That only happens every 55 days, but we&#8217;re going for bulletproof remember?<\/li>\n<li>We don&#8217;t need to multiply and divide by the timechange anymore.  Since it&#8217;s a constant we&#8217;re able to move it from the compute code [lines 15+16] and lump it in with the tuning constants [lines 31+32].  Mathematically it works out the same, but it saves a multiplication and a division every time the PID is evaluated<\/li>\n<\/ol>\n<h3> Side note about interrupts<\/h3>\n<p>If this PID is going into a microcontroller, a very good argument can be made for using an interrupt.  SetSampleTime sets the interrupt frequency, then Compute gets called when it&#8217;s time.  There would be no need, in that case, for lines 9-12, 23, and 24.  If you plan on doing this with your PID implentation, go for it!  Keep reading this series though. You&#8217;ll hopefully still get some benefit from the modifications that follow.<br \/>\nThere are three reasons I didn&#8217;t use interrupts<\/p>\n<ol>\n<li>As far as this series is concerned, not everyone will be able to use interrupts.<\/li>\n<li>Things would get tricky if you wanted it implement many PID controllers at the same time.<\/li>\n<li>If I&#8217;m honest, it didn&#8217;t occur to me.  <a href=\"http:\/\/JimmiePRodgers.com\">Jimmie Rodgers<\/a> suggested it while proof-reading the series for me.  I may decide to use interrupts in future versions of the PID library.<\/li>\n<\/ol>\n<p><a href=\"http:\/\/brettbeauregard.com\/blog\/2011\/04\/improving-the-beginner\u2019s-pid-derivative-kick\">Next >><\/a><br \/>\n<br \/>\n<a rel=\"license\" href=\"http:\/\/creativecommons.org\/licenses\/by-sa\/3.0\/\"><img decoding=\"async\" style=\"border-width: 0\" src=\"http:\/\/i.creativecommons.org\/l\/by-sa\/3.0\/80x15.png\" alt=\"Creative Commons License\" \/><\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>(This is Modification #1 in a larger series on writing a solid PID algorithm) The Problem The Beginner&#8217;s PID is designed to be called irregularly. This causes 2 issues: You don&#8217;t get consistent behavior from the PID, since sometimes it&#8217;s &hellip; <a href=\"http:\/\/brettbeauregard.com\/blog\/2011\/04\/improving-the-beginners-pid-sample-time\/\">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-762","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\/762","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=762"}],"version-history":[{"count":92,"href":"http:\/\/brettbeauregard.com\/blog\/wp-json\/wp\/v2\/posts\/762\/revisions"}],"predecessor-version":[{"id":7244,"href":"http:\/\/brettbeauregard.com\/blog\/wp-json\/wp\/v2\/posts\/762\/revisions\/7244"}],"wp:attachment":[{"href":"http:\/\/brettbeauregard.com\/blog\/wp-json\/wp\/v2\/media?parent=762"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/brettbeauregard.com\/blog\/wp-json\/wp\/v2\/categories?post=762"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/brettbeauregard.com\/blog\/wp-json\/wp\/v2\/tags?post=762"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}