Купив по дешёвке несколько датчиков для Arduino, я стал придумывать куда их приспособить, и решил создать бортовой компьютер для водяной ракеты, который бы измерял параметры полёта, такие как: ускорение, скорость, высота подъёма, и мог бы в верхней точке траектории дать команду на выпуск парашюта. Также, желательно было иметь возможность после посадки ракеты прочитать «чёрный ящик» и узнать, с каким ускорением она взлетала, как быстро набрала максимальную скорость, и какой была высота взлёта. Если же парашют не раскроется — чёрный ящик помог бы узнать, с какой скоростью ракета врезается в землю 🙂
Контроллер
В качестве вычислительного устройства я выбрал Arduino UNO и Arduino Nano. Они оба обладают одинаковой вычислительной мощность, объёмом памяти, и количеством контактов. По сути, это одинаковые устройства, разница только в размере:
За счёт того, что начинка у обоих Arduino одинаковая, можно отлаживать схему и код на большой и удобной Arduino UNO, а, когда всё будет готово, собрать готовое устройство на Arduino Nano.
Датчик положения
Чтобы летающий бортовой компьютер мог понимать что с ним происходит, ему нужен гироскоп для измерения угловых скоростей, акселерометр для измерения линейных ускорений, барометр, для вычисления высоты, а также, трёхмерный компас для определения направления на север. Зачем покупать все эти датчики по отдельности, если можно купить один датчик, соединяющий в себе эти функции? Как раз таким является GY-88, который, по сути, состоит из датчиков ускорения, угловых скоростей, давления и магнитного поля, установленным на небольшой плате. Выглядит это так:
Регистрация данных
Для последующего за запуском анализа полетных данных на земле понадобится устройство хранения информации. Поскольку используемые контроллеры Arduino на базе ATmega328 и ATmega328P имеют всего 1024 байта энергонезависимой памяти, её нам явно не хватит. К счастью, можно без проблем приобрести кардридер, подключаемый к Arduino, и карту памяти, на которую будет вестись запись. Мало того, на самом деле, можно подключить карту памяти SD или MicroSD напрямую к ардуино, однако это будет несколько сложнее, так что воспользуемся дополнительным устройством, имеющим, кроме прочего, удобный разъём для вставки карты MicroSD.
Питание
Поскольку длинный кабель питания тянуть к ракете — не вариант, понадобится источник питания, который полетит вместе с ней. Для этого подойдёт любая батарея на 5 и более вольт. Хотя можно было взять 4 пальчиковые батарейки, я выбрал одну «крону», на 9 вольт, которая при таком напряжении обладает гораздо меньше массой, чем 3-4 пальчиковых батарейки. Встроенный в Arduino стабилизатор напряжения самостоятельно сможет снизить входные 9 вольт до необходимых ему пяти.
Для подключения, разумеется, понадобились клеммы:
Подключение GY-88
Для начала нужно убедиться что GY-88 работает. Для этого, во-первых, нужно правильно его подключить к Arduino, а, во-вторых, написать кусочек кода, который будет отвечать за чтение данных с датчиков, и отправку их на ПК (для контроля).
Мне достался Gy-88 среди входов которого был подписанный «3V3», что означает «вход на 3.3 вольта». Поскольку в других инструкциях по подключению такой вход не встречается, я нарисовал схему для своего случая:
Если у вас нет контакта «3V3», то у вас будет только 4 провода вместо 5.
Проверка работоспособности GY-88
1. Нужно собрать на макетной плате нарисованную выше схему
2. В редакторе кода Arduino IDE нужно установить библиотеку Wire:
2.1 Открываем менеджер библиотек:
2.2 Находим Wire и устанавливаем:
3. Создать скетч, и скопировать в него следующий код (взять его можно также по адресу: https://github.com/MihanEntalpo/flight-computer-app/blob/master/gy-88.ino):
#include <Wire.h> #include "BMP085.h" #include "I2Cdev.h" #include "MPU6050.h" #include "HMC5883L.h" HMC5883L compass; BMP085 pressure_m; MPU6050 accelgyro; int16_t ax, ay, az; int16_t gx, gy, gz; #define LED_PIN 13 bool blinkState = false; double dt = 0; long counter = 0; double t0 = -1; double millis_t = 0; void setup(){ //Включаем последовательный порт на максимальную скорость Serial.begin(230400); //Включаем протокол Wire Wire.begin(); TWBR = 24; Serial.println("Initializing I2C devices..."); //инициализируем акселерометр с гироскопом accelgyro.initialize(); accelgyro.setMasterClockSpeed(13); accelgyro.setI2CMasterModeEnabled(true); //инициализируем компас compass = HMC5883L(); setupHMC5883L(); //проверяем содеинение Serial.println("Testing device connections..."); Serial.println(accelgyro.testConnection() ? "MPU6050 connection successful" : "MPU6050 connection failed"); //калибруем датчик давления pressure_m.bmp085Calibration(); } void loop() { millis_t = millis(); //Считаем температуру (её знает датчик давления) float temperature = pressure_m.bmp085GetTemperature(); //MUST be called first //Считаем давление float pressure = pressure_m.bmp085GetPressure(); //Вычислим высоту над уровнем моря float altitude = pressure_m.calcAltitude(pressure); //Вычислим dt (интервал между замерами в миллисекундах) if (t0 < 0) { t0 = millis_t; } else { dt = millis_t - t0; t0 = millis_t; } //Интервал между замерами Serial.print("dt:"); Serial.print(dt / 1000, 3); //Сколько времени прошло с запуска? Serial.print(" tm:"); Serial.print(millis_t); //Температура, в градусах Цельсия Serial.print(" t:"); Serial.print(temperature, 2); //Давление, в паскалях Serial.print(" p:"); Serial.print(pressure, 0); //Высота над уровнем моря, в метрах Serial.print(" alt:"); Serial.print(altitude, 2); //Считаем значения с магнитометра MagnetometerScaled scaled = compass.ReadScaledAxis(); //Считаем значения с акселерометра и гироскопа accelgyro.getMotion6(&ax, &ay, &az, &gx, &gy, &gz); //Данные акселерометра: Serial.print(" ax:"); Serial.print(ax); Serial.print(" ay:"); Serial.print(ay); Serial.print(" az:"); Serial.print(az); //Данные гироскопа Serial.print(" wx:"); Serial.print(gx); Serial.print(" wy:"); Serial.print(gy); Serial.print(" wz:"); Serial.print(gz); //Данные магнитометра Serial.print(" cx:"); Serial.print(scaled.XAxis, 3); Serial.print(" cy:"); Serial.print(scaled.YAxis, 3); Serial.print(" cz:"); Serial.print(scaled.ZAxis, 3); Serial.println(); } //Настройка модуля HMC5883L (компаса) void setupHMC5883L(){ //Setup the HMC5883L, and check for errors int error; error = compass.SetScale(1.3); //Set the scale of the compass. if(error != 0) Serial.println(compass.GetErrorText(error)); //check if there is an error, and print if so error = compass.SetMeasurementMode(Measurement_Continuous); // Set the measurement mode to Continuous if(error != 0) Serial.println(compass.GetErrorText(error)); //check if there is an error, and print if so } |
4. Подключить Arduino к ПК
5. Загрузить скетч на Arduino
6. Открыть монитор порта и выбрать для него правильную скорость порта (230400, как в коде)
7. Внимательно посмотреть на выдачу данных, и убедиться что там что-то вроде этого:
dt:0.040 tm:3419.00 t:30.20 p:100612 alt:59.52 ax:624 ay:36 az:-16816 wx:-243 wy:-55 wz:-163 cx:24508.800 cy:7121.720 cz:3551.200 dt:0.041 tm:3460.00 t:30.20 p:100610 alt:59.69 ax:624 ay:16 az:-17012 wx:-261 wy:-89 wz:-163 cx:24508.800 cy:7121.720 cz:3551.200 dt:0.040 tm:3500.00 t:30.20 p:100605 alt:60.11 ax:456 ay:-48 az:-16808 wx:-245 wy:-66 wz:-169 cx:24508.800 cy:7121.720 cz:3551.200 dt:0.039 tm:3539.00 t:30.20 p:100606 alt:60.03 ax:436 ay:-88 az:-16800 wx:-251 wy:-40 wz:-147 cx:24508.800 cy:7121.720 cz:3551.200 dt:0.040 tm:3579.00 t:30.20 p:100608 alt:59.86 ax:620 ay:32 az:-16848 wx:-262 wy:-50 wz:-157 cx:24508.800 cy:7121.720 cz:3551.200 dt:0.041 tm:3620.00 t:30.20 p:100602 alt:60.36 ax:560 ay:28 az:-16912 wx:-272 wy:-28 wz:-154 cx:24508.800 cy:7121.720 cz:3551.200 dt:0.040 tm:3660.00 t:30.20 p:100609 alt:59.77 ax:652 ay:548 az:-16960 wx:119 wy:183 wz:-268 cx:24508.800 cy:7121.720 cz:3551.200 dt:0.040 tm:3700.00 t:30.20 p:100601 alt:60.44 ax:1108 ay:-604 az:-15632 wx:-449 wy:-135 wz:-11 cx:24508.800 cy:7121.720 cz:3551.200 dt:0.041 tm:3741.00 t:30.20 p:100612 alt:59.52 ax:796 ay:-112 az:-16296 wx:-357 wy:-523 wz:-773 cx:24508.800 cy:7121.720 cz:3551.200 dt:0.040 tm:3781.00 t:30.20 p:100611 alt:59.61 ax:64 ay:464 az:-17420 wx:-3037 wy:-4332 wz:-314 cx:24508.800 cy:7121.720 cz:3551.200 dt:0.040 tm:3821.00 t:30.20 p:100610 alt:59.69 ax:-164 ay:668 az:-16992 wx:-168 wy:-355 wz:1685 cx:24508.800 cy:7121.720 cz:3551.200
Подключение кардридера
Проверка работы кардридера
1. Нужно собрать на макетной плате нарисованную выше схему
2. В кардридер нужно вставить micro-SD карту, предварительно убедившись, что на ней ничего нет (на всякий случай)
3. В Arduino IDE нужно вписать следующий код (его также можно взять здесь: https://github.com/MihanEntalpo/flight-computer-app/blob/master/sdcard_write.ino):
#include <SPI.h> #include <SD.h> File myFile; void setup() { // Open serial communications and wait for port to open: Serial.begin(9600); while (!Serial) { ; // wait for serial port to connect. Needed for native USB port only } Serial.print("Initializing SD card..."); if (!SD.begin(4)) { Serial.println("initialization failed!"); return; } Serial.println("initialization done."); // open the file. note that only one file can be open at a time, // so you have to close this one before opening another. // myFile = SD.open("test.txt", FILE_WRITE); myFile = SD.open("test.txt", O_WRITE | O_CREAT); // if the file opened okay, write to it: if (myFile) { Serial.print("Writing to test.txt..."); myFile.println("testing 1, 2, 3."); // close the file: myFile.close(); Serial.println("done."); } else { // if the file didn't open, print an error: Serial.println("error opening test.txt"); } // re-open the file for reading: myFile = SD.open("test.txt"); if (myFile) { // read from the file until there's nothing else in it: while (myFile.available()) { Serial.write(myFile.read()); } // close the file: myFile.close(); Serial.println("file.txt successfuly writen"); } else { // if the file didn't open, print an error: Serial.println("error opening test.txt"); } } void loop() { // nothing happens after setup } |
4. Подключить Arduino к ПК
5. Загрузить скетч на Arduino
6. Открыть монитор порта и выбрать для него правильную скорость порта (230400, как в коде)
7. Посмотреть выдачу данных в мониторе, там должно быть написано
Initializing SD card... initialization done. file.txt successfuly writen
8. После этого, нужно вытащить SD-карту из кардридера, подключённого к Arduino и, вставив её в ПК или другое устройство, убедиться, что на карте есть файл «file.txt», в котором записана строка текста «testing sd card write»
Устройство бортового компьютера
Компоненты устройства:
1. Платформа Arduino
2. Модуль гироскопа + акселерометра + высотомера
3. Модуль записи на SD-карте + SD-карта
4. Батарея питания
5. Тумблер отключения питания
6. Кнопка для сброса высоты
7. Светодиоды-индикаторы взвода и зажигания
8. Реле для замыкания внешней цепи при зажигании
9. Несколько резисторов, указанных на схеме
Принципы работы устройства:
1. Устройство непрерывно замеряет текущую высоту с помощью датчика высоты, и вычисляет среднее значение за последние 16 измерений — скользящее среднее (это нужно для сглаживания колебаний измерения, так как они довольно сильно «скачут»).
2. Если нажата кнопка сброса высоты, текущее скользящее среднее высоты берётся в качестве начала отсчёта, и, в дальнейшем измеряется так называемая «высота от точки старта» — это высота за вычетом данного начала отсчёта. Соответственно, в месте, где кнопка была нажата, будет 0 этой высоты.
3. В ходе вычислений замеряется максимальная «высота от точки старта», после достижения которой 10 метров (например), устройство переходит в состояние «взвод», максимальная высота продолжает замеряться (любая измеренная высота больше максимальной — объявляется новой максимальной), при этом зажигается светодиод взвода
4. Если, находясь в состоянии «взвод» замерянная высота окажется меньше максимальной на 1 метр (например), устройство переходит в состояние «запуск», замыкая реле, и зажигая светодиод запуска. Светодиод взвода при этом гасится. Реле может выполнить любую работу, например, подать напряжение на сервопривод, выпускающий парашют.
5. Через 2 секунды после «запуска» реле размыкается, светодиод гасится, устройство переходит в режим «работа выполнена» и дальше ничего уже делать не будет.
Общая схема подключения компонентов:
Модули, необходимые для приложения:
Для работы программы я собрал несколько библиотек, либо не входящих в репозиторий Arduino, либо других версий, которые удалось заставить работать.
Библиотека BMP085:
BMP085.cpp
BMP085.h
Библиотека HMC5883L
HMC5883L.cpp
HMC5883L.h
Библиотека I2C
I2Cdev.cpp
I2Cdev.h
Библиотека MPU6050
MPU6050.cpp
MPU6050.h
Все эти модули можно найти вместе с основным файлом программы в репозитории.
Код приложения:
#include <Wire.h> #include "BMP085.h" #include "I2Cdev.h" #include "MPU6050.h" #include "HMC5883L.h" #include <SPI.h> #include <Wire.h> #include "BMP085.h" #include "I2Cdev.h" #include "MPU6050.h" #include "HMC5883L.h" #include <SPI.h> #include <SD.h> #define TRUE 1 #define FALSE 0 File myFile; HMC5883L compass; BMP085 pressure_m; MPU6050 accelgyro; //Контакт, куда подключено рэле #define EXEC_PIN 3 //Контакт, куда подключена кнопка "сброс высоты" #define RESET_ALT_PIN 6 //Контакт, куда подключен светодиод "взвод" (красный) #define ARMED_PIN 9 //Время в течении которого надо держать рэле замкнутым, в миллисекундах #define FIRE_TIME 6000 //Количество итераций для вычисления скользящего среднего высоты (лучше, чтобы было степенью двойки) #define RUN_ALT_NUM 16 //Контакт, куда подключена кнопка "положение X" #define POS_X_PIN 7 //Контакт, куда подключена кнопка "положение Y" #define POS_Y_PIN 8 //Контакт, куда подключена кнопка "положение Z" #define POS_Z_PIN 10 int16_t ax, ay, az; int16_t gx, gy, gz; bool blinkState = false; double dt = 0; long counter = 0; double t0 = -1; double millis_t = 0; double io_t = 0; double io_dt = 40; double fired_millis_t = 0; float base_altitude = 0; float run_alt[RUN_ALT_NUM]; int run_alt_index = 0; int last_run_alt_index = 0; int run_alt_count = 0; float run_alt_summ = 0; bool is_prepared = FALSE; bool is_armed = FALSE; bool is_fired = FALSE; bool is_led_armed = FALSE; bool is_led_fired = FALSE; bool is_done = FALSE; float arming_altitude = 10; float fire_minus_altitude = 1; float max_altitude = 0; char filename[] = "GY88_000.TXT"; void reset_running_alt(); void reset_alt(float current_alt); /** * Сгенерировать имя файла */ void generateFileName() { for (int i = 0; i< 1000; i++) { filename[5] = i/100 + '0'; filename[6] = (i%100)/10 + '0'; filename[7] = (i%10) + '0'; if (SD.exists(filename)) continue; Serial.print("File: "); Serial.println(filename); break; } } /** * Отладочная функция, вычисляет сумму скользящего среднего "по-настоящему" */ float debug_get_alt_summ() { float summ = 0; for (int i=0; i< run_alt_count; i++) { summ += run_alt[i]; } return summ; } void setup(){ //Настраиваем режимы работы для цифровых контактов pinMode(EXEC_PIN, OUTPUT); pinMode(RESET_ALT_PIN, INPUT); pinMode(ARMED_PIN, OUTPUT); //Сбрасываем значения для скользящего среднего по высоте reset_running_alt(); //Включаем последовательный порт на максимальную скорость Serial.begin(230400); //while (!Serial) { ; // wait for serial port to connect. Needed for native USB port only //} Serial.print("Initializing SD card..."); if (!SD.begin(4)) { Serial.println("initialization failed!"); return; } Serial.println("initialization done."); //Сгенерируем имя файла generateFileName(); myFile = SD.open(filename, O_WRITE | O_CREAT); //Включаем протокол Wire Wire.begin(); TWBR = 24; Serial.println("Initializing I2C devices..."); //инициализируем акселерометр с гироскопом accelgyro.initialize(); accelgyro.setMasterClockSpeed(13); accelgyro.setI2CMasterModeEnabled(true); //инициализируем компас compass = HMC5883L(); setupHMC5883L(); //проверяем содеинение Serial.println("Testing device connections..."); Serial.println(accelgyro.testConnection() ? "MPU6050 connection successful" : "MPU6050 connection failed"); //калибруем датчик давления pressure_m.bmp085Calibration(); } void loop() { millis_t = millis(); //Считаем температуру (её знает датчик давления) float temperature = pressure_m.bmp085GetTemperature(); //MUST be called first //Считаем давление float pressure = pressure_m.bmp085GetPressure(); //Вычислим высоту над уровнем моря float altitude = pressure_m.calcAltitude(pressure); float altitude_b = altitude - base_altitude; //Проведём вычисления для скользящего среднего по высоте run_alt[run_alt_index] = altitude_b; //run_alt_summ += altitude_b; if (run_alt_count == RUN_ALT_NUM) { last_run_alt_index = (run_alt_index == 0) ? RUN_ALT_NUM - 1 : run_alt_index - 1; //run_alt_summ -= run_alt[last_run_alt_index]; } else { run_alt_count +=1; } run_alt_index = (run_alt_index + 1 == RUN_ALT_NUM) ? 0 : run_alt_index + 1; //Временно вычисляем медленным методом run_alt_summ = debug_get_alt_summ(); float run_altitude = run_alt_summ / (float) run_alt_count; //Вычислим dt (интервал между замерами в миллисекундах) if (t0 < 0) { t0 = millis_t; } else { dt = millis_t - t0; t0 = millis_t; } if (run_altitude > max_altitude) { max_altitude = run_altitude; } if (millis_t - io_t > io_dt) { io_t = millis_t; if (digitalRead(RESET_ALT_PIN) == HIGH) { reset_alt(altitude); } if (!is_done) { if (!is_fired) { if (is_prepared) { if (!is_armed) { if (max_altitude > arming_altitude) { is_armed = TRUE; } } else { if (max_altitude - run_altitude > fire_minus_altitude) { is_armed = FALSE; is_fired = TRUE; fired_millis_t = millis_t; } } } } else { if (millis_t - fired_millis_t > FIRE_TIME) { is_fired = FALSE; is_done = TRUE; is_prepared = FALSE; } } } if (is_armed != is_led_armed) { is_led_armed = is_armed; digitalWrite(ARMED_PIN, is_armed ? HIGH : LOW); } if (is_fired != is_led_fired) { is_led_fired = is_fired; digitalWrite(EXEC_PIN, is_fired ? HIGH : LOW); } } //Интервал между замерами Serial.print("dt:"); Serial.print(dt / 1000, 3); myFile.print("dt:"); myFile.print(dt / 1000, 3); //Сколько времени прошло с запуска? Serial.print(" tm:"); Serial.print(millis_t); myFile.print(" tm:"); myFile.print(millis_t); //Температура, в градусах цельсия Serial.print(" t:"); Serial.print(temperature, 2); myFile.print(" t:"); myFile.print(temperature, 2); //Давление, в паскалях Serial.print(" p:"); Serial.print(pressure, 0); myFile.print(" p:"); myFile.print(pressure, 0); //Высота над уровнем моря, в метрах Serial.print(" alt:"); Serial.print(altitude, 2); myFile.print(" alt:"); myFile.print(altitude, 2); //Высота над базовым уровнем, в метрах Serial.print(" altb:"); Serial.print(altitude_b, 2); myFile.print(" altb:"); myFile.print(altitude_b, 2); //Высота над базовым уровнем, в метрах, скользящее среднее Serial.print(" altr:"); Serial.print(run_altitude, 2); myFile.print(" altr:"); myFile.print(run_altitude, 2); //Высота над базовым уровнем, в метрах, максимальная Serial.print(" Malt:"); Serial.print(max_altitude, 2); myFile.print(" Malt:"); myFile.print(max_altitude, 2); //Статусы: Serial.print(" P"); Serial.print(is_prepared ? "1" : "0"); myFile.print(" P"); myFile.print(is_prepared ? "1" : "0"); Serial.print(" A"); Serial.print(is_armed ? "1" : "0"); myFile.print(" A"); myFile.print(is_armed ? "1" : "0"); Serial.print(" F"); Serial.print(is_fired ? "1" : "0"); myFile.print(" F"); myFile.print(is_fired ? "1" : "0"); Serial.print(" D"); Serial.print(is_done ? "1" : "0"); myFile.print(" D"); myFile.print(is_done ? "1" : "0"); //Считаем значения с магнитометра MagnetometerScaled scaled = compass.ReadScaledAxis(); //Считаем значения с акселерометра и гироскопа accelgyro.getMotion6(&ax, &ay, &az, &gx, &gy, &gz); //Данные акселерометра: Serial.print(" ax:"); Serial.print(ax); Serial.print(" ay:"); Serial.print(ay); Serial.print(" az:"); Serial.print(az); myFile.print(" ax:"); myFile.print(ax); myFile.print(" ay:"); myFile.print(ay); myFile.print(" az:"); myFile.print(az); //Данные гироскопа Serial.print(" wx:"); Serial.print(gx); Serial.print(" wy:"); Serial.print(gy); Serial.print(" wz:"); Serial.print(gz); myFile.print(" wx:"); myFile.print(gx); myFile.print(" wy:"); myFile.print(gy); myFile.print(" wz:"); myFile.print(gz); //Данные магнитометра Serial.print(" cx:"); Serial.print(scaled.XAxis, 3); Serial.print(" cy:"); Serial.print(scaled.YAxis, 3); Serial.print(" cz:"); Serial.print(scaled.ZAxis, 3); Serial.println(); myFile.print(" cx:"); myFile.print(scaled.XAxis, 3); myFile.print(" cy:"); myFile.print(scaled.YAxis, 3); myFile.print(" cz:"); myFile.print(scaled.ZAxis, 3); myFile.println(); myFile.flush(); } //Настройка модуля HMC5883L (компаса) void setupHMC5883L(){ //Setup the HMC5883L, and check for errors int error; error = compass.SetScale(1.3); //Set the scale of the compass. if(error != 0) Serial.println(compass.GetErrorText(error)); //check if there is an error, and print if so error = compass.SetMeasurementMode(Measurement_Continuous); // Set the measurement mode to Continuous if(error != 0) Serial.println(compass.GetErrorText(error)); //check if there is an error, and print if so } /** * Сброс высоты и перевод в состояние готовности - выполняется по нажатию кнопки */ void reset_alt(float current_alt) { base_altitude = current_alt; is_prepared = TRUE; is_armed = FALSE; max_altitude = 0; reset_running_alt(); } /** * Сброс скользящего среднего для высоты */ void reset_running_alt() { run_alt_index = 0; run_alt_count = 0; run_alt_summ = 0; last_run_alt_index = 0; for (int i =0; i<RUN_ALT_NUM; i++) { run_alt[i]=0; } } |
Данные, выдаваемые программой, должны быть примерно такими:
dt:0.048 tm:5760.00 t:26.80 p:100898 alt:35.61 altb:35.61 altr:35.60 Malt:35.60 P0 A0 F0 D0 ax:-1084 ay:16 az:-15708 wx:-282 wy:66 wz:-68 cx:24508.800 cy:7121.720 cz:3551.200 dt:0.047 tm:5807.00 t:26.80 p:100898 alt:35.61 altb:35.61 altr:35.61 Malt:35.61 P0 A0 F0 D0 ax:-1132 ay:100 az:-16276 wx:-282 wy:213 wz:-81 cx:24508.800 cy:7121.720 cz:3551.200 dt:0.048 tm:5855.00 t:26.80 p:100898 alt:35.61 altb:35.61 altr:35.63 Malt:35.63 P0 A0 F0 D0 ax:-1016 ay:16 az:-15740 wx:-279 wy:149 wz:-78 cx:24508.800 cy:7121.720 cz:3551.200 dt:0.047 tm:5902.00 t:26.80 p:100895 alt:35.86 altb:35.86 altr:35.66 Malt:35.66 P0 A0 F0 D0 ax:-1152 ay:-48 az:-15596 wx:-247 wy:60 wz:-60 cx:24508.800 cy:7121.720 cz:3551.200 dt:0.047 tm:5949.00 t:26.80 p:100904 alt:35.11 altb:35.11 altr:35.64 Malt:35.66 P0 A0 F0 D0 ax:-1080 ay:-52 az:-15704 wx:-276 wy:54 wz:-61 cx:24508.800 cy:7121.720 cz:3551.200 dt:0.047 tm:5996.00 t:26.80 p:100902 alt:35.27 altb:35.27 altr:35.62 Malt:35.66 P0 A0 F0 D0 ax:-1152 ay:-44 az:-15652 wx:-290 wy:69 wz:-79 cx:24508.800 cy:7121.720 cz:3551.200 dt:0.047 tm:6043.00 t:26.80 p:100900 alt:35.44 altb:35.44 altr:35.60 Malt:35.66 P0 A0 F0 D0 ax:-1128 ay:-8 az:-15496 wx:-281 wy:37 wz:-70 cx:24508.800 cy:7121.720 cz:3551.200 dt:0.047 tm:6090.00 t:26.80 p:100897 alt:35.69 altb:35.69 altr:35.60 Malt:35.66 P0 A0 F0 D0 ax:-1048 ay:24 az:-15620 wx:-280 wy:59 wz:-75 cx:24508.800 cy:7121.720 cz:3551.200 dt:0.047 tm:6137.00 t:26.80 p:100902 alt:35.27 altb:35.27 altr:35.55 Malt:35.66 P0 A0 F0 D0 ax:-1164 ay:0 az:-15736 wx:-292 wy:73 wz:-69 cx:24508.800 cy:7121.720 cz:3551.200 dt:0.047 tm:6184.00 t:26.80 p:100899 alt:35.52 altb:35.52 altr:35.56 Malt:35.66 P0 A0 F0 D0 ax:-1088 ay:204 az:-15404 wx:-296 wy:-9 wz:-38 cx:24508.800 cy:7121.720 cz:3551.200 dt:0.048 tm:6232.00 t:26.80 p:100897 alt:35.69 altb:35.69 altr:35.57 Malt:35.66 P0 A0 F0 D0 ax:-1220 ay:-340 az:-15976 wx:-261 wy:32 wz:-106 cx:24508.800 cy:7121.720 cz:3551.200 dt:0.048 tm:6280.00 t:26.80 p:100902 alt:35.27 altb:35.27 altr:35.59 Malt:35.66 P0 A0 F0 D0 ax:-880 ay:144 az:-15828 wx:-289 wy:120 wz:-76 cx:24508.800 cy:7121.720 cz:3551.200 dt:0.047 tm:6327.00 t:26.80 p:100906 alt:34.94 altb:34.94 altr:35.53 Malt:35.66 P0 A0 F0 D0 ax:-1300 ay:52 az:-15292 wx:-274 wy:97 wz:-71 cx:24508.800 cy:7121.720 cz:3551.200 dt:0.047 tm:6374.00 t:26.80 p:100903 alt:35.19 altb:35.19 altr:35.50 Malt:35.66 P0 A0 F0 D0 ax:-1072 ay:-76 az:-15452 wx:-264 wy:55 wz:-74 cx:24508.800 cy:7121.720 cz:3551.200 dt:0.047 tm:6421.00 t:26.80 p:100905 alt:35.02 altb:35.02 altr:35.44 Malt:35.66 P0 A0 F0 D0 ax:-1040 ay:-4 az:-15844 wx:-297 wy:70 wz:-80 cx:24508.800 cy:7121.720 cz:3551.200 dt:0.047 tm:6468.00 t:26.80 p:100901 alt:35.36 altb:35.36 altr:35.40 Malt:35.66 P0 A0 F0 D0 ax:-1108 ay:64 az:-15644 wx:-294 wy:88 wz:-65 cx:24508.800 cy:7121.720 cz:3551.200 dt:0.047 tm:6515.00 t:26.80 p:100901 alt:35.36 altb:35.36 altr:35.39 Malt:35.66 P0 A0 F0 D0 ax:-1136 ay:80 az:-15628 wx:-287 wy:75 wz:-62 cx:24508.800 cy:7121.720 cz:3551.200 dt:0.047 tm:6562.00 t:26.80 p:100903 alt:35.19 altb:35.19 altr:35.36 Malt:35.66 P0 A0 F0 D0 ax:-1120 ay:-136 az:-15608 wx:-256 wy:37 wz:-76 cx:24508.800 cy:7121.720 cz:3551.200 dt:0.047 tm:6609.00 t:26.80 p:100895 alt:35.86 altb:35.86 altr:35.38 Malt:35.66 P0 A0 F0 D0 ax:-1032 ay:-8 az:-15600 wx:-283 wy:81 wz:-68 cx:24508.800 cy:7121.720 cz:3551.200 dt:0.048 tm:6657.00 t:26.80 p:100900 alt:35.44 altb:35.44 altr:35.35 Malt:35.66 P0 A0 F0 D0 ax:-1112 ay:-16 az:-15612 wx:-283 wy:59 wz:-77 cx:24508.800 cy:7121.720 cz:3551.200 dt:0.048 tm:6705.00 t:26.80 p:100901 alt:35.36 altb:35.36 altr:35.37 Malt:35.66 P0 A0 F0 D0 ax:-1084 ay:64 az:-15624 wx:-282 wy:73 wz:-66 cx:24508.800 cy:7121.720 cz:3551.200
Что означают данные:
dt — интервал времени в миллисекундах с прошлого замера
tm — количество миллисекунд, прошедшее с включения устройства
t — температура воздуха
p — давление воздуха
alt — высота по данным датчика-высотомера
altb — высота за вычетом «базовой» высоты, полученной при нажатии кнопки
altr — скользящее среднее по последним 16 замерам высота altb
Значения-флаги (могут принимать значение 0 или 1, что означает НЕТ и ДА)
P — Была ли нажата кнопка установки базовой высоты, т.е. подготовлено ли устройство? (Prepared)
A — Взведено ли устройство? Т.е. пройдена ли точка минимальной необходимой для взвода высоты (10 метров) (Armed)
F — Активировано ли реле? т.е. выполнено ли условие снижения не 1 метр относительно максимальной набранной высоты? (Fired)
D — Закончена ли работа устройства? Этот флаг включается, когда выключается флаг F. После этого устройство больше никаких функций выполнять не будет, и для работы с ним его потребуется либо перезагрузить либо выключить и включить снова.
При этом те же самые данные программа будет писать на SD-карту, вставленную в кардридер. При каждом запуске устройства будет создаваться новый файл со следующим порядковым номером.
Результаты сборки устройства:
Как сделать парашют для водяной ракеты:
Отлично, реле, замыкающее контакты при начале падения у нас есть. Есть много способов как использовать это для выброса парашюта, из которого выберем следующий:
1) Привяжем парашют к бутылке-ракете
2) Приложим его к головной части ракеты, и прижмём его куском другой пластиковой бутылки
3) Прижимающий кусок пластика соединим тонкой полимерной верёвочкой (например, капроновой)
4) на верёвочке намотаем пару оборотов нихромовой нити, которую подключим к батарейке, а в разрыв цепи вставим реле, которое как раз у нас уже готово к работе.
В результате получится: при срабатывании реле нихромовая нить нагревается, пережигает верёвочку, и воздушным потоком срывает пластиковую накладку и парашют вырывается на свободу.
Вот как это выглядит:
Ракета с упакованным парашютом:
А вот что получилось в результате пробного запуска (Видео снято на тапок):
Дальнейшие планы
1) Первая планируемая доработка — сохранять файлы на SD-карту в формате CSV с заголовком, чтобы облегчить дальнейшую работу с ними в программах типа Excel и Libreoffice. Сейчас приходится сначала очищать данные в текстовом редакторе.
2) Также, по аналогии с кнопкой сброса высоты, планируется добавить кнопки сброса линейных ускорений — для того, чтобы можно было, по очереди поворачивая устройство в плоскости параллельные горизонтальной и двум вертикальным, и нажимая соответствующие кнопки, «объяснить» устройству как соотносятся его оси и горизонтальная плоскость земли, и какие значения датчика линейных ускорений нужно считать нулевыми
3) Пока в устройстве толком нет интеграции данных по ускорениям, скоростям и координатам, единственный способ выяснить скорость — посчитать разницу между высотами и разделить её на интервал времени, за который происходит замер — так можно будет выяснить вертикальную скорость.
В дальнейшем планируется доработать программу для численного интегрирования показаний датчиков угловых скоростей и ускорений, и вычисления скорости и координат в трёхмерном пространстве.
4) Датчик ускорения по умолчанию настроен на максимальное ускорение в 2G. Судя по документации, его можно настроить на работу с максимальными ускорениями в 16G, но сделать этого пока не удалось. А стоило бы.
Здравствуйте, очень понравился ваш проект, хочу сделать что-то подобное. Но только на ардуино нано (из за габаритов). Есть ли у вас такой же проект, только для нано? Или просто инструкция, как сделать тоже самое на ардуино нано? Спасибо
Как ни странно, этот проект и так сделан на Arduino Nano. Обычное Arduino нарисовано в статье просто для иллюстраци
Контакты у Arduino nano такие же как у Arduino uno, поэтому сделать такую схему на nano — никаких проблем ( собственно она и была сделана на nano, что видно из фотографий)
Насколько точны показания продолжения в пространстве? Можно ли использовать несколько датчиков в одной системе?
Достаточно точны, но только по вертикали. Пока тестировал — подниманием руки с контролером вверх и вниз добился плавного изменения высоты по датчику. По другим осям так и не дошли руки сделать вычисления на базе акселерометра.
Думаю несколько можно использовать. Только зачем?