ノイズの多い正弦波のゼロクロッシング


9

正弦波を方形波に変換するために、正弦波のゼロクロッシングを見つけようとしています。唯一の問題は、正弦波にノイズが多いため、大量のジッターと誤ったゼロ交差が発生することです。

誰でも簡単な擬似コードや関連資料を推薦できますか?これまでのところ、私はこのようなものを持っています:

if (sample[i]>0 && sample[i+1]<0) || (sample[i]<0 && sample[i+1]>0)

誰かがより堅牢な方法を推奨できますか?


方形波にしようとする目的は何ですか?信号の始まりと終わりの場所を見つけようとしていますか?もしそうなら私は方法をお勧めできます。
Spacey

if((sample [i] * sample [i + 1])<0)zero_crossing ++;
マリウス・フリスカ2017

回答:


8

入力信号をローパスフィルター処理して、滑らかなゼロ交差を取得することもできます(正弦波の周波数の場所をよく理解している場合は、バンドパスフィルター処理を行うこともできます)。リスクは、サンプルの正確な位相情報がアプリケーションに不可欠である場合、フィルターからの追加の遅延が問題になる可能性があることです。

別のアプローチ:正弦波を方形波に変換しようとする代わりに、独立した方形波発振器を位相/周波数で正弦波に合わせる方法はどうでしょうか?これは、フェーズロックループで行うことができます


6

あなたが確かに示したことは、ゼロクロス検出器です。あなたの状況を改善するかもしれないいくつかのことが頭に浮かびます:

  • 信号の帯域外にノイズがある場合(入力は純粋なトーンであるため、ほぼ確実に当てはまります)、対象の信号の周りにバンドパスフィルターを適用することにより、信号対ノイズ比を改善できます。 。フィルターの通過帯域幅は、正弦波周波数をアプリオリにどれだけ正確に知っているかに基づいて選択する必要があります。正弦波に存在するノイズの量を減らすことにより、正しいゼロ交差の数と正しい交差時間に関するジッターが減少します。

    • 余談ですが、事前に適切な情報がない場合は、アダプティブラインエンハンサーと呼ばれるより洗練された手法を使用できます。これは、その名前が示すように、周期的な入力信号を強化するアダプティブフィルターです。ただし、これはやや高度なトピックであり、通常、信号の周波数について十分に理解しているため、このようなアプローチは必要ありません。
  • ゼロクロッシング検出器自体に関しては、プロセスにヒステリシスを追加できます。これにより、正しい交差の瞬間に余分な測定交差が生成されるのを防ぐことができます。検出器にヒステリシスを追加すると、次のようになります。

    if ((state == POSITIVE) && (sample[i - 1] > -T) && (sample[i] < -T))
    {
        // handle negative zero-crossing
        state = NEGATIVE;
    }
    else if ((state == NEGATIVE) && (sample[i - 1] < T) && (sample[i] > T))
    {
        // handle positive zero-crossing
        state = POSITIVE;
    }
    

    事実上、ゼロクロッシング検出器にいくつかの状態を追加します。入力信号が正の値を持っていると思われる場合は-T、実際のゼロ交差を宣言するために、信号が選択したしきい値を下回る必要があります。同様に、T信号が発振して再び正に戻ったことを宣言するには、信号がしきい値を超えて上昇する必要があります。

    スレッショルドは好きなように選択できますが、正弦波のようなバランスの取れた信号の場合、ゼロについて対称にすることが理にかなっています。このアプローチは、よりきれいに見える出力を提供するのに役立ちますが、ゼロクロッシングではなく非ゼロしきい値クロッシングを実際に測定しているため、多少の遅延が発生します。

ピシェネットが彼の答えで示唆したように、PLLはあなたがやろうとしていることをほぼ正確に行うので、フェーズロックループが最も良い方法です。つまり、入力正弦波と並行して実行される方形波ジェネレータを実行します。PLLは、正弦波で周期的な位相測定を行い、方形波発生器の瞬時周波数を制御するために、測定のストリームをフィルタリングします。ある時点で、ループは(うまくいけば)ロックされます。この時点で、方形波は入力の正弦波で周波数と位相がロックされるはずです(もちろん、ある程度の誤差があります。エンジニアリングでは完璧ではありません)。


それはシュミットトリガーですか?
Davorin 2013

実際、これはシュミットトリガーのソフトウェアバージョンであると言えます。シュミットトリガーの特徴は、ヒステリシスを備えたコンパレータであることです
Jason R

遷移を検出しないようにするには、両方の条件のいずれかにしきい値も含めますT。の代わりに&& (sample[i - 1] > -T) && (sample[i] < -T))、を使用します&& (sample[i - 1] >= -T) && (sample[i] < -T))。これはifおよびelse ifステートメントの両方に適用する必要があります。
絞りかす

2

私は時々信号のサインの変化を見つけるための非常に簡単な方法で良い経験をしています:

  1. a = diff(sign(signal))!= 0#符号の変化を検出します
  2. candidates = times [a]#これらは、偽交差を含むすべての候補点です
  3. 候補の点のクラスターを見つける
  4. 各クラスターの平均/中央値、これはあなたのサインの変化です

  5. 4によって予測された点でステップ関数との相関を行う

  6. 相関結果に曲線を適合させ、ピークを見つける

私の場合、5と6はメソッドの精度を上げません。信号をノイズでディ​​ザリングして、効果があるかどうかを確認できます。


2

この質問はかなり古いですが、最近ゼロクロッシングを実装する必要がありました。私はダンが提案した方法を実装し、結果にかなり満足しています。誰かが興味があれば私のPythonコードをここに載せます。私は本当にエレガントなプログラマーではありません。

import numpy as np
import matplotlib.pyplot as plt
from itertools import cycle

fig = plt.figure()
ax = fig.add_subplot(111)

sample_time = 0.01
sample_freq = 1/sample_time

# a-priori knowledge of frequency, in this case 1Hz, make target_voltage variable to use as trigger?
target_freq = 1
target_voltage = 0

time = np.arange(0.0, 5.0, 0.01)
data = np.cos(2*np.pi*time)
noise = np.random.normal(0,0.2, len(data))
data = data + noise


line, = ax.plot(time, data, lw=2)

candidates = [] #indizes of candidates (values better?)
for i in range(0, len(data)-1):
    if data[i] < target_voltage and data[i+1] > target_voltage:
        #positive crossing
        candidates.append(time[i])
    elif data[i] > target_voltage and data[i+1] < target_voltage:
        #negative crossing
        candidates.append(time[i])

ax.plot(candidates, np.ones(len(candidates)) * target_voltage, 'rx')
print('candidates: ' + str(candidates))

#group candidates by threshhold
groups = [[]]
time_thresh = target_freq / 8;
group_idx = 0;

for i in range(0, len(candidates)-1):
    if(candidates[i+1] - candidates[i] < time_thresh):
        groups[group_idx].append(candidates[i])
        if i == (len(candidates) - 2):
            # special case for last candidate
            # in this case last candidate belongs to the present group
            groups[group_idx].append(candidates[i+1])
    else:
        groups[group_idx].append(candidates[i])
        groups.append([])
        group_idx = group_idx + 1
        if i == (len(candidates) - 2):
            # special case for last candidate
            # in this case last candidate belongs to the next group
            groups[group_idx].append(candidates[i+1])



cycol = cycle('bgcmk')
for i in range(0, len(groups)):
    for j in range(0, len(groups[i])):
        print('group' + str(i) + ' candidate nr ' + str(j) + ' value: ' + str(groups[i][j]))
    ax.plot(groups[i], np.ones(len(groups[i])) * target_voltage, color=next(cycol), marker='o',  markersize=4)


#determine zero_crosses from groups
zero_crosses = []

for i in range(0, len(groups)):
    group_median = groups[i][0] + ((groups[i][-1] - groups [i][0])/2)
    print('group median: ' + str(group_median))
    #find index that best matches time-vector
    idx = np.argmin(np.abs(time - group_median))
    print('index of timestamp: ' + str(idx))
    zero_crosses.append(time[idx])


#plot zero crosses
ax.plot(zero_crosses, np.ones(len(zero_crosses)) * target_voltage, 'bx', markersize=10) 
plt.show()

Pls注:私のコードは兆候を検出せず、ターゲット周波数のアプリオリな知識を少し使用して、時間しきい値を決定します。このしきい値は、グループの中央値に最も近いものが選択される複数の交差点(画像内の異なる色の点)をグループ化するために使用されます(画像内の青い十字)。

ゼロクロスがマークされたノイズの多い正弦波

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.