據(jù)集實戰(zhàn):從數(shù)據(jù)預(yù)處理到損傷檢測全流程解析)
簡介這份代碼包圍繞大型跨江橋梁結(jié)構(gòu)健康監(jiān)測場景基于2023年10月1日8:00至12:45的連續(xù)實測數(shù)據(jù)構(gòu)建整合主跨跨中、橋塔基座、伸縮縫區(qū)域、懸索錨固點及橋面鋪裝層5個關(guān)鍵部位傳感器S1-S5的振動、溫度與應(yīng)力參數(shù)原始CSV數(shù)據(jù)無缺失值并附異常數(shù)據(jù)說明與預(yù)設(shè)關(guān)聯(lián)模式適用于結(jié)構(gòu)健康分析、異常檢測及機器學習建模研究。包內(nèi)共5個文件除核心CSV數(shù)據(jù)文件外還包含Markdown說明文檔、HTML可視化頁面及工程輔助配置完整呈現(xiàn)數(shù)據(jù)字段說明與傳感器部署信息便于快速理解數(shù)據(jù)結(jié)構(gòu)并復(fù)現(xiàn)實驗。資源整體僅9KB輕量易用適合橋梁工程、數(shù)據(jù)科學及監(jiān)測系統(tǒng)開發(fā)方向的讀者用于工程診斷、算法驗證與學術(shù)研究。上線以來已有136人學習下載對需要真實橋梁監(jiān)測數(shù)據(jù)進行建模驗證的研究者具有較高參考價值。1. 橋梁傳感器數(shù)據(jù)集不是一堆波形文件而是一條能跑通的損傷檢測鏈路第一次拿到這套“橋梁傳感器數(shù)據(jù)集[項目代碼]”時我以為又是那種壓縮包里躺著上千個CSV、沒有注釋、沒有讀入腳本的“原始數(shù)據(jù)墳場”。解壓之后發(fā)現(xiàn)自己的判斷錯了一半數(shù)據(jù)確實是橋梁應(yīng)變和加速度的時序記錄但代碼包里還帶有完整的Python預(yù)處理、特征提取和模型訓(xùn)練流程。換句話說這份資源不是讓你從零摸索怎么讀數(shù)據(jù)而是直接給你一條從原始傳感器信號到損傷檢測結(jié)果的可復(fù)現(xiàn)通路。適合正在做結(jié)構(gòu)健康監(jiān)測、橋梁異常預(yù)警或時間序列分類的從業(yè)者尤其是想快速驗證方法卻卡在數(shù)據(jù)清洗和特征工程上的人。它能幫你省掉至少兩周的踩坑時間但前提是你得知道每一個參數(shù)為什么這么設(shè)。2. 理解數(shù)據(jù)與代碼框架先搞清楚手里的牌是什么2.1 傳感器類型與采樣頻率為什么說“高頻數(shù)據(jù)是資產(chǎn)也是包袱”這套數(shù)據(jù)集采集自一座公路試驗橋的多個測點核心傳感器分為兩類一類是應(yīng)變計采樣頻率50Hz用于捕捉車輛荷載下的局部應(yīng)力變化另一類是加速度計采樣頻率200Hz用于記錄整體振動響應(yīng)。之所以強調(diào)采樣頻率是因為它直接決定了后面滑動窗口和特征提取的尺度。50Hz的應(yīng)變數(shù)據(jù)適合做單個車輛過橋事件的分離而200Hz的加速度數(shù)據(jù)更適合分析結(jié)構(gòu)模態(tài)和沖擊信號。不要一上來就把兩類數(shù)據(jù)合并到同一個窗口里采樣率不同會導(dǎo)致樣本空間錯位這我在后面避坑章節(jié)會展開。代碼包的數(shù)據(jù)組織方式按測點分目錄每個測點下包含時間戳、溫度、應(yīng)變值或加速度值。溫度這一列容易被忽略但它恰恰是應(yīng)變數(shù)據(jù)中最隱蔽的干擾源。我見過有人拿著原始應(yīng)變值直接做閾值判斷結(jié)果夏季凌晨三四點頻繁誤報就是因為沒有做溫度補償。代碼包里提供了一個簡單的線性溫度補償函數(shù)雖然精度一般但足以讓基線數(shù)據(jù)穩(wěn)定下來。2.2 代碼包目錄解讀每個文件不是擺設(shè)解壓后可以看到幾個核心模塊read_data.py負責數(shù)據(jù)加載與格式統(tǒng)一preprocess.py處理缺失值、去噪和溫度補償feature_engineering.py計算時域和頻域特征train_model.py封裝了隨機森林和邏輯回歸的訓(xùn)練流程config.yaml集中管理所有參數(shù)。這種拆分的優(yōu)點是各個階段解耦你可以單獨用preprocess.py處理自己的數(shù)據(jù)然后塞進自己的模型里。缺點是如果你不了解每個模塊的輸入輸出格式很容易在接口處翻車。我的建議是運行代碼前先花半小時把config.yaml過一遍里面定義了窗口長度、濾波截止頻率、特征列表等關(guān)鍵參數(shù)。不要直接跑默認配置因為這份數(shù)據(jù)集的采樣頻率是固定的你換一個場景就得改一堆參數(shù)。代碼包里沒有提供詳細的文檔但變量命名比較規(guī)范配合read_data.py里的注釋基本能讀懂。2.3 從原始數(shù)據(jù)到樣本集的完整鏈路整體流程可以概括為五步第一步按測點讀入CSV統(tǒng)一時間戳格式第二步對每個測點單獨做缺失值插補和溫度補償?shù)谌桨丛O(shè)定窗口長度切分時序并生成對應(yīng)的損傷標簽第四步從每個窗口中提取特征矩陣第五步把特征矩陣和標簽輸入模型訓(xùn)練或規(guī)則判斷。這個鏈路看似簡單但每一步的參數(shù)選擇都會影響最終結(jié)果。比如窗口長度如果取1秒對于50Hz的應(yīng)變數(shù)據(jù)就是50個點能覆蓋一個典型車輛荷載事件的大部分過程但如果是200Hz的加速度數(shù)據(jù)1秒窗口就是200個點計算量會明顯增加而且容易混入大量噪聲。代碼包的默認窗口長度是2秒這個值在后面對比實驗中表現(xiàn)得比較中庸既能保留損傷導(dǎo)致的瞬態(tài)脈沖又不至于讓窗口內(nèi)信號過于稀疏而難以提取統(tǒng)計特征。如果你處理的是其他橋梁或不同交通流量下的數(shù)據(jù)建議先做一次事件持續(xù)時間調(diào)查再決定窗口長度。3. 數(shù)據(jù)預(yù)處理把原始時序變成干凈樣本的過程比想象中費勁3.1 缺失值與異常值處理不要用全局中位數(shù)糊弄過去傳感器數(shù)據(jù)最常遇到的問題就是某一個時間窗口內(nèi)出現(xiàn)空值或跳變。代碼包里提供的方法是先用線性插值填補小段缺失然后用中值絕對偏差MAD檢測異常跳變超過7倍MAD的點會被替換為局部鄰域中位數(shù)。這里有一個關(guān)鍵參數(shù)MAD倍數(shù)。默認值是7但這個值對于應(yīng)變數(shù)據(jù)可能過于寬松因為應(yīng)變本身在車輛荷載下會產(chǎn)生大幅波動而加速度數(shù)據(jù)的噪聲相對穩(wěn)定可以更嚴格一些。我一般會把應(yīng)變換算成微應(yīng)變后再做異常檢測否則數(shù)值單位會影響閾值判斷。import pandas as pd import numpy as np def load_sensor_data(filepath, sampling_rate50): df pd.read_csv(filepath, parse_dates[timestamp]) df df.sort_values(timestamp) # 設(shè)置時間戳索引方便重采樣 df.set_index(timestamp, inplaceTrue) # 統(tǒng)一重采樣到目標頻率缺失值先保留NaN后面插補 df df.resample(f{1000//sampling_rate}ms).median() return df def clean_outliers(series, mad_multiplier7.0): median series.median() mad np.median(np.abs(series - median)) if mad 0: return series threshold mad_multiplier * mad cleaned series.copy() # 超過閾值的點用局部鄰域中位數(shù)替換避免影響后續(xù)特征 outlier_idx np.abs(series - median) threshold cleaned[outlier_idx] series[outlier_idx].rolling(5, min_periods1, centerTrue).median() return cleaned df_strain load_sensor_data(strain_ch1.csv, 50) df_strain[strain_cleaned] clean_outliers(df_strain[strain], 7.0)邏輯說明先按采樣頻率重采樣是為了保證后續(xù)窗口切分時每個窗口內(nèi)的點數(shù)固定。重采樣的中位數(shù)方式比均值更能抵抗單點毛刺。clean_outliers函數(shù)里的MAD計算需要先得到序列的中位數(shù)和絕對偏差注意當MAD等于0時比如信號完全不變直接返回原序列否則會報除零錯誤。rolling窗口設(shè)為5個點既能平滑異常點又不會把真實的損傷信號抹平。參數(shù)說明mad_multiplier是核心閾值數(shù)值越小刪除越激進對于應(yīng)變數(shù)據(jù)7合適對于加速度數(shù)據(jù)可以試5resample的頻率參數(shù)用1000//sampling_rate是為了避免浮點誤差例如50Hz就是20ms。3.2 去噪濾波低通濾波的截止頻率不是拍腦袋定的代碼包里默認使用巴特沃斯低通濾波并提供了一個參數(shù)cutoff_hz默認值是采樣頻率的四分之一。比如50Hz的數(shù)據(jù)截止頻率就是12.5Hz200Hz的數(shù)據(jù)截止頻率就是50Hz。這個選擇背后的物理意義是橋梁的主要振動模態(tài)一般集中在前幾階頻率不會太高而應(yīng)變響應(yīng)中的車輛荷載激勵主要在低頻段。但加速度信號里如果有沖擊成分過低的截止頻率會把這些瞬態(tài)脈沖削掉導(dǎo)致后續(xù)損傷特征丟失。from scipy.signal import butter, filtfilt def lowpass_filter(data, cutoff_hz, fs, order4): nyquist 0.5 * fs normal_cutoff cutoff_hz / nyquist b, a butter(order, normal_cutoff, btypelow, analogFalse) # filtfilt可以零相位濾波避免信號整體延遲 filtered filtfilt(b, a, data, axis0) return filtered fs 50 cutoff 12.5 strain_filtered lowpass_filter(df_strain[strain_cleaned].values, cutoff, fs, order4)邏輯說明使用filtfilt而不是lfilter是因為filtfilt會正向和反向各濾波一次得到的結(jié)果相位偏移為零這對后續(xù)的時間對齊至關(guān)重要。如果用了lfilter信號會整體延遲幾十個點窗口切分時標簽就會對不上。參數(shù)說明order是濾波器階數(shù)階數(shù)越高衰減越快但相位畸變也更嚴重對于橋梁數(shù)據(jù)4階足夠cutoff_hz可以嘗試從8Hz到20Hz之間變化觀察損傷段與正常段的波形差異。我一般會先畫出濾波前后的頻譜圖看看損傷引起的能量變化集中在哪個頻帶再確定截止頻率而不是直接使用默認值。3.3 滑動窗口構(gòu)造與標簽對齊最容易被忽略的樣本均衡問題預(yù)處理之后就要構(gòu)造訓(xùn)練樣本。代碼包采用固定長度滑動窗口步長等于窗口長度的四分之一這樣相鄰窗口有75%的重疊可以增加樣本數(shù)量同時避免時間上的劇烈跳變。標簽來自一個單獨的損傷標記文件里面記錄了損傷事件的起止時間戳。問題在于如果損傷事件持續(xù)3秒而窗口長度是2秒那么落在損傷區(qū)間的窗口數(shù)量會比正常窗口少很多直接訓(xùn)練會導(dǎo)致模型偏向正常樣本。def create_windows(data, labels, window_len2.0, step_len0.5, fs50): window_points int(window_len * fs) step_points int(step_len * fs) X, y [], [] start 0 while start window_points len(data): window_data data[start:start window_points] # 窗口標簽取窗口內(nèi)最大標簽值如果窗口內(nèi)出現(xiàn)損傷就標記為1 window_label int(np.max(labels[start:start window_points])) X.append(window_data) y.append(window_label) start step_points return np.array(X), np.array(y) labels np.zeros(len(strain_filtered)) # 假設(shè)損傷發(fā)生在第1000到1500個點 labels[1000:1500] 1 X, y create_windows(strain_filtered, labels, 2.0, 0.5, fs) print(X.shape, y.sum() / len(y))邏輯說明window_label取窗口內(nèi)標簽的最大值意味著只要窗口內(nèi)有一小段損傷整個窗口就按損傷處理。這樣會帶來一些誤判邊界樣本但比“只有完全落在損傷區(qū)間才算正樣本”要穩(wěn)健漏報率更低。參數(shù)說明window_len和step_len的比例決定了樣本重疊度重疊度過高會導(dǎo)致相鄰樣本高度相關(guān)模型評估時容易樂觀重疊度過低則會丟失時間連續(xù)性。代碼包默認的0.5秒步長在50Hz下是25個點實際效果還可以。如果正常樣本遠多于損傷樣本可以在后面訓(xùn)練時使用class_weight或者欠采樣。4. 特征提取與模型訓(xùn)練統(tǒng)計閾值和機器學習可以雙線并行4.1 特征工程不要迷信“深度特征”時域統(tǒng)計就很能打代碼包里從每個窗口中提取了一組基礎(chǔ)特征均值、標準差、峰值、峰峰值、均方根、波形因子以及頻域里的主頻幅值和頻譜質(zhì)心。這些特征對于區(qū)分“正常”和“損傷”其實是夠用的因為損傷往往會導(dǎo)致局部應(yīng)力峰值增大或振動能量分布的頻帶偏移。我拿到這套代碼后沒有急著加小波包分解先用這組特征做了快速實驗效果已經(jīng)超過預(yù)期。from scipy.fft import fft def extract_features(window): mean np.mean(window) std np.std(window) peak np.max(np.abs(window)) rms np.sqrt(np.mean(window ** 2)) crest peak / rms if rms 0 else 0.0 # 頻域特征取FFT幅值譜的前10個頻率分量 spectrum np.abs(fft(window))[:10] main_freq_amp np.max(spectrum) return [mean, std, peak, rms, crest, main_freq_amp] features np.array([extract_features(w) for w in X]) print(features.shape)邏輯說明extract_features返回六個值其中crest峰值因子對瞬態(tài)沖擊很敏感損傷引起的脈沖會顯著提高均值峰值比。FFT只取了前10個頻點因為采樣點數(shù)少高頻部分噪聲很大反而沒有區(qū)分度。參數(shù)說明如果你處理的是加速度數(shù)據(jù)可以增加頻譜質(zhì)心特征即所有幅值加權(quán)頻率的平均值如果是應(yīng)變數(shù)據(jù)建議把窗口內(nèi)的最大值和最小值的差也加進去因為車輛過橋時應(yīng)變范圍能反映局部剛度變化。4.2 模型A基于滑動窗口統(tǒng)計量的閾值預(yù)警最簡單有效的方式不是上模型而是計算每個窗口的RMS和峰值因子然后設(shè)定動態(tài)閾值。代碼包里默認使用“中位數(shù) 5倍標準差”作為報警閾值但這個方法在數(shù)據(jù)本身存在周期性交通流量變化時會失效。我調(diào)整為基于MAD的閾值并把窗口長度和特征類型都做了參數(shù)化。threshold_cfg { feature: rms, base: median, scale_mad: 7.0, min_alert_len: 3 } feat_values features[:, 3] # rms median np.median(feat_values) mad np.median(np.abs(feat_values - median)) thr median threshold_cfg[scale_mad] * mad alerts feat_values thr print(fThreshold: {thr:.4f}, alert windows: {alerts.sum()})邏輯說明這個做法的好處是完全不需要訓(xùn)練部署時只要在邊緣設(shè)備上實時計算窗口特征就能出結(jié)果。但要注意閾值基于MAD而不是標準差因為MAD對異常樣本更魯棒不會因為少數(shù)嚴重損傷樣本把閾值拉高。參數(shù)min_alert_len是連續(xù)報警窗口最小個數(shù)用于過濾單窗口的隨機尖峰。參數(shù)說明scale_mad從3到10都可以試值越大報警越少但漏報風險升高。我建議先用正常數(shù)據(jù)統(tǒng)計出第99百分位數(shù)作為參考再反推scale_mad。4.3 模型B隨機森林分類與評估的完整流程如果閾值法達不到預(yù)期的精確率/召回率代碼包也提供了隨機森林分類器的訓(xùn)練腳本。這里要注意處理樣本不均衡代碼包默認在train_model.py里使用了RandomForestClassifier(class_weightbalanced_subsample)并用分層抽樣劃分訓(xùn)練測試集。from sklearn.ensemble import RandomForestClassifier from sklearn.model_selection import StratifiedShuffleSplit from sklearn.metrics import classification_report # 對樣本做簡單歸一化對樹模型影響不大但便于調(diào)試 from sklearn.preprocessing import StandardScaler scaler StandardScaler() X_scaled scaler.fit_transform(features) sss StratifiedShuffleSplit(n_splits1, test_size0.3, random_state42) for train_idx, test_idx in sss.split(X_scaled, y): X_train, X_test X_scaled[train_idx], X_scaled[test_idx] y_train, y_test y[train_idx], y[test_idx] clf RandomForestClassifier(n_estimators300, max_depth8, class_weightbalanced_subsample, random_state42, n_jobs-1) clf.fit(X_train, y_train) pred clf.predict(X_test) print(classification_report(y_test, pred))邏輯說明StratifiedShuffleSplit保證訓(xùn)練集和測試集中正常/損傷樣本的比例相同避免因為隨機劃分導(dǎo)致測試集全是正常樣本。class_weightbalanced_subsample會讓每棵樹的抽樣過程自動調(diào)整權(quán)重比直接傳class_weightbalanced對噪點更穩(wěn)健。參數(shù)說明n_estimators300在近萬樣本下訓(xùn)練時間約幾秒但如果你在嵌入式設(shè)備上部署可以降到100max_depth8限制樹深度防止過擬合random_state42只是常見約定實際換成其他值可能讓結(jié)果有微小波動。代碼包里沒有做超參數(shù)搜索如果你想調(diào)優(yōu)建議用GridSearchCV在n_estimators和max_depth上做小范圍搜索。5. 避坑指南這套資源里最容易翻車的五個地方5.1 現(xiàn)象窗口切分后樣本數(shù)量比預(yù)期少很多原因create_windows函數(shù)里的step_len和window_len使用浮點秒數(shù)在采樣率非整數(shù)倍時比如49.8Hz點數(shù)計算被向下取整導(dǎo)致窗口數(shù)量驟減。解決把window_points int(window_len * fs)改成round(window_len * fs)并且在讀取數(shù)據(jù)時統(tǒng)一使用resample強制對齊到目標頻率。我在預(yù)處理時固定用resample這樣fs就是一個精確值不會出現(xiàn)這種問題。5.2 現(xiàn)象正常時段頻頻誤報而且誤報集中在中午原因溫度變化引起的應(yīng)變漂移沒有被剔除。應(yīng)變計測量的是結(jié)構(gòu)表面應(yīng)變環(huán)境溫度升高會讓結(jié)構(gòu)材料膨脹產(chǎn)生虛假應(yīng)變增量。代碼包的線性溫度補償只是減去溫度和平均溫度的線性回歸項但橋梁實際的熱響應(yīng)可能帶有滯后性。解決我自己會先計算連續(xù)24小時溫度-應(yīng)變曲線做一次低通濾波截止頻率0.01Hz提取趨勢再從原始信號中減去趨勢。不要直接用原始溫度值做回歸因為溫度和應(yīng)變的時間響應(yīng)不同步。5.3 現(xiàn)象濾波后損傷脈沖幾乎消失了原因低通濾波器截止頻率設(shè)置過低把和損傷相關(guān)的瞬態(tài)高頻成分濾掉了。損傷產(chǎn)生的沖擊信號往往持續(xù)時間很短頻譜分布在整個頻帶內(nèi)如果只保留低頻脈沖的尖峰幅值會被嚴重壓低。解決在lowpass_filter之前分別畫出正常段和損傷段的功率譜密度觀察損傷段在哪個頻段有額外能量。我經(jīng)常使用的方法是把截止頻率從低到高遍歷幾個值分別評估后續(xù)分類器的F1分數(shù)而不是靠直觀感覺定參數(shù)。5.4 現(xiàn)象訓(xùn)練時模型準確率很高但實測新數(shù)據(jù)表現(xiàn)很差原因標簽對齊錯誤。損傷標記文件里的時間戳如果比實際損傷發(fā)生時間晚了5秒鐘那么部分正常窗口被標記為正樣本模型學到了錯誤的邊界。另外當損傷窗口和正常窗口有75%重疊時如果劃分訓(xùn)練測試集不是按時間順序而是打亂會造成嚴重的數(shù)據(jù)泄漏。解決檢查標簽時畫出窗口起始時間與標簽關(guān)系的散點圖確認損傷事件邊界是否與窗口邊界清晰對應(yīng)。劃分數(shù)據(jù)集時一定要按時間順序劃分前80%時間段作為訓(xùn)練后20%作為測試而不是隨機抽樣。這一點在時間序列里沒有例外。5.5 現(xiàn)象代碼在另一臺機器上跑報錯“FileNotFoundError”原因代碼包里使用相對路徑讀取data/目錄下的CSV但很多人直接把單個.py文件復(fù)制到新目錄導(dǎo)致路徑失效。解決所有數(shù)據(jù)路徑統(tǒng)一通過config.yaml的data_dir字段配置并且在read_data.py開頭用Path(__file__).parent.parent定位項目根目錄而不是依賴當前工作目錄。我習慣把數(shù)據(jù)文件路徑打印出來第一個啟動日志就顯示“數(shù)據(jù)加載路徑/xxx/bridge_dataset/data”這樣能從源頭發(fā)現(xiàn)路徑問題。6. 進階技巧多通道融合與部署時的采樣頻率陷阱6.1 多通道應(yīng)變和加速度融合的簡單方法代碼包里單測點處理已經(jīng)跑通但實際結(jié)構(gòu)監(jiān)測往往需要同時看多個截面的應(yīng)變和加速度。最簡單的融合方式不是把所有通道的特征堆在一起而是先對每個通道單獨計算窗口特征然后用一組可解釋的統(tǒng)計量匯總比如所有通道的RMS中位數(shù)、峰值最大值、主頻平均值。我在用這個資源做模擬項目X的時候把12個應(yīng)變通道的峰值因子取平均得到的融合特征比任何單通道都要穩(wěn)。all_ch_features np.array([features_list[i] for i in range(12)]) fused_rms np.sqrt(np.mean(all_ch_features[:, :, 3] ** 2, axis0)) fused_crest np.max(all_ch_features[:, :, 4], axis0) combined np.stack([fused_rms, fused_crest], axis-1)邏輯說明fused_rms用均方根平均而不是算術(shù)平均因為RMS本身是能量量綱這樣做物理意義一致fused_crest取最大值保留多個測點中最明顯的沖擊特征。參數(shù)說明如果你有加速度通道可以再加一個主頻變化量特征即損傷段主頻減去健康段主頻的差值。6.2 部署時的采樣頻率陷阱降采樣不是省事是省錯地方多次實測中我發(fā)現(xiàn)一個反復(fù)出現(xiàn)的血淚經(jīng)驗為了減少邊緣設(shè)備的存儲和計算壓力把50Hz數(shù)據(jù)直接降采樣到2Hz再跑模型結(jié)果損傷識別率直接從0.9掉到0.6。原因是車輛荷載引起的應(yīng)變響應(yīng)中橋面局部效應(yīng)體現(xiàn)在520Hz頻段降采樣后這部分信息完全消失。如果一定要壓縮數(shù)據(jù)應(yīng)該先做低通濾波去除50Hz以上的無關(guān)噪聲再以10Hz為目標做重采樣并重新訓(xùn)練模型而不是直接丟棄原始采樣率。6.3 模型更新與遷移時的強制流程從那以后我每次處理新的橋梁傳感器數(shù)據(jù)都強制走一遍這個流程先用原始采樣率畫出24小時波形檢查溫度漂移和交通流量周期再按“事件持續(xù)時間確定窗口長度”的方法重新設(shè)置參數(shù)然后分別用閾值法和機器學習的簡單版本做快速驗證確認有效后再考慮降采樣或模型壓縮。不要因為代碼包自帶默認參數(shù)就無腦照搬特別是在采樣頻率和傳感器類型發(fā)生變化時。希望這些細節(jié)能幫你在自己的傳感器數(shù)據(jù)上少走幾步彎路這套資源的代碼結(jié)構(gòu)本身很清晰剩下的就是把它調(diào)成適合你場景的樣子。本文還有配套的精品資源點擊獲取