このドキュメントでは、TSP(Time Stretched Pulse)信号の生成方法と、インパルス応答を取得する方法について説明します。Pythonを使用して、TSP信号を生成し、その信号を逆TSP信号と畳み込むことでインパルス応答を取得します。また、インパルス応答を窓関数で短縮する方法についても説明します。
以下のコードは、TSP信号を生成する関数と逆TSP信号を生成する関数を定義しています。
import numpy as np
import scipy.signal as signal
def generate_tsp(N):
tsp = signal.chirp(np.linspace(0, 1, N), f0=0.1, f1=0.9, t1=N, method='linear')
return tsp
def generate_inverse_tsp(tsp):
N = len(tsp)
inverse_tsp = np.flip(tsp)
inverse_tsp = inverse_tsp / np.sum(tsp ** 2)
return inverse_tsp
# TSP信号の生成
N = 1024
tsp = generate_tsp(N)
inverse_tsp = generate_inverse_tsp(tsp)
以下のコードは、TSP信号を使用して録音した信号と逆TSP信号を畳み込むことでインパルス応答を取得します。また、取得したインパルス応答を窓関数で短縮し、FIRフィルタのパラメータとして利用する方法も示しています。
import numpy as np
class NLMS:
def __init__(self, filter_order, step_size, epsilon=1e-8):
"""
filter_order: フィルタの次数
step_size: ステップサイズ(学習率)
epsilon: ゼロ除算を防ぐための小さな定数
"""
self.filter_order = filter_order
self.step_size = step_size
self.epsilon = epsilon
self.weights = np.zeros(filter_order)
def adapt(self, desired, input_signal):
"""
desired: 望ましい出力信号(ターゲット信号)
input_signal: フィルタの入力信号
"""
input_signal = np.asarray(input_signal)
if len(input_signal) < self.filter_order:
input_signal = np.concatenate([np.zeros(self.filter_order - len(input_signal)), input_signal])
x = input_signal[-self.filter_order:] # 入力信号の一部(フィルタの次数に対応)
y = np.dot(self.weights, x) # 現在のフィルタ係数で出力を計算
error = desired - y # エラーを計算
# フィルタ係数の更新
norm_factor = np.dot(x, x) + self.epsilon
self.weights += (self.step_size / norm_factor) * error * x
return y, error
def filter(self, input_signal):
"""
input_signal: フィルタの入力信号
"""
input_signal = np.asarray(input_signal)
if len(input_signal) < self.filter_order:
input_signal = np.concatenate([np.zeros(self.filter_order - len(input_signal)), input_signal])
x = input_signal[-self.filter_order:] # 入力信号の一部(フィルタの次数に対応)
y = np.dot(self.weights, x) # 現在のフィルタ係数で出力を計算
return y
# 使用例
nlms = NLMS(filter_order=4, step_size=0.1)
input_signals = [-0.9, -0.7, 0.3, 0.5, -0.1, 0.2, -0.8, 0.7, -0.4, 0.6]
desired_signals = [-0.8, -0.6, 0.2, 0.4, -0.05, 0.15, -0.7, 0.6, -0.3, 0.5]
for desired, input_signal in zip(desired_signals, input_signals):
y, error = nlms.adapt(desired, input_signal)
print(f"入力: {input_signal}, 望ましい出力: {desired}, フィルタ出力: {y}, エラー: {error}")
# フィルタを使用して新しい入力信号を処理
new_input_signal = [0.2, -0.5, 0.3, -0.1]
output = nlms.filter(new_input_signal)
print(f"新しい入力信号のフィルタ出力: {output}")
import numpy as np
class FilteredXLMS:
def __init__(self, filter_order, step_size, secondary_path, epsilon=1e-8):
"""
filter_order: フィルタの次数
step_size: ステップサイズ(学習率)
secondary_path: 二次経路のインパルス応答
epsilon: ゼロ除算を防ぐための小さな定数
"""
self.filter_order = filter_order
self.step_size = step_size
self.epsilon = epsilon
self.weights = np.zeros(filter_order)
self.secondary_path = np.asarray(secondary_path)
self.secondary_path_order = len(self.secondary_path)
self.x_buffer = np.zeros(self.filter_order + self.secondary_path_order - 1)
def adapt(self, desired, input_signal):
"""
desired: 望ましい出力信号(ターゲット信号)
input_signal: フィルタの入力信号
"""
self.x_buffer = np.roll(self.x_buffer, -1)
self.x_buffer[-1] = input_signal
x = self.x_buffer[-self.filter_order:] # 入力信号の一部(フィルタの次数に対応)
filtered_x = np.convolve(self.x_buffer, self.secondary_path, mode='valid')[:self.filter_order]
y = np.dot(self.weights, x) # フィルタの出力は入力信号(x)に基づく
error = desired - y # エラーを計算
norm_factor = np.dot(filtered_x, filtered_x) + self.epsilon
self.weights += (self.step_size / norm_factor) * error * filtered_x
return y, error
def filter(self, input_signal):
"""
input_signal: フィルタの入力信号
"""
self.x_buffer = np.roll(self.x_buffer, -1)
self.x_buffer[-1] = input_signal
x = self.x_buffer[-self.filter_order:] # 入力信号の一部(フィルタの次数に対応)
y = np.dot(self.weights, x) # フィルタの出力は入力信号(x)に基づく
return y
# 使用例
filter_order = 4
step_size = 0.1
secondary_path = [0.2, 0.3, 0.5]
filtered_x_lms = FilteredXLMS(filter_order, step_size, secondary_path)
input_signals = [0.5, -0.4, 0.3, 0.7, -0.2, 0.4, -0.6, 0.8, -0.1, 0.5]
desired_signals = [0.45, -0.35, 0.25, 0.65, -0.15, 0.35, -0.55, 0.75, -0.05, 0.45]
for desired, input_signal in zip(desired_signals, input_signals):
y, error = filtered_x_lms.adapt(desired, input_signal)
print(f"入力: {input_signal}, 望ましい出力: {desired}, フィルタ出力: {y}, エラー: {error}")
# フィルタを使用して新しい入力信号を処理
new_input_signal = [0.2, -0.5, 0.3, -0.1]
for signal in new_input_signal:
output = filtered_x_lms.filter(signal)
print(f"新しい入力信号のフィルタ出力: {output}")
Filtered-X LMSアルゴリズムにおいて、フィルタのタップ数(フィルタの長さ)を大きくしすぎると、フィルタの出力が-1.0~1.0の範囲を超える可能性があります。これは、フィルタのタップ数が増えると、フィルタの出力が入力信号の影響を強く受けやすくなるためです。 具体的には、以下の要因がフィルタの出力範囲に影響を与えます。 フィルタ係数の合計値: フィルタ係数の合計値が大きくなると、入力信号が増幅されることがあります。 入力信号の振幅: 入力信号が-1.0~1.0の範囲内である場合でも、フィルタ係数によってスケーリングされるため、出力信号がその範囲を超えることがあります。 タップ数の影響: フィルタのタップ数が増えると、フィルタ係数の合計値が増加する可能性があり、それによって出力信号が増幅されることがあります。 これを防ぐためには、フィルタ係数の正規化やクリッピングを行うことが有効です。フィルタ係数の正規化は、フィルタ係数の合計値が一定の範囲内に収まるように調整する方法です。また、出力信号が-1.0~1.0の範囲を超えた場合には、クリッピングによって信号をその範囲内に制限することができます。 以下に、フィルタ係数の正規化と出力信号のクリッピングを追加したFiltered-X LMSアルゴリズムの実装例を示します。
import numpy as np
class FilteredXLMS:
def __init__(self, filter_order, step_size, secondary_path, epsilon=1e-8):
"""
filter_order: フィルタの次数
step_size: ステップサイズ(学習率)
secondary_path: 二次経路のインパルス応答
epsilon: ゼロ除算を防ぐための小さな定数
"""
self.filter_order = filter_order
self.step_size = step_size
self.epsilon = epsilon
self.weights = np.zeros(filter_order)
self.secondary_path = np.asarray(secondary_path)
self.secondary_path_order = len(self.secondary_path)
self.x_buffer = np.zeros(self.filter_order + self.secondary_path_order - 1)
def adapt(self, desired, input_signal):
"""
desired: 望ましい出力信号(ターゲット信号)
input_signal: フィルタの入力信号
"""
self.x_buffer = np.roll(self.x_buffer, -1)
self.x_buffer[-1] = input_signal
x = self.x_buffer[-self.filter_order:] # 入力信号の一部(フィルタの次数に対応)
filtered_x = np.convolve(self.x_buffer, self.secondary_path, mode='valid')[:self.filter_order]
y = np.dot(self.weights, x) # フィルタの出力は入力信号(x)に基づく
error = desired - y # エラーを計算
norm_factor = np.dot(filtered_x, filtered_x) + self.epsilon
self.weights += (self.step_size / norm_factor) * error * filtered_x
# フィルタ係数の正規化
self.weights = self.weights / np.linalg.norm(self.weights)
return y, error
def filter(self, input_signal):
"""
input_signal: フィルタの入力信号
"""
self.x_buffer = np.roll(self.x_buffer, -1)
self.x_buffer[-1] = input_signal
x = self.x_buffer[-self.filter_order:] # 入力信号の一部(フィルタの次数に対応)
y = np.dot(self.weights, x) # フィルタの出力は入力信号(x)に基づく
# 出力信号のクリッピング
y = np.clip(y, -1.0, 1.0)
return y
# 使用例
filter_order = 4
step_size = 0.1
secondary_path = [0.2, 0.3, 0.5]
filtered_x_lms = FilteredXLMS(filter_order, step_size, secondary_path)
input_signals = [0.5, -0.4, 0.3, 0.7, -0.2, 0.4, -0.6, 0.8, -0.1, 0.5]
desired_signals = [0.45, -0.35, 0.25, 0.65, -0.15, 0.35, -0.55, 0.75, -0.05, 0.45]
for desired, input_signal in zip(desired_signals, input_signals):
y, error = filtered_x_lms.adapt(desired, input_signal)
print(f"入力: {input_signal}, 望ましい出力: {desired}, フィルタ出力: {y}, エラー: {error}")
# フィルタを使用して新しい入力信号を処理
new_input_signal = [0.2, -0.5, 0.3, -0.1]
for signal in new_input_signal:
output = filtered_x_lms.filter(signal)
print(f"新しい入力信号のフィルタ出力: {output}")