この記事は obniz Advent Calendar 2020 13日目の記事です。
やったこと
obnizを使って、スマホの傾きでtoioをラジコンのように操作できるようにしてみました。
研究室にtoioがたくさん転がっていたので、toioで遊んでみたかったのが動機です。
@tos pic.twitter.com/2KD5uxLgA6
— さくあ@思いつきぶん投げ(?) (@sakua_create) December 12, 2020
toioについて
toioはロボットを使った新しいあそびのプラットフォームです。ひとりでもみんなでも、専用タイトルをかえることでゲームや工作、絵本、運転などいろいろあそびを通して楽しむことができます。いよいよ本格化するプログラミング教育に向けて、気軽に楽しく自宅学習にもお使いいただけます。
(HPより抜粋)
まだ触ったばかりで理解は浅いのですが、いくつかのセンサや小さいタイヤのついた、白いキューブを使ったプラットフォームのようです。
予めコンテンツが用意されているのでそのままでも遊べますが、ブロックプログラミングやコーディングによって自分だけの拡張も楽しめます。
材料
- obniz
- toio
- スマートフォン
- モバイルバッテリー
プログラム
全体の流れは、
- toioを探してBLEで接続する
- 接続できたら常にスマホの回転姿勢を取得する
- 回転姿勢によって左右のタイヤの回転方向・スピードを調整
となっています。
1. toioを探してBLEで接続
obnizのパーツライブラリの toio コア キューブ のページを参考に、toio コア キューブとの接続をしています。
if(Toio_CoreCube.isDevice(peripheral)){...}
のところで受信したBLEのアドバタイズがtoioなのかを判定してインスタンスを作り、.connectWait()
で接続しています。
await obniz.ble.initWait();
const Toio_CoreCube = Obniz.getPartsClass("toio_CoreCube");
obniz.ble.scan.onfind = async (peripheral) => {
//toioの探索
if(Toio_CoreCube.isDevice(peripheral)){
console.log("find");
const toio = new Toio_CoreCube(peripheral);
toio.ondisconnect = (reason) => {
console.log(reason);
}
//toioとの接続
await toio.connectWait();
console.log("connected");
...
}
...
}
2. スマートフォンの回転姿勢を取得
toioとの接続が完了したら、以下の部分でスマートフォンの回転姿勢を取得しています。
今回はbeta
とgamma
の値しか使っていませんが、以下の3つの値を取得できます。
-
alpha
: z軸を基準にしたデバイスの角度(画面を水平にしたときの水平回転) -
beta
: x軸を基準にしたデバイスの角度(画面を水平にしたときの前後の傾き) -
gamma
: y軸を基準にしたデバイスの角度(画面を水平にしたときの左右の傾き)
window.addEventListener("deviceorientation", async (e) => {
//X軸を中心にしたデバイスの角度(画面を水平にしたときの前後の傾き) -180~180
let beta = e.beta;
//y軸を中心にしたデバイスの角度(画面を水平にしたときの左右の傾き) -90~90
let gamma = e.gamma;
...
}
});
3. 左右のタイヤの回転方向・スピードを調整
前節でbeta
で前後の傾き、gamma
で左右の傾きを取得しました。これらの値に応じて左右のタイヤの回転方向やスピードを調整して、toioの動く方向を制御します。
まずは、beta
で前後の傾きを取得し、奥へ傾けたときは前進、手前に傾けたときは後退するように条件分けしました。また、水平からある閾値HORIZONTAL_THRESHOLD
(今回は±10度)までは水平とみなし、toioを静止させるようにしました。
if(beta > HORIZONTAL_THRESHOLD){
//後ろ(手前)に進むように調整
...
}else if(beta < -HORIZONTAL_THRESHOLD){
//前(奥)に進むように調整
...
}else{
//静止
...
}
次に、gamma
の左右の傾きの値も使って、それぞれの条件の中で左右のタイヤのスピードを計算しています。
計算結果が正の値だとタイヤが前方回転、負の値だと後方回転します。左右どちらかの回転スピードを緩めると、緩めた方に曲がって進みます。
回転姿勢を使ったタイヤの回転の計算方法がわからなかったので、自分でこんな感じの式を立ててみました。
(最大スピード) * (縦の傾き具合0.0〜1.0) ± (横の傾き角度/20)
縦の傾き具合によって、最大スピードの何割で進むのかを決めています。
また、横の傾きを調整した値を足し引きすることによって、曲がる方のタイヤのスピードを緩めています。
if(beta > HORIZONTAL_THRESHOLD){
//後ろ(手前)に進むとき
speedLeft = MAX_SPEED * beta / MAX_TILT_BETA - gamma / 20;
speedRight= MAX_SPEED * beta / MAX_TILT_BETA + gamma / 20;
}else if(beta < -HORIZONTAL_THRESHOLD){
//前(奥)に進むとき
speedLeft = MAX_SPEED * beta / MAX_TILT_BETA + gamma / 20;
speedRight= MAX_SPEED * beta / MAX_TILT_BETA - gamma / 20;
}else{
//静止
speedLeft = 0;
speedRight = 0;
}
最後に、100msおきに、計算した左右のスピードに合わせてtoioを動かしています。
obniz.repeat(async () => {
await toio.moveAroundWait(speedLeft,speedRight);
}, 100);
完成したコード
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link
rel="stylesheet"
href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
/>
<!--- <link rel="stylesheet" href="/css/starter-sample.css" /> -->
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<script
src="https://unpkg.com/obniz@3.10.1/obniz.js"
crossorigin="anonymous"
></script>
</head>
<body>
<div id="obniz-debug"></div>
<div id="ui-buttons" class="text-center">
<h3 class="text-center">スマホの傾きでtoioを操作</h3>
<button class="btn btn-primary" id="stop">ストップ(補助用)</button>
</div>
<script>
let obniz = new Obniz("OBNIZ_ID_HERE");
const HORIZONTAL_THRESHOLD = 10;
const MAX_SPEED = 255;
const MAX_TILT_BETA = 180;
let speedLeft = 0;
let speedRight = 0;
obniz.onconnect = async() => {
await obniz.ble.initWait();
const Toio_CoreCube = Obniz.getPartsClass("toio_CoreCube");
obniz.ble.scan.onfind = async (peripheral) => {
//toioの探索
if(Toio_CoreCube.isDevice(peripheral)){
console.log("find");
const toio = new Toio_CoreCube(peripheral);
toio.ondisconnect = (reason) => {
console.log(reason);
}
//toioとの接続
await toio.connectWait();
console.log("connected");
window.addEventListener("deviceorientation", async (e) => {
//X軸を中心にしたデバイスの角度(画面を水平にしたときの縦の傾き) -180~180
let beta = e.beta;
//y軸を中心にしたデバイスの角度(画面を水平にしたときの横の傾き) -90~90
let gamma = e.gamma;
if(beta > HORIZONTAL_THRESHOLD){
//後ろ(手前)に進むとき
speedLeft = MAX_SPEED * beta / MAX_TILT_BETA - gamma / 20;
speedRight= MAX_SPEED * beta / MAX_TILT_BETA + gamma / 20;
}else if(beta < -HORIZONTAL_THRESHOLD){
//前(奥)に進むとき
speedLeft = MAX_SPEED * beta / MAX_TILT_BETA + gamma / 20;
speedRight= MAX_SPEED * beta / MAX_TILT_BETA - gamma / 20;
}else{
//静止
speedLeft = 0;
speedRight = 0;
}
});
//緊急ストップ用
$('#stop').on('click', async () => {
console.log("stop");
await toio.moveAroundWait(0,0);
});
obniz.repeat(async () => {
await toio.moveAroundWait(speedLeft,speedRight);
}, 100);
}
};
await obniz.ble.scan.startWait();
};
</script>
</body>
</html>
さいごに
お疲れであろう同期に、このラジコンtoioで栄養ドリンクを届けてみました...
(危ないので皆さんは真似しないでくださいね。)
@tos pic.twitter.com/erfqWARIrb
— さくあ@思いつきぶん投げ(?) (@sakua_create) December 12, 2020