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-26

記事公開日:2018-12-26

アクセス数:8109

ロボットに搭載されているカメラの映像を遠隔PCから再生できるブラウザベースのインターフェイスを作った.

前記事「」で,ロボットのLEDや測距センサを使うことができていたので,それに動画配信を追加した形となる.

 

ただの動画配信であれば,ROSのmjpeg_server packageあたりを使えば簡単に実装できるが,将来的にはロボットと遠隔PCの間に中間サーバーをはさみたいため,Node.jsで直接カメラから画像を取得して配信する方式にした.

 

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

1.開発環境

前記事「」の続きである.

ロボット

Raspberry Pi Mouse

Raspberry Pi 3

Ubuntu Server 16.04.5 LTS (Xenial Xerus)

Node.js v10.14.2

ROS kinetic

UVC対応カメラ:LOAS MCM-15W

遠隔PC

Microsoft Windows 10 Home 1803 (64bit)

Google Chrome 71.0.3578.80 (Official Build) (64bit)

ネットワーク

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

2.中継サーバーを想定しない,もっとも簡単な動画配信方法

USBにUVC対応カメラを挿せば,/dev/video0/あたりにマウントされる.

中継サーバーを想定しないのであれば,ROSのmjpeg_server packageが使える.

以下で必要なパッケージ類はそろうはずである.

$ sudo apt install ros-kinetic-cv-bridge
$ sudo apt install ros-kinetic-cv-camera
$ sudo apt install ros-kinetic-image-transport-plugins
$ cd ~/catkin_ws/src
$ git clone git@github.com:RobotWebTools/mjpeg_server.git
$ cd ~/catkin_ws
$catkin_make

パッケージインストール,ビルド後に,ターミナルを3つ開いて,

$ roscore
$ rosrun cv_camera cv_camera_node
$ rosrun mjpeg_server mjpeg_server _port:=10000

を走らせれば,遠隔PCのブラウザよりhttp://${robot_ip}:10000/stream?topic=/cv_camera/image_rawにアクセスするだけで動画がみれる.

mjpeg_serverがいい感じにhtmlをつくり,内部の映像を更新してくれているようだ.

3.Node.jsを用いてカメラの画像を取得し,それをWebSocketを使って遠隔PCに配信

まず,Node.jsからUVCカメラを扱うために使えるパッケージをインストールする.

v4l2cameraでも使おうかと思ったが,インストールでコケたので,node-webcamを使用した.

 

以下でインストールできる.

$ sudo apt install fswebcam
$ npm install --save node-webcam

 

実装としては,5Hzで画像を取得し,Base64でテキストエンコーディングして配信,といった雰囲気である.

(画像がチューニングできてないのは許して.)

 

前記事「」のコードに以下の部分を追加した.

中略となっている部分は,前回の記事を参照のこと.

<!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="cameraOn"  class="col-md-1 btn btn-danger">ON</button>
		<button type="button" id="cameraOff" class="col-md-1 btn btn-danger">OFF</button>
	</div>
	<img id="cameraCapture">
	<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_CAMERA_DATA",  function(data){UpdateCamera(data.value)});

	... 中略 ...

	$("button#cameraOn").on('click', function() {
		console.log("CAMERA ON");
		socket.emit("c2s_CAMERA_ON", null);
	});

	$("button#cameraOff").on('click', function() {
		console.log("CAMERA OFF");
		socket.emit("c2s_CAMERA_OFF", null);
	});
});

... 中略 ...

function UpdateCamera(data) {
	console.log(data);
	$("#cameraCapture").attr('src', data);
}
pimouse.js
@charset "utf-8";

div.container div.row {
	margin-bottom: 20px;
}

button {
	margin: 3px;
}

h4 {
	margin-bottom: 0;
	margin-top: 25px;
}

div.row {
	margin-bottom: 5px;
}

div.log p {
	margin: 0;
	padding: 0;
	line-height: 1.2;
}

#cameraCapture {
	width: 320px;
	height: 240px;
}
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タイプはここに追記
};

// web camera
var NodeWebcam  = require('node-webcam');
var opts_camera = {
	width:          320,
	height:         240,
	callbackReturn: "base64"
};

... 中略 ...

io.sockets.on('connection', function(socket) {
	// タイマー変数の初期化
	var timer_camera = {
		id : null,
		is_on : 0,
	}

	... 中略 ...

	socket.on('c2s_CAMERA_ON', function(data) {
		if (timer_camera.is_on == 0) {
			timer_camera.id = setInterval(SendCameraCapture, 200);
		}
		timer_camera.is_on = 1;
	});

	socket.on('c2s_CAMERA_OFF', function(data) {
		if (timer_camera.is_on == 1) {
			clearInterval(timer_camera.id);
		}
		timer_camera.is_on = 0;
	});

	... 中略 ...

	socket.on('disconnect', function() {
		if (timer_lt.is_on == 1) {
			clearInterval(timer_lt.id);
			clearInterval(timer_camera.id);
		}
		OffLed();
	});
});

... 中略 ...

// Camera
function SendCameraCapture() {
	NodeWebcam.capture( "test_picture", opts_camera, function( err, data ) {
		var ret = {
			value : null,
		};
		// ret.value = 'data:image/jpeg;base64,' + data;
		ret.value = data;
		io.sockets.emit('s2c_CAMERA_DATA', {value : ret.value});
	});
}
node_app_pimouse.js

4.続き

次回はいよいよ中継サーバーを建てて,それ経由で通信を行う.

中継サーバーのみがグローバルIPを保持すれば通信可能なシステムをつくる.

次記事:「

5.出典

koi.mashykom.com. RaspberryPi Mouseロボットの操作. Retrieved December 26, 2018, from http://www.koi.mashykom.com/Pimouse.html
npm. v4l2camera. Retrieved December 26, 2018, from https://www.npmjs.com/package/v4l2camera
npm. node-webcam. Retrieved December 26, 2018, from https://www.npmjs.com/package/node-webcam

関連記事

コメントを投稿

名前

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

コメント