iT邦幫忙

2026 iThome 鐵人賽

DAY 6
0
AI Engineering

30 天拆解 Wearable × AI:從穿戴裝置生理訊號到AI健康洞察系列 第 6

Day 06|Sampling Rate:取樣頻率為何決定你能相信什麼?

  • 分享至 

  • xImage
  •  

今天為什麼研究這個?

Day 3 發現 PPG-DaLiA 的峰間期全是 15.625 ms 的整數倍:64 Hz 的 PPG,峰只能落在每 15.625 ms 一個的格點上。我當時寫了「上游的取樣率是下游指標的精度上限」,但沒有驗證。

但64 Hz 是量測那一瞬間照 64 次嗎?同一份資料,腕上 PPG 是 64 Hz,胸前 ECG 是 700 Hz,差了十倍。64 Hz 算心率夠嗎?算 HRV 呢?今天把同一段 PPG 降到更低的取樣率,看哪個指標先壞。

Concept

1. 64 Hz 是什麼意思

每秒 64 個等距的點,間隔 1/64 秒=15.625 ms,不是一瞬間連拍 64 次。安靜時一拍約 0.8–1 秒,大約是 51–64 個點。

但對 HRV 重要的不是一拍有幾個點,而是峰只能落在格點上:峰間期一定是 15.625 ms 的整數倍,心跳快慢都一樣。

2. 先猜:HR、SDNN、RMSSD 哪個先壞

我猜 RMSSD 最先壞,SDNN 其次,HR 最後。

HR 最耐。一段時間的平均間期=(最後一峰 − 第一峰)÷ 間期數,中間每個峰的誤差一加一減會抵消,只剩頭尾兩個。25 Hz 下頭尾各最多偏 20 ms,兩峰相距 7 秒的話,平均心率最多差約 0.6%。所以低取樣率下 HR 壞掉的方式是數錯拍,不是峰的位置不準。

RMSSD 最脆弱,原因在「相鄰相減」:一個峰偏了,前一個間期變長、後一個變短,兩個再相減,誤差變兩倍。假設誤差是四捨五入到格點、彼此獨立,取樣週期 T=1/fs,RMSSD² 會多出 T²/2,SDNN² 只多出 T²/6。而且 RMSSD 本來就比較小:Day 4 的 nsr001,5 分鐘視窗的 RMSSD 中位數 25.09 ms、SDNN 49.66 ms,而 25 Hz 的格點是 40 ms。

由此還能推出兩件事:RMSSD 只會往上偏;偏多少不是常數,本來 RMSSD 就低的人偏得更多。這是理想化的預測,以實驗結果為準。

3. 降採樣的三種做法

  • 每 N 點取 1: 不濾波,高頻會折回低頻,混進訊號。
  • decimate 先低通濾波再抽點,只能整數倍(64 → 32、16、8 Hz)。scipy 預設正反各濾一次(zero_phase=True),不會把峰往後推。
  • resampleresample_poly 也會先抗混疊,比例不必是整數,例如 64 → 25 Hz。resample 用 FFT,假設訊號頭尾相接,邊界容易出假波紋。

4. 為什麼裝置不乾脆用高取樣率

取樣率越高,LED 打光和讀取的次數越多,要算、要存的資料也越多,代價是電量。各家實際用多少 Hz,我沒找到公開來源,這裡不寫數字。而且拉高取樣率救不了白天最大的誤差:動作。這也是多數廠商選在睡眠時算 HRV 的原因(Day 5)。

5. 不同取樣率的 RMSSD,能直接比嗎

不能。低取樣率的 RMSSD 偏高,而且因人而異,減一個常數校正不了。

這件事不該丟給 LLM 自己判斷,而是由 pipeline 用規則擋下:時間解析度不同的數值,不放進同一條 baseline、不互相比較。時間解析度要看取樣率和峰怎麼定位(有沒有內插),這兩項接在 Day 4 列的 sourcewindow_sbeat_filterddof 後面。同一台裝置也會變:廠商的演算法可能不定期更新(Dial 2025)。如果更新動到取樣率,baseline 會斷在那一天,模型只看到「這週 HRV 變了」,很容易說成壓力或疲勞。

Hands-on

Dataset

兩份資料,角色不同:

  • 合成 PPG(主結果): jittered_rr 產生 600 個間期,平均 900 ms,標準差 5、10、20、40 ms 四組(seed=6),真值 RMSSD 是 6.82、13.63、27.27、54.53 ms。synthetic_ppg_from_rr 把它畫成 64 Hz 的波形,同時回傳 601 個峰的連續時間。峰在哪一刻是已知的,誤差可以直接算。
  • PPG-DaLiA 靜坐段(對照): S1、S2 的 activity 1(sitting),資料集說明寫它是 "motion-artefact-free baseline";S1 700 秒、S2 600 秒。腕上 E4 的 BVP 是 64 Hz,胸前 ECG 是 700 Hz,資料集附了 R-peak。本機只解壓了這兩位。

Method

  1. 把 64 Hz 降到 32、25、16、8 Hz,四種做法:每 N 點取 1(naive)、decimateresample_polyresample。64 → 25 Hz 不是整數倍,只有後兩種能做。真實資料先對整段靜坐段降採樣再切窗,邊界效應只落在整段頭尾。
  2. 用 Day 3 的 find_pulse_peaks 找峰:峰時間就是最大樣本的位置,沒有內插。再用 match_peaks 和參照一對一配對(容許 0.2 秒,時間差小的先配,避免 Day 3 配到隔壁拍的錯)。配到的峰中間有洞的窗不算指標。
  3. 參照:合成資料用峰的連續時間;真實資料用同一窗 64 Hz 找到的峰。
  4. 真實資料先過一道閘門。整段一起找峰會大量漏峰(S1 找到 441 個、ECG 有 597 個;S2 是 51 對 630):prominence 門檻取整段振幅的 15%,一個大波動就把門檻拉高。所以改成 30 秒一窗,只留 64 Hz 峰數與同窗 ECG R-peak 數差 ≤ 1 的窗:S1 留 14/23 窗,S2 留 8/20 窗。

Code

import numpy as np
from wearable_ai.hrv import rmssd
from wearable_ai.signal_processing import find_pulse_peaks
from wearable_ai.signal_processing.matching import match_peaks
from wearable_ai.signal_processing.sampling import downsample
from wearable_ai.synthetic import jittered_rr, synthetic_ppg_from_rr

rr = jittered_rr(600, 900.0, 5.0, seed=6)
x, truth_s = synthetic_ppg_from_rr(rr, 64.0)                  # 64 Hz 波形、601 個峰的連續時間(秒)
y = downsample(x, 64.0, 16.0, "resample_poly")
peaks_s = find_pulse_peaks(y, 16.0) / 16.0                    # 最大樣本的位置,沒有內插
ref_idx, test_idx = match_peaks(truth_s, peaks_s, tol_s=0.2)  # 601 對
rmssd(np.diff(truth_s) * 1000)                                # 6.82
rmssd(np.diff(peaks_s[test_idx]) * 1000)                      # 55.71

完整實驗在 days/day06/sampling_rate.py,加 --plot 輸出下面四張圖。

合成資料:真值已知

https://ithelp.ithome.com.tw/upload/images/20260919/20184206x66CZ4p8U0.png
合成 PPG 的 HR、SDNN、RMSSD 相對真值的誤差對取樣率;HR 都在 0.02% 以內,RMSSD 真值 6.8 ms 那條在 8 Hz 到 +1,055%

取樣率 T/4(ms) 峰時間誤差中位數(ms) RMSSD,真值 6.82 13.63 27.27 54.53
64 Hz 3.91 3.62–4.07 13.39 17.66 29.31 54.89
32 Hz 7.81 7.92–8.30 21.10 25.57 34.00 59.47
25 Hz 10.00 9.02–10.25 36.65 34.36 37.45 60.96
16 Hz 15.63 15.55–15.76 55.71 53.75 53.14 72.05
8 Hz 31.25 31.31–31.85 78.79 78.79 83.30 99.82

64 Hz 是直接取樣,其餘用 resample_poly 降採樣。

  • 順序和猜的一樣。 HR 在 60 種組合(4 組變異 × 15 種取樣率與做法)的誤差都在 −0.007% 到 +0.021%。SDNN 也被灌大,但比 RMSSD 少:16 Hz、sd 5 時 SDNN 5.03 → 30.64 ms,RMSSD 6.82 → 55.71 ms。
  • 峰時間誤差就是四捨五入。 中位數都貼著 T/4(誤差均勻落在 ±T/2 時的中位數),四種降採樣做法幾乎一樣。
  • 64 Hz 本身就不夠。 真值 6.82 ms 的序列,64 Hz 就量到 13.39 ms。
  • 只往上偏,低變異的偏更多。 60 種組合沒有一個低於真值。16 Hz 時,真值 6.82 的多了 48.89 ms,54.53 的多了 17.52 ms。
  • T²/2 只在變異夠大時準。 降採樣後,平方的增量是理論的 0.79–1.15 倍(sd 20、40)與 0.77–1.62 倍(sd 5、10)。變異比格點小很多時,誤差不再彼此獨立,偏哪邊由「平均間期 ÷ 格點」的餘數決定:直接四捨五入就能重現,平均間期改成 910、920 ms,比值整組換掉(days/day06/results.md 第 7 節)。
  • 和文獻方向一致。 RMSSD 約 54 ms 那組,64 Hz 偏 +0.7%、32 Hz 偏 +9.1%;Béres 2022 的博論在 31 位年輕健康者(平均 RMSSD 53.78 ms)身上測到,不內插時 RMSSD 要 50 Hz 才能讓相對誤差小於 5%。參照不同(連續真值對 500 Hz 的原始 PPG),只能說方向一致。

PPG-DaLiA:參照本身就偏了

閘門只看峰數,多一個假峰、漏一個真峰照樣過關。保留的 22 窗心率 43–65 bpm,其中 5 窗的 64 Hz 最短峰間期只有 265.62–453.12 ms(同窗 ECG 最短 R-R 是 791.43–1041.43 ms)。64 Hz PPG 的 RMSSD 因此是同窗 ECG 的 1.40 倍(S1 中位數)與 3.65 倍(S2)。

https://ithelp.ithome.com.tw/upload/images/20260919/20184206Ki2CtWcO4F.png
PPG-DaLiA 兩位受試者,降採樣後 HR、SDNN、RMSSD 相對 64 Hz 的誤差中位數對取樣率;32、25 Hz 接近 0,8 Hz 升到 5–26%

以這個參照算相對誤差,RMSSD 在 32、25、16、8 Hz 只偏 +0.26%、+0.60%、+3.17%、+14.51%(resample_poly,22 窗的中位數),HR 都在 0.06% 以內。數字小,是因為分母已經被灌大。所以另外比「平方的增量」:誤差彼此獨立時,它和參照的大小無關,可以直接對理論值 (T² − T₆₄²)/2 與 /6。

https://ithelp.ithome.com.tw/upload/images/20260919/20184206UJSaCQVzi1.png
RMSSD² 與 SDNN² 比 64 Hz 多出的量對取樣率,虛線是理論值;8、16 Hz 兩位受試者都在虛線附近,32、25 Hz 時 S2 高於虛線、S1 是負值

  • 16、8 Hz 對得上理論,32、25 Hz 看不出來。 RMSSD² 增量的平均是理論的 0.95、0.89 倍(resample_poly,22 窗合計);32、25 Hz 是 −1.28、0.32 倍,標準誤 934、1,386 ms²,比理論值 366、678 ms² 還大。
  • 圖中 S1 在 32、25 Hz 的負值,包含一個跳動的峰。 S1 第 17 窗的 64 Hz 波形有兩個相距 156 ms、高度只差 0.79 的局部高點,降到 32 Hz 後換成另一個,峰早了 140.62 ms;這一帶 64 Hz 本來就多找了一個峰(緊接著一個 296.9 ms 的峰間期)。這一窗的 RMSSD² 增量是 −17,610 ms²,同組 21 窗(含這一窗)加起來才 −9,860.8 ms²。排除這類峰跳動後,32 Hz 是理論的 1.00–1.06 倍,16 Hz 0.91–1.36,8 Hz 0.81–1.11,25 Hz 2.00(標準誤 828 ms²)。
  • 單窗的方向說不準。 RMSSD 比 64 Hz 低的窗:32 Hz 10/21、25 Hz 9/22、16 Hz 6/19、8 Hz 3/14。只用四捨五入模擬 26 個間期、RMSSD 約 156 ms 的窗,比例是 42%、39%、30%、15%,和實測接近。單窗的增量還含一項「逐拍變化 × 格點誤差」,平均為零,但起伏隨 RMSSD 變大,二十多個差值平均不掉。

https://ithelp.ithome.com.tw/upload/images/20260919/20184206kYo8ARncJZ.png
S1 靜坐段 60–66 秒,16 Hz decimate、8 Hz decimate、8 Hz naive 的樣本與找到的峰,灰色虛線是 64 Hz 找到的峰

8 Hz 時,naive 在 61.8 秒那一拍早了 140.62 ms;decimate 在 60.2、65.8 秒各晚 78.12 ms,超過兩次四捨五入的上限 70.31 ms。兩者只差在有沒有濾波,多出來的偏移來自濾波後的波形。四種做法到 16 Hz 以下才分歧,方向也不一致:8 Hz 配對有洞的窗 naive 8、decimate 5、resample_poly 8,22 窗分不出優劣。

結果與意外

  • RMSSD 先壞、SDNN 其次、HR 最耐,但放大誤差的是相鄰相減,不是平方。 RMSSD² 的增量約是 SDNN² 的 3 倍,而且大致和真值無關,所以低變異的偏更多。64 Hz 就把 6.82 量成 13.39 ms。
  • 偏高是期望值,單窗的方向說不準。 600 拍的合成序列全部往上偏;但真實資料的 30 秒窗在 32 Hz 有 10/21 窗比 64 Hz 低,只有格點誤差的模擬也有 42%。
  • T²/2 只在變異夠大時準。 變異 20、40 ms 時,實測是理論的 0.79–1.15 倍;變異比格點小很多時是 0.77–1.62 倍,由「平均間期 ÷ 格點」的餘數決定。
  • 降採樣做法不是主角,格點才是。 合成資料上四種做法幾乎一樣;真實資料到 16 Hz 以下才分歧,22 窗分不出優劣。
  • 參照本身就偏。 64 Hz PPG 的 RMSSD 是同窗 ECG 的 1.40–3.65 倍,峰數閘門擋不掉「多一個、少一個」。相對誤差因此被壓小,32 Hz 的平方增量還被一個跳動的峰翻成負的。

Limitations

  1. 參照本身含假峰。 閘門只比峰數,擋不掉「多一個假峰、漏一個真峰」。保留的 22 窗裡,有 5 窗的最短峰間期只有同窗 ECG 最短 R-R 的 0.29–0.50 倍。所以 64 Hz 的 RMSSD 本身就是 ECG 的 1.40 倍(S1)與 3.65 倍(S2),拿它當分母,相對誤差會被壓小(32 Hz 只有 +0.26%)。
  2. 一個峰就能翻轉平均。 S1 第 17 窗有兩個相距 156 ms、高度只差 0.79 的局部高點,降到 32 Hz 後換成另一個,峰跳了 140.62 ms。這一窗的 RMSSD² 增量是 −17,610 ms²,其他 20 窗加起來是 +7,749.2 ms²,32 Hz 的平均因此從 +387.46 ms²(理論 366.21)變成 −469.56 ms²。
  3. 只有 2 人、22 窗。 32 與 25 Hz 的標準誤(934、1,386 ms²)比理論值(366、678 ms²)還大,看不出規律。要更多受試者與更多靜止片段。
  4. 合成資料太乾淨。 波形的主峰兩側對稱,沒有真實 PPG 上升快、下降慢的形狀;間期是獨立亂數,RMSSD 約是 SDNN 的 √2 倍;平均間期固定 900 ms。變異小時偏差比值主要由「平均間期 ÷ 格點」的餘數決定,換成 910、920 ms 就整組改變,不能直接推到其他心率。
  5. 事後降採樣不等於低取樣率的裝置。 今天模擬的是同一台 E4 的訊號在軟體裡濾波、抽點,不是原生就用低取樣率的裝置。
  6. 沒做峰內插。 峰時間直接取最大樣本的位置。文獻上,在峰附近內插能大幅改善:Reali 2022 在 32 Hz 內插後,間期與 256 Hz PPG 已無實質差異;Béres 2022 的博論中,內插後 RMSSD 20 Hz 就夠,不內插要 50 Hz。今天的數字是沒做內插的結果。
  7. PPG 的脈波間期不是 ECG 的 R-R。 兩者本來就不是同一個量,差多少留到 Day 9。

這對 AI Engineering 的意義

今天最該帶走的不是「幾 Hz 才夠」,而是誤差要跟誰比。以 64 Hz 為參照,32 Hz 的 RMSSD 只差 +0.26%,看起來能放心降採樣;但參照本身是同窗 ECG 的 1.40–3.65 倍。之後評估 LLM 也有同樣的陷阱:拿一份本身有錯的參考答案算一致率,分數會好看。所以報告誤差之前,先量參照離可信來源多遠;分母不可信時,改用不隨分母縮放的量(今天是平方的增量),或換一個參照(Day 9 用 ECG)。

第二,解析度造成的偏差不是能事後扣掉的常數。平方的增量平均是 T²/2,但要很多拍平均才看得到:單一 30 秒窗的方向可能相反;變異比格點小時,實測是理論的 0.77–1.62 倍;一個峰跳 140 ms,就讓 21 窗的平均翻號。所以 Concept 第 5 節說的「由 pipeline 用規則擋下」,除了取樣率與峰怎麼定位,還要看得到每個數字是幾拍算出來的。

第三,下游看到的「變化」要先大過解析度本身的起伏。同一段真值 6.82 ms 的波形,64 Hz 量到 13.39 ms,降到 25 Hz 量到 36.65 ms。LLM 只看到「RMSSD 從 13 變 37」,很容易說成恢復變好,其實只是格點變粗。Day 17 做 deviation detection 時,門檻要先把這一層算進去。


上一篇
Day 05|每晚一個 HRV 數字是怎麼來的?從 5 分鐘視窗到 nightly metric
下一篇
Day 07|Motion Artifact:為什麼穿戴式資料這麼難用?
系列文
30 天拆解 Wearable × AI:從穿戴裝置生理訊號到AI健康洞察10
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言