【发布时间】:2018-05-11 04:55:00
【问题描述】:
我正在尝试在 Python 中提出一种通用方法来识别在一组计划的航天器机动过程中发生的俯仰旋转。您可以将其视为shift detection 问题的特例。
让我们考虑一下我的一组测量值中的solar_elevation_angle 变量,它确定了从航天器仪器测量的太阳仰角。对于那些可能想要玩数据的人,我保存了solar_elevation_angle.txt 文件here。
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import gridspec
from scipy.signal import argrelmax
from scipy.ndimage.filters import gaussian_filter1d
solar_elevation_angle = np.loadtxt("solar_elevation_angle.txt", dtype=np.float32)
fig, ax = plt.subplots()
ax.set_title('Solar elevation angle')
ax.set_xlabel('Scanline')
ax.set_ylabel('Solar elevation angle [deg]')
ax.plot(solar_elevation_angle)
plt.show()
扫描线是我的时间维度。斜率变化的四个点确定了航天器的俯仰旋转。
如您所见,航天器机动区域之外的太阳仰角演变几乎是线性的,作为时间的函数,对于这个特定的航天器来说应该始终如此(重大故障除外)。
请注意,在每次航天器操纵期间,坡度变化显然是连续的,尽管在我的角度值集中是离散的。这意味着:对于每个机动,尝试定位发生机动的单个扫描线是没有意义的。我的目标是为每个操作确定一个“代表性”扫描线,该扫描线在定义操作发生的时间间隔的扫描线范围内(例如,中间值或左边界)。
一旦我得到一组“有代表性的”扫描线索引,其中所有动作都发生了,我就可以使用这些索引粗略估计动作持续时间,或者在图上自动放置标签。
到目前为止,我的解决方案是:
- 使用以下方法计算太阳仰角的二阶导数
np.gradient。 - 计算绝对值并截取结果 曲线。剪辑是必要的,因为我认为是 线性段中的离散化噪声,这将严重影响第 4 点中“真实”局部最大值的识别。
- 对生成的曲线应用平滑,以消除多个峰值。我正在使用 scipy 的 1d 高斯滤波器和一个试错 sigma 值。
- 识别局部最大值。
这是我的代码:
fig = plt.figure(figsize=(8,12))
gs = gridspec.GridSpec(5, 1)
ax0 = plt.subplot(gs[0])
ax0.set_title('Solar elevation angle')
ax0.plot(solar_elevation_angle)
solar_elevation_angle_1stdev = np.gradient(solar_elevation_angle)
ax1 = plt.subplot(gs[1])
ax1.set_title('1st derivative')
ax1.plot(solar_elevation_angle_1stdev)
solar_elevation_angle_2nddev = np.gradient(solar_elevation_angle_1stdev)
ax2 = plt.subplot(gs[2])
ax2.set_title('2nd derivative')
ax2.plot(solar_elevation_angle_2nddev)
solar_elevation_angle_2nddev_clipped = np.clip(np.abs(np.gradient(solar_elevation_angle_2nddev)), 0.0001, 2)
ax3 = plt.subplot(gs[3])
ax3.set_title('absolute value + clipping')
ax3.plot(solar_elevation_angle_2nddev_clipped)
smoothed_signal = gaussian_filter1d(solar_elevation_angle_2nddev_clipped, 20)
ax4 = plt.subplot(gs[4])
ax4.set_title('Smoothing applied')
ax4.plot(smoothed_signal)
plt.tight_layout()
plt.show()
然后我可以使用 scipy 的 argrelmax 函数轻松识别局部最大值:
max_idx = argrelmax(smoothed_signal)[0]
print(max_idx)
# [ 689 1019 2356 2685]
正确识别我正在寻找的扫描线索引:
fig, ax = plt.subplots()
ax.set_title('Solar elevation angle')
ax.set_xlabel('Scanline')
ax.set_ylabel('Solar elevation angle [deg]')
ax.plot(solar_elevation_angle)
ax.scatter(max_idx, solar_elevation_angle[max_idx], marker='x', color='red')
plt.show()
我的问题是:有没有更好的方法来解决这个问题?
我发现必须手动指定削波阈值以消除高斯滤波器中的噪声和 sigma 会大大削弱这种方法,使其无法应用于其他类似情况。
【问题讨论】:
标签: python numpy scipy signal-processing