実験・測定パート(Show)
それでは私は悪魔を錬成していきたいと思います
材料は水35L、炭素20㎏、アンモニア4L、石灰1.5㎏、リン800g、塩分250g、硝石100g、硫黄80g、フッ素7.5g、鉄5g、ケイ素3g、その他少量の15の元素ではなくこちら
ではさっそく始めていきたいと思います
基本的な方針
ジャイロセンサーで水平になっている状態を特定する
磁気センサーで真南の方向を特定する
ジャイロセンサーで測定しながら適当に傾けて、照度センサ―が一番明るくなったところが太陽の方向
そもそもジャイロセンサーってなんぞやって話を少しだけしておくと、X, Y, Zの3方向のの加速度(どれぐらい動いたか)と角速度(どれぐらい回転したか)を測定することができるセンサーです。詳細な原理や計算過程をここに記すには余白が狭すぎるのですが、この測定値をよしなに計算すると今回測定したい水平方向偏角と仰角を求めることができます。けっして理解できていないわけではなくてですね…ついでに今回のセンサーは磁気センサーも入っているので方角が分かります。ていうか手持ちのジャイロセンサーでいけると思ってたら磁気センサーが入ってなかったので買いました
方法・実装
簡単そうですね!では、実装して測定していきます
回路図はこう
ソースコードはこちら。わからん人は読み飛ばしても全く問題ないです。(ていうかじっくり見られたくはないのでわかる人も飛ばしてください)note折りたたむやつないのくそ不便じゃない?
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BNO055.h>
#include <utility/imumaths.h>
#include <LiquidCrystal.h>
// LCDの表示のための初期設定
const int rs = 12, en = 11, d4 = 5, d5 = 4, d6 = 3, d7 = 2;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);
// 9軸センサの初期設定
uint16_t BNO055_SAMPLERATE_DELAY_MS = 100;
Adafruit_BNO055 bno = Adafruit_BNO055(55, 0x28, &Wire);
// 磁気センサの平均用
bool isStartCalibrated = false;
bool isCalibrated = false;
#define forCalibration 10
int cnt = 0;
double aveNorth = 0;
double aveGyX = 0;
double Calibration = 0;
#define biasAngle 0.0
// 仰角・偏角の平均用
double elevateAve = -1;
double ang2southAve = -1;
#define forAveCnt 10
double elevateAveArray[forAveCnt] = {0};
double ang2southAveArray[forAveCnt] = {0};
int aveCnt = 0;
// 照度センサーの値を読みだす用
int analogPin = 16;
double lightMax = -1;
void setup(void)
{
// LCD画面表示の初期化
lcd.begin(16, 2);
// Print a message to the LCD.
lcd.print("hello, world!");
// 9軸センサの初期化
if (!bno.begin())
{
/* 失敗したらエラー吐いておわり */
while (1)
{
;
}
}
// ボタンの割り込みの設定
PCICR |= B00000100;
PCMSK2 |= B01000000;
// ちょっと待つ
delay(1000);
}
void loop(void)
{
// could add VECTOR_ACCELEROMETER, VECTOR_MAGNETOMETER,VECTOR_GRAVITY...
sensors_event_t orientationData, angVelocityData, linearAccelData, magnetometerData, accelerometerData, gravityData;
bno.getEvent(&orientationData, Adafruit_BNO055::VECTOR_EULER);
bno.getEvent(&angVelocityData, Adafruit_BNO055::VECTOR_GYROSCOPE);
bno.getEvent(&linearAccelData, Adafruit_BNO055::VECTOR_LINEARACCEL);
bno.getEvent(&magnetometerData, Adafruit_BNO055::VECTOR_MAGNETOMETER);
bno.getEvent(&accelerometerData, Adafruit_BNO055::VECTOR_ACCELEROMETER);
bno.getEvent(&gravityData, Adafruit_BNO055::VECTOR_GRAVITY);
// 北に対して何度の方向を方向を向いているかを計算する
double heading = (180.0 / PI) * atan2(magnetometerData.magnetic.y, magnetometerData.magnetic.x);
while (360.0 < heading) // ~360に収める
{
heading -= 360.0;
}
// センサーをキャリブレーションする
uint8_t system, gyro, accel, mag = 0;
bno.getCalibration(&system, &gyro, &accel, &mag);
// オイラー角を取得する
imu::Vector<3> euler = bno.getVector(Adafruit_BNO055::VECTOR_EULER);
// euler.x()を南から何度水平方向に傾いているかに変換できるようなキャリブレーション用の値を磁気センサのデータheadingから算出する
if (!isCalibrated && cnt < forCalibration && isStartCalibrated)
{
aveNorth += heading;
aveGyX += euler.x();
cnt++;
}
else if (!isCalibrated && isStartCalibrated)
{
isCalibrated = true;
aveNorth /= forCalibration;
aveGyX /= forCalibration;
Calibration = aveNorth - aveGyX;
lcd.clear();
}
// キャリブレーション値を引いて良い感じにする
double angle2north = (euler.x() + Calibration);
while (360.0 < angle2north) // ~360に収める
{
angle2north -= 360.0;
}
// 明るさを取得する
int bright = analogRead(analogPin);
// 1番明るいとき(太陽の方向を向いているとき)だけする処理
if (isCalibrated && (bright >= lightMax - 2))
{
if (bright > lightMax)
{
lightMax = bright;
}
double tmpAng = (angle2north - 180.0 - biasAngle);
double tmpEle = (euler.y());
elevateAve -= elevateAveArray[aveCnt];
ang2southAve -= ang2southAveArray[aveCnt];
elevateAveArray[aveCnt] = tmpEle;
ang2southAveArray[aveCnt] = tmpAng;
elevateAve += tmpEle;
ang2southAve += tmpAng;
aveCnt = (aveCnt + 1) % forAveCnt;
}
else if (!isCalibrated && (bright >= lightMax - 1))
{
if (bright > lightMax)
{
lightMax = bright;
}
if (heading < 180.1 && heading > 179.9)
{
Serial.println(euler.y());
double tmpEle = (euler.y());
elevateAve -= elevateAveArray[aveCnt];
elevateAveArray[aveCnt] = tmpEle;
elevateAve += tmpEle;
aveCnt = (aveCnt + 1) % forAveCnt;
}
}
// 画面に測定値を表示する
if (isCalibrated)
{
lcd.setCursor(0, 0);
lcd.print("S" + String(ang2southAve / forAveCnt) + " e" + String(elevateAve / forAveCnt));
lcd.setCursor(0, 1);
lcd.print("br" + String(bright) + " M" + String(int(lightMax)) + " C" + String(aveCnt));
}
else
{
lcd.setCursor(0, 0);
lcd.print("C"+ String(aveCnt) + " e" + String(elevateAve/ forAveCnt));
lcd.setCursor(0, 1);
lcd.print("br" + String(bright) + " N" + String(heading));
}
// ちょっと待つ
delay(BNO055_SAMPLERATE_DELAY_MS);
}
ISR(PCINT2_vect)
{
// ボタンが押されたらキャリブレーションスタート(なんか起動後しばらくは磁気センサーの値が安定しないので手動)
isStartCalibrated = true;
}
ここまで来るのにも紆余曲折ありましたし、中身についても色々触れたいところですが本当に余白と時間がないのでまた場を改めて書けたらどこかで書く
測定と結果
早速ですが問題発生!!屋外に出て照度センサーを太陽の方に向けると値がサチってしまって正確な太陽の方向を特定することができません。そこそこ指向性の高そうなやつを選んだのですが、全然無理でした!お外はとても明るい!そりゃそう
というわけで、
塩ビパイプ25cm ¥250×2本 = ¥500也
ヨドバシエクストリーム便最強!
なんとなくそれっぽい方向を測定できるようになりました
通報されないかかなり心配な見た目ですがとりあえずヨシ!
手動でぐるぐる回しながら表示している明るさの数値が最大になったところを目視で確認して、その時の偏角と仰角を手動でメモします
というのはさすがに愚かすぎるので上記のプログラムでは、明るさが最大になっていることは自動で判定して、その時の偏角と仰角を10回分メモしておいて平均してから表示するようにしました
結果①12時の仰角と偏角(2023年12月10日12:00)
偏角は南から西の方向に8.37°
仰角は水平方向から31.063°
結果②南中時刻と南中高度(2023年12月10日)
時刻は11:32(179.9<北からの向き<180.1でかつ明るさがMaxの時のみ数値が更新されるようにしたので、目視でもっとも数値が更新された時刻を確認)
高度は33.2625°(画面には最大10回分を平均しようとした数値が表示されているが8回しか更新されなかったので×10/8する)
これがどれぐらいちゃんと測れているのかは他の人のデータと突き合わせてからのお楽しみですね
課題、時間があればやりたかったこと
画面が見づらすぎる
16×2文字しか表示領域がないので、表示できる情報が限られていたのと安いやつだった太陽を測定したいのに日差しの下だと画面がほとんど見えない
スマホやPCと接続してデータを送信することもできるので、データで送ってパソコンでデータを処理したりする方が簡単だったと思う
ジンバル的なものを作る
ただの支える台を作るだけでもいいし、モーターの制御なんかもできるはずなので自動で明るい方向を探して太陽の位置を測定したり、太陽を追いかけて南中時刻・南中高度を自動で測定する的なこともできるはず(左手で棒を持ちながら画面を見たり撮影したりするのは大変)
北極と磁北の補正
たぶんめちゃくちゃズレますたしか7-8°、今回の測定値がおよそ7°なので100%の誤差です
期間中、大阪出張があったのに太陽の塔の前で測定できなかった
そんな感じで反省点はいろいろありますが、わたしの測定パートはこれにて終了、以下参考
参考一覧
使用したパーツ
この記事が気に入ったらサポートをしてみませんか?