为了更好地进行接下来代码的编写,我们进行了三种数据预处理:
通过数据预处理,我们就能够更为方便地加载、处理数据,以及最后的预测。
由于输入和输出的位置信息是以经纬度为准的,而经纬度作为位置信息有一些缺点:
针对上面问题,我们使用了一个公式:
对经纬度进行转换,其中
而
我们的输入有五个文件(忽略气压计数据),分别为:
Accelerometer.csv
(加速度计数据,50 Hz)Linear Accelerometer.csv
(线性加速度计数据,50 Hz)Gyroscope.csv
(陀螺仪数据,50 Hz)Magnetometer.csv
(磁力计数据,50 Hz)Location_input.csv
(GPS 位置信息,1 Hz)它们存在一个严重的问题:由于传感器周期不一致,记录的数据并没有严格对齐。
在这里,我们使用了 最近邻插值 的方法,以 Location_input.csv
的时间轴为基准,进行了最近邻插值,对数据进行了对齐处理。大致算法如下:
Location_input.csv
的 1 Hz 的时间轴 time_location
线性扩充成 50 Hz 的时间轴 time
, 例如 time_locaiton = [1., 2.]
的话,便会扩充成 time = [1., 1.02, 1.04, ..., 1.98, 2., 2.02, ..., 2.98]
, 其中 len(time) = 100
。time
时间轴上,最近邻插值即为每一个点匹配到最近的样本点。例如 Accelerometer.csv
的数据 [[1.01, 1], [1.02, 2], [1.03, 3], [1.04, 4]]
可能就会被最近邻插值为 [[1.00, 1], [1.02, 2], [1.04, 4]]
。# 通过文件路径加载一个 TestCase
test_case = TestCase("test_case0")
# 可以对 TestCase 进行切片, 单位为秒
new_test_case = test_case.slice(100, 500)
# 1 Hz 的时间轴
print(test_case.time_location)
# 50 Hz 的时间轴
print(test_case.time)
# 加速度数据
print(test_case.a)
print(test_case.a_x)
print(test_case.a_y)
print(test_case.a_z)
# 加速度幅值
print(test_case.a_mag)
步伐检测主要分为两个部分:
通过脚步提取和步伐检测,我们就能得到 任意时刻行人走过的路程。
需要的数据有:
使用 test_case0
中的部分时间段内的加速度幅度作为处理示例,未经处理的数据如下:
通过肉眼可以清晰的看出加速度的震荡具有规律性,较大峰值个数表示行人的可能步数。接着进行处理:
此时先用滤波器进行滤波,将波动变得更加光滑,消除小波峰:
### 滤波器
def filter(range,data):
filter = np.ones(range) / range
return np.convolve(data, filter, mode="same")
new_test_case = test_case.slice(10, 15)
### 对 a_mag 进行滤波
filtered_a = filter(10,new_test_case.a_mag)
一般人的步频最大为 3 Hz ,设定 0.4 秒的间隔查找峰值,如果含有多个峰值,则其中含有假峰,选取波峰最大的那一个。
在选取好候选波峰后,其中依然可能含有假波峰,例如行人行走过程中突然停下来,停下来的期间不可避免的会有微小振动,而这些振动中的波峰也会被识别到。此时对所有候选波峰求平均,并保留大于 平均值*0.8 的部分。
波峰采集效果:
我们通过前百分之十的数据,对步幅进行一个估计。
在预测前,我们发现并不知道行人每一步迈出的具体长度;而脚步波峰对应的时间 与 经纬度坐标的时间没有对齐。
处理这个问题的方式是通过相距的两个点间的距离,除以这两坐标点间的脚步数,作为这两点间脚步的步幅。这里我们设定一个超参数 distance_frac_step
,作为这两个坐标点间一共有包含多少坐标点。
步幅本身因人而异,它是一个与行人有关的变量。然而对于同一行人,步幅长短主要与行人的步频
步频
除了线性回归外,我们还尝试了 sklearn
中其他的学习器,这里便不过多赘述。
为了实现准确且稳定的前进方向预测功能,我们主要使用三类数据:
注意,我们并没有用到陀螺仪的数据,因为陀螺仪测出来的是角速度,很容易被手机的摇摆振荡的角速度变化干扰。
人的行走会引起振荡,导致了手机的摇摆。这也会引起手机坐标轴发生变化,进而导致测出来的磁力和加速度数据在 xyz 三个轴上不断地振荡变化。
为了消除这种振荡,我使用了一个截止频率相关参数 Wn
大致在 0.005 左右的 2 阶 Butterworth 低通滤波器,将磁力和加速度进行低通滤波,得到了更为平滑的磁力和加速度数据。
这里有一个很有趣的事实,由于加速度经过了低通滤波,几乎就等于被取了平均值,和重力无关的加速度被抵消,因此此时我们有:
加速度
b, a = signal.butter(2, 0.005, 'lowpass')
m_x = signal.filtfilt(b, a, tc.m_x)
m_y = signal.filtfilt(b, a, tc.m_y)
m_z = signal.filtfilt(b, a, tc.m_z)
这里有很关键的一点,这是作业文档中提到的一个假设,并且也是我们后续进行方向预测的核心假设:
在实验过程中,手机大部分时间保持在同一个状态下(如一直拿在手上)。
有了这个假设,我们就可以推出一个更强的结论:
经过低通滤波后的数据,可以等同于,手机相对于人没有发生任何旋转、位移与振荡时(即完全固定),记录下来的数据。
手机经过低通滤波后不再振荡,可以认为相对于人来说位置和方向是固定的。
借助这个结论,我们就可以使用空间解析几何的方法,通过磁力计等数据预测出当前的前进方向。
首先我们有两个很重要的前提:
第一步,首先用重力加速度向量
第二步,选出一个初始东向量,例如这里可以选取第 10% 个东向量作为初始东向量。
第三步,通过东向量与初始东向量的点乘除以模获取东向量和初始东向量的夹角,并转成角度制。
注意,此时我们并不知道
第四步,通过东向量与初始东向量叉乘后,与重力加速度点乘,得到对应的正负符号。
最后,我们就得到了前进方向与
最后的前进方向就是初始方向
还要记得最后取模
选取初始东向量
第一种是直接选用
第二种方法是,使用优化器,以及前 10% 的数据,最小化平均误差函数:
我们使用 scipy
的优化器,对应的优化代码为:
# 平均误差
error_fn = lambda x: np.mean(
direction_diff(direction_valid, (direction_offset + x)))
# 最小化误差获取最佳初始值
direction0 = scipy.optimize.minimize(error_fn, 0).x[0]
最后我们画出 test_case0
对应的图像,可以发现角度预测基本吻合。
在 test_case0 上的运行情况:
在 test_case0 上耗时
dist_error
:dir_error
:dir_ratio
: 在 10 个测试集上耗时
由于没有真实 GPS 数据,无法获得准确性指标。
最后的结果保存于 Result
文件夹或 TestSet
文件夹中。
在 51 个收集的行走数据集上耗时
dist_error
:dir_error
:dir_ratio
: 在 17 个收集的骑行或跑步数据集上耗时
dist_error
:dir_error
:dir_ratio
: