diff --git a/.gitignore b/.gitignore index 057a577..916e75c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ node_modules temp.js -temp \ No newline at end of file +temp +package-lock.json diff --git a/README.md b/README.md index f992949..1b94f82 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,19 @@ You can also pass options as arguments: let ctr = new Controller(0.25, 0.01, 0.01, 1); // k_p, k_i, k_d, dt ``` +### Re-set the tuning + +```js +ctr.setTuning(0.25, 0.01, 0.01, 1); // k_p, k_i, k_d, dt + +ctr.setTuning({ // as object + k_p: 0.25, + k_i: 0.01, + k_d: 0.01, + dt: 1 +}); +``` + ### Set the target ```js diff --git a/index.d.ts b/index.d.ts index 4586022..4560729 100644 --- a/index.d.ts +++ b/index.d.ts @@ -31,6 +31,9 @@ declare class Controller { public setTarget(target: number): void; + public setTuning(options: Controller.Options); + public setTuning(k_p?: number, k_i?: number, k_d?: number, dt?: number); + public update(currentValue: number): number; public reset(): number; diff --git a/lib/index.js b/lib/index.js index 3d07030..f5418a5 100644 --- a/lib/index.js +++ b/lib/index.js @@ -5,6 +5,21 @@ */ class Controller { constructor(k_p, k_i, k_d, dt) { + + this.setTuning(k_p, k_i, k_d, dt); + + this.sumError = 0; + this.lastError = 0; + this.lastTime = 0; + + this.target = 0; // default value, can be modified with .setTarget + } + + setTarget(target) { + this.target = target; + } + + setTuning(k_p, k_i, k_d, dt) { let i_max; if (typeof k_p === 'object') { let options = k_p; @@ -16,30 +31,22 @@ class Controller { } // PID constants - this.k_p = (typeof k_p === 'number') ? k_p : 1; - this.k_i = k_i || 0; - this.k_d = k_d || 0; + if (typeof k_p !== 'number') k_p = this.k_p || 1; + + this.k_p = k_p; + this.k_i = k_i || this.k_i || 0; + this.k_d = k_d || this.k_d || 0; // Interval of time between two updates // If not set, it will be automatically calculated - this.dt = dt || 0; + this.dt = dt || this.dt || 0; // Maximum absolute value of sumError - this.i_max = i_max || 0; - - this.sumError = 0; - this.lastError = 0; - this.lastTime = 0; - - this.target = 0; // default value, can be modified with .setTarget - } - - setTarget(target) { - this.target = target; + this.i_max = i_max || this.i_max || 0; } update(currentValue) { - if(!currentValue) throw new Error("Invalid argument"); + if (isNaN(currentValue)) throw new Error("Invalid argument"); this.currentValue = currentValue; // Calculate dt @@ -58,22 +65,22 @@ class Controller { } let error = (this.target - this.currentValue); - this.sumError = this.sumError + error*dt; + this.sumError = this.sumError + error * dt; if (this.i_max > 0 && Math.abs(this.sumError) > this.i_max) { let sumSign = (this.sumError > 0) ? 1 : -1; this.sumError = sumSign * this.i_max; } - let dError = (error - this.lastError)/dt; + let dError = (error - this.lastError) / dt; this.lastError = error; - return (this.k_p*error) + (this.k_i * this.sumError) + (this.k_d * dError); + return (this.k_p * error) + (this.k_i * this.sumError) + (this.k_d * dError); } reset() { - this.sumError = 0; + this.sumError = 0; this.lastError = 0; - this.lastTime = 0; + this.lastTime = 0; } } diff --git a/test/index.js b/test/index.js index 9684b3a..7c4f0d7 100644 --- a/test/index.js +++ b/test/index.js @@ -40,6 +40,24 @@ describe('pid-controller', () => { ctr.dt.should.equal(options.dt); }); + it('should have set the coefficient from an new options object', () => { + let newOptions = { + k_p: 0.7, + k_i: 0.2, + k_d: 0.3, + dt: 10 + } + + ctr.setTuning(newOptions.k_p, newOptions.k_i, newOptions.k_d, newOptions.dt); + + ctr.k_p.should.equal(newOptions.k_p); + ctr.k_i.should.equal(newOptions.k_i); + ctr.k_d.should.equal(newOptions.k_d); + ctr.dt.should.equal(newOptions.dt); + + ctr.setTuning(options); // Reset tunings + }); + it('should set the target', () => { let v = 120; // 120km/h ctr.setTarget(v); @@ -60,15 +78,22 @@ describe('pid-controller', () => { }); it('should return the correction for the given update interval', () => { - ctr.dt = 2; // 2 seconds between updates + ctr.setTuning({ + dt: 2 // 2 seconds between updates + }) let correction = ctr.update(115); correction.should.equal(4); - ctr.dt = options.dt; // Reset dt + + ctr.setTuning({ + dt: options.dt // Reset dt + }) }); it('should return the correction with sumError <= i_max', () => { let ctr = new Controller(options); - ctr.i_max = 5; // sumError will be 10 + ctr.setTuning({ + i_max: 5 // sumError will be 10 + }); ctr.setTarget(120); let correction = ctr.update(110); correction.should.equal(7.5); @@ -83,7 +108,7 @@ describe('pid-controller', () => { }); it('should throw error when updating a NaN value', () => { - let ctr = new Controller(0,0,0); + let ctr = new Controller(0, 0, 0); ctr.setTarget(20); should.throws(() => { ctr.update(NaN);