initial commit
This commit is contained in:
3
node_modules/node-pid-controller/.npmignore
generated
vendored
Normal file
3
node_modules/node-pid-controller/.npmignore
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
node_modules
|
||||
temp.js
|
||||
temp
|
||||
16
node_modules/node-pid-controller/Makefile
generated
vendored
Normal file
16
node_modules/node-pid-controller/Makefile
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
MOCHA_OPTS=
|
||||
REPORTER = dot
|
||||
|
||||
test:
|
||||
@NODE_ENV=test ./node_modules/.bin/mocha \
|
||||
--reporter $(REPORTER) \
|
||||
$(MOCHA_OPTS)
|
||||
|
||||
test-w:
|
||||
@NODE_ENV=test ./node_modules/.bin/mocha \
|
||||
--reporter $(REPORTER) \
|
||||
--growl \
|
||||
--watch \
|
||||
$(MOCHA_OPTS)
|
||||
|
||||
.PHONY: test test-w
|
||||
77
node_modules/node-pid-controller/README.md
generated
vendored
Normal file
77
node_modules/node-pid-controller/README.md
generated
vendored
Normal file
@@ -0,0 +1,77 @@
|
||||
# node-pid-controller
|
||||
|
||||
Simple Node.js PID controller.
|
||||
|
||||

|
||||
|
||||
## Installation
|
||||
|
||||
```
|
||||
$ npm install node-pid-controller
|
||||
```
|
||||
|
||||
## Example
|
||||
|
||||
Let's take the example of a car cruise control. We want the car driving at 120km/h.
|
||||
|
||||
### Create a Controller instance
|
||||
|
||||
`k_p`, `k_i` and `k_d` are the proportional, integral and derivative terms. `dt` is the interval of time between two measures. If not set, it will be automatically calculated.
|
||||
|
||||
```js
|
||||
var Controller = require('node-pid-controller');
|
||||
|
||||
var ctr = new Controller({
|
||||
k_p: 0.25,
|
||||
k_i: 0.01,
|
||||
k_d: 0.01,
|
||||
dt: 1
|
||||
});
|
||||
```
|
||||
|
||||
You can also pass options as arguments:
|
||||
```js
|
||||
var ctr = new Controller(0.25, 0.01, 0.01, 1); // k_p, k_i, k_d, dt
|
||||
```
|
||||
|
||||
### Set the target
|
||||
|
||||
```js
|
||||
ctr.setTarget(120); // 120km/h
|
||||
```
|
||||
|
||||
### Get the correction
|
||||
|
||||
```js
|
||||
var correction = ctr.update(110); // 110km/h is the current speed
|
||||
```
|
||||
|
||||
### Real example
|
||||
|
||||
Normally, you use the correction to a measure, in a closed loop.
|
||||
|
||||
```js
|
||||
var goalReached = false
|
||||
while (!goalReached) {
|
||||
var output = measureFromSomeSensor();
|
||||
var input = ctr.update(output);
|
||||
applyInputToActuator(input);
|
||||
goalReached = (input === 0) ? true : false; // in the case of continuous control, you let this variable 'false'
|
||||
}
|
||||
```
|
||||
|
||||
## Options
|
||||
|
||||
* `k_p`, `k_i`, `k_d`: the PID's coefficients
|
||||
* `dt`: interval of time (in seconds) between two measures. If not provided, it will be automatically calculated.
|
||||
* `i_max`: the maximum absolute value of the integral term (optional)
|
||||
|
||||
## Test
|
||||
|
||||
```js
|
||||
mocha test
|
||||
```
|
||||
|
||||
## Author
|
||||
|
||||
Philmod <philippe.modard@gmail.com>
|
||||
1
node_modules/node-pid-controller/index.js
generated
vendored
Normal file
1
node_modules/node-pid-controller/index.js
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
module.exports = require('./lib/index.js');
|
||||
75
node_modules/node-pid-controller/lib/index.js
generated
vendored
Normal file
75
node_modules/node-pid-controller/lib/index.js
generated
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
/**
|
||||
* PID Controller.
|
||||
*/
|
||||
var Controller = function(k_p, k_i, k_d, dt) {
|
||||
var i_max;
|
||||
if (typeof k_p === 'object') {
|
||||
var options = k_p;
|
||||
k_p = options.k_p;
|
||||
k_i = options.k_i;
|
||||
k_d = options.k_d;
|
||||
dt = options.dt;
|
||||
i_max = options.i_max;
|
||||
}
|
||||
|
||||
// PID constants
|
||||
this.k_p = (typeof k_p === 'number') ? k_p : 1;
|
||||
this.k_i = k_i || 0;
|
||||
this.k_d = k_d || 0;
|
||||
|
||||
// Interval of time between two updates
|
||||
// If not set, it will be automatically calculated
|
||||
this.dt = 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
|
||||
};
|
||||
|
||||
Controller.prototype.setTarget = function(target) {
|
||||
this.target = target;
|
||||
};
|
||||
|
||||
Controller.prototype.update = function(currentValue) {
|
||||
this.currentValue = currentValue;
|
||||
|
||||
// Calculate dt
|
||||
var dt = this.dt;
|
||||
if (!dt) {
|
||||
var currentTime = Date.now();
|
||||
if (this.lastTime === 0) { // First time update() is called
|
||||
dt = 0;
|
||||
} else {
|
||||
dt = (currentTime - this.lastTime) / 1000; // in seconds
|
||||
}
|
||||
this.lastTime = currentTime;
|
||||
}
|
||||
if (typeof dt !== 'number' || dt === 0) {
|
||||
dt = 1;
|
||||
}
|
||||
|
||||
var error = (this.target - this.currentValue);
|
||||
this.sumError = this.sumError + error*dt;
|
||||
if (this.i_max > 0 && Math.abs(this.sumError) > this.i_max) {
|
||||
var sumSign = (this.sumError > 0) ? 1 : -1;
|
||||
this.sumError = sumSign * this.i_max;
|
||||
}
|
||||
|
||||
var dError = (error - this.lastError)/dt;
|
||||
this.lastError = error;
|
||||
|
||||
return (this.k_p*error) + (this.k_i * this.sumError) + (this.k_d * dError);
|
||||
};
|
||||
|
||||
Controller.prototype.reset = function() {
|
||||
this.sumError = 0;
|
||||
this.lastError = 0;
|
||||
this.lastTime = 0;
|
||||
};
|
||||
|
||||
module.exports = Controller;
|
||||
34
node_modules/node-pid-controller/package.json
generated
vendored
Normal file
34
node_modules/node-pid-controller/package.json
generated
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"name": "node-pid-controller",
|
||||
"version": "0.1.2",
|
||||
"description": "Node.js PID controller",
|
||||
"keywords": [
|
||||
"pid",
|
||||
"controller",
|
||||
"robotic",
|
||||
"drone",
|
||||
"system"
|
||||
],
|
||||
"author": "Philmod <philippe.modard@gmail.com>",
|
||||
"homepage": "http://github.com/philmod/node-pid-controller",
|
||||
"engines": {
|
||||
"node": ">= v0.8.0"
|
||||
},
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"should": "",
|
||||
"mocha": ""
|
||||
},
|
||||
"scripts": {
|
||||
"test": "make test"
|
||||
},
|
||||
"main": "index",
|
||||
"directories": {
|
||||
"test": "test"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/Philmod/node-pid-controller.git"
|
||||
},
|
||||
"license": "BSD"
|
||||
}
|
||||
83
node_modules/node-pid-controller/test/index.js
generated
vendored
Normal file
83
node_modules/node-pid-controller/test/index.js
generated
vendored
Normal file
@@ -0,0 +1,83 @@
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var Controller = require('../')
|
||||
, should = require('should')
|
||||
, assert = require('assert')
|
||||
;
|
||||
|
||||
/**
|
||||
* Tests
|
||||
*/
|
||||
describe('pid-controller', function(){
|
||||
|
||||
var options = {
|
||||
k_p: 0.5,
|
||||
k_i: 0.1,
|
||||
k_d: 0.2,
|
||||
dt: 1
|
||||
};
|
||||
|
||||
// Create the controller
|
||||
var ctr = new Controller(options.k_p, options.k_i, options.k_d, options.dt);
|
||||
|
||||
it('should have set the coefficient', function() {
|
||||
ctr.k_p.should.equal(options.k_p);
|
||||
ctr.k_i.should.equal(options.k_i);
|
||||
ctr.k_d.should.equal(options.k_d);
|
||||
ctr.dt.should.equal(options.dt);
|
||||
});
|
||||
|
||||
it('should have set the coefficient from an options object', function(){
|
||||
var ctr = new Controller(options);
|
||||
|
||||
ctr.k_p.should.equal(options.k_p);
|
||||
ctr.k_i.should.equal(options.k_i);
|
||||
ctr.k_d.should.equal(options.k_d);
|
||||
ctr.dt.should.equal(options.dt);
|
||||
});
|
||||
|
||||
it('should set the target', function(){
|
||||
var v = 120; // 120km/h
|
||||
ctr.setTarget(v);
|
||||
ctr.target.should.equal(v);
|
||||
});
|
||||
|
||||
it('should return the correction', function(){
|
||||
var vt = 110; // current speed
|
||||
var correction = ctr.update(vt);
|
||||
correction.should.equal(8);
|
||||
});
|
||||
|
||||
it('should reset the controller', function(){
|
||||
ctr.reset();
|
||||
ctr.sumError.should.equal(0);
|
||||
ctr.lastError.should.equal(0);
|
||||
ctr.lastTime.should.equal(0);
|
||||
});
|
||||
|
||||
it('should return the correction for the given update interval', function(){
|
||||
ctr.dt = 2; // 2 seconds between updates
|
||||
var correction = ctr.update(115);
|
||||
correction.should.equal(4);
|
||||
ctr.dt = options.dt; // Reset dt
|
||||
});
|
||||
|
||||
it('should return the correction with sumError <= i_max', function() {
|
||||
var ctr = new Controller(options);
|
||||
ctr.i_max = 5; // sumError will be 10
|
||||
ctr.setTarget(120);
|
||||
var correction = ctr.update(110);
|
||||
correction.should.equal(7.5);
|
||||
ctr.sumError.should.be.belowOrEqual(ctr.i_max);
|
||||
});
|
||||
|
||||
it('should return a null correction', function(){
|
||||
var ctr = new Controller(0, 0, 0);
|
||||
ctr.setTarget(120);
|
||||
var correction = ctr.update(110);
|
||||
correction.should.equal(0);
|
||||
});
|
||||
|
||||
});
|
||||
Reference in New Issue
Block a user