In this example the fuzzy controller will be implemented with three rules. The GUI will show only the desired and actual value; user sets the desired value by pot. Figure of the GUI:
In this example socket.io library is used.
var http = require("http").createServer(handler); // on req - hand
var io = require("socket.io").listen(http); // socket library
var fs = require("fs"); // variable for file system for providing html
var firmata = require("firmata");
console.log("Starting the code");
var board = new firmata.Board("/dev/ttyACM0", function(){
console.log("Connecting to Arduino");
board.pinMode(0, board.MODES.ANALOG); // enable analog pin 0
board.pinMode(1, board.MODES.ANALOG); // enable analog pin 1
board.pinMode(2, board.MODES.OUTPUT); // direction of DC motor
board.pinMode(3, board.MODES.PWM); // PWM of motor
board.pinMode(4, board.MODES.OUTPUT); // direction of DC motor
});
function handler(req, res) {
fs.readFile(__dirname + "/example28.html",
function (err, data) {
if (err) {
res.writeHead(500, {"Content-Type": "text/plain"});
return res.end("Error loading html page.");
}
res.writeHead(200);
res.end(data);
})
}
var desiredValue = 0; // desired value var
var actualValue = 0; // actual value var
var factor = 0.1; // proportional factor that deterimes speed of res.
var pwm = 0; // set pwm as global variable
http.listen(8080); // server will listen on port 8080
var sendValueViaSocket = function(){}; // var for sending messages
board.on("ready", function(){
board.analogRead(0, function(value){
desiredValue = value; // continuous read of analog pin 0
});
board.analogRead(1, function(value){
actualValue = value; // continuous read of analog pin 1
});
startControlAlgorithm();
io.sockets.on("connection", function(socket) {
socket.emit("messageToClient", "Srv connected, board OK");
setInterval(sendValues, 40, socket); // on 40ms trigerr func. sendValues
}); // end of sockets.on connection
}); // end of board.on ready
////////////////////////////////////////////////fuzzy
var NFuzzyVars = 2;
var NFuzzyOutputs = 1;
var NFuzzySets = new Array(NFuzzyVars);
NFuzzySets[0] = 3;
NFuzzySets[1] = 3;
var FSvalues = new Array(NFuzzyVars);
for (var i=0;i!=NFuzzyVars;i++)
{
FSvalues[i] = new Array(NFuzzySets[i]);
for (var j=0;j!=NFuzzySets[i];j++)
{
FSvalues[i][j] = new Array(3);
}
}
var NRules = 3;
var RBase = new Array(NRules);
for (var i=0;i!=NRules;i++)
{
RBase[i] = new Array(NFuzzyVars);
}
var ValuesForFuzzy = new Array(NFuzzyVars);
ValuesForFuzzy[0] = 0;
ValuesForFuzzy[1] = 0;
var AlphaCut = new Array(NFuzzySets[0]);
var RN = 0;
RBase[RN][0] = 0; RBase[RN][1] = 0; RN++;
RBase[RN][0] = 1; RBase[RN][1] = 1; RN++;
RBase[RN][0] = 2; RBase[RN][1] = 2; RN++;
FSvalues[0][0][0] = -100; // number of var, num of fuzzy sets, number of points, that define fs
FSvalues[0][0][1] = -100;
FSvalues[0][0][2] = 0;
FSvalues[0][1][0] = -100;
FSvalues[0][1][1] = 0;
FSvalues[0][1][2] = 100;
FSvalues[0][2][0] = 0;
FSvalues[0][2][1] = 100;
FSvalues[0][2][2] = 100;
FSvalues[1][0][0] = -15;
FSvalues[1][0][1] = -15;
FSvalues[1][0][2] = 0;
FSvalues[1][1][0] = -15;
FSvalues[1][1][1] = 0;
FSvalues[1][1][2] = 15;
FSvalues[1][2][0] = 0;
FSvalues[1][2][1] = 15;
FSvalues[1][2][2] = 15;
function getMRFromFSvalues(value,NumOfVar,NumOfSet)
{
if (NumOfSet == 0)
{
if (value < FSvalues[NumOfVar][NumOfSet][0])
{
return 1;
}
else if (value > FSvalues[NumOfVar][NumOfSet][2])
{
return 0;
}
else
{
return (FSvalues[NumOfVar][NumOfSet][2] - value) / (FSvalues[NumOfVar][NumOfSet][2] - FSvalues[NumOfVar][NumOfSet][1]);
}
}
else if (NumOfSet == NFuzzySets[NumOfVar] - 1)
{
if (value < FSvalues[NumOfVar][NumOfSet][0])
{
return 0;
}
else if (value > FSvalues[NumOfVar][NumOfSet][2])
{
return 1;
}
else if (value < FSvalues[NumOfVar][NumOfSet][1])
{
return (value - FSvalues[NumOfVar][NumOfSet][0]) / (FSvalues[NumOfVar][NumOfSet][1] - FSvalues[NumOfVar][NumOfSet][0]);
}
}
else
{
if (value < FSvalues[NumOfVar][NumOfSet][0])
{
return 0;
}
else if (value > FSvalues[NumOfVar][NumOfSet][2])
{
return 0;
}
else if (value < FSvalues[NumOfVar][NumOfSet][1])
{
return (value - FSvalues[NumOfVar][NumOfSet][0]) / (FSvalues[NumOfVar][NumOfSet][1] - FSvalues[NumOfVar][NumOfSet][0]);
}
else
{
return (FSvalues[NumOfVar][NumOfSet][2] - value) / (FSvalues[NumOfVar][NumOfSet][2] - FSvalues[NumOfVar][NumOfSet][1]);
}
}
}
function getDesiredValuesFuzzy()
{
for (var j=0;j!=NFuzzySets[0];j++)
{
AlphaCut[j] = 0;
}
for (var i=0;i!=NRules;i++)
{
var tempMR = 0;
var tempMinMR = 1;
for (var CurrentVar = NFuzzyOutputs;CurrentVar!=NFuzzyVars;CurrentVar++)
{
tempMR = getMRFromFSvalues(ValuesForFuzzy[CurrentVar],CurrentVar,RBase[i][CurrentVar]);
if (tempMR < tempMinMR)
tempMinMR = tempMR;
}
for (var CurrentVar = 0;CurrentVar!=NFuzzyOutputs;CurrentVar++)
{
if (tempMinMR > AlphaCut[RBase[i][CurrentVar]])
AlphaCut[RBase[i][CurrentVar]] = tempMinMR;
}
}
for (var CurrentVar = 0;CurrentVar!=NFuzzyOutputs;CurrentVar++)
{
var FuzzyRange = FSvalues[CurrentVar][NFuzzySets[CurrentVar]-1][2] - FSvalues[CurrentVar][0][0];
var NIntervals = 100;
var CoordinateMassSumm = 0;
var MassSumm = 0;
for (var i=0;i!=NIntervals;i++)
{
var TempCoordinate = FSvalues[CurrentVar][0][0] + FuzzyRange/NIntervals*i;
var TempMass1 = 0;
var TempMass2 = 0;
for (var j=0;j!=NFuzzySets[CurrentVar];j++)
{
TempMass2 = getMRFromFSvalues(TempCoordinate,CurrentVar,j);
if (TempMass2 > AlphaCut[j])
TempMass2 = AlphaCut[j];
if (TempMass2 > TempMass1)
TempMass1 = TempMass2;
}
MassSumm += TempMass1;
CoordinateMassSumm += TempCoordinate * TempMass1;
}
if (MassSumm != 0)
ValuesForFuzzy[CurrentVar] = CoordinateMassSumm / MassSumm;
if (ValuesForFuzzy[CurrentVar].isNaN)
ValuesForFuzzy[CurrentVar] = 0;
}
}
////////////////////////////////////////////////unfuzzy
function controlAlgorithm () {
ValuesForFuzzy[1] = desiredValue-actualValue;
console.log(ValuesForFuzzy[1]);
getDesiredValuesFuzzy();
pwm = ValuesForFuzzy[0];
console.log(pwm);
if (pwm > 255) {pwm = 255}; // to limit pwm values
if (pwm < -255) {pwm = -255}; // to limit pwm values
if (pwm > 0) {board.digitalWrite(2,1); board.digitalWrite(4,0);}; // direction if > 0
if (pwm < 0) {board.digitalWrite(2,0); board.digitalWrite(4,1);}; // direction if < 0
board.analogWrite(3, Math.abs(pwm));
};
function startControlAlgorithm () {
setInterval(function(){controlAlgorithm();}, 30); // call the alg. on 30ms
console.log("Control algorithm has been started.");
};
function sendValues (socket) {
socket.emit("clientReadValues",
{
"desiredValue": desiredValue,
"actualValue": actualValue
});
};
<!DOCTYPE html>
<meta charset = utf8>
<html>
<head>
<title>Example with potentiometer</title>
</head>
<body onload="load();">
<div>
<canvas id="canvas1" width ="200" height = "100" style="border: 1px dashed #00c3c3;"></canvas>
</div>
<p></p>
<div id="divForPrint"></div>
<br>
<script type="text/javascript" src="/socket.io/socket.io.js"></script>
<script type="text/javascript">
var potValue1 = 0; // value of the first potentiometer
var potValue2 = 0; // value of the second potentiometer
var x1 = new Array(); // array for x1
var y1 = new Array(); // array for y1
var x2 = new Array(); // array for x2
var y2 = new Array(); // array for y2
var canvas1;
var ctx1;
function load() { // function that is started, when we open the page
canvas1 = document.getElementById("canvas1");
ctx1 = canvas1.getContext("2d");
// initialization of first graph
for (var i=0; i<200; i++) {
x1[i] = i; // for x values are 0, 1, 2, ...
y1[i] = 0; // for y values are 0
};
// initializaion of second graph
for (var i=0; i<200; i++) {
x2[i] = i; // x2 values are 0, 1, 2, ...
y2[i] = 0; // for y2 values are 0
};
};
var divForPrint = document.getElementById("divForPrint");
// var for printing messages
var numberOfLinesInLog = 20; // variable for the number of lines in log div
var counterOfLogs = 0; // variable for counting the logs
function log(msg) { // function to print messages to div with implemented scroll
var node=document.createElement("tr"); // we create variable node as tr (table row)
var textnode=document.createTextNode(counterOfLogs + " | " + msg); // create elem. with text
node.appendChild(textnode); // add to "node", i.e. table row
divForPrint.insertBefore(node, divForPrint.childNodes[0]); // insert into variable divForPrint -> document.getElementById("divForPrint");
if (counterOfLogs > numberOfLinesInLog-1) { // if there are more numbers as e.g. 10
divForPrint.removeChild(divForPrint.childNodes[numberOfLinesInLog]); // remove the oldest printout
}
counterOfLogs = counterOfLogs + 1; // increase the counter of logs
}
var socket = io.connect("192.168.254.149:8080"); // connect via socket
socket.on("messageToClient", function (msg){
log(msg); // add msg to div
});
socket.on("clientReadValues", function(value) {
potValue1 = value.desiredValue;
potValue2 = value.actualValue;
// Draw graph No1 *****************************************
ctx1.lineWidth = "1";
ctx1.strokeStyle = "#ff0000";
ctx1.clearRect(0, 0, canvas1.width, canvas1.height); // clear the canvas
ctx1.beginPath(); // to start drawing new line
y1.splice(0, 1); // on the position 0 in the field y1 we erase one value
y1[199] = potValue1; // new value is added at the end
for (var i=0; i<200; i++) {
ctx1.lineTo(x1[i], (100 - (y1[i] / 1023) * 100)); // 0,0 x,y coordinate is in upper left corner
};
ctx1.stroke(); // to draw the line
// End of draw graph No1 ***********************************
// Draw graph No2 *****************************************
ctx1.strokeStyle = "#00ff00"; // green line
ctx1.beginPath(); // to start drawing new line
y2.splice(0, 1); // on the position 0 in the field y2 we erase one value
y2[199] = potValue2; // new value is added at the end
for (var i=0; i<200; i++) {
ctx1.lineTo(x2[i], (100 - (y2[i] / 1023) * 100)); // 0,0 x,y coordinate is in upper left corner
};
ctx1.stroke(); // to draw the line
// End of draw graph No2 ***********************************
log(value.desiredValue + "|" + value.actualValue);
})
socket.on("disconnect", function(){
log("Disconnected from the server"); // we print status of disconn. to div
});
</script>
</body>
</html>