Help us understand the problem. What is going on with this article?

自転車のパワーをRaspberryPiで可視化する

はじめに

近年、自転車(ロードバイク)では、ペダルを踏んだ力を「パワー」として数値化(単位はワット:W)できる「パワーメーター」が普及しつつあります。

4iiii.png
$\small{出典:4iii-innovations}$

筆者のパワーメーターは上記の画像の様にクランクに接着されていて、ペダルを踏んだ際のクランクの歪み度合いを、パワーに変換しています。

そんなパワーメーターを利用したバーチャルサイクリングアプリ「Zwift」が、自転車乗りの世界で人気沸騰中です。
今回はZwiftの紹介とRaspberryPiを利用し、出力したパワーをLEDの色で可視化する方法を紹介します。

Zwiftとは?

zwift.png

Zwift はMMO(Massively Multiplayer Online)ゲーム形式のサイクリング・ランニングトレーニングプログラム。世界中の参加者が仮想世界の中でトレーニングしたり、競争したりすることができる。
(Wikipediaより)

上記画像の様に、自転車をローラーという器具に固定し、パワーメーターの情報を元にZwiftのアバターをバーチャル世界で走らせます。
高性能なローラーだとパワーの検出、仮想世界の斜度に応じた負荷の変化、路面の凸凹を再現などもでき、より実走に近い感覚でサイクリングができます。

天候や時間に左右されることなく、世界中の人とレースを楽しみながら、トレーニングもできてしまう夢のようなアプリです。

やりたいこと

今回やりたいのはRaspberryPiを利用し、自分が出力しているパワー(以下、"パワー値")をLEDの色で可視化することです。

Zwiftで世界中の猛者と戦う中、必死に漕いでいると意識が朦朧としてきます。
そんな状況ではワット数を気にしている余裕がありません。そこでパワー値を色で表現すればわかりやすいのではないか、というのが今回の動機です。

用意するもの

実装(ハードウェア)

RaspberryPiとWS2812の配線は下記の通りです。
パワーメーターとの通信はANT+で行います。データの受信にはAnt受信ドングルを用います。
ws2812.png

実装(ソフトウェア)

以下に、ソフトウェアの実装手順と実行コマンドを示します。
まずはRaspberryPiにログインし、インストールを行っていきましょう。

USBライブラリのインストール

Ant受信ドングルを接続し、接続情報を取得、IDをメモしておきます。
結果は下記以外のUSB機器の情報も表示されます。「Dynastream Innovations」がAnt受信ドングルの情報です。

command
lsusb
Bus 001 Device 00X: ID 0fcf:1008 Dynastream Innovations, Inc. 

USBライブラリのインストールを行います。

command
sudo apt-get install -y libusb-1.0-0-dev libudev-dev

設定ファイルを作成します。IDが異なっていた場合は修正して下さい。

command
echo SUBSYSTEM=="usb", ATTRS{idVendor}=="0fcf", ATTRS{idProduct}=="1008", RUN+="/sbin/modprobe usbserial vendor=0x0fcf product=0x1008", MODE="0666", OWNER="pi", GROUP="root" | sudo tee /etc/udev/rules.d/garmin-ant2.rules 

Node.js、各種ライブラリのインストール

Node.jsに加えて、下記のライブラリもインストールします。
・ant-plus (npm GitHub)
・rpi-ws281x-v2 (npm GitHub)

commnad
sudo apt install -y nodejs npm  // Node.jsのインストール

mkdir -p ~/project/ant-ws2812  // Project作成
cd ~/project/ant-ws2812
npm init
npm install ant-plus // ライブラリのインストール
npm install rpi-ws281x-v2 --save

ソース

インストールが終わったところで、ようやく実装です!
GitHubにあるサンプルコードを元に実装していきます。
WS2812のサンプルコードはモジュールのLED数とGPIOのピン位置が異なるため変更します。

~/project/ant-ws2812/index.js
const Ant = require("ant-plus");
const ws281x = require("rpi-ws281x-v2")
const stick = new Ant.GarminStick2();
const sensor = new Ant.HeartRateSensor(stick);
const bicyclePowerSensor = new Ant.BicyclePowerSensor(stick);
// パワー範囲の定義
const Zone1 = 160;
const Zone2 = 210;
const Zone3 = 250;
const Zone4 = 300;
const Zone5 = 350;

let count = 0;

class Light {

        constructor() {
                this.config = {};
                this.config.leds = 8; // モジュールのLED数変更
                this.config.dma = 5;
                this.config.brightness = 100;
                this.config.gpio = 12; // GPIOピン変更
                this.config.strip = 'grb';
                ws281x.configure(this.config);
        }

        run(str) {
                var pixels = new Uint32Array(this.config.leds);
                switch (str){
                        // LED色の定義
                        case "red": red = 255; break;
                        case "orange": red = 255; green = 165; break;
                        case "yellow": red = 255; green = 255; break;
                        case "green": green = 255; break;
                        case "blue": blue = 255; break;
                        case "off": red = 0; green = 0; blue = 0; break;
                }
                var color = (red << 16) | (green << 8)| blue;

                for (var i = 0; i < this.config.leds; i++)
                        pixels[i] = color;

                // Render to strip
                ws281x.render(pixels);
        }

};

var light = new Light();
bicyclePowerSensor.on("powerData", function(data) { 
                count += 1;
                var num;
                num = data.ComputedHeartRate;
                // パワー値毎のLED色分け
                if(num < Zone1){
                        light.run("blue");
                }else if(Zone1 <= num && num < Zone2){
                        light.run("green")
                }else if(Zone2 <= num && num < Zone3){
                        light.run("yellow")
                }else if(Zone3 <= num && num < Zone4){
                        light.run("orange")
                }else if(Zone5 <= num){
                        light.run("red")
                }
                // パワー値の出力
                console.log(count, data.DeviceID, num);
                });

stick.on("startup", function() {
                console.log("on start up");
                bicyclePowerSensor.attach(0, 0);
                });
async function main() {
        if (!stick.open()) {
                console.log("Stick not found!");
                return;
        }
}
main();

実行

sudo node index.js

結果

以下が、実装後、Zwiftプレイ中のLEDの様子を撮影した動画です!

分かりにくいですが、画面左上(スマホの左)のLEDの色がパワー値に応じて変化しました!
※RaspberryPiの数値はリアルタイムで検出しているのに対し、Zwiftは3秒平均のパワーを表示させているためZwiftのパワー値は遅れて表示されています。

まとめ

Node.jsのライブラリを使用することで、簡単にパワー値の検出・LEDの点灯を実装することができました。これで日々のトレーニングに生かせそうです。また、LEDの色を変化させる事のない様に漕げば、パワーにムラの出ないペダリングも習得できそうです。
今後は、ディスプレイを接続して心拍など様々な情報も出力してみたいと思います。

最後までお読みいただきありがとうございました!

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした