资讯动态

人类活动识别全流程:从传感器数据到模型部署

发布时间:2026/9/12 18:26:26 来源:尧图企业网站定制
简介这是一份面向机器学习与行为识别入门者的人类活动建模资源包聚焦如何将智能手机采集的GPS、加速度计、陀螺仪等多源传感数据转换为可分析特征并构建识别行走、跑步、驾驶等活动的模型。资源共34个文件约54.63MB以txt说明文档、Python脚本及数据集配套文件为主其中Python代码覆盖数据加载、清洗预处理、特征工程与模型训练等关键环节txt文件则提供数据集字段说明、活动标签与使用指引便于边读边练。已有127人学习适合有Python基础、希望结合真实传感数据掌握人类活动识别流程的开发者参考。通过该资源可系统了解从原始传感器数据到活动分类模型的完整链路并可直接复用其中脚本开展实验为后续健康管理、智能家居等领域应用打下基础。1. 人类活动建模的真实痛点信号并不认识动作加速度计输出的三轴数值叠加了重力、颠簸和走路节奏直接丢给分类器并不会得到可用的动作标签。人类活动建模要解决的是把这串连续信号切成短窗口再把每个窗口映射成站、走、跑、上楼、下楼中的一个动作。补齐这条链路不需要特别深的算法却非常依赖数据处理顺序传感器采样率如何对齐、窗口滑动多少、训练数据按人还是按随机行切分每一步都直接影响最终分数。更重要的是这套“传感器数据特征工程建模代码”的组合可以复用到跌倒检测、老人看护、运动能耗估算等场景里。下文把从原始数据到可部署模型的全过程逐步拆开。2. 攒数据智能手机数据从哪读、怎么洗成可用样本先解决一个基础问题拿到的智能手机数据长什么样又能从中读到什么。业界处理人类活动建模HARHuman Activity Recognition时常用数据源是手机内置的运动传感器其中加速度计与陀螺仪是识别站、坐、走、跑的核心。2.1 智能手机数据里有什么传感器的职责与采样率手机里的传感器各管一段运动模态读到的信号形态完全不同。加速度计感知的是线加速度与重力加速度的合量静态放置时读到的数值就是重力投影所以走路时波形会叠在一个接近 1g 的直流分量上陀螺仪输出角速度对转身、旋腕这类旋转运动非常敏感磁力计看方向的长期漂移气压计则能感知微小高度变化对楼层识别帮助最大。传感器输出典型采样率从它身上能读出的活动特征加速度计三轴加速度m/s²50Hz走路节奏、冲击强度、站起的瞬时加速度陀螺仪三轴角速度rad/s50Hz转身、弯腰、手臂摆动幅度磁力计三轴磁场强度10~30Hz朝向辅助、室内定位修正气压计气压hPa5~25Hz楼层变化能直接区分上楼和下楼GPS经纬度、速度1Hz室外移动速度辅助判断采样率不是越高越好。50Hz 已经能覆盖人正常活动的主要能量频段再高只会增加功耗和存储。处理多传感器数据时第一步是把它们按时间戳对齐到同一个采样网格否则后面切窗后每个窗口内各轴长度不一致特征矩阵根本拼不起来。2.2 公开数据集怎么选从 UCI HAR 到自采数据不想从零标数据一般从 UCI HAR 数据集起步30 名受试者6 类活动走、上楼、下楼、坐、站、躺使用三星手机内置加速度计和陀螺仪采集并做了滤波。数据已经切成 128 个采样点的窗口附带 561 维预提取特征适合快速验证模型思路。WISDM 数据集则更“原始”它直接给出 CSV 格式的原始加速度记录没有预切窗口适合想亲手走完整个数据处理链路的人。公开数据集可以帮你把流程跑通但真实业务里往往需要自己采把手机放裤袋、拿手上、放包里采集完成后让用户自报标签。自采数据的优势是传感器位置可控、标签可靠代价是需要写一段带界面的采集应用。维度UCI HARWISDM采样率50Hz20Hz窗口已切好128 点未切需要自己处理活动类别6 类6 类传感器加速度陀螺仪仅加速度计适合阶段模型快速验证练完整数据链路2.3 数据预处理的 Python 基线重采样、去均值与文件对齐UCI HAR 的文件是空格分隔的文本读取时常犯的错是直接用默认分隔符导致列错位正确做法是用正则分隔符读取。import pandas as pd # UCI HAR 的特征表是空格分隔的固定宽度文本 X_train pd.read_csv(UCI_HAR/train/X_train.txt, sepr\s, headerNone) y_train pd.read_csv(UCI_HAR/train/y_train.txt, sepr\s, headerNone, names[activity]) subject_train pd.read_csv(UCI_HAR/train/subject_train.txt, sepr\s, headerNone, names[subject]) train pd.concat([X_train, y_train, subject_train], axis1) print(train.shape)sepr\s会把连续多个空格当作一个分隔符处理避免列偏移names[activity]给标签列命名方便后续 groupbyconcat(..., axis1)按列拼接时三个文件的每一行必须严格对应同一条记录。UCI HAR 官方文件行数相同可以安全拼接但自采数据不一定满足这一点拼接前要注意先按时间戳排序。拿到原始加速度后需要做去重力和降采样。去均值是一个快速替代方案严格做法是用低通滤波器估计重力方向再从原始信号中减掉。from scipy import signal import numpy as np def remove_gravity_and_resample(raw_accel, timestamps, target_fs50): # raw_accel: (N, 3) 原始三轴加速度 fs_old 1.0 / np.mean(np.diff(timestamps)) # 静态时重力在窗口内接近直流分量去均值可大致剥离重力 accel_detrend raw_accel - raw_accel.mean(axis0) # 重采样到统一频率便于多传感器拼接 resampled signal.resample(accel_detrend, int(len(accel_detrend) * target_fs / fs_old), axis0) return resampledraw_accel.mean(axis0)计算每个轴在整个样本段上的平均值这个值近似重力分量对快速切换动作的窗口并不精确但作为基线够用。signal.resample是按采样点数做傅里叶方法的插值target_fs / fs_old算出目标长度与源长度之比注意它假设信号全程等间隔自采数据如果不是等间隔需要先做三次样条插值。提示严格去重力应使用截止频率 0.5Hz 的低通滤波器估计重力再用原始信号减去滤波结果。去均值只适合整段信号姿态变化较小的场景。3. 特征工程滑动窗口、时域频域与按用户切分的坑原始信号不能直接进分类器一是维度太高二是孤立一个时间点的数值没有意义。人类活动建模通常把信号切成长度为数秒的短窗口再从每个窗口里提取统计特征。3.1 滑动窗口切多大、重叠取多少两个必调参数窗口长度决定了特征的时间分辨率也直接决定模型能感知的动作粒度。走路一步约 0.5 秒一次迈步周期约 1 秒窗口短于 1 秒装不下完整步态周期窗口超过 5 秒又容易把“走完改站住”这种状态变化混在一个样本里。常见做法是 2 秒窗口重叠 50%。窗口长度重叠率单样本延迟特征稳定性适用动作1s50%低偏低易抖动手势、点按2s50%中等稳定走、跑、上楼、下楼5s0%高很高长期姿态监测睡姿判断滑动窗口切分逻辑简单但边界条件容易写错。窗口起点步进是stride不是 1否则样本量爆炸且相邻样本几乎完全重复。import numpy as np def sliding_window(data, window_length, stride): # data: (N, C)返回 (num_windows, window_length, C) num_windows 1 (len(data) - window_length) // stride windows np.stack([ data[i * stride : i * stride window_length] for i in range(num_windows) ]) return windowswindow_length是采样点数不是秒数2 秒 50Hz 就是 100stride是 1 秒 50Hz 就是 50。num_windows的公式里1保证最后一段尾部能覆盖到data[i * stride : i * stride window_length]的切片步进帮你去掉末尾不足窗口长度的残缺段避免模型学到不完整的动作信号。3.2 特征工程的 Python 实现时域与频域特征时域特征描述信号的幅度和波动速度频域特征描述周期性。走路时的重心起伏有一个稳定主频跑步时这个主频升高而“坐”这种静态动作三个轴的能量都集中在极低频两者特征空间差异很大。def extract_features(window, sample_rate50): feats {} n len(window) for axis in range(window.shape[1]): x window[:, axis] feats[frms_{axis}] np.sqrt(np.mean(x**2)) feats[fzero_crossing_{axis}] np.diff(np.signbit(x)).sum() # FFT 前加汉宁窗降低频谱泄漏 win x * np.hanning(n) fft_vals np.abs(np.fft.rfft(win)) * 2.0 / n freqs np.fft.rfftfreq(n, d1.0 / sample_rate) # 去掉直流分量找主频 main_idx np.argmax(fft_vals[1:]) 1 feats[fmain_freq_{axis}] freqs[main_idx] feats[fpeak_power_{axis}] fft_vals[main_idx] return featsnp.diff(np.signbit(x)).sum()统计正负符号跳变次数过零率体现波形抖动的频率坐着时信号平稳过零率低走路时每个步态周期都会引起几次方向变化。np.hanning(n)给原始信号加窗防止 FFT 时截断处产生频谱泄漏泄漏会拖出大量不存在的低频分量。fft_vals[1:]从索引 1 开始取是因为第 0 个频率是直流幅度通常远大于运动频率不剔除会永远当选主频。3.3 训练集怎么切不能偷懒按受试者留出而非随机切特征矩阵拼好后最隐蔽的坑出现在划分训练集和验证集时。直接train_test_split(X, y, test_size0.2)会把同一受试者的不同窗口同时分到两边这些窗口有重叠、高度相关验证分数会虚高百分之五到百分之十真实环境换新人识别时精度大跌。from sklearn.model_selection import GroupShuffleSplit # X 是(样本数, 特征维度)的特征矩阵subjects 是每个样本对应的受试者编号 gss GroupShuffleSplit(n_splits1, test_size0.2, random_state42) train_idx, val_idx next(gss.split(X, y, groupssubjects)) X_train, X_val X[train_idx], X[val_idx] y_train, y_val y[train_idx], y[val_idx]GroupShuffleSplit与普通划分的核心区别在第三个参数groups它要求同一group内的样本要么全进训练集要么全进验证集。n_splits1只切一次random_state固定后结果可复现。人类活动建模里一个受试者就是一组这样验证的才是“没见过的人”而不是“同一人的另一个瞬间”。4. 建模与参数从随机森林到 LSTM 的取舍与训练验证特征矩阵拿到手后建模的起点不是最复杂的模型而是最快能验证特征质量的那个。人类活动数据通常样本量不大特征维度不低树模型往往比深度学习先出效果。4.1 结构化特征先上树模型随机森林的参数与代码特征提取完成的本质是结构化数据建模每一行是一个窗口的统计量。随机森林对这种输入有天然优势不需要做特征归一化能处理部分无关特征训练时还能输出特征重要性供排查。参数不贪多默认设置加上深度限制就能跑出好结果。from sklearn.ensemble import RandomForestClassifier from sklearn.metrics import classification_report rf RandomForestClassifier( n_estimators200, max_depth12, class_weightbalanced, n_jobs-1, random_state42 ) rf.fit(X_train, y_train) y_pred rf.predict(X_val) print(classification_report(y_val, y_pred, digits3))n_estimators200棵树对几千样本足够再大收益递减且训练变慢max_depth12限制单棵树深度防止在少量样本上过拟合树深太深时训练分数 1.0、验证分数明显更低class_weightbalanced对坐、站这类时长长但标签重复率高的类别自动加权n_jobs-1让所有 CPU 核并行训练。特征重要性可以直接从rf.feature_importances_看我之前发现频域主频通常排最前说明周期性和动作类别的关联比幅度更强。4.2 序列模型什么时候值得上LSTM/1D-CNN 的建模切入点当样本量突破数万甚至数十万并且原始窗口时序性更重要时再考虑深度学习。LSTM 能建模窗口内的时间依赖比如走路时相邻几十毫秒的加速度变化是连续的统计特征无法保留这种先后关系LSTM 可以。代价是训练时间长、需要在训练前做标准化、且推理时资源占用更高。import torch import torch.nn as nn class HARLSTM(nn.Module): def __init__(self, n_channels, hidden_size32, num_classes6): super().__init__() self.lstm nn.LSTM( input_sizen_channels, hidden_sizehidden_size, batch_firstTrue, bidirectionalTrue ) self.head nn.Linear(hidden_size * 2, num_classes) def forward(self, x): # x: (batch, seq_len, n_channels) out, _ self.lstm(x) # out: (batch, seq_len, hidden_size * 2) return self.head(out[:, -1, :]) # 取最后一个时间步bidirectionalTrue让模型从前后两个方向读时序因为走路这类循环动作前向和后向信息都能帮助判断动作类型out[:, -1, :]取最后时间步的输出作为整段窗口表征这是 LSTM 常用的做法。输入x的形状对应上一章切好的原始窗口(batch, seq_len, channels)不需要压平成特征向量。损失函数用nn.CrossEntropyLoss训练前对每个通道做均值方差标准化否则 LSTM 收敛会很慢。4.3 验证指标怎么读混淆矩阵与宏平均 F1分类报告里的 accuracy 在活动识别里不够用因为各动作时长不均衡坐和站占了大量样本走和跑样本少整体准确率高可能是数据倾斜带来的假象。更稳的是macro F1它先按类算 F1 再取平均给少数类同样权重。from sklearn.metrics import confusion_matrix, f1_score cm confusion_matrix(y_val, y_pred) print(confusion matrix:\n, cm) print(macro F1:, f1_score(y_val, y_pred, averagemacro))confusion_matrix输出的每行是真实类别每列是预测类别对角线越亮越好。活动识别里最常见的混淆有两个方向走路与跑步在快速行走时界限模糊站与坐在静止时区别极小。若发现上下楼互相错分常规解法是加上气压计数据因为高度变化是区分这两种动作的最直接证据如果只依赖加速度和三轴陀螺仪这两个类别本来就高度相似。5. 部署到手机降采样、int8 量化与活动平滑投票模型在电脑上跑通只是第一步真实智能手机上部署要考虑电池和发热。业内把训练好的模型转换到手机端时通常会同步做三件事把采样率从 50Hz 降到 25Hz压缩输入数据量对小模型做 int8 量化降低体积和计算量加一层输出平滑过滤瞬时误判。5.1 端侧推理的三个现实约束采样率、量化、内存人正常活动的能量几乎都低于 25Hz所以 50Hz 数据直接降一半采样有效信息丢失很小但传感器功耗和计算量都显著下降。落地时可以用 TensorFlow Lite 转换模型先做量化再部署。import tensorflow as tf converter tf.lite.TFLiteConverter.from_keras_model(model) converter.optimizations [tf.lite.Optimize.DEFAULT] converter.representative_dataset generate_rep_data # 校准数据生成器 tflite_model converter.convert()optimizations[tf.lite.Optimize.DEFAULT]启用默认量化策略通常作用于权重和激活值模型体积可压缩到原来的四分之一representative_dataset是量化校准的关键它必须是训练数据的一小部分子集约几百个片段即可作用是让量化器看到真实数据分布来调整量化范围。校准数据取太少会导致量化后精度骤降取多一点又会让转换变慢。量化后的 int8 模型在传感器数据上准确率掉点通常在 1 个百分点以内因为运动信号本身有噪声对量化并不敏感。5.2 活动平滑投票把单秒判断变成连续状态单窗口预测天然会有抖动即使是很准的模型也经常在两个相邻动作的边界处来回横跳。一个不依赖额外训练的实用技巧是滑动投票取最近几个窗口的预测结果投票得票最多的作为最终输出。from collections import Counter def vote_smooth(predictions, vote_window5): smoothed [] for i in range(len(predictions)): start max(0, i - vote_window 1) votes predictions[start : i 1] smoothed.append(Counter(votes).most_common(1)[0][0]) return smoothedvote_window5意味着连续五个窗口的预测都被纳入计票只有刚落进窗口的多数动作才能翻转当前判断Counter(votes).most_common(1)取出现次数最多的类别平票时返回最先出现的那个满足稳定优先的要求。投票窗越长识别越稳但延迟也越大一个 2 秒窗口加 5 次投票从动作切换到最终确认约需 3 到 4 秒。这个延迟对记步、坐姿检测完全够用对跌倒检测则不可接受只适合部署后再做一次低延迟通道单独处理紧急事件。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价