家庭用サイクルトレーナーにmicro:bitをつなぐ（その２）- STEP信号からワイヤレス運動測量計に表示されているスピードをmicro:bitで推定する

# 家庭用サイクルトレーナーにmicro:bitをつなぐ（その２）

[前回の記事](https://qiita.com/jp-96/items/110b6364a3da89553b27)では、家庭用サイクルトレーナーのワイヤレス運動測量計とmicro:bitをつなぎ、STEP信号からケイデンスを算出しました。

- リンク  
[家庭用サイクルトレーナーにmicro:bitをつなぐ（その１）- ワイヤレス運動測量計から出力されるクランク回転センサーの信号をモニターする](https://qiita.com/jp-96/items/110b6364a3da89553b27)

今回、そのケイデンスから、ワイヤレス運動測量計に表示されているように、そのスピードを推定します。

# スピード合わせと計算式

一定の速さでペダルを回転させて、スピードを計測し、ケイデンスセンサーのデータを元にしたスピードの計算式を求めます。

## 計測方法

メトロノームに合わせて、ペダルを漕いだ時のスピードを計測します。

|項目|内容|備考|
|---|---|---|
|メトロノーム|[サウンドブレナーメトロノーム (Soundbrenner)](https://play.google.com/store/apps/details?id=com.soundbrenner.pulse&hl=ja&gl=US)|Androidアプリ|
|ケイデンス|70RPM|左右で140bpm|


## 計測結果

ペダルの回転とスピードが安定したところで、スピードを読み取り、計測値としました。

|計測項目|計測値|
|---|---|
|第1計測|23.3km/h|
|第2計測|22.8km/h|
|第3計測|23.8km/h|
|平均スピード|23.3km/h|

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252891/f3f74b09-082b-5bea-cf21-8cbe8b15c5d7.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252891/ea7b5d3d-a6b5-bbe3-d476-ce366bee6f4b.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252891/54fe1316-01be-c64d-eeca-4e4a0998077c.png)

## スピードの計算式

スピード(KM/H) Sとケイデンス(RPM) Cとの関係は、係数kを使って、次の計算式で定義します。

> S ＝ C × k

今回の計測結果（平均スピード）から、係数kを求めると

> k ＝ 23.3 ÷ 70 ＝ 0.33285714285 ≒ 0.333333333・・・

であり、

>k=1/3

と推定されます。

よって、

> 計算式: スピード(km/h) S ＝ ケイデンス(RPM) C ÷ 3


## ケイデンスセンサーによるスピード計算

[前回の記事（その１）](https://qiita.com/jp-96/items/110b6364a3da89553b27)で、ケイデンス(RPM)は、次の計算式で求められることを確認しました。

> 計算式: ケイデンス(RPM) = 3 × 60000000 ÷ (T&lt;n&gt;-T&lt;n-3&gt;)

したがって、スピード(km/h) Sは、次の計算式で求めることができます。

> 計算式: スピード(km/h) S ＝ 60000000 ÷ (T&lt;n&gt;-T&lt;n-3&gt;)

# micro:bitでのケイデンスとスピードの実装

## ソースコード

端子P2に接続したSTEP（下側）の信号線からの立ち下がりをイベントで検出し、そのタイムスタンプ（ナノ秒）を3回分記録し、ケイデンスとスピードを計算し、その計算結果をUSBシリアル通信で出力します。

ソースコードのビルドは、[Arm Mbed オンライン - https://ide.mbed.com/compiler/](https://ide.mbed.com/compiler/)で可能です。

Source URLに `https://github.com/jp-96/microbit-digital-capture2.git` と入力し、Programとしてインポートして、コンパイルします。

```main.cpp
#include "inttypes.h"
#include "MicroBit.h"

#define K (uint64_t)60000000

MicroBit uBit;
MicroBitPin sensorPin(MICROBIT_ID_IO_P2, MICROBIT_PIN_P2, PIN_CAPABILITY_DIGITAL);

uint64_t lastFallTimestamp=0;
uint32_t fallCount=0;
MicroBitImage IMG_FALL("1,1,1,0,0\n0,0,1,0,0\n0,0,1,0,0\n0,0,1,0,0\n0,0,1,1,1\n");

uint64_t t[] = {0, 0, 0, 0};

void onEdgeSensorPin(MicroBitEvent e)
{
    int cadence;
    int speed10;

    lastFallTimestamp = e.timestamp;
    fallCount++;

    t[0]=t[1];
    t[1]=t[2];
    t[2]=t[3];
    t[3] = lastFallTimestamp;

    if (fallCount>3) {
        cadence = (int)( K* 3/(t[3]-t[0]));
        speed10 = (int)( K*10/(t[3]-t[0]));
    } else {
        cadence = 0;
        speed10 = 0;
    }  

    uBit.serial.printf("%" PRIu32 ", %" PRIu32 ", %d, %d\r\n"
        , fallCount, (uint32_t)e.timestamp, cadence, speed10);
}

void setup(void)
{
    uBit.serial.printf("#, Timestamp, Cadence, Speed10\r\n");

    uBit.messageBus.listen(MICROBIT_ID_IO_P2, MICROBIT_PIN_EVT_FALL, onEdgeSensorPin);
    sensorPin.getDigitalValue(PullUp);
    sensorPin.eventOn(MICROBIT_PIN_EVENT_ON_EDGE);

    uBit.display.scrollAsync("GO!");
}

int main()
{
    // Initialise the micro:bit runtime.
    uBit.init();

    create_fiber(setup);

    // If main exits, there may still be other fibers running or registered event handlers etc.
    // Simply release this fiber, which will mean we enter the scheduler. Worse case, we then
    // sit in the idle task forever, in a power efficient sleep.
    release_fiber();
}

```

## 出力結果
実際にペダルを漕いで、USBシリアル通信でCSV形式ファイルとして出力してみました。
先ほどのスピード合わせのとおりの平均スピードが計算できました。

|項目|内容|備考|
|---|---|---|
|ケイデンス|70RPM|左右で140bpm|
|平均スピード|23.3km/h|

``` 出力結果.csv
#, Timestamp, Cadence, Speed10
1, 543490, 0, 0
2, 1412411, 0, 0
3, 2291045, 0, 0
4, 3154021, 68, 229
5, 3999089, 69, 231
6, 4847042, 70, 234
7, 5659946, 71, 239
8, 6512228, 71, 238
9, 7404415, 70, 234
10, 8292137, 68, 227
11, 9115782, 69, 230
12, 9959264, 70, 234
13, 10813715, 71, 237
14, 11649806, 71, 236
15, 12500833, 70, 236
16, 13365074, 70, 235
17, 14211175, 70, 234
18, 15068869, 70, 233
19, 15933390, 70, 233
20, 16792536, 69, 232
21, 17655459, 69, 231
22, 18529174, 69, 231
23, 19397909, 69, 230
24, 20256822, 69, 230
25, 21071043, 70, 236
```

## ワイヤレス運動測量計の再確認

ところが、ワイヤレス運動測量計のスピードと比べながら、USBシリアル通信で出力されるスピードを見てみると、1割程度、スピードが速く計算されているようです。

![IMG_20210208_185216.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252891/24324cd4-4042-648f-549b-5409bbb77e90.jpeg)

### 追試 (2021/02/10 追記)

再度、140bpmのメトロノームに合わせて、ペダルを漕いで、スピードを目視確認しました。

|計測項目|計測値|
|---|---|
|第1計測|21.1km/h|
|第2計測|21.4km/h|
|第3計測|21.5km/h|
|平均スピード|21.3km/h|

### 係数kの再計算

追試の計測結果（平均スピード）から、係数kを求めると

> k ＝ 21.3 ÷ 70 ＝ 0.30428571428 ≒ 0.3

と推定されます。

### スピードの計算式の見直しとソースコードの修正

追試で求めた係数kから、スピード(km/h) Sの計算式は、

> 計算式: スピード(km/h) S ＝ ケイデンス(RPM) C × 0.3

であり、スピード計算のコードを次のように修正しました。

```(修正版)main.cpp

    if (fallCount>3) {
        cadence = (int)( K* 3/(t[3]-t[0]));
        //speed10 = (int)( K*10/(t[3]-t[0]));
        speed10 = (int)( K* 9/(t[3]-t[0]));
    } else {

```

```修正後の出力結果.csv
#, Timestamp, Cadence, Speed10
1, 499732, 0, 0
2, 1304940, 0, 0
3, 2182415, 0, 0
4, 3097602, 69, 207
5, 3956556, 67, 203
6, 4800022, 68, 206
7, 5656200, 70, 211
8, 6566044, 68, 206
9, 7399554, 69, 207
10, 8239933, 69, 208
11, 9080191, 71, 214
12, 9904967, 71, 215
13, 10736046, 72, 216
14, 11568010, 72, 217
15, 12393877, 72, 216
16, 13250880, 71, 214
17, 14078611, 71, 215
18, 14955375, 70, 210
19, 15821034, 70, 210
20, 16691279, 68, 206
21, 17541048, 69, 208
22, 18372295, 70, 211
23, 19237419, 70, 212
24, 20076264, 70, 212
25, 20960647, 69, 208

```

目視ですが、ワイヤレス運動測量計のスピードと同様の値(単位：0.1km/h)が計算出力されています。
原因は、メトロノームに合わせて漕いでいたつもりでしたが、実際には、少し早いテンポでペダルを漕いでいた為だったようです。

# おわりに

・メトロノームに合わせて、ペダルを漕いだ時のスピードを計測しました
・ケイデンスセンサーによるスピードの計算式を求めました
・micro:bitでプログラミングし、ケイデンスとスピードをUSBシリアル通信で出力しました
・再度、ペダルを漕いだ時のスピードを目視で確認し、計算式を修正しました

[次回（その３）](https://qiita.com/jp-96/items/3863e31b1c92a9caa44b)は、STEP信号からケイデンスとスピードを求めるモジュールをドライバー化し、ペダリングの開始時や停止時のケイデンスとスピードを出力できるようにしてみます。

<!--
リンクを別タブで開く
https://qiita.com/yukichi/items/df4f5126f193a1c8efdb
-->
<script>
jQuery(document).ready(function(){
    jQuery('a[href^=http]')
    .not('[href*="'+location.hostname+'"]')
    .attr({target:"_blank"})
    .addClass("external")
    ;})  
</script>