AWS Lambda作为一个针对AWS计算资源的IFTTT脚本创建器和管理器,结合开源神经网络预测库Brain,在物联网会爆发出怎么的火花?
笔者认为,物联网生态圈,最复杂的不是各制式设备间,不同协议间的互联互通,比如Zigbee 6lowpan Z-wave BLE甚至谷歌在推的Thread,他们之间的互通,从技术和实现上难度都不大,OIC联盟和Allseen联盟都正在做这方面的工作,且进展都很快。至于苹果则划定了厂商在自家的HomeKit/WatchKit平台上才能接入,当然对于大家的诸多诟病 用一句”为了安全“来解释也说得通,毕竟人家牛在可以不带你玩。这些都不是我们今天谈论的焦点,正如笔者在之前那篇“SDT(software-defined Things)?软件定义的物件“一文中提到的,针对用户智能 便捷 友好才是重点,目前市场上纷乱繁杂各种IOT解决方案,无外乎家里用智能平台(手机,TV)控制Arduino或其他芯片板的一个switch、一个灯 乃至空调 冰箱各种家用设备,至于工业上则是针对更庞大的sensors network搜集信息汇总,进行数据分析以便采取决策方案,最终达到节省资源,控制损耗,及时反映市场变化,达到效率的最优化。如果一套智能解决方案只是一堆技术的堆积,而且需要用户设置这设置那进行一堆繁琐的工作,很难想象用户会有很持久的热情,而且这也背离了IOT的初衷”让工作、生活更美好 给用户更多便利“。所以如何让IOT方案更智能无疑是及其重要的一环。
我们先想象一下,一家工厂有两种传感器,分别检测温度和压力。温度和压力构成一对组合,这些组合有些无疑是危险的,比如温度过高,压力过大等,都应该触发报警阈值报警并采取相应措施。问题是,由于各种因素的关系,温度,压力和报警的关系不能用一个具体的方程来描述。在这样的情况下,机器学习就派上了用场。
Azure的Machine Learning服务比较复杂,更面向专家型用户或分析人员,而2014年底AWS推出的Lambda服务则刚好切合了订制小型IFTTT脚本的需求,至于智能推理这样机器学习的工作,就交给Brain(https://github.com/harthur/brain)来完成。下面我们结合ThingFabric IOT平台来介绍如何完成规则的智能定制。
Lambda发生动作的步骤:
温度和压力构成一对组合,放在有效载荷中部署到一个特定的S3 bucket,lambda 已经做了触发设置,Brain网络会根据一个训练集做初始训练,然后结合温度压力对做评估预测,如何触发Lambda的设定就会发一个MQTT消息给ThingFabric平台,ThingFabric平台根据收到的MQTT消息做后续动作(比如关闭阀门,发送一份邮件或短信给负责人……)
Lambda初始脚本lambda_init.js
var aws = require('aws-sdk');
var s3 = new aws.S3({apiVersion: '2006-03-01'});
var brain = require("brain"); var mqtt = require('mqtt');
var config = {
mqtt: {
clientId: "<CLIENT ID>",
username: "<THING FABRIC USERNAME>",
md5Pass: "<THING FABRIC MD5 PASSWORD>",
outputTopic: "<THING FABRIC DOMAIN>/lambda"
},
alarm: {
maxTemp: 200,
maxPressure: 800,
alarmThreshold: 0.6
}
};
exports.handler = function(event, context) {
return context.done(null);
};
payload.json
{"device_id": "foo", "values": { "t": 100, "p": 400 } },
{ "device_id": "bar", "values": { "t": 120, "p": 320 } },
{ "device_id": "foobar", "values": { "t": 90, "p": 220 } }
s3_parser.js
function S3Parser(event) {
var _this = this;
_this.event = event;
_this.s3 = new aws.S3({apiVersion: '2006-03-01'});
_this.parseS3Object = function(cb) {
_this.s3.getObject({
Bucket: _this.event.Records[0].s3.bucket.name,
Key: _this.event.Records[0].s3.object.key
}, function (err, data) {
return cb(JSON.parse(data.Body.toString()));
});
};
}
增加MQTT客户端
mqtt_client.js
function MqttClient(config, onClose, onError) {
var _this = this;
_this.username = config.username;
_this.pass = config.md5Pass;
_this.clientId = config.clientId;
_this.client = mqtt.connect(
"mqtt://" + _this.username + ":" + _this.pass + "@q.m2m.io:1883",
{ "clientId": _this.clientId }
);
_this.client.on("close", onClose);
_this.client.on("error", function(error) { onError(error) });
_this.publish = function(topicName, payload) {
return _this.client.publish(topicName, JSON.stringify(payload));
};
_this.disconnect = function() {
return _this.client.end();
};
}
增加Brain网络 training_data.js
var trainingData = [
{ input: { t: 10, p: 275 }, output: { alarm: 0 } },
{ input: { t: 14, p: 230 }, output: { alarm: 0 } },
{ input: { t: 65, p: 240 }, output: { alarm: 0 } },
{ input: { t: 89, p: 301 }, output: { alarm: 1 } }, ...
];
alarm.js
function Alarm(config) {
var _this = this;
_this.maxTemp = config.maxTemp;
_this.maxPressure = config.maxPressure;
_this.alarmThreshold = config.alarmThreshold;
_this.nn = new brain.NeuralNetwork();
_this.tempToInput = function(t) {
return t / _this.maxTemp;
};
_this.pressureToInput = function(p) {
return p / _this.maxPressure;
};
_this.parseTrainingData = function(data) {
return data.map(function(point) {
return {
input: {
t: _this.tempToInput(point.input.t),
p: _this.pressureToInput(point.input.p)
},
output: {
alarm: point.output.alarm
}
}
});
};
_this.train = function(data) {
return _this.nn.train(_this.parseTrainingData(data));
};
_this.isTriggered = function(data) {
var guessedAlarm = _this.nn.run({
t: _this.tempToInput(data.t),
p: _this.pressureToInput(data.p)
}).alarm;
return {
triggered: (guessedAlarm > _this.alarmThreshold),
guessedAlarm: guessedAlarm
};
};
}
综合以上js脚本lambda_final.js
exports.handler = function(event, context) {
var parser = new S3Parser(event),
mqtt = new MqttClient(
config.mqtt,
function () {
return context.done(null);
},
function (error) {
return context.done(null, error);
}
),
alarm = new Alarm(config.alarm);
parser.parseFakeS3Object(function(payload) {
payload.readings.forEach(function(reading) {
var dataPoint = reading.values,
outputTopic = config.mqtt.outputTopic + "/" + reading.device_id;
if (dataPoint.t > config.alarm.maxTemp || dataPoint.p > config.alarm.maxPressure) {
mqtt.publish(
outputTopic, { alarm: 1, values: dataPoint }
);
}else {
alarm.train(trainingData);
var trigger = alarm.isTriggered(dataPoint);
if (trigger.triggered) {
mqtt.publish(
outputTopic, { alarm: trigger.guessedAlarm, values: dataPoint }
);
}
}
});
mqtt.disconnect();
});
};
这样,当Temperature 或pressure超过阀值200,800时,或者通过对(Temperature,Pressure)组合经Brain网络预测评估以决定是否触发门限alarmThreshold,发给Fabric的MQTT服务端,message格式形如
{"alarm": 0.8217286279146672,value:{"t",120,"p":320}},至于ThingFabric会如何处理,我们可以在ThingFabric平台设置类似rule
"<YOUR DOMAIN>/lambda/#" {"alarm": val where val > 0.7} -> sms to:"17209999999" text:"Temperature or pressure is too high!"
将Alarm转发给手机或Email,或发给Device进行相应动作。