テクノロジーに興味がなかった整形外科医が作成した関節可動域角度測定アプリ（スマホ＋Github＋LINE message API＋Google spread sheets）

# この記事の内容


・はじめに
・スマホで関節可動域の角度を測定・記録するアプリ
・使用した技術
・ここがこだわり！
・今後の課題・展望
・おわりに

# はじめに
はじめまして。私は整形外科医で、股関節を専門に外来診療や手術に従事しています。これまで、テクノロジーには全く関心がありませんでした。

整形外科の現場では、患者さんが「手術＝治療」と誤解していることがよく見受けられます。しかし実際には、手術と同じくらい術後のリハビリも重要です。手術とリハビリが一体となって初めて「治療」といえるのです。私たち整形外科医は、患者さんに自らリハビリ（特に関節可動域訓練）を積極的に行っていただきたいと常に考えています。

しかし、患者さんが自主的にリハビリを行う際、関節可動域訓練の目標（角度）やゴールが不明確なため、リハビリが効果的に進まず、意欲が低下してしまうことがあります。その結果、関節が硬くなってしまうケースも少なくありません。

そこで今回は、股関節手術後の患者さんに対して、可動域の目標（角度）やゴールを設定し、その途中経過を評価して患者さんに通知・記録できるシステムを構築したいと考えました。このシステムを通じて、患者さんのリハビリ意欲を高め、正常な股関節の可動域獲得を支援できればと考えています。そのため、さまざまなテクノロジーを学び、この課題を解決していきたいと思います。

# スマホで関節可動域の角度を測定・記録するアプリ

この課題を解決するために、次のような取り組みを行いました。

・股関節の可動域を測定するために、スマホを大腿部に取り付ける装置を使用
・スマホの加速度センサーを利用して、股関節の可動域を測定する
・LINE messaging APIを使用して、スマホの加速度センサーのデータ（測定した角度）をLINEにリプライメッセージをして送信する
・測定した角度をスプレッドシートに送信、記録する

まず、スマホケースとゴムバンドで作った装置にスマホを取り付け（左写真参照）、それを大腿部（今回は右の太もも）に装着します（右写真参照）。

![名称未設定のデザイン.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3862262/08885558-7bd8-3a5b-7b1d-3f6bfb61b9f7.png)

スマホ画面には「測定開始」ボタンと「確定」ボタンがあります（下図）。手順は以下の通りです。

1.「測定開始」ボタンを押す
2.患者さんに股関節を曲げられる範囲まで屈曲してもらう
3.「確定」ボタンを押す
これにより、測定した角度がLINEおよびスプレッドシートに送信されます。

![画像4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3862262/7cbf1a72-d87b-d8c6-1ffd-2effcfcf34d7.png)

測定した角度がLINEに送信され、角度に応じた評価メッセージが添付されるようにしました（左図）。
また、スプレッドシートには、測定日時、測定角度、評価メッセージが記録されるようにしました（右図）。

![画像6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3862262/243e53ea-fb8f-153c-7d8e-b66e7ae33a12.png)

こちらは実際のスマホ画面の動画です。測定開始ボタンを押し、股関節を屈曲して、確定ボタンを押すと、LINEに通知が送信される流れとなっています。

https://x.com/eitoku2371/status/1827732878735655278

患者さんに実際に装着していただき、ご自身で股関節を屈曲させながら操作してもらった動画です。
高齢者でも操作しやすいように工夫を施しています。

https://x.com/eitoku2371/status/1827733373005250973


# 使用した技術
![画像8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3862262/61f0579f-5313-b573-f01b-2c45a4925d11.png)


まず、開発環境としてGitHub Codespacesを使用し、スマホに内蔵されている加速度センサーの3軸のうち、Y軸を角度測定に用いるコードを作成しました。

``` let lockedYValue = null;

function permission() {
    const accY = document.getElementById('accY');

    if (DeviceMotionEvent && typeof DeviceMotionEvent.requestPermission === 'function') {
        console.log('iOS (13+) Safari');
        DeviceMotionEvent.requestPermission().then(response => {
            if (response === 'granted') {
                window.addEventListener('deviceorientation', handleOrientationEvent);
            } else {
                alert('パーミッションエラー: 加速度取得不可');
            }
        }).catch(error => {
            console.error('パーミッションリクエスト中にエラーが発生しました:', error);
        });
    } else {
        window.addEventListener('deviceorientation', handleOrientationEvent);
    }
}

function handleOrientationEvent(e) {
    const accY = document.getElementById('accY');
    if (lockedYValue === null) {
        accY.textContent = e.beta.toFixed(2);
        console.log('Orientation event:', e.beta); // デバッグ用ログ
    }
}

function lockY() {
    const accY = document.getElementById('accY');
    lockedYValue = accY.textContent;
    alert(`角度の値が確定されました: ${lockedYValue}`);
    sendToServer();
}

function sendToServer() {
    const accY = lockedYValue !== null ? lockedYValue : document.getElementById('accY').textContent;
    const userId = 'your LINE ID'; // ここに固定のLINE IDを設定
    console.log('Sending to server:', { angle: accY, userId }); // デバッグ用ログ

    fetch('/save-angle', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json'
        },
        body: JSON.stringify({ angle: accY, userId: userId })
    })
    .then(response => {
        if (!response.ok) {
            return response.json().then(data => { throw new Error(data.message || 'Unknown error'); });
        }
        return response.json();
    })
    .then(data => {
        if (data.success) {
            alert('サーバーに送信されました');
        } else {
            alert('送信に失敗しました: ' + data.message);
        }
    })
    .catch(error => {
        console.error('エラー:', error);
        alert('送信中にエラーが発生しました。コンソールを確認してください。');
    });
}


 
```
さらに、LINE Messaging APIを使用して、測定した角度がLINEに通知されるコードを作成しました。
その際、条件分岐を追加し、角度に応じた評価メッセージが自動的に添付されるようにしました。
また、Googleスプレッドシートにも、測定日時、角度、評価メッセージが記録されるようにしました。

# ここがこだわり！
・到達角度ごとに条件分岐を設定し、評価することで、患者さんのモチベーション向上を目指しました。

![画像10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3862262/f27ff396-ac6c-651b-9b0b-8023c7679efd.png)

・高齢者でもわかりやすいように、操作の手順を少なくし、簡便なものにしました。
・LINEとスプレッドシートの両方に記録することで、日々の成果が可視化され、やる気が向上するようにしました。
・スプレッドシートの記録を患者さんの家族や主治医とも共有することで、皆でリハビリの成果を実感できるようにしました。 

# 今後の課題・展望
今後の課題としては、次の点が挙げられます。

・現在は1台の端末でしか使用できないため、LINE Front-end Framework（LIFF）などを活用し、複数のユーザーが利用できる仕組みを作りたいと考えています。
・股関節だけでなく、他の関節の可動域測定にも対応できるように改良したいと考えています。
・ゲーム要素を取り入れたアプリを開発し、例えば目標の可動域に到達すると敵を倒せるといった仕組みで、患者さんの可動域訓練への意欲をさらに高めたいと考えています。

このような課題に取り組んでいくことを考えています。

# おわりに
もともとテクノロジーに全く興味がなかった私ですが、スマホを活用した関節可動域角度測定アプリを作ってみました。
まだまだ未完成ですが、ものづくりの喜び、テクノロジーという私にとって未知の世界の扉が開けた事への楽しみや興味、患者さんに還元できる可能性への期待など、やってみてよかったと思う事ばかりでした。
より良いものを作れるようにこれからも精進していきたいと思います。

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