1. 概要・目的
現在のアプリは「サドル高」のみを扱っている(このブログ投稿前の話)
色々できる感が無いので異なるセッティングについても機能追加したいが…
バイクフィッティングにおいてサドル高と並んでよく調整される項目にサドル前後位置(フォア・アフト)
これをやってみたいがアプリにするにはどのようにするか?
そこで、この項目を計算方式の一つとして知られるKOPS法(Knee Over Pedal Spindle)をベースに、アプリへどう落とし込むかを検討してみる
とにかく使ってみたい方はこちらへ➡️
対象範囲は以下の4つ
① KOPS法の理論と限界の整理
② アプリの機能・画面設計
③ データ設計と計算ロジック
④ 既存機能(膝角度の微調整・計算履歴)との統合方針
2. KOPS法ってなに?って話
KOPS法は、1970年代から使われている伝統的なサドル前後位置の決め方

手順:
1. クランクを3時位置(水平・前側)にする
2. 膝のお皿の下にある骨の出っ張り(脛骨粗面)から、重りを付けた紐(下げ振り)を垂らす
3. 紐がペダル軸の真上を通っていれば、その位置が基準点
紐がペダル軸より前に落ちればサドルが前寄りすぎ、後ろに落ちれば後ろ寄りすぎと判断し、逆方向にサドルをスライドさせて調整する。
2.1 理論的な位置づけと限界
KOPS法には明確な生体力学的根拠はないとされており、近年のバイクフィッティング業界では「絶対のルールではなく、あくまで初期設定のための出発点」という位置づけが一般的になっている。実務上は、KOPSのポイントよりもやや後方(スピンドルから0〜20mm後ろ程度)が、多くの持久系ライダーにとって良いデフォルトとされることが多い。また、タイムトライアルやトライアスロンのようにより前傾・前乗りのポジションを取る種目ではKOPSの基準から大きく外れることが一般的で、逆にリラックスした乗り方のライダーはより後方に位置することもある。
この特性から、サドル高の計算式(股下×係数)のように「唯一の正解値」を出す方式ではなく
「基準点からのズレを測ってもらい、目安の範囲に収まるよう調整を提案する」という、既存の「膝角度による微調整」機能と同じ設計思想が適しているかな~
3. アプリへの落とし込み方
3.1 既存機能との類似性
膝角度の微調整機能は、次の3ステップで構成
- 測り方を説明する図解を表示
- 測定値(角度)を入力してもらう
- 適正範囲との差分から、調整方向・量をメッセージで返す
KOPS法もまったく同じ構造で実装できる。むしろKOPSの方が計算がシンプル
「測定したズレの量」=「動かすべき量」という直接的な関係になる
(膝角度のような「1度=1mm」という換算係数が不要)
3.2 画面構成
「膝角度でさらに微調整」セクションと横並びの構成で、
新しく「サドル前後位置(フォア・アフト)」セクションを追加
- 測り方の説明図(下げ振りを使った側面図。既存の膝角度図と同じ描画パターンを流用できる)
- 入力欄:「紐は脛骨粗面からペダル軸に対して、前後どちらに・何cmズレていたか」
- 符号の扱いが分かりにくいため、数値入力+「前にズレていた/後ろにズレていた」の選択(ラジオボタンかトグル)の組み合わせにする
- 計算ボタン
- 結果表示:ズレの量と目安の範囲(0〜20mm後ろ)を踏まえた、「サドルを◯mm前方/後方にスライドしてください」というメッセージ
3.3 入力設計の注意点
膝角度の入力は「度」という単位で直感的だったが、フォア・アフトのズレは「前か後ろか」という方向の概念が入るため、単純な数値入力だけだと符号の意味を間違えやすい。
方向を明示的に選ばせるUIにするか、自転車の図の上に「前」「後ろ」のラベルを添えて視覚的に迷わないようにする、という2案を検討
後者かな
4. データ設計・計算ロジック(実装イメージ)
既存の膝角度機能(kneeAngle / kneeAdjustmentMm / kneeAdjustmentMessage)とほぼ並行する構造で実装できる想定
// 目安の範囲:脛骨粗面がペダル軸から0〜20mm後ろにあるのが良いデフォルト
const KOPS_OFFSET_MIN_MM = 0; // スピンドル直上(伝統的なKOPSの基準点)
const KOPS_OFFSET_MAX_MM = 20; // スピンドルより20mm後ろまでは許容範囲
foreAftOffsetMm: number | null = null; // 測定したズレの量(mm)
foreAftDirection: 'front' | 'behind' = 'behind'; // ズレの方向(紐がどちら側に落ちたか)
foreAftAdjustmentMessage = '';
adjustForeAft(): void {
if (this.foreAftOffsetMm == null || this.foreAftOffsetMm < 0) {
this.foreAftAdjustmentMessage = 'ズレの量を入力してください。';
return;
}
// 「前にズレていた」場合は、常にサドルを後方へ動かす必要がある
if (this.foreAftDirection === 'front') {
this.foreAftAdjustmentMessage =
`サドルを約 ${this.foreAftOffsetMm} mm 後方にスライドしてください。`;
return;
}
// 「後ろにズレていた」場合、目安の範囲(0〜20mm)内なら調整不要
if (this.foreAftOffsetMm <= KOPS_OFFSET_MAX_MM) {
this.foreAftAdjustmentMessage = '目安の範囲内です。今の位置のままで問題ありません。';
return;
}
const excess = this.foreAftOffsetMm - KOPS_OFFSET_MAX_MM;
this.foreAftAdjustmentMessage =
`サドルを約 ${excess} mm 前方にスライドしてください。`;
}
サドル高の計算式のように「入力から一意に答えが出る」ものではなく、範囲判定になる点が構造的な違いである。この判定ロジックは、目安の範囲(0〜20mm)を将来変更しやすいよう定数化しておく。
5. 既存機能との統合ポイント
- 計算履歴:既存のHistoryEntryに、foreAftOffsetMm / foreAftDirection / foreAftAdjustmentMessageを追加し、膝角度と同じ考え方で保存・復元する
- 基本計算のリセット処理:calculate()を再実行したときに膝角度の結果をリセットしているのと同様、フォア・アフトの結果もリセットする
- 図解のスタイル統一:新しく作る下げ振りの図は、既存の膝角度図で使っている配色・線の太さ(.leg-line、.dashed-ref、.angle-arcなど)を踏襲し、見た目のトーンを揃える
6. 制限事項・注意点
- KOPS法自体に生体力学的な裏付けが薄いという指摘があるため、アプリ内の説明文でも「絶対の正解ではなく、調整の出発点」であることを明記する
- 前後位置の調整は、サドル高の調整と相互に影響し合う(前後位置を変えると実質的な高さも変わる)。将来的には、「フォア・アフトを調整したら、サドル高も再チェックしてください」という案内を添えることが望ましい
- 種目(ロード・タイムトライアル・トライアスロンなど)によって適正な前乗り具合が大きく異なるため、種目選択によって目安の範囲を出し分ける拡張も将来的に検討の余地がある

