質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.49%

  • C

    3800questions

    C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

リアルタイムで波形の最大値を求めたい

解決済

回答 3

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 470

sotooki

score 9

 前提・実現したいこと

dsPICのプログラムを用いてA/D変換された4000点の計測データの最大値と何点目で最大値であったかを求めようとしています。
A/D変換前のオシロスコープの波形

 発生している問題・エラーメッセージ

リアルタイムで前後のデータを比較し、値の高いデータを”山登り法”を用いて求めていますが、ノイズが混入しているため、時々異なる値が最大値として出力されてしまいます。

イメージ説明

 該当のソースコード

void __attribute__((__interrupt__, __shadow__))_T2Interrupt(void)//割り込み処理関数
{

 IFS0bits.T2IF=0;//割込みフラグクリア
 dds_CTRL=1;
 delay_ns(50);
 dds_CTRL=0;
 SigIn[0] = ReadADC12(0);// Input to A/D converter ()
 Data_new = SigIn[0]+0x8000;//0~FFFFで与える 
 n=n+1;

 // 最大値検出(トーナメント) //
 if(Data_new > TopDataV)//山登り法を用いた前後の計測値の比較
 {
  TopDataV = Data_new;
  TopDataf = n;
 }

/***  掃引終了判定* **/
 if(n>=4000)//4000点比較したら最大値を格納
 {
   count=count+1;
  Vmin[count]=TopDataV;//最大値格納
  fmin[count]=TopDataf;//最大値が何点目であったか格納

  dds_Int=1;
  delay_ns(50);
  n=0;
  dds_Int=0;

 }
}

 試したこと

4000点をいったん配列に格納しようと試みましたが、メモリが足りませんでした。

 補足情報(FW/ツールのバージョンなど)

dsPIC
C言語
MPLAB

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • ozwk

    2018/06/07 16:04

    一定波高の模擬入力パルスを入れているが、測定値がずれるのをなんとかしたいという話でしょうか? であれば、どのぐらいずれてますか?

    キャンセル

  • sotooki

    2018/06/07 18:12

    形は変わりませんが一定波高ではなく、計測によって上下左右にシフトする波形を計測しております。

    キャンセル

回答 3

checkベストアンサー

0

4000点の配列を、ローカル変数で確保せずに、グローバル変数(関数の外)で確保してみましょう
イマドキのPCでは数万点程度ではびくともしませんぜ


って、PICなの?それじゃ無理やねー
せいぜい十数点程度のバッファを持って、移動平均しながら最大値求める、ってテですかね

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/06/07 16:04

    PICなんです。
    移動平均ですか。
    候補として考えてみます。

    キャンセル

  • 2018/06/07 16:07

    メディアンフィルタとどちらが良いでしょうか

    キャンセル

  • 2018/06/07 16:07

    16点とか32点のバッファを持っといて、これをリングバッファにしといて、そいつでの移動平均を求めていく、ってテですね。
    移動平均の分ピーク値はずれるけど、その分補正してやればいいってことで。

    キャンセル

  • 2018/06/07 16:09

    PICだと計算量だいじょうぶ?<メディアンフィルタ
    って、最近のPICはでかなってるかw
    まあ、いろいろやってみればいいかと

    キャンセル

  • 2018/06/07 16:18

    簡単に移動平均の際のプログラムを教えていただけませんか。

    キャンセル

  • 2018/06/07 16:23

    16点分の計測値を積算しといてそれを1/16すれば、16点の平均値が取れますね
    これをリアルタイムでやればいいはなしです
    問題は、16点分積算した変数があって、次の値が入ってきたとき、単に加算すると17点分の値になってしまうので、一番古い点の値を引いてから次の値を加算して平均するってことですね
    このために、16点分のバッファを設けておいて、順繰りに計測値を入れていく必要があります
    #最新の値と16個前の値だけあればいい

    キャンセル

  • 2018/06/07 16:36

    ありがとうございます。
    プラスして、その最大値の点が4000点のうちの何点目かを把握するためにはどうしたらよいでしょうか。

    キャンセル

  • 2018/06/07 16:43

    SRAMが1KB(dsPIC30F2011,dsPIC30F2012)とか2KB(dsPIC30F3012,dsPIC30F3013)しかないPICマイコンで、12bit A/Dコンバータで値(2バイト)を収集するのですから、計測できる(SRAMに収納できる)のは512点か1024点が上限です。「4000点の計測データ」というところで破綻してませんか? また、割り込みのタイミングと、計測しているデータは同期しているのですか? 同期していなければ、"何点目で最大値であったか"が無秩序に変動すると思いますが、それで良いのですか?

    キャンセル

  • 2018/06/07 16:44

    16点分の平均だとすると、その真ん中8点目の値だと推定されるので、実際の結果から8を引けばいいかと。
    まあ、これが32点の移動平均だったら16引くという話になりますねー
    どんだけの幅の平均にするかってのは実際のデータでやってみて、いい感じのところを探してください
    乗っているノイズの幅以上の平均幅がないとちゃんとした値は出てこないでしょうし、あんまし平均幅を大きく取りすぎると、これまた誤差が大きくなります

    キャンセル

  • 2018/06/07 16:56

    割り込みは現状、適切であると考えます。
    何点目かもだいたいで大丈夫なので、アドバイスを参考に作成したいと思います。

    キャンセル

  • 2018/06/07 17:48 編集

    /*** Grobal変数定義 ***/
    unsigned int Data[16];
    unsigned int t=0;
    unsigned int cnt=0;
    unsigned long sum=0;
    unsigned int average_new=0;
    unsigned int average_old=0;


    /*** タイマ2 割り込み処理関数 ***/
    void __attribute__((__interrupt__, __shadow__))_T2Interrupt(void)
    {

    IFS0bits.T2IF=0;//割込みフラグクリア
    dds_CTRL=1;
    delay_ns(50);
    dds_CTRL=0;
    SigIn[0] = ReadADC12(0);// Input to A/D converter ()
    Data_new = SigIn[0]+0x8000;//0~FFFFで与える 0x8000=32768

    ーーーーーーーーーーーーーーーーーーーー変更点
    if(n=0)//初期設定
    {
    for(t=0;t<16;t++)
    {
    Data[t] = Data_new;
    }
    }
    n=n+1;

    if(cnt > 15){cnt=0;}

    sum = sum - Data[cnt];
    Data[cnt] = Data_new;
    sum = sum + Data[cnt];

    cnt = cnt +1;

    average_old = average_new;
    average_new = sum / 16;


    // 最大値検出(トーナメント) //
    if((average_new > average_old))
    {
    TopDataV = average_new;
    }
    ーーーーーーーーーーーーーーーーーーーーーーーーー
    /*** 掃引終了判定* **/
    if(n>=4000)
    {
    Green=1;
    count=count+1;
    Vmin[count]=TopDataV;
    fmin[count]=TopDataf;
    TopDataV=0;
    TopDataf=0;

    dds_Int=1;
    delay_ns(50);
    n=0;
    dds_Int=0;

    }
    }

    キャンセル

  • 2018/06/07 17:49

    変更してみたのですが計測が行えなくなってしまいました。
    何が問題なのでしょうか、、、。

    キャンセル

  • 2018/06/07 17:56

    > if(cnt < 16){cnt=0;}
    これまちがってますぜ

    何でもかんでも割り込みルーチンの中でさせずに初期化ぐらいはメインルーチンの中でさせようよw

    キャンセル

  • 2018/06/07 18:03

    分かりました。失礼いたしました。

    キャンセル

  • 2018/06/07 18:10

    それと、sum変数は変換結果(16ビット?)を16個積算してオーバーフローしないか注意ね。
    int とlong のビット幅を把握しておこう

    キャンセル

  • 2018/06/07 18:27

    オーバーフローは問題なさそうです。
    先ほどの指摘部分を改善してみましたがやはり動作が確認できません。
    あとは何が考えられますでしょうか。

    キャンセル

  • 2018/06/07 18:38

    最大値の時の位置を求めるなら、nの値をとってこないといかんのでは。

    開発環境はなんか知らんけど、デバッグ機能はないの?
    途中でプログラム止めて変数の内容をモニタできるとかできればいいんだけど。

    キャンセル

  • 2018/06/08 12:33

    この質問からはちと外れるけど、アドバイスをいくつか。
    タイマ割り込みルーチンの中では、時間のかかる処理をしないようにしよう
    ・A/Dの起動後、データを取るまでウェイトしてるけど、これをデータとった後、A/Dの起動をかけるようにすればウェイトはいらなくなります(次のタイマ割り込みでデータ取得)
    ・変数の初期化はメインルーチンで実行すれ。
    ・バッファの容量を16とか32みたいな数字にしてるのはちゃんと理由があります
    cnt &= ~0xf; // if がいらない
    average_new = sum >>4; // 割り算いらない

    キャンセル

0

こんにちは。

単に最大値ではダメなのでしょうか?
極大点を求めるわけでないのであれば、単なる最大値で良いはずです。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/06/07 18:08

    周波数掃引を行い共振点(グラフの最大地点)を求めたく、また最大値の共振周波数を求めたいので何点目かもわかることで周波数掃引の値から導き出しております。
    したがって、最大値および何点目かの情報を得たいです。

    キャンセル

  • 2018/06/07 18:37 編集

    ごめんなさい。何をおっしゃっているのか理解できません。
    周期的な波形が有るように見えませんので「周波数」との関係が見えません。

    ところで、ソースをみました。単なる最大値を求めるプログラムです。山登り法ではありません。
    http://www.gifu-nct.ac.jp/elec/deguchi/sotsuron/oguri/node15.html

    山登り法のWikipediaの記事は大嘘ですね。単なる最大値を求めるプログラムを提示しています。
    ↓意味不明ですし、判ってない人が記述してしまっているようです。
    「極値を探索するアルゴリズムのため、評価関数の最小値・最大値の探索手法としては不完全である。しかし実装が単純なため、最小値・最大値の探索としても、しばしば用いられる。」
    全く逆です。単なる最大値・最小値を求める方法では極値を求めることができないので、色々工夫して極値を求めるのですよ。

    山登り法はノイズに弱いのでローパスフィルタ(移動平均等)を用いてノイズ除去して処理することが多いです。y_waiwaiさんの回答も恐らくその意味でのノイズ除去の提案と思います。

    追加されたグラフのようなスパイクノイズに対しては、ローパスフィルタはあまり適切ではないです。スパイクノイズにはメディアンフィルタが有効です。通常は画像のような2次元データで用いますが、前後3点を使ったメディアンフィルタで対処してみるのも有りと思います。他にも適切なフィルタがありそうな気がしますが、把握していません。

    キャンセル

  • 2018/06/07 18:59

    回答が不適切で申し訳ありませんでした。
    メディアンも検討しておりますが、まずはプログラムが容易な移動平均を利用できればと思いプログラムを書き換えております。

    キャンセル

  • 2018/06/07 22:16

    ひょっとして、質問の最初の画像はスペクトルアナライザのスクリーンだったりしますか? スペクトルアナライザなら「周波数掃引」と関係が深いですが、高々2048バイトしかSRAMが無いdsPICにリアルタイムFFTは荷が重いような。

    キャンセル

  • 2018/06/07 22:32

    最初の画像はオシロスコープで得た波形になります。
    思った以上に複雑なフィルタを作れないと痛感しております。

    キャンセル

  • 2018/06/08 22:42

    > 山登り法のWikipediaの記事は大嘘
    https://ja.wikipedia.org/wiki/%E5%B1%B1%E7%99%BB%E3%82%8A%E6%B3%95
    こちら確認してみましたが、プログラムはきちんと山登り法のもので最大値を求めるようなことはありませんね。
    (そもそも最大値が求められれば苦労しない)
    > 全く逆です。単なる最大値・最小値を求める方法では極値を求めることができないので、色々工夫して極値を求めるのですよ。
    失礼ながら、1次元などの全数探索が容易な場合だけを考えていらっしゃいませんか?

    キャンセル

  • 2018/06/09 00:27 編集

    ikadzuchiさん、コメントありがとうございます。

    Wikipediaのコードを良く見てみました。確かに山登り法になっていますね。私の見方が甘かったようです。訂正ありがとうございます。(WikipediのプログラムのcurrentNodeはbestNodeの間違いですね。)

    > 失礼ながら、1次元などの全数探索が容易な場合だけを考えていらっしゃいませんか?

    全くその通りです。
    当たり前と思いますが、最大値を求める時は全数探索します。どうしても全数探索が困難な場合に限って極値で代用することもあるかもしれませんが、それは一般的ではないと思います。特に説明がない以上一般的なケースを想定するのが適切と思います。

    キャンセル

  • 2018/06/10 02:10

    なるほど。私は山登り法(など局所探索法)を使うのは次元数が多く全数探索が現実的でない場合が一般的かなと思ったもので。
    currentNote・bestNodeは確かにおかしいですね。

    キャンセル

0

とりあえず、異常値を捨てる という方針ではいかがでしょうか?
思い切って、以下のどれかで捨てては?
a. 2700 以上の値は捨てる。
b. 直前の値より300以上大きな値は捨てる。
c. 折角のDSPicなので、周波数成分を求めて、ハイパスフィルタする。

真剣に対策をとるなら、何が原因で大きく外れたように見える値が測定されるか分析したほうがいいです。
目立たないだけで小さい方にも異常値があるかもしれません。もともと全く測定できていないかもしれないです。(ランダムノイズを分析している)
素人でも思いつく外れる原因は、
a. 測定範囲オーバーフロー。
b. 計算途中でオーバー・アンダーフロー
c. データ取り込みのタイミングエラー
d. 電源などのノイズ。測定系の電圧が瞬間的に低くなっているなど。 

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/06/08 00:01

    ノイズの計測データはVBを用いてエクセル出力したのもので、VBのプログラムでa,bのような考え方でノイズを無くすことには成功しました。
    現在はdsPIC内でこのノイズを解消したいと思い、今回質問いたしました。
    データ取り込みのタイミングエラーは考えられると思います。ありがとうございます。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.49%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • C

    3800questions

    C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。