事象発生日:2018-12-26
記事公開日:2018-12-26
アクセス数:8878
ロボットに搭載されているカメラの映像を遠隔PCから再生できるブラウザベースのインターフェイスを作った.
前記事「」で,ロボットのLEDや測距センサを使うことができていたので,それに動画配信を追加した形となる.
ただの動画配信であれば,ROSのmjpeg_server packageあたりを使えば簡単に実装できるが,将来的にはロボットと遠隔PCの間に中間サーバーをはさみたいため,Node.jsで直接カメラから画像を取得して配信する方式にした.
Raspberry Pi 3
Ubuntu Server 16.04.5 LTS (Xenial Xerus)
Node.js v10.14.2
ROS kinetic
UVC対応カメラ:LOAS MCM-15W
Microsoft Windows 10 Home 1803 (64bit)
Google Chrome 71.0.3578.80 (Official Build) (64bit)
ロボット,PC,ともにWiFiによって同一LANにいる.
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をつくり,内部の映像を更新してくれているようだ.
まず,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>
$(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);
}
@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;
}
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});
});
}
次回はいよいよ中継サーバーを建てて,それ経由で通信を行う.
中継サーバーのみがグローバルIPを保持すれば通信可能なシステムをつくる.
名前
Email (※公開されることはありません)
コメント