iT邦幫忙

2026 iThome 鐵人賽

DAY 22
0
Build on Google AI

單鐵的人生如履薄冰!AI 教練 APP 30天開發旅程,你說能走到最後嗎?系列 第 22

Day 22 | 繞過 API 授權地獄,用 Gemini 多模態看懂跑錶與跑步機螢幕

  • 分享至 

  • xImage
  •  

前言:為什麼要用「視覺」解決痛點?

當跑者結束今天的訓練,最希望能把剛跑完的數據記錄下來。傳統運動 APP 為了取得使用者的里程與配速,必須去向 Garmin 或 Strava 申請商業開發者 API,走過冗長的審核與 OAuth 2.0 授權流程。

但這次,我們可以嘗試用不一樣的解法——「多模態視覺解析 (Multimodal Vision)」

管你是用 Garmin、COROS、Apple Watch,甚至是在健身房跑跑步機,只要拍張照螢幕截圖上傳給 KAKERU,後端的 Gemini 視覺大腦就能瞬間看懂畫面中的配速、距離、心率,並給予專屬教練評語。今天我們就要把這個實現在 APP 中!

觀念解說:圖片到結構化 JSON 的奇幻漂流

要在 React Native 中實現這套多模態辨識系統,資料流如下:

[手機相簿 / 拍照]
       ↓ (expo-image-picker 壓縮品質並轉 Base64)
[前端 React Native]
       ↓ (POST /analyze-chart)
[後端 Spring Boot]
       ↓ (轉發 Base64 + 嚴格 JSON Prompt)
[Gemini Vision 大腦]
       ↓ (回傳純 JSON: 距離、配速、心率、評語)
[前端 CheckInModal] ➔ 彈出辨識結果卡片與防呆驗證!

動手實作 1:安裝並實作 Expo Image Picker

首先,打開終端機安裝 Expo 官方的圖片選擇套件:

npxexpoinstallexpo-image-picker

接著在打卡元件中撰寫挑選圖片的邏輯。為了節省網路傳輸頻寬並符合 Gemini API 的要求,我們設定 quality: 0.7 壓縮品質,關閉強制正方形裁切以完整讀取長條截圖,並直接請套件回傳 base64:

// 1. 開啟相簿並選取截圖
constpickImage=async ()=> {
constresult=awaitImagePicker.launchImageLibraryAsync({
mediaTypes: ['images'],
allowsEditing:false,// 允許直接選取完整跑錶長截圖,不強制正方形裁切
quality:0.7,// 適度壓縮以加快網路傳輸
base64:true,// 關鍵:直接取得 Base64 編碼
  });

if (!result.canceled) {
setImageUri(result.assets[0].uri);
setBase64Image(result.assets[0].base64);
  }
};

結果如圖:
https://ithelp.ithome.com.tw/upload/images/20260831/20165043N1HMsCqlFQ.png

動手實作 2:後端 AI 教練的「火眼金睛」Prompt

在後端 (Spring Boot),我們呼叫 Gemini 的多模態端點,並透過 Prompt 鎖死它的輸出格式與防呆機制:

你現在是一位世界級的馬拉松 AI 教練。
跑者剛剛完成了一項訓練,並上傳了他的跑錶數據或跑步機面板截圖。
請分析這張圖片,提取出關鍵數據,並給予跑者一句 30 字以內的專業鼓勵與復盤。

【嚴格要求】
你「必須」且「只能」回傳以下格式的純 JSON 結構(若非運動數據或無法辨識請填 0):
{
  "actualDistanceKm": 數值 (若非運動數據或無法辨識請填 0),
  "averagePace": "字串,例如 05:30 (若無法辨識填 00:00)",
  "averageHeartRate": 數值 (若無法辨識請填 0),
  "coachFeedback": "給跑者的專業評語或無法辨識之提示"
}

動手實作 3:前端呼叫與「防呆攔截」機制

前端將 Base64 發送給後端 API,收到 AI 整理好的 JSON 後,我們加上關鍵的防呆驗證——如果跑者誤傳了食物或風景照片,AI 回傳的距離為 0 km,我們將攔截並提示,避免誤判:

consthandleAnalyzeAndSubmit=async ()=> {
if (!base64Image) {
Alert.alert('提示','請先上傳圖片才能進行 AI 解析!');
return;
  }

setIsAnalyzing(true);
try {
// 呼叫後端多模態視覺端點 (POST /analyze-chart)
constaiResult=awaitanalyzeWorkoutImage(base64Image);

// 🛡️ 防呆驗證:若 AI 判定里程為 0,代表圖片非運動數據或無法辨識
if (!aiResult.actualDistanceKm||aiResult.actualDistanceKm<=0) {
Alert.alert('⚠️ 無法辨識',aiResult.coachFeedback||'未偵測到跑步數據,請確認圖片清晰!');
return;
    }

// 辨識成功!彈出成果與教練評語
Alert.alert(
'🎉 數據解析成功!',
`【實跑距離】:${aiResult.actualDistanceKm} km\n`+
`【平均配速】:${aiResult.averagePace}/km\n`+
`【平均心率】:${aiResult.averageHeartRate} bpm\n\n`+
`【教練評語】:${aiResult.coachFeedback}`
    );
  }catch (error) {
Alert.alert('解析失敗','教練眼睛有點茫,請稍後再試。');
  }finally {
setIsAnalyzing(false);
  }
};

上傳真實跑錶截圖後,成功彈出 AI 解析結果與教練評語的 Alert 畫面:
https://ithelp.ithome.com.tw/upload/images/20260831/20165043icL0jKaoem.png

上傳食物或風景照片時,觸發防呆攔截「無法辨識有效跑步數據」的畫面:
https://ithelp.ithome.com.tw/upload/images/20260831/20165043vWib7r0onR.png

今日總結與明日預告

今天我們利用 Gemini 的多模態 (Multimodal) 能力,繞過了第三方運動平台的 API 授權地獄

隨著今天多模態圖片解析的實裝,KAKERU 的「Phase 2 前端核心功能開發」也正式告一段落!從個人的資料設定、課表生成、動態微調診療室,到今天的跑錶視覺辨識,我們已經實現並驗證了所有 AI 核心功能的可行性。

然而,當這款 APP 要從一個「功能雛形」邁向「Production 產品」時,我們原本的資料結構與單純的 4 週假資料將面臨嚴峻考驗。

明天(Day 23)將展開 Phase 3 終極進化:週期引擎升級!實作 UUID 匿名身份隔離,並加入 賽事日期選擇器,自動推算長達 24 週的備賽大週期。我們明天見!


上一篇
Day 21 | AI 動態重算實戰:將狀態發送至雲端,智慧合併新課表並無縫刷新 UI
系列文
單鐵的人生如履薄冰!AI 教練 APP 30天開發旅程,你說能走到最後嗎?22
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言