From owner-svn-src-all@freebsd.org Thu Jun 7 02:54:13 2018 Return-Path: Delivered-To: svn-src-all@mailman.ysv.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mailman.ysv.freebsd.org (Postfix) with ESMTP id EBD65FF0B28; Thu, 7 Jun 2018 02:54:12 +0000 (UTC) (envelope-from alc@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client CN "mxrelay.nyi.freebsd.org", Issuer "Let's Encrypt Authority X3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 88B727916B; Thu, 7 Jun 2018 02:54:12 +0000 (UTC) (envelope-from alc@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 4B3B91A448; Thu, 7 Jun 2018 02:54:12 +0000 (UTC) (envelope-from alc@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id w572sCmR018462; Thu, 7 Jun 2018 02:54:12 GMT (envelope-from alc@FreeBSD.org) Received: (from alc@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id w572sC76018461; Thu, 7 Jun 2018 02:54:12 GMT (envelope-from alc@FreeBSD.org) Message-Id: <201806070254.w572sC76018461@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: alc set sender to alc@FreeBSD.org using -f From: Alan Cox Date: Thu, 7 Jun 2018 02:54:12 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r334752 - head/sys/kern X-SVN-Group: head X-SVN-Commit-Author: alc X-SVN-Commit-Paths: head/sys/kern X-SVN-Commit-Revision: 334752 X-SVN-Commit-Repository: base MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.26 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 07 Jun 2018 02:54:13 -0000 Author: alc Date: Thu Jun 7 02:54:11 2018 New Revision: 334752 URL: https://svnweb.freebsd.org/changeset/base/334752 Log: pidctrl_daemon() implements a variation on the classical, discrete PID controller that tries to handle early invocations of the controller, in other words, invocations before the expected end of the interval. However, there were some calculation errors in this early invocation case. Notably, if an early invocation occurred while the error was negative, the derivative term was off by a large amount. One visible effect of this error was that processes were being killed by the virtual memory system's OOM killer when in fact there was plentiful free memory. Correct a couple minor errors in the sysctl descriptions, and apply some style fixes. Reviewed by: jeff, markj Modified: head/sys/kern/subr_pidctrl.c Modified: head/sys/kern/subr_pidctrl.c ============================================================================== --- head/sys/kern/subr_pidctrl.c Thu Jun 7 02:30:48 2018 (r334751) +++ head/sys/kern/subr_pidctrl.c Thu Jun 7 02:54:11 2018 (r334752) @@ -59,14 +59,14 @@ pidctrl_init_sysctl(struct pidctrl *pc, struct sysctl_ &pc->pc_olderror, 0, "Error value from last interval"); SYSCTL_ADD_INT(NULL, parent, OID_AUTO, "integral", CTLFLAG_RD, &pc->pc_integral, 0, "Accumulated error integral (I)"); - SYSCTL_ADD_INT(NULL, parent, OID_AUTO, "derivative", - CTLFLAG_RD, &pc->pc_derivative, 0, "Error derivative (I)"); + SYSCTL_ADD_INT(NULL, parent, OID_AUTO, "derivative", CTLFLAG_RD, + &pc->pc_derivative, 0, "Error derivative (D)"); SYSCTL_ADD_INT(NULL, parent, OID_AUTO, "input", CTLFLAG_RD, &pc->pc_input, 0, "Last controller process variable input"); SYSCTL_ADD_INT(NULL, parent, OID_AUTO, "output", CTLFLAG_RD, &pc->pc_output, 0, "Last controller output"); SYSCTL_ADD_INT(NULL, parent, OID_AUTO, "ticks", CTLFLAG_RD, - &pc->pc_ticks, 0, "Last controler runtime"); + &pc->pc_ticks, 0, "Last controller runtime"); SYSCTL_ADD_INT(NULL, parent, OID_AUTO, "setpoint", CTLFLAG_RW, &pc->pc_setpoint, 0, "Desired level for process variable"); SYSCTL_ADD_INT(NULL, parent, OID_AUTO, "interval", CTLFLAG_RD, @@ -96,21 +96,20 @@ pidctrl_classic(struct pidctrl *pc, int input) Kid = MAX(pc->pc_Kid, 1); Kdd = MAX(pc->pc_Kdd, 1); - /* Compute P (proportional error), I (integral), D (derivative) */ + /* Compute P (proportional error), I (integral), D (derivative). */ pc->pc_error = error; pc->pc_integral = MAX(MIN(pc->pc_integral + error, pc->pc_bound), -pc->pc_bound); pc->pc_derivative = error - pc->pc_olderror; /* Divide by inverse gain values to produce output. */ - output = ((pc->pc_error / Kpd) + - (pc->pc_integral / Kid)) + + output = (pc->pc_error / Kpd) + (pc->pc_integral / Kid) + (pc->pc_derivative / Kdd); /* Save for sysctl. */ pc->pc_output = output; pc->pc_input = input; - return output; + return (output); } int @@ -121,17 +120,18 @@ pidctrl_daemon(struct pidctrl *pc, int input) error = pc->pc_setpoint - input; /* - * When ticks expired we reset our variables and start a new + * When ticks expires we reset our variables and start a new * interval. If we're called multiple times during one interval * we attempt to report a target as if the entire error came at * the interval boundary. */ - if ((u_int)(ticks - pc->pc_ticks) >= pc->pc_interval) { + if ((u_int)ticks - pc->pc_ticks >= pc->pc_interval) { pc->pc_ticks = ticks; pc->pc_olderror = pc->pc_error; pc->pc_output = pc->pc_error = 0; } else { - error = MAX(error + pc->pc_error, 0); + /* Calculate the error relative to the last call. */ + error -= pc->pc_error - pc->pc_output; } /* Fetch gains and prevent divide by zero. */ @@ -139,19 +139,19 @@ pidctrl_daemon(struct pidctrl *pc, int input) Kid = MAX(pc->pc_Kid, 1); Kdd = MAX(pc->pc_Kdd, 1); - /* Compute P (proportional error), I (integral), D (derivative) */ - pc->pc_error = error; + /* Compute P (proportional error), I (integral), D (derivative). */ + pc->pc_error += error; pc->pc_integral = MAX(MIN(pc->pc_integral + error, pc->pc_bound), 0); - pc->pc_derivative = error - pc->pc_olderror; + pc->pc_derivative = pc->pc_error - pc->pc_olderror; /* Divide by inverse gain values to produce output. */ - output = ((error / Kpd) + - (pc->pc_integral / Kid)) + + output = (error / Kpd) + (pc->pc_integral / Kid) + (pc->pc_derivative / Kdd); output = MAX(output - pc->pc_output, 0); + /* Save for sysctl. */ pc->pc_output += output; pc->pc_input = input; - return output; + return (output); }