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

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

新規登録して質問してみよう
ただいま回答率
85.48%
C

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

Q&A

解決済

3回答

1688閲覧

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

sotooki

総合スコア15

C

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

0グッド

0クリップ

投稿2018/06/07 06:56

編集2018/06/07 09:11

前提・実現したいこと

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

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

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

イメージ説明

該当のソースコード

c言語

1void __attribute__((__interrupt__, __shadow__))_T2Interrupt(void)//割り込み処理関数 2{ 3 4 IFS0bits.T2IF=0;//割込みフラグクリア 5 dds_CTRL=1; 6 delay_ns(50); 7 dds_CTRL=0; 8 SigIn[0] = ReadADC12(0);// Input to A/D converter () 9 Data_new = SigIn[0]+0x8000;//0~FFFFで与える 10 n=n+1; 11 12 // 最大値検出(トーナメント) // 13 if(Data_new > TopDataV)//山登り法を用いた前後の計測値の比較 14 { 15 TopDataV = Data_new; 16 TopDataf = n; 17 } 18 19/*** 掃引終了判定* **/ 20 if(n>=4000)//4000点比較したら最大値を格納 21 { 22 count=count+1; 23 Vmin[count]=TopDataV;//最大値格納 24 fmin[count]=TopDataf;//最大値が何点目であったか格納 25 26 dds_Int=1; 27 delay_ns(50); 28 n=0; 29 dds_Int=0; 30 31 } 32}

試したこと

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

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

dsPIC
C言語
MPLAB

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ozwk

2018/06/07 07:04

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

2018/06/07 09:12

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

回答3

0

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

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

投稿2018/06/07 14:43

gm300

総合スコア580

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

sotooki

2018/06/07 15:01

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

0

こんにちは。

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

投稿2018/06/07 07:12

Chironian

総合スコア23272

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

sotooki

2018/06/07 09:08

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

2018/06/07 09:49 編集

ごめんなさい。何をおっしゃっているのか理解できません。 周期的な波形が有るように見えませんので「周波数」との関係が見えません。 ところで、ソースをみました。単なる最大値を求めるプログラムです。山登り法ではありません。 http://www.gifu-nct.ac.jp/elec/deguchi/sotsuron/oguri/node15.html 山登り法のWikipediaの記事は大嘘ですね。単なる最大値を求めるプログラムを提示しています。 ↓意味不明ですし、判ってない人が記述してしまっているようです。 「極値を探索するアルゴリズムのため、評価関数の最小値・最大値の探索手法としては不完全である。しかし実装が単純なため、最小値・最大値の探索としても、しばしば用いられる。」 全く逆です。単なる最大値・最小値を求める方法では極値を求めることができないので、色々工夫して極値を求めるのですよ。 山登り法はノイズに弱いのでローパスフィルタ(移動平均等)を用いてノイズ除去して処理することが多いです。y_waiwaiさんの回答も恐らくその意味でのノイズ除去の提案と思います。 追加されたグラフのようなスパイクノイズに対しては、ローパスフィルタはあまり適切ではないです。スパイクノイズにはメディアンフィルタが有効です。通常は画像のような2次元データで用いますが、前後3点を使ったメディアンフィルタで対処してみるのも有りと思います。他にも適切なフィルタがありそうな気がしますが、把握していません。
sotooki

2018/06/07 09:59

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

2018/06/07 13:16

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

2018/06/07 13:32

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

2018/06/08 13:42

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

2018/06/08 15:31 編集

ikadzuchiさん、コメントありがとうございます。 Wikipediaのコードを良く見てみました。確かに山登り法になっていますね。私の見方が甘かったようです。訂正ありがとうございます。(WikipediのプログラムのcurrentNodeはbestNodeの間違いですね。) > 失礼ながら、1次元などの全数探索が容易な場合だけを考えていらっしゃいませんか? 全くその通りです。 当たり前と思いますが、最大値を求める時は全数探索します。どうしても全数探索が困難な場合に限って極値で代用することもあるかもしれませんが、それは一般的ではないと思います。特に説明がない以上一般的なケースを想定するのが適切と思います。
ikadzuchi

2018/06/09 17:10

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

0

ベストアンサー

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


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

投稿2018/06/07 07:00

編集2018/06/07 07:03
y_waiwai

総合スコア87749

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

sotooki

2018/06/07 07:04

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

2018/06/07 07:07

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

2018/06/07 07:07

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

2018/06/07 07:09

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

2018/06/07 07:18

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

2018/06/07 07:23

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

2018/06/07 07:36

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

2018/06/07 07:43

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

2018/06/07 07:44

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

2018/06/07 07:56

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

2018/06/07 09:27 編集

/*** 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; } }
sotooki

2018/06/07 08:49

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

2018/06/07 08:56

> if(cnt < 16){cnt=0;} これまちがってますぜ 何でもかんでも割り込みルーチンの中でさせずに初期化ぐらいはメインルーチンの中でさせようよw
sotooki

2018/06/07 09:03

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

2018/06/07 09:10

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

2018/06/07 09:27

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

2018/06/07 09:38

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

2018/06/08 03:33

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問