色々とトラブルがありながらも、ロボットを改修しています。今回はブラウザからロボットを操作出来るようにしてみました。

外観は当初予定していたカメラ用のアームを外し(理由は後ほど)、超音波センサーを取り付けてみました。

ローカルブラウザから操作出来るようにしたのでiPhoneからも操作可能です。こんな感じです。

操作画面はこんな風に作ってみました。ちょっと昔のSF映画調フォントがこだわりポイントです。

ボタンでロボットを操作しています。超音波センサーを使ってロボットから障害物までの距離を計測出来ます。

センサーを利用して障害物が10cmまで迫ったら停止します。

このロボットはモーターやセンサー部分はArduino、指示を出すサーバーはRaspberryPiを利用して動いています。

どちらもJavaScriptで操作する事が可能なので、この様にブラウザからの操作も比較的簡単に行えます。さて、ちょっとまとめてみます。

Socket.io

Socket.ioと聞いて、「非同期通信」とか「リアルタイムチャット」などという言葉を連想される方も多いと思います。この「非同期通信」はWeb上だけでなくロボット制御にも威力を発揮します。

ものすごくシンプルなサンプルを公開してくれている方がいますのでこれをそのまま利用してみました。

cdugd/index.html

コメントに「Great!」とされている方もいる様に、素晴らしく簡潔なコードです。サーバーはSocket.ioで起動させ、操作用のhtmlファイルはfsを利用して読み込んでいるので、サーバーを2つ立てる必要がありません。(このやり方は気がつかなかった…。)

Socket.ioを利用してサーバーサイドとクライアントサイドが非同期接続されているので、センサーからのデータを受け取ったり、モーター制御の数値を受け渡したり、という事がリアルタイムで行う事が可能になります。

こんな感じ

app.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
//モジュールの読み込み
var app = require('http').createServer(handler),
io = require('socket.io').listen(app),
fs = require('fs'),
five = require('johnny-five');
//Socket.ioサーバポートを指定
app.listen(8080);
//htmlファイルをfsで読み込みindex.htmlに指定
function handler (req, res) {
fs.readFile(__dirname + '/index.html',
function (err, data) {
if (err) {
res.writeHead(500);
return res.end('Error loading index.html');
}
res.writeHead(200);
res.end(data);
});
}
//arduinoを指定
board = new five.Board();
//モーターとセンサーのピンを指定
board.on("ready", function() {
var a = new five.Motor({
controller: "GROVE_I2C_MOTOR_DRIVER",
pin: "A",
});
var b = new five.Motor({
controller: "GROVE_I2C_MOTOR_DRIVER",
pin: "B",
});
var proximity = new five.Proximity({
controller: "HCSR04",
pin: 7
});
//Socket.ioと接続
io.sockets.on('connection', function (socket) {
//センサーデータを送信
proximity.on("data", function() {
console.log("cm: ", this.cm);
socket.emit('message', this.cm);
});
socket.on('click_motor_forward', function () {
console.log("FORWARD");
a.rev(252);
b.fwd(252);
});
//モーター操作のボタンクリックを受信、モーター操作
socket.on('click_motor_stop', function () {
console.log("STOP");
a.stop();
b.stop();
});
socket.on('click_motor_right', function () {
console.log("TURN_RIGHT");
a.rev(381);
b.rev(381);
});
socket.on('click_motor_left', function () {
console.log("TURN_LEFT");
a.fwd(381);
b.fwd(381);
});
socket.on('click_motor_backward', function () {
console.log("BACKWARD");
a.fwd(252);
b.rev(252);
});
});
});

index.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
<html>
<head>
<meta charset="utf-8">
<title>PonBot01</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<link href="https://fonts.googleapis.com/css?family=Orbitron" rel="stylesheet">
<style>
body{
font-family: 'Orbitron', sans-serif;
background-color: #3D8EB9;
color: #f6f6f6;
max-width: 480px;
margin: 0 auto;;
padding: 20px;
}
</style>
</head>
<body>
<article>
<h1>PonBot01</h1>
<!-- モーター制御ボタン -->
<h2>I2C Motor Driver</h2>
<p class="text-center">
<a id="motor_forward" type="button" class="btn btn-default btn-lg"> Forward </a>
</p>
<p class="btn-group btn-group-justified btn-group-lg" role="group">
<a id="motor_left" type="button" class="btn btn-default">Left</a>
<a id="motor_stop" type="button" class="btn btn-default">Stop</a>
<a id="motor_right" type="button" class="btn btn-default">Right</a>
</p>
<p class="text-center">
<a id="motor_backward" type="button" class="btn btn-default btn-lg">Backward</a>
</p>
<br>
<!-- 超音波センサーデータ -->
<h2>HC-SR04</h2>
<h3><span id="distance"></span></h3>
<h3><span id="attention_1"></span></h3>
</article>
<script src="https://cdn.socket.io/socket.io-1.2.0.js"></script>
<script src="http://code.jquery.com/jquery-1.11.1.js"></script>
<script>
$(document).ready(function() {
//Socket.ioと接続
var socket = io.connect('http://192.168.0.12');
//モーター制御ボタンクリックをサーバーサイドに送信
$('#motor_forward').click(function(e){
socket.emit('click_motor_forward');
e.preventDefault();
});
$('#motor_stop').click(function(e){
socket.emit('click_motor_stop');
e.preventDefault();
});
$('#motor_left').click(function(e){
socket.emit('click_motor_left');
e.preventDefault();
});
$('#motor_right').click(function(e){
socket.emit('click_motor_right');
e.preventDefault();
});
$('#motor_backward').click(function(e){
socket.emit('click_motor_backward');
e.preventDefault();
});
//超音波センサーデータをサーバーサイドから受信
socket.on('message', function(data){
$('#distance').text(data + "cm");
});
//超音波センサーデータが10cm以下だったら停止ボタンクリックを送信
socket.on('message', function(data){
$('#distance_1').text(data + "cm");
if(data < 10){
socket.emit('click_motor_stop');
$('#attention_1').text("WARNING : " + data + "cm");
}
});
});
</script>
</body>
</html>

結構シンプルに、簡易的なチャットを作る様な記述でロボットを操作する事が出来ます。

まとめ

しかしながら冒頭にロボットが暴走した様に、RaspberryPiを使ってシリアル通信する際は、電圧による注意が必要です。

Servo Arduino Raspberry Pi serial port #719

johnny-fiveのコミュニティボードにこんな投稿がありました。私もハマったのですが、RaspberryPiのシリアル通信を使ってArduinoに接続したサーボモータを制御しようとするとエラーになってしまいます。(Macなどからシリアル通信は可能)

どうもRaspberryPiから供給できる電圧に制限があり、仕様として制限をかけている様です。(古いタイプのRaspberryPiだとサーボによる電圧の変化に耐えきれずショートする場合もあるみたいです)

RaspberryPiとArduinoを利用する際はここはもう一工夫必要ですね。

ともあれI2C接続したモーターやアナログセンサー、デジタルセンサーはこのSocket.ioを使ったシリアル通信で様々なデータを取得したり、指示を送信したりする事が可能です。

Groveのスターターキットに様々なセンサーが入っていたので色々試してみようと思います。