事象発生日:2018-12-26
記事公開日:2018-12-26
アクセス数:8109
ロボットに搭載されているカメラの映像を遠隔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 (※公開されることはありません)
コメント