MENU

溶けかけてるうさぎ HP GALLERY BLOG TOP RECENT ARTICLES POPULAR ARTICLES ABOUT THIS BLOG

CATEGORY

大学 (140) 仕事 (17) 航空宇宙 (104) 写真 (78) 旅行 (32) 飯・酒 (17) コンピュータ (119) その他 (44)

TAG

ARCHIVE

RECENT

【写真】撮影写真を Map 上に表示できるようにした 【カメラ】X100 シリーズが好きすぎる(主にリーフシャッタ) 【カメラ】X100V から X100VI に買い替えました 【自宅サーバー】Google Domains から Cloudflare にドメインを移管 【カメラ】FUJIFILM XF レンズのサイズ比較ができるようにしてみた

【WebSocket】Raspberry Pi搭載ロボットをPCのブラウザから遠隔操縦する(簡易操作編)

事象発生日:2018-12-19

記事公開日:2018-12-19

アクセス数:8057

WebSocketを利用して,Raspberry Piを積んだロボットを遠隔操作してみた.

最低限の動作確認ができたので,映像配信を追加したり,テレメトリをグラフ化したりと,作り込んでいきたい.

 

トップ画像の出典はこちらこちら

1.開発環境

前記事「」で構築した環境である.

ロボット

Raspberry Pi Mouse

Raspberry Pi 3

Ubuntu Server 16.04.5 LTS (Xenial Xerus)

Node.js v10.14.2

遠隔PC

Microsoft Windows 10 Home 1803 (64bit)

Google Chrome 71.0.3578.80 (Official Build) (64bit)

ネットワーク

ロボット,PC,ともにWiFiによって同一LANにいる.

2.ロボット遠隔操作

概要

この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>
pimouse.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 + "

"); }
pimouse.js
@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;
}
pimouse.css
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});
}
node_app_pimouse.js

3.今後

最低限の制御ができることが試せたので,さらに作り込んでいく.

また,ロボットに取り付けてあるカメラの映像も配信できるようにしていく.

 

↓映像配信を実装しました!

4.出典・参考

動かざることバグの如し 近づきたいよ 君の理想に. Socket.IOの送信コマンドまとめ. Retrieved December 19, 2018, from http://thr3a.hatenablog.com/entry/20141123/1416723018
@IT. チャットアプリ開発に見る、Socket.IOの基本ライブラリの使い方 (2/3). Retrieved December 19, 2018, from http://www.atmarkit.co.jp/ait/articles/1604/27/news026_2.html

関連記事

コメントを投稿

名前

Email (※公開されることはありません)

コメント