2. 方向盘转角传感器信号采集与预处理
任务说明
本案例针对SBW线控转向系统中方向盘转角传感器(SAS)的标定与数据预处理环节,完成以下闭环目标:
- 建立传感器原始码值到物理转角(度)的线性映射模型,包含零点偏移与比例系数标定;
- 实现针对原始信号的预处理流程:剔除跳变野值、滑动均值滤波、以及跨±180°边界的方向角连续性处理;
- 提供可独立运行的Python实现,并给出验证方法。
标定模型与预处理算法
方向盘转角传感器输出为数字码值N,物理转角θ(单位:度)通过两点标定法确定线性关系:
θ = (N - Nzero) × (θref2 - θref1) / (Nref2 - Nref1)
其中Nzero为方向盘居中时的零点码值。预处理流程依次为:
- 野值剔除:相邻采样角度差超过设定阈值(如10°)时,判定为跳变野值,用前一有效值替代;
- 滑动均值滤波:窗口宽度为5的移动平均,抑制高频噪声;
- 角度连续性处理:将滤波后角度映射到(-180°, 180°]区间,并处理跨边界时的跳变(如179°→-179°应视为+2°变化而非-358°)。
代码实现
import numpy as np
class SteeringAngleSensor:
"""方向盘转角传感器标定与预处理"""
def __init__(self, n_zero, n_ref1, theta_ref1, n_ref2, theta_ref2):
"""
参数:
n_zero: 零点码值
n_ref1, theta_ref1: 标定点1 (码值, 角度°)
n_ref2, theta_ref2: 标定点2 (码值, 角度°)
"""
self.n_zero = n_zero
# 线性比例系数: 度/码值
self.scale = (theta_ref2 - theta_ref1) / (n_ref2 - n_ref1)
self.offset = theta_ref1 - self.scale * (n_ref1 - n_zero)
def raw_to_angle(self, raw_code):
"""原始码值转物理角度(未滤波)"""
return self.scale * (raw_code - self.n_zero) + self.offset
@staticmethod
def remove_outliers(angles, threshold_deg=10.0):
"""剔除跳变野值: 相邻差超阈值则用前值替代"""
clean = np.array(angles, dtype=float)
for i in range(1, len(clean)):
if abs(clean[i] - clean[i-1]) > threshold_deg:
clean[i] = clean[i-1]
return clean
@staticmethod
def moving_average(angles, window=5):
"""滑动均值滤波"""
kernel = np.ones(window) / window
# 使用'valid'模式保持长度一致(边缘用原值)
filtered = np.convolve(angles, kernel, mode='same')
# 修正边缘效应: 前window//2和后window//2个点用原值
half = window // 2
filtered[:half] = angles[:half]
filtered[-half:] = angles[-half:]
return filtered
@staticmethod
def unwrap_angle(angles):
"""角度连续性处理: 消除±180°边界跳变"""
wrapped = np.array(angles, dtype=float)
# 将角度映射到(-180, 180]
wrapped = (wrapped + 180.0) % 360.0 - 180.0
# 处理跨边界跳变: 相邻差大于180°时修正
for i in range(1, len(wrapped)):
delta = wrapped[i] - wrapped[i-1]
if delta > 180.0:
wrapped[i] -= 360.0
elif delta < -180.0:
wrapped[i] += 360.0
return wrapped
def process(self, raw_codes):
"""完整预处理流水线"""
# 1. 码值转角度
angles = np.array([self.raw_to_angle(c) for c in raw_codes])
# 2. 野值剔除
angles = self.remove_outliers(angles)
# 3. 滑动均值滤波
angles = self.moving_average(angles)
# 4. 角度连续性处理
angles = self.unwrap_angle(angles)
return angles
# ===== 标定与验证示例 =====
if __name__ == "__main__":
# 标定数据: 两点标定法
# 点1: 码值1000对应-120°, 点2: 码值4000对应+120°
sas = SteeringAngleSensor(
n_zero=2500, # 居中码值
n_ref1=1000, theta_ref1=-120.0,
n_ref2=4000, theta_ref2=120.0
)
# 模拟原始码值序列: 包含正常转动、野值跳变、跨±180°边界
raw_sequence = [
2500, 2520, 2540, 3000, 2580, 2600, # 正常小幅度转动(含野值3000)
2620, 2640, 2660, 2680, 2700, 2720, # 持续右转
2740, 2760, 2780, 2800, 2820, 2840, # 接近+180°
2860, 2880, 2900, 2920, 2940, 2960, # 跨过+180°边界
2980, 3000, 3020, 3040, 3060, 3080 # 继续转动(应显示为负角)
]
# 执行预处理
processed = sas.process(raw_sequence)
# 打印前10个点的对比
print("原始码值 → 预处理后角度(°)")
for i in range(10):
raw = raw_sequence[i]
ang = processed[i]
print(f"{raw:6d} → {ang:8.2f}")
# 验证跨边界连续性
print("\n跨边界检查(索引20-25):")
for i in range(20, 25):
print(f" 码值{raw_sequence[i]:5d} → 角度{processed[i]:8.2f}°")
落地与验证
运行方式:将代码保存为sas_calibration.py,在Python 3.6+环境执行python sas_calibration.py(需安装NumPy)。
验证方法与判定标准:
- 标定正确性:输入码值1000时输出应接近-120°,码值4000时输出接近+120°,码值2500时输出接近0°;
- 野值剔除:序列中第4个点码值3000(相对前后跳变约460码值≈27.6°)应被修正为接近前一值2520对应的角度;
- 滤波效果:输出曲线应比原始转换角度更平滑,无阶跃突变;
- 边界连续性:当码值从2900(约+168°)继续增大到2960时,输出角度应连续从约+168°过渡到约-168°(即跨边界后显示为负角),而非跳变到+192°。观察索引20-25的输出应呈现连续变化。
预期输出(部分):前3个点码值2500/2520/2540对应角度约为0.00°/1.44°/2.88°,第4个点(野值3000)输出仍接近2.88°而非27.6°,后续点按约1.44°步进连续变化;跨边界区域角度从约+168°连续过渡到-168°附近。