====== GPS-телеметрия для любительского картинга ======
В любительском картинге всегда остро стоял вопрос объективного контроля тренировок. Профессионалам-то легко, с ними работает целая команда техников, инженеров, тренеров. Каждое движение руля и педалей фиксируется с миллиметровой точностью. Все ошибки чётко видны на телеметрии.
А что делать прокатчику, если хочется не просто «бомбить» трассу, а работать над улучшением результатов? Из точных данных — лишь распечатка по итогам заездов. Кто-то снимает свои круги на видео, кто-то пользуется фитнес-трекерами и друзьями с секундомерами.
{{ :projects:racing-gps-tracker:gps-imu-tracker.jpg |}}
Мы решили сделать свою — простую и бюджетную — телеметрию. За основу взяли GPS-модуль и скрестили его с акселерометром. Данные пишутся на SD-карту в формате, который напрямую импортируется на Google Maps и Яндекс.Карты.
* Платформы: Arduino Leonardo, Iskra Neo
* Языки программирования: Arduino (C++)
* Теги: GPS, GLONASS, MP3, IMU, телеметрия, автогонки
===== Видеообзор =====
{{youtube>m9vcIC1_z3k?large}}
===== Что это =====
==== Базовое устройство ====
Простое устройство, повторяющее мини-проект «[[/slot-box:gps-tracker|GPS-трекера]]».
Координаты и скорость определяются [[amp>product/troyka-gps-glonass?utm_source=proj&utm_campaign=slot-box-xl&utm_medium=wiki|приёмником спутниковой навигации]] и вместе с данными [[amp>product/troyka-accelerometer?utm_source=proj&utm_campaign=slot-box-xl&utm_medium=wiki|акселерометра]] записываются на [[amp>product/microsd-16gb?utm_source=proj&utm_campaign=slot-box-xl&utm_medium=wiki|microSD]] с помощью [[amp>product/troyka-sd?utm_source=proj&utm_campaign=slot-box-xl&utm_medium=wiki|картридера]]. Запись трека включается и выключается [[amp>product/troyka-led-button?utm_source=proj&utm_campaign=slot-box-xl&utm_medium=wiki|светодиодной кнопкой]].
{{ :projects:racing-gps-tracker:yandex.n.maps.jpg |}}
Данные сохраняются в электронную таблицу dataGPS.csv, формат которой соответствует требованиям сервиса «[[https://n.maps.yandex.ru/|Яндекс. Народная карта]]».
=== Что понадобится ===
- [[amp>product/iskra-neo?utm_source=man&utm_campaign=slot-box-xl&utm_medium=wiki | Iskra Neo]]
- [[amp>product/arduino-troyka-slot-shield?utm_source=man&utm_campaign=slot-box-xl&utm_medium=wiki | Troyka Slot Shield]]
- [[amp>product/troyka-gps-glonass?utm_source=man&utm_campaign=slot-box-xl&utm_medium=wiki | GPS/GLONASS приёмник (Troyka-модуль)]]
- [[amp>product/troyka-accelerometer?utm_source=proj&utm_campaign=slot-box-xl&utm_medium=wiki|акселерометр]]
- [[amp>product/troyka-led-button?utm_source=proj&utm_campaign=slot-box-xl&utm_medium=wiki|светодиодная кнопка]]
- [[amp>product/troyka-sd?utm_source=man&utm_campaign=slot-box-xl&utm_medium=wiki | SD картридер (Troyka-модуль)]]
- [[amp>product/microsd-16gb?utm_source=proj&utm_campaign=slot-box-xl&utm_medium=wiki|карта microSD]]
- [[amp>product/structor-slot-box?utm_source=proj&utm_campaign=slot-box-xl&utm_medium=wiki|Slot Box (#Структор)]]
=== Программный код ===
// библиотека для работы I²C
#include
// библиотека для работы с модулями IMU
#include
// библиотека для работы с устройствами по SPI
#include
// библиотека для работы с SD-картой
#include
// библиотека для работы с GPS устройством
#include
// создаём объект класса GPS и передаём в него объект Serial1
GPS gps(Serial1);
// создаём объект для работы с акселерометром
Accelerometer accel;
// пин светодиода
#define LED_PIN A0
// пин кнопки
#define BUTTON_PIN 13
// пин CS micro-sd карты
#define SD_CHIP_SELECT_PIN 9
// интервал времени записи данных на карту
#define INTERVAL 5000
// задаём размер массива для времени, даты, широты и долготы
#define MAX_SIZE_MASS 16
// массив для хранения текущего времени
char time[MAX_SIZE_MASS];
// состояние записи на карту памяти
bool stateRec = false;
// запоминаем текущее время
long startMillis = millis();
// счётчк записи
int counter = 0;
void setup()
{
// устанавливаем светодиод в режим выхода
pinMode(LED_PIN, OUTPUT);
// устанавливаем кнопку в режим входа
pinMode(BUTTON_PIN, INPUT_PULLUP);
// открываем последовательный порт для мониторинга действий в программе
Serial.begin(115200);
// ждём, пока не откроется монитор последовательного порта
// для того, чтобы отследить все события в программе
while (!Serial) {
}
Serial.print("Serial init OK\r\n");
// открываем Serial-соединение с GPS-модулем
Serial1.begin(115200);
// выводим информацию об инициализации в Serial-порт
Serial.println("Initializing SD card...");
// инициализируем SD-карту
while (!SD.begin(SD_CHIP_SELECT_PIN)) {
Serial.println("Card failed, or not present");
delay(1000);
}
// выводим информацию в Serial-порт
Serial.println("Card initialized");
// выводим сообщение о начале инициализации акселерометра
Serial.println("Initializing accelerometer...");
// инициализация акселерометра
accel.begin();
// устанавливаем чувствительность акселерометра
// 2g — по умолчанию, 4g, 8g
accel.setRange(RANGE_2G);
// выводим сообщение об удачной инициализации
Serial.println("Accelerometer initialized");
}
void loop()
{
// Фиксируем нажатие кнопки
if (digitalRead(BUTTON_PIN)) {
// меняем состояние «запись» / «не запись» на карту памяти
stateRec = !stateRec;
// меняем состояние светодиода индикации
digitalWrite(LED_PIN, stateRec);
}
// если пришли данные с gps-модуля
if (gps.available()) {
// считываем данные и парсим
gps.readParsing();
// считываем состояние GPS-модуля
switch(gps.getState()) {
// всё OK
case GPS_OK:
Serial.println("GPS is OK");
// если прошёл заданный интервал времени и запись данных включена
if (millis() - startMillis > INTERVAL && stateRec) {
// сохраняем данные GPS и акселерометра на карту памяти
saveSD();
// запоминаем текущее время
startMillis = millis();
}
break;
// ошибка данных
case GPS_ERROR_DATA:
Serial.println("GPS error data");
break;
// нет соединение со спутниками
case GPS_ERROR_SAT:
Serial.println("GPS no connect to satellites");
break;
}
}
}
// функция сохарение данных на карту памяти
void saveSD() {
File dataFile = SD.open("dataGPS.csv", FILE_WRITE);
// если файл существует и открылся
if (dataFile) {
// считывает текущее время
gps.getTime(time, MAX_SIZE_MASS);
// записываем время на карту памяти
// считываем и записываем координаты широты и долготы на карту памяти
dataFile.print(gps.getLatitudeBase10(), 6);
dataFile.print("\t");
dataFile.print(gps.getLongitudeBase10(), 6);
dataFile.print("\t");
dataFile.print(counter++);
dataFile.print("\t");
dataFile.print(gps.getSpeedKm());
dataFile.print("km/h");
dataFile.print("\t");
dataFile.print(accel.readAX());
dataFile.print("\t");
dataFile.print(accel.readAX());
dataFile.print("\t");
dataFile.println(time);
// записываем данные направления и величины ускорения в м/с² по оси X и Y
dataFile.close();
Serial.println("Save OK");
} else {
Serial.println("Error opening dataGPS.csv");
}
}
==== Устройство в герметичном корпусе ====
Главный слоган данного девайса: «Безопасность и надёжность». За основу взят «GPS-трекер», с небольшими изменениями:
* [[amp>product/krona-battery?utm_source=proj&utm_campaign=slot-box-xl&utm_medium=wiki|Крона]] заменена на [[amp>product/arduino-power-shield-li-ion?utm_source=proj&utm_campaign=slot-box-xl&utm_medium=wiki|Power Shield]]
* [[amp>product/structor-slot-box?utm_source=proj&utm_campaign=slot-box-xl&utm_medium=wiki|Slot Box (#Структор)]] заменён на [[amp>product/sealed-enclosure-115x90x80?utm_source=proj&utm_campaign=slot-box-xl&utm_medium=wiki|Герметичный корпус 115×90×80]]
* [[amp>product/troyka-led-button?utm_source=proj&utm_campaign=slot-box-xl&utm_medium=wiki|Кнопка со светодиодом (Troyka-модуль)]] заменена на [[amp>product/troyka-touch-sensor?utm_source=proj&utm_campaign=slot-box-xl&utm_medium=wiki|Сенсорная кнопка (Troyka-модуль)]] и [[amp>product/troyka-led-module?utm_source=proj&utm_campaign=slot-box-xl&utm_medium=wiki|Светодиод «Пиранья» (Troyka-модуль)]]
Благодаря данным изменениям устройство сможет работать и в снег, и в дождь.
=== Что понадобится ===
- [[amp>product/iskra-neo?utm_source=man&utm_campaign=slot-box-xl&utm_medium=wiki | Iskra Neo]]
- [[amp>product/arduino-troyka-slot-shield?utm_source=man&utm_campaign=slot-box-xl&utm_medium=wiki | Troyka Slot Shield]]
- [[amp>product/troyka-gps-glonass?utm_source=man&utm_campaign=slot-box-xl&utm_medium=wiki | GPS/GLONASS приёмник (Troyka-модуль)]]
- [[amp>product/troyka-accelerometer?utm_source=proj&utm_campaign=slot-box-xl&utm_medium=wiki|Акселерометр]]
- [[amp>product/troyka-sd?utm_source=man&utm_campaign=slot-box-xl&utm_medium=wiki | SD картридер (Troyka-модуль)]]
- [[amp>product/microsd-8gb?utm_source=proj&utm_campaign=slot-box-xl&utm_medium=wiki|Карта microSD]]
- [[amp>product/troyka-touch-sensor?utm_source=proj&utm_campaign=slot-box-xl&utm_medium=wiki|Сенсорная кнопка (Troyka-модуль)]]
- [[amp>product/troyka-led-module?utm_source=proj&utm_campaign=slot-box-xl&utm_medium=wiki|Светодиод «Пиранья» (Troyka-модуль)]]
- [[amp>product/sealed-enclosure-115x90x80?utm_source=proj&utm_campaign=slot-box-xl&utm_medium=wiki|Герметичный корпус 115×90×80]]
=== Программный код ===
// библиотека для работы I²C
#include
// библиотека для работы с модулями IMU
#include
// библиотека для работы с устройствами по SPI
#include
// библиотека для работы с SD-картой
#include
// библиотека для работы с GPS устройством
#include
// создаём объект класса GPS и передаём в него объект Serial1
GPS gps(Serial1);
// создаём объект для работы с акселерометром
Accelerometer accel;
// пин светодиода
#define LED_PIN A4
// пин кнопки
#define BUTTON_PIN 8
// пин CS micro-sd карты
#define SD_CHIP_SELECT_PIN 9
// интервал времени записи данных на карту
#define INTERVAL 5000
// задаём размер массива для времени, даты, широты и долготы
#define MAX_SIZE_MASS 16
// массив для хранения текущего времени
char time[MAX_SIZE_MASS];
// состояние записи на карту памяти
bool stateRec = false;
// запоминаем текущее время
long startMillis = millis();
// счётчк записи
int counter = 0;
void setup()
{
// устанавливаем светодиод в режим выхода
pinMode(LED_PIN, OUTPUT);
// устанавливаем кнопку в режим входа
pinMode(BUTTON_PIN, INPUT_PULLUP);
// открываем последовательный порт для мониторинга действий в программе
Serial.begin(115200);
// ждём, пока не откроется монитор последовательного порта
// для того, чтобы отследить все события в программе
while (!Serial) {
}
Serial.print("Serial init OK\r\n");
// открываем Serial-соединение с GPS-модулем
Serial1.begin(115200);
// выводим информацию об инициализации в Serial-порт
Serial.println("Initializing SD card...");
// инициализируем SD-карту
while (!SD.begin(SD_CHIP_SELECT_PIN)) {
Serial.println("Card failed, or not present");
delay(1000);
}
// выводим информацию в Serial-порт
Serial.println("Card initialized");
// выводим сообщение о начале инициализации акселерометра
Serial.println("Initializing accelerometer...");
// инициализация акселерометра
accel.begin();
// устанавливаем чувствительность акселерометра
// 2g — по умолчанию, 4g, 8g
accel.setRange(RANGE_2G);
// выводим сообщение об удачной инициализации
Serial.println("Accelerometer initialized");
}
void loop()
{
// Фиксируем нажатие кнопки
if (digitalRead(BUTTON_PIN)) {
// меняем состояние «запись» / «не запись» на карту памяти
stateRec = !stateRec;
// меняем состояние светодиода индикации
digitalWrite(LED_PIN, stateRec);
}
// если пришли данные с gps-модуля
if (gps.available()) {
// считываем данные и парсим
gps.readParsing();
// считываем состояние GPS-модуля
switch(gps.getState()) {
// всё OK
case GPS_OK:
Serial.println("GPS is OK");
// если прошёл заданный интервал времени и запись данных включена
if (millis() - startMillis > INTERVAL && stateRec) {
// сохраняем данные GPS и акселерометра на карту памяти
saveSD();
// запоминаем текущее время
startMillis = millis();
}
break;
// ошибка данных
case GPS_ERROR_DATA:
Serial.println("GPS error data");
break;
// нет соединение со спутниками
case GPS_ERROR_SAT:
Serial.println("GPS no connect to satellites");
break;
}
}
}
// функция сохарение данных на карту памяти
void saveSD() {
File dataFile = SD.open("dataGPS.csv", FILE_WRITE);
// если файл существует и открылся
if (dataFile) {
// считывает текущее время
gps.getTime(time, MAX_SIZE_MASS);
// записываем время на карту памяти
// считываем и записываем координаты широты и долготы на карту памяти
dataFile.print(gps.getLatitudeBase10(), 6);
dataFile.print("\t");
dataFile.print(gps.getLongitudeBase10(), 6);
dataFile.print("\t");
dataFile.print(counter++);
dataFile.print("\t");
dataFile.print(gps.getSpeedKm());
dataFile.print("km/h");
dataFile.print("\t");
dataFile.print(accel.readAX());
dataFile.print("\t");
dataFile.print(accel.readAX());
dataFile.print("\t");
dataFile.println(time);
// записываем данные направления и величины ускорения в м/с² по оси X и Y
dataFile.close();
Serial.println("Save OK");
} else {
Serial.println("Error opening dataGPS.csv");
}
}
==== Трекер с MP3-магнитолой ====
Мы пошли дальше и решили скреативить. Сделаем стильный музыкальный корпус устройства из #структора, детского железного конструктора и зальём всё это парой баллонов краски:
* [[amp>product/structor-slot-box?utm_source=proj&utm_campaign=slot-box-xl&utm_medium=wiki|Slot Box (#Структор)]] заменим на [[amp>product/structor-slot-box-xl?utm_source=proj&utm_campaign=slot-box-xl&utm_medium=wiki|Slot Box XL (#Структор)]]
* [[amp>product/krona-battery?utm_source=proj&utm_campaign=slot-box-xl&utm_medium=wiki|Крона]] заменим на [[amp>product/arduino-power-shield-li-ion?utm_source=proj&utm_campaign=slot-box-xl&utm_medium=wiki|Power Shield]]
* Добавим позитива с помощью [[amp>product/df-player-mp3-module?utm_source=man&utm_campaign=slot-box-xl&utm_medium=wiki | MP3-плеера]]
=== Что понадобится ===
- [[amp>product/iskra-neo?utm_source=man&utm_campaign=slot-box-xl&utm_medium=wiki | Iskra Neo]]
- [[amp>product/arduino-troyka-slot-shield?utm_source=man&utm_campaign=slot-box-xl&utm_medium=wiki | Troyka Slot Shield]]
- [[amp>product/troyka-gps-glonass?utm_source=man&utm_campaign=slot-box-xl&utm_medium=wiki | GPS/GLONASS приёмник (Troyka-модуль)]]
- [[amp>product/troyka-accelerometer?utm_source=proj&utm_campaign=slot-box-xl&utm_medium=wiki|Акселерометр]]
- [[amp>product/troyka-led-button?utm_source=proj&utm_campaign=slot-box-xl&utm_medium=wiki|Светодиодная кнопка]]
- [[amp>product/troyka-sd?utm_source=man&utm_campaign=slot-box-xl&utm_medium=wiki | SD картридер (Troyka-модуль)]]
- [[amp>product/df-player-mp3-module?utm_source=man&utm_campaign=slot-box-xl&utm_medium=wiki | MP3-плеер]]
- [[amp>product/microsd-8gb?utm_source=proj&utm_campaign=slot-box-xl&utm_medium=wiki|Карта microSD]] 2 шт.
- [[amp>product/stackable-pin-headers-with-icsp?utm_source=proj&utm_campaign=slot-box-xl&utm_medium=wiki|Контактные колодки Arduino с проставкой под ICSP]] 2 шт.
- [[amp>product/structor-slot-box-xl?utm_source=proj&utm_campaign=slot-box-xl&utm_medium=wiki|Slot Box XL (#Структор)]]
=== Программный код ===
// библиотека программного Serial-порта
#include
// библиотека для работы с mp3 плеером
#include
// библиотека для работы I²C
#include
// библиотека для работы с модулями IMU
#include
// библиотека для работы с устройствами по SPI
#include
// библиотека для работы с SD-картой
#include
// библиотека для работы с GPS устройством
#include
// создаём объект класса GPS и передаём в него объект Serial1
GPS gps(Serial1);
// создаём объект для работы с акселерометром
Accelerometer accel;
// инициализируем новый последовательный порт
// RX 10, TX 11
SoftwareSerial mp3Serial(10, 11);
// пин воспрозведение музыки
#define PLAY_PIN 7
// пин увеличение громкости
#define VOLUME_UP_PIN A5
// пин уменьшение громкости
#define VOLUME_DOWN_PIN A4
// пин светодиода
#define LED_PIN A0
// пин кнопки
#define BUTTON_PIN 13
// пин CS micro-sd карты
#define SD_CHIP_SELECT_PIN 9
// интервал времени записи данных на карту
#define INTERVAL 5000
// задаём размер массива для времени, даты, широты и долготы
#define MAX_SIZE_MASS 16
// массив для хранения текущего времени
char time[MAX_SIZE_MASS];
// состояние записи на карту памяти
bool stateRec = false;
// запоминаем текущее время
long startMillis = millis();
// счётчк записи
int counter = 0;
// уровень громкости плеера
int volume = 15;
void setup()
{
// устанавливаем светодиод в режим выхода
pinMode(LED_PIN, OUTPUT);
// устанавливаем кнопку в режим входа
pinMode(BUTTON_PIN, INPUT_PULLUP);
// открываем последовательный порт для мониторинга действий в программе
Serial.begin(115200);
// ждём, пока не откроется монитор последовательного порта
// для того, чтобы отследить все события в программе
while (!Serial) {
}
Serial.print("Serial init OK\r\n");
// открываем Serial-соединение с GPS-модулем
Serial1.begin(115200);
// выводим информацию об инициализации в Serial-порт
Serial.println("Initializing SD card...");
// инициализируем SD-карту
while (!SD.begin(SD_CHIP_SELECT_PIN)) {
Serial.println("Card failed, or not present");
delay(1000);
}
// выводим информацию в Serial-порт
Serial.println("Card initialized");
// выводим сообщение о начале инициализации акселерометра
Serial.println("Initializing accelerometer...");
// инициализация акселерометра
accel.begin();
// устанавливаем чувствительность акселерометра
// 2g — по умолчанию, 4g, 8g
accel.setRange(RANGE_2G);
// выводим сообщение об удачной инициализации
Serial.println("Accelerometer initialized");
// открываем программный Serial-порт
mp3Serial.begin(9600);
// выбор Serial для упрапвления mp3-плеером
mp3_set_serial(mp3Serial);
// установка громкости
mp3_set_volume(volume);
}
void loop()
{
// если нажата кнопка воспроизведение музыки
if (!digitalRead(PLAY_PIN)) {
mp3_play();
delay(100);
}
// если нажата кнопка увеличение громкости
if (!digitalRead(VOLUME_UP_PIN)) {
volume = volume + 5;
delay(50);
mp3_set_volume(volume);
}
// если нажата кнопка уменьшение громкости
if (!digitalRead(VOLUME_DOWN_PIN)) {
volume = volume - 5;
delay(50);
mp3_set_volume(volume);
}
// Фиксируем нажатие кнопки
if (digitalRead(BUTTON_PIN)) {
// меняем состояние «запись» / «не запись» на карту памяти
stateRec = !stateRec;
// меняем состояние светодиода индикации
digitalWrite(LED_PIN, stateRec);
}
// если пришли данные с gps-модуля
if (gps.available()) {
// считываем данные и парсим
gps.readParsing();
// считываем состояние GPS-модуля
switch(gps.getState()) {
// всё OK
case GPS_OK:
Serial.println("GPS is OK");
// если прошёл заданный интервал времени и запись данных включена
if (millis() - startMillis > INTERVAL && stateRec) {
// сохраняем данные GPS и акселерометра на карту памяти
saveSD();
// запоминаем текущее время
startMillis = millis();
}
break;
// ошибка данных
case GPS_ERROR_DATA:
Serial.println("GPS error data");
break;
// нет соединение со спутниками
case GPS_ERROR_SAT:
Serial.println("GPS no connect to satellites");
break;
}
}
}
// функция сохарение данных на карту памяти
void saveSD() {
File dataFile = SD.open("dataGPS.csv", FILE_WRITE);
// если файл существует и открылся
if (dataFile) {
// считывает текущее время
gps.getTime(time, MAX_SIZE_MASS);
// записываем время на карту памяти
// считываем и записываем координаты широты и долготы на карту памяти
dataFile.print(gps.getLatitudeBase10(), 6);
dataFile.print("\t");
dataFile.print(gps.getLongitudeBase10(), 6);
dataFile.print("\t");
dataFile.print(counter++);
dataFile.print("\t");
dataFile.print(gps.getSpeedKm());
dataFile.print("km/h");
dataFile.print("\t");
dataFile.print(accel.readAX());
dataFile.print("\t");
dataFile.print(accel.readAX());
dataFile.print("\t");
dataFile.println(time);
// записываем данные направления и величины ускорения в м/с² по оси X и Y
dataFile.close();
Serial.println("Save OK");
} else {
Serial.println("Error opening dataGPS.csv");
}
}
===== Что дальше =====
Добавьте к устройству [[amp>product/troyka-imu-10-dof?utm_source=proj&utm_campaign=slot-box-xl&utm_medium=wiki|IMU-модуль на 10 степеней свободы]] и соберите настоящий «чёрный ящик» для легкомоторного самолёта.
Подключите [[amp>product/arduino-gprs-shield?utm_source=proj&utm_campaign=slot-box-xl&utm_medium=wiki|GPRS Shield]] и соберите автономную сигнализацию для мотоцикла или велосипеда.
Черпайте вдохновение для новых устройств в подборке [[slot-box:start|мини-проектов для Arduino и Iskra Neo]].