事象発生日:2018-12-19
記事公開日:2018-12-19
アクセス数:8057
WebSocketを利用して,Raspberry Piを積んだロボットを遠隔操作してみた.
最低限の動作確認ができたので,映像配信を追加したり,テレメトリをグラフ化したりと,作り込んでいきたい.
Raspberry Pi 3
Ubuntu Server 16.04.5 LTS (Xenial Xerus)
Node.js v10.14.2
Microsoft Windows 10 Home 1803 (64bit)
Google Chrome 71.0.3578.80 (Official Build) (64bit)
ロボット,PC,ともにWiFiによって同一LANにいる.
このRaspberry Pi Mouseは,ROSで制御することを考えているが,/dev/
配下のデバイスファイルを直接叩くことでも制御できる.
そこでここでは,前記事(「」)でのサンプルプログラムなどを参考に,特定のイベントを受信したらデバイスファイルを叩き,必要があれば結果を返すものを作った.
また,定期実行(周期実行)なども実装した.
最低限のことはできることが確認できたので,ロボットの本格的な遠隔操作もできそうである.
こんな感じ.
Lチカができるし,測距センサの値を読める.
連続開始
を押すと,0.5秒毎に測距センサの値が届くようになり,連続終了
を押すと止まる.
ソースコードを載せる.
ロボット側(サーバー側)で,
$ node node_app_pimouse.js
と実行しておき,遠隔PCのブラウザでロボットのIP,ポートへアクセスすれば,上のような画面が表示される.
以下,4ファイルのソースコードを掲載する.
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <title>RaspPi Mouse Console</title> <link rel="stylesheet"href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css"> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <script type="text/javascript" src="/socket.io/socket.io.js"></script> <script type="text/javascript" src="./pimouse.js"></script> <link href="./pimouse.css" type="text/css" rel="stylesheet"> </head> <body> <div class="container"> <h1>RaspPi Mouse Console</h1> <h4>LED:</h4> <div class="row"> <button type="button" id="ledOn" class="col-md-1 btn btn-danger">LED ON</button> <button type="button" id="ledOff" class="col-md-1 btn btn-danger">LED OFF</button> </div> <h4>測距センサ:</h4> <div class="row"> <button type="button" id="lightSensorSingle" class="col-md-1 btn btn-danger">シングル</button> <button type="button" id="lightSensorSeqBegin" class="col-md-1 btn btn-danger">連続開始</button> <button type="button" id="lightSensorSeqEnd" class="col-md-1 btn btn-danger">連続終了</button> </div> <div id="lightSensorLogs" class="log"></div> </div> </body> </html>
$(function() { var socket = io.connect(); socket.on("s2c_LS_DATA", function(data){AppendLsLog(data.value)}); $("form").submit(function(e){ console.log("submit"); var message = $("#msgForm").val(); $("#msgForm").val(''); socket.emit("client_to_server", {value : message}); e.preventDefault(); }); $("button#ledOn").on('click', function() { console.log("LED ON"); socket.emit("c2s_LED_ON", null); }); $("button#ledOff").on('click', function() { console.log("LED OFF"); socket.emit("c2s_LED_OFF", null); }); $("button#lightSensorSingle").on('click', function() { socket.emit("c2s_LS_SINGLE", null); }); $("button#lightSensorSeqBegin").on('click', function() { socket.emit("c2s_LS_SEQ_BEGIN", null); }); $("button#lightSensorSeqEnd").on('click', function() { socket.emit("c2s_LS_SEQ_END", null); }); }); function AppendLsLog(text) { console.log(text); $("#lightSensorLogs").append("" + text + "
"); }
@charset "utf-8"; div.container div.row { margin-bottom: 20px; } button { margin: 3px; } h4 { margin-bottom: 0; } div.log p { margin: 0; padding: 0; line-height: 1.2; }
var http = require('http'); var socketio = require('socket.io'); var path = require('path'); var fs = require('fs'); var mime = { ".html": "text/html", ".js" : "application/javascript", ".css" : "text/css", // 読み取りたいMIMEタイプはここに追記 }; var server = http.createServer(function(req, res) { if (req.url == '/') { filePath = '/pimouse.html'; // 仮 } else { filePath = req.url; } var fullPath = __dirname + filePath; console.log('fullPath : ' + fullPath); res.writeHead(200, {"Content-Type": mime[path.extname(fullPath)] || "text/plain"}); fs.readFile(fullPath, function(err, data) { if (err) { // エラー時の応答 } else { res.end(data, 'UTF-8'); } }); }).listen(3000); console.log('Server running at http://localhost:3000/'); var io = socketio.listen(server); io.sockets.on('connection', function(socket) { // タイマー変数の初期化 var timer_lt = { id : null, is_on : 0, } socket.on('client_to_server', function(data) { io.sockets.emit('server_to_client', {value : data.value}); }); socket.on('c2s_LED_ON', function(data) { OnLed(); }); socket.on('c2s_LED_OFF', function(data) { OffLed(); }); socket.on('c2s_LS_SINGLE', function(data) { SendLtValue(); }); socket.on('c2s_LS_SEQ_BEGIN', function(data) { if (timer_lt.is_on == 0) { timer_lt.id = setInterval(SendLtValue, 500); } timer_lt.is_on = 1; }); socket.on('c2s_LS_SEQ_END', function(data) { if (timer_lt.is_on == 1) { clearInterval(timer_lt.id); } timer_lt.is_on = 0; }); socket.on('disconnect', function() { if (timer_lt.is_on == 1) { clearInterval(timer_lt.id); } OffLed(); }); }); // LED ON function OnLed() { // shell実行(非同期) // http://tkybpp.hatenablog.com/entry/2016/04/25/163246 const exec = require('child_process').exec; exec('echo 1 > /dev/rtled1', (err, stdout, stderr) => { // if (err) { console.log(err); } // console.log(stdout); }); console.log("LED ON"); } // LED OFF function OffLed() { // shell実行(非同期) // http://tkybpp.hatenablog.com/entry/2016/04/25/163246 const exec = require('child_process').exec; exec('echo 0 > /dev/rtled1', (err, stdout, stderr) => { // if (err) { console.log(err); } // console.log(stdout); }); console.log("LED OFF"); } // 光センサの値を読み取って返す function SendLtValue() { var ret = { value : null, }; // shell実行(同期) // http://tkybpp.hatenablog.com/entry/2016/04/25/163246 const execSync = require('child_process').execSync; const result = execSync('cat /dev/rtlightsensor0').toString(); console.log(result); ret.value = result; // console.log(ret.value); io.sockets.emit('s2c_LS_DATA', {value : ret.value}); }
最低限の制御ができることが試せたので,さらに作り込んでいく.
また,ロボットに取り付けてあるカメラの映像も配信できるようにしていく.
↓映像配信を実装しました!
名前
Email (※公開されることはありません)
コメント