質問するログイン新規登録
C

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

Arduino

Arduinoは、AVRマイコン、単純なI/O(入出力)ポートを備えた基板、C言語を元としたArduinoのプログラム言語と、それを実装した統合開発環境から構成されたシステムです。

Q&A

解決済

2回答

473閲覧

Arduinoでの時系列データ形式で静電容量測定した時の挙動について

toooomuuuu

総合スコア3

C

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

Arduino

Arduinoは、AVRマイコン、単純なI/O(入出力)ポートを備えた基板、C言語を元としたArduinoのプログラム言語と、それを実装した統合開発環境から構成されたシステムです。

0グッド

0クリップ

投稿2025/09/18 10:22

編集2025/09/19 08:13

0

0

実現したいこと

1番初めに出てくる静電容量の値が明らかにおかしいのでどうにかしたい

前提

arduinoを使って下記のソースコードで静電容量の値を時系列データとして出力してるのですが、1番初めに出てくる値が2番目以降に吐き出される値に対して異常に大きい値になるので出力されないようにしたい。

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

測定結果の例
2.770e-12
1.771e-12
1.634e-12
1.688e-12
1.826e-12
1.798e-12

該当のソースコード

const int OUT_PIN = A2; const int IN_PIN = A0; //Capacitance between IN_PIN and Ground //Stray capacitance is always present. Extra capacitance can be added to //allow higher capacitance to be measured. const float IN_STRAY_CAP_TO_GND = 24.48; //initially this was 30.00 const float IN_EXTRA_CAP_TO_GND = 0.0; const float IN_CAP_TO_GND = IN_STRAY_CAP_TO_GND + IN_EXTRA_CAP_TO_GND; const int MAX_ADC_VALUE = 1023; void setup() { pinMode(OUT_PIN, OUTPUT); //digitalWrite(OUT_PIN, LOW); //This is the default state for outputs pinMode(IN_PIN, OUTPUT); //digitalWrite(IN_PIN, LOW); Serial.begin(115200); } void loop() { //Capacitor under test between OUT_PIN and IN_PIN //Rising high edge on OUT_PIN pinMode(IN_PIN, INPUT); digitalWrite(OUT_PIN, HIGH); int val = analogRead(IN_PIN); //Clear everything for next measurement digitalWrite(OUT_PIN, LOW); pinMode(IN_PIN, OUTPUT); //Calculate and print result delay(500); float capacitance = (float)val * IN_CAP_TO_GND / (float)(MAX_ADC_VALUE - val); Serial.print(capacitance, 3); Serial.print(F("e-12")); Serial.println(F(" ")); while (millis() % 500 != 0) ; }

試したこと

googleで調べたがわからなかった

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

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

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

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

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

YAmaGNZ

2025/09/19 05:23

C#ではないのでC#のタグは外したほうがいいかと思います。
guest

回答2

0

まぁ、「1回目は値が暴れるから出力しない」でも確かに用は足りるかも知れませんが、「なんで1回目は値が違うのだろう?」という検討はなされないのでしょうかね。

Arduino

1void setup() { 2 pinMode(OUT_PIN, OUTPUT); 3 //digitalWrite(OUT_PIN, LOW); //This is the default state for outputs 4 pinMode(IN_PIN, OUTPUT); 5 //digitalWrite(IN_PIN, LOW); 6 analogRead(IN_PIN); // 追加 7 pinMode(IN_PIN, OUTPUT); //追加 8 9 Serial.begin(115200); 10}

loop()の方はもとのままで、これでわたしのてもとでは「1回目」の値の暴れが抑えられます。

私の原因推定は、

  • マイコンの多入力ADCって、大抵ADC本体はひとつで入力をアナログスイッチで切り替えて使うもの。で、アナログスイッチがオフになった状態って電荷の移動ができない、つまりコンデンサになっているのでいきなり電位差があるところに繋がれると測定値に至るまで(多少)時間が掛かる。質問のプログラムではリセット後一度もADCに切り替えられておらず、1回目の測定で電位差が吸収されるまでの電圧が見えてしまう

ということですけれどいかが。

そういう意味では、ADCはどこかのチャンネルに固定し、切り替えをさせないほうが応答がよかったりしないかしら...と思ってやってみたけどあんまり変わった気がしませんね。大きな変動があるとステップ応答で差が見えるかしら?

Arduino

1const int OUT_PIN = A2; 2const int VIN = A1; //電圧測定専用 A0とA1はショートする 3const int IN_PIN = A0; 4 5//Capacitance between IN_PIN and Ground 6//Stray capacitance is always present. Extra capacitance can be added to 7//allow higher capacitance to be measured. 8const float IN_STRAY_CAP_TO_GND = 24.48; //initially this was 30.00 9const float IN_EXTRA_CAP_TO_GND = 0.0; 10const float IN_CAP_TO_GND = IN_STRAY_CAP_TO_GND + IN_EXTRA_CAP_TO_GND; 11const int MAX_ADC_VALUE = 1023; 12 13void setup() { 14 pinMode(OUT_PIN, OUTPUT); 15 //digitalWrite(OUT_PIN, LOW); //This is the default state for outputs 16 pinMode(IN_PIN, OUTPUT); 17 //digitalWrite(IN_PIN, LOW); 18 analogRead(VIN); // ADCに切り替える 19 20 Serial.begin(115200); 21} 22 23void loop() { 24 //Capacitor under test between OUT_PIN and IN_PIN 25 //Rising high edge on OUT_PIN 26 pinMode(IN_PIN, INPUT); 27 digitalWrite(OUT_PIN, HIGH); 28 int val = analogRead(VIN); 29 30 //Clear everything for next measurement 31 digitalWrite(OUT_PIN, LOW); 32 pinMode(IN_PIN, OUTPUT); 33 34 //Calculate and print result 35 float capacitance = (float)val * IN_CAP_TO_GND / (float)(MAX_ADC_VALUE - val); 36 37 Serial.print(capacitance, 3); 38 Serial.print(F("e-12")); 39 Serial.println(F(" ")); 40 41}

投稿2025/09/19 12:41

thkana

総合スコア7769

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

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

toooomuuuu

2025/09/19 14:17

詳しい解説ありがとうございます。 値が暴れるの原因まで考えていませんでした。教えていただいた方法でも一度試してみます。
thkana

2025/09/19 23:53

なんだか判らないけど最初の1回はおかしいから飛ばそう...では2回目以降が「大丈夫」だとどうして言えるのでしょう?見た目がそれっぽいから? > 値が暴れるの原因まで考えていませんでした。 いやいや、事象を見つけてしまった以上、原因・理由を考えるのが最初でしょう。理由によっては測定そのものが危機に陥ったりします。 今回だって、「急に大きく値が変わったら直後の値は信頼できるのだろうか?」という疑問も見えてくるわけです。使用状況や必要な精度から「問題ない」という結論ももちろんありなのですが、結論を出せるのは問題を把握したから。「見なかったふり」を解決にすべきではありません。 余談ですが、AVRの328PBにはATMELの静電容量式タッチセンサQTouchが載ったのではありませんでしたっけ。もしかして容量測定にも使えたりしないかしら。
guest

0

ベストアンサー

ループの最初の実行であるかどうかを判定してスキップするなら、以下のようにフラグを使うのはどうでしょう。
もし、何回か飛ばしたいのであれば、カウンタにして減算するとかでしょうね。

必要なところだけ書きます。

arduino

1 2bool isFirst = true; // 初回判定するためのフラグを宣言 3 4void setup() { 5} 6 7void loop() { 8 9 //データを取得する処理をここに書く。 10 11 if (isFirst) { 12 // 初回なのでフラグを落す 13 isFirst = false; 14 15 // ここでは何もする必要なし 16 } else { 17 // 初回以外はここに来るので、以下のところに結果の計算処理や表示しょりを実行する。 18 } 19}

投稿2025/09/18 10:33

編集2025/09/18 13:37
TakaiY

総合スコア14641

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

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

toooomuuuu

2025/09/18 10:40 編集

ご回答ありがとうございます。 ちなみに上記のif文の中で"初回なのでフラグを落す"と"何もしない。"の中は具体的にどのようなプログラムになりますか。後者はスキップとかのような気がするのですが。
TakaiY

2025/09/18 10:52

フラグを落すのところは書いてあるとおり、isFirstをfalseにすることです。 その他は何もしないので何もなしでいいです。 その代りに、else節のところで出力をします。 そういえば、ループのディレイは、このif文の外に入れなければなりませんね。2箇所ありますから必要なら前後にいれればよいかと。(なぜ2箇所に入っているかはわかりません)
toooomuuuu

2025/09/18 12:31

ご回答ありがとうございます。 delayですが上から1つ目のものは間違って入れてしまったもので要りません。なので、if文の後に入れるということでいいですか。
TakaiY

2025/09/18 13:34

loop関数のどこか必ず通る場所に1つ入っていればいいと思いますよ。
toooomuuuu

2025/09/19 03:32

ありがとうございます。無事に最初の実行をスキップして問題が解決しました。追加でお聞きしたいのですが、Arduinoでの時系列データを扱ってると値の揺らぎが気になります。その解決策としては、下記のページの"ローパス処理"等があるようですが、これ以外に良い方法はご存じですか。 https://jumbleat.com/2016/08/17/reduce_fluctuations/
kikukiku

2025/09/19 04:43

上記のURLには、ローパス処理以外にも提案されています。 それではダメなのですか? なぜローパス処理ではダメなのかの説明が必要と思います。 また、この件は別質問になると思うので、 もう1つ質問を新規にたてた方が良いです。
TakaiY

2025/09/19 04:43

詳しくないのよくわかりません。 どのようなフィルタを掛けるのかは、取得するデータの性質によるでしょう。参照のサイトでは高周波成分が不要ということでローパス(=ハイカット)フィルタを掛けるということのようですが、最後の方式はなんでそれがいいのかわかりません。 移動平均とる方がレスポンスよさそうですけど。 いずれにしても、フィルタ掛けるなら、どの方式も各種パラメータをいろいろ調整して最適な値を探っていく必要はあります。
kikukiku

2025/09/19 04:50

>どのようなフィルタを掛けるのかは、取得するデータの性質によるでしょう。 その通りだと思います。 フィルタをかけるということは、大雑把に言うと平均化することになるので、 逆に言うと、値の急激な変化に対応できなくなります。 メリット、デメリットを良く把握して 運用に合う方式を選択してください。
fana

2025/09/19 06:48 編集

そのリンク先に書かれている X = X*α + 最新観測値*(1.0 - α) みたいなやつ,意図通りの効果(時系列データへの重み具合)になるのには開始からしばらくたつ必要がある点に注意が必要かも? (「最初のデータだけが変」という話が,これ使うと「開始付近のデータがちょっと変かも」みたいな話になり得る) 測定開始らへんのデータもある程度救いたいなら,αの値を0から開始して→ある値(例えば0.9)まで徐々に上げていくみたいなことをするとか?
fana

2025/09/19 06:56 編集

それはそれとして(Arduinoとか知らんのですが) 「loop()内で初回フラグを用いる」のかわりに,「setup()というところで1回データ取得する」というのではダメなのだろうか?
TakaiY

2025/09/19 08:01

> 「setup()というところで1回データ取得する」というのではダメなのだろうか? それでもいい場合もあるでしょうしわかりやすいですが、測定対象によってはインターバルが重要かもしれず、その場合はループの中に入れたほうが制御しやすいかもしれません。
kikukiku

2025/09/19 08:12

>X = X*α + 最新観測値*(1.0 - α) 上記説明しやすいように 下記とした場合、  X = X + 最新観測値*(1/5) 上記を5回目回すと、  X=観測値5個分の和/5 と5回分の平均値と同値になります。 なので、大雑把に説明すると平均値と同じようなことをしています。 このような平均値処理を行うのであれば、 初回の大きな観測値も平均化され、目立たなくなると思うため、 初回処理は必要なくなることもあり得ます。 このあたりは、やはり、どんな性質のデータを観測し、 どんな結果を得たいかを明確にしないと、結論がでない領域になるかと。 >「setup()というところで1回データ取得する」というのではダメなのだろうか? ありだと思います。 ただし、setupのところ、500msの遅延を最後に入れた方が良いかも。
fana

2025/09/19 08:47 編集

> 平均値と同じようなこと 要は「新しい観測値ほど重みが大きいような加重平均」ですよね. {最新の観測値を v[t],1つ前の観測値を v[t-1],2つ前の観測値を v[t-2] ,… }とするとき, 最新の「ローパス値X」に含まれる各観測値の重みというのが v[t - s] に対して (1-α)*(α^s) になる,っていう話で,(α<1.0 なので)過去のデータほど重みが減衰していくっていう. で,こういうのをやろうとすると 「よーいドンで観測を始める際の「ローパス値X」の初期値はどうするの?」っていうのが問題で, リンク先みたく適当に「初期値は 0 」とかすると開始後しばらくはめちゃくちゃ小さい結果になってしまうし, かといって「そしたら初回だけは X = 初回観測値 ってことにするぜ!」とかやると,今度はしばらくの間,初回観測値の重みが不当にでかい状態になっちゃう. (初回の観測値「だけ」を捨てる,っていう方向の話を考えている様子と見受けたので,そしたらこういうのを使う際にはこの辺に注意が必要かもよ? っていう) --- > これ以外に… なんでしょうね,最新のN個のデータに適当な自由度の曲線(二次関数とか)を当てはめるとか? (「カルマンフィルタ」とかいうのを聞いたことはあるけど「名前しか知らない」レベルなので,こういう場合に良い方法たり得るのかはわからない.)
toooomuuuu

2025/09/19 08:41

皆さん色々ありがとうございます。 ちなみに自分が扱っているデータとしては、時々刻々と変化する平行平板状のキャパシタ構造の静電容量を測定して、センサーのようなものを作りたいと思ってます。そうなると、値の急激な変化が拾えなくなりそうなので平均化は望ましくないのですかね。
fana

2025/09/19 09:06 編集

> 値の急激な変化が拾えなくなりそうなので平均化は望ましくないのですかね その変化をどのくらい遅れて検知してもよいのか? という話と, 測定値の変動をどのくらいなら許容してよいのか? という話との兼ね合い? でしょうか? 前者は測定インターバルを短くしさえすれば改善していくだろうけど,それが後者の面にどう影響を与えるのかはやってみないとわからないし. 処理パラメタ値(上記のローパスであればαの値)を外部入力で増減できるとかならば「そこは使う人に任せるぜ!(いい感じに調整してね)」みたいな方向は無いのかな?
TakaiY

2025/09/19 09:11 編集

そうですね。書きましたし他の方も言及していますが、それが何のデータなのか、どのように利用するのかによって、どのように処理すべきなのかが変わります。 「時系列データを扱ってると値の揺らぎが**気になります**」とある「気になる」の本質を捉えて対処する必要があります。 まずは、データを取得してみて、検出したいものを測定して、必要な結果を得るにはどのように処理したらいいか考えてみれば、その気になるの正体がわかるでしょう。 また、何をどのようなタイミングで検出したくて、実際に得られるデータがどのようなものなのかによって、処理の方法は変わります。 そのセンサーがリアルタイムのゲームのコントローラなのか、寝ている時の体圧の分布を計測するものなのか。
toooomuuuu

2025/09/19 09:32

一番近いのはリアルタイムのゲームのコントローラぐらいの速度で信号を拾いたいと思ってます。モーションセンサーみたいなものですね。
TakaiY

2025/09/19 09:42

であれば、書いたとおり、まずは、入力に対してどのようなデータがどのようなタイミングで出力されるのかを、まずは未加工のデータで確認して、何を検知すればいいのか検討するのがいいでしょう。
toooomuuuu

2025/09/19 09:49 編集

ご助言ありがとうございます。 おかげさまで一通りの測定環境は立ち上げられたと思いますので、一度測定をやっていきたいと思います。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.30%

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

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

質問する

関連した質問