НОВОСТИ [Из песочницы] Сквозь тернии к звездам: делаем утройство для наведения лазерной указки на любой небесный объект

Alvaros
Онлайн
Регистрация
14.05.16
Сообщения
21.452
Реакции
101
Репутация
204
Привет, Хабр!

Решил показать свою небольшую самоделку, которая работает примерно так:

bvyistbmdjpnx1eefu9ak1aedom.jpeg


Если КДПВ сделала свое дело — тогда добро пожаловать под кат :)

Небольшой спойлер
Я старался писать так, чтобы было максимально понятно всем


Аппаратная часть


Итак, для создания минимально работающего прототипа нам понадобятся:

  1. Кусок доски или чего угодно, на чем можно закрепить все компоненты
  2. Почти любой микроконтроллер. Я взял ардуино уно как самый простой вариант
  3. GPS модуль (с него мы берем дату, время и координаты). Теоретически можно вместо него взять модуль часов реального времени, но тогда ваши координаты вам придется вводить вручную и «из коробки» устройство не заработает, но зато время холодного старта сильно сократится
  4. Два сервопривода, или еще лучше два шаговых двигателя
  5. Лазерная указка
  6. Разная мелочь: паяльник, термоклей, макетная плата, провода, кнопка, конденсатор, ну и прямые руки.

По сборке особых хитростей нет: просто закрепить все компоненты на основании термоклеем.
Только надо постараться чтобы оси сервоприводов и лазера были максимально перпендикулярны друг другу, это уменьшит погрешность наведения.

Подключение


Тут тоже ничего сложного:

  • GPS подключается по uart (понадобится только Rx, так как нам ничего не нужно отправлять на модуль
  • сервоприводы – в пины 10 (ось азимута) и 11 (ось высоты)
  • кнопка – во 2 пин
  • питание на все модули
  • опционально – конденсатор по питанию

Фотографии моей реализации(19Мб)
lzbaod4hfn_xobvq6v4_1y67ttm.jpeg


bgasibn0eig_unnb1lhcb4hbxtg.jpeg


jnov8ytspu0z5kylgtv_mezrvdc.jpeg


Программная часть


Переходим к самому интересному.

Весь код можно условно разделить на три части:

  1. Работа с GPS, кнопкой и сервами
  2. Работа с астрономией
  3. Главный цикл программы

Заметки по коду:

Для жпс использована библиотека tinygps++
Для кнопки — GyverButton
Когда жпс понимает где он, на ардуине загорается светодиод на 13м пине
Для примера в коде есть массив с координатами разных ярких звёзд

Исходный код

#include
#include
#include
#include "TinyGPS++.h"
TinyGPSPlus gps;
ServoSmooth yaw;
ServoSmooth pitch;
GButton but(2);
int yr = 0, mo = 0, d = 0, h = 0, m = 0, s = 0;
float phi = 0, lambda = 0;
float az = 0, height = 0;
int counter = 0;
float alpha = 0.0, delta = 0.0;
float sunalpha = 0, sundelta = 0;
float Coordinates[10][2] =
{
{0, 0},
{sunalpha * 360 / 2 / PI, sundelta * 360 / 2 / PI}, //sun
{297.9458, 8.9233}, //altair
{279.4083, 38.8038}, //vega
{310.5333, 45.3538}, //deneb
{79.55, 46.0163},//capella
{89.0708, 7.4092}, //betelgeuse
{152.3625, 11.8672}, //regul
{51.4458, 49.9319} //mirfak
};
int Days(int d, int m, int y)//тут считаем, сколько дней прошло с момента весеннего равноденствия (21.03)
{
int days = 0;
int yearNotLeap[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
int yearIsLeap[12] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };

if (((y % 4 == 0) && (y % 100 != 0)) || (y % 400 == 0))
{
for (int i = 0; i < m - 1; i++)
{
days += yearIsLeap;
}
}
else
{
for (int i = 0; i < m - 1; i++)
{
days += yearNotLeap;
}
}
if (m == 1) {

return d - 81;
}
else
return days + d - 81;
}
void GpsGetData()
{
while (Serial.available() > 0)
gps.encode(Serial.read());
if (gps.location.isUpdated())
{
phi = gps.location.lat(); //Широта в градусах (double)
lambda = gps.location.lng(); // Долгота в градусах (double)
yr = gps.date.year(); // Год (2000+) (u16)
mo = gps.date.month(); // Месяц (1-12) (u8)
d = gps.date.day(); // День (1-31) (u8)
h = gps.time.hour(); // Час (0-23) (u8)
m = gps.time.minute(); // Минуты (0-59) (u8)
s = gps.time.second(); // Секунды (0-59) (u8)
}
}
void CalculateParams()
{
float _time = (h + (float)m / 60 + (float)s / 3600) * 360.0 / 24.0;//вычисляем время в часах
float sunlambda = (float)Days(d, mo, yr) * 360 / 365; // вычисляем эклиптическую долготы солнца в градусах
sundelta = asin(0.398749 * sin(sunlambda * 2 * PI / 360));//вычисляем склонение солнца в радианах
sunalpha = asin(tan(sundelta) / 0.434812); // вычисляем прямое восхождение солнца в радианах
float tS = _time + lambda - 180 + sunalpha * 360 / (2 * PI) - alpha; //вычисляем часовой угол звезды в градусах
height = 360 / 2 / PI * asin(sin(phi * 2 * PI / 360) * sin(delta * 2 * PI / 360) + cos(phi * 2 * PI / 360) * cos(delta * 2 * PI / 360) * cos(tS * 2 * PI / 360));//вычисляем высоту
az = 360 / 2 / PI * asin(sin(tS * 2 * PI / 360) * cos(delta * 2 * PI / 360) / cos(height * 2 * PI / 360)); //вычисляем астрономический азимут (с юга по часовой)
if (tS > 90 || tS < -90)
{
az = 180 - az;
}
// if (az > 180 && az < 270)
// az = az - 360;
}

void setup() {
Serial.begin(9600);
yaw.attach(10, 90);
pitch.attach(11, 180);
yaw.setSpeed(20);
yaw.setAccel(0.1);
pitch.setSpeed(20);
pitch.setAccel(0.1);
yaw.setAutoDetach(false);
pitch.setAutoDetach(false);
pinMode(LED_BUILTIN, OUTPUT);
}

void loop() {
but.tick();
yaw.tick();
pitch.tick();
if (but.isSingle())
{
counter += 1;
if (counter == 9)
counter = 0;
}
alpha = Coordinates[counter][0];
delta = Coordinates[counter][1];
GpsGetData();
CalculateParams();
digitalWrite(LED_BUILTIN, gps.location.isValid());
if (az > 90 && az < 180)
{
yaw.setTargetDeg(270 - az);
pitch.setTargetDeg(height);
}
if (az < -90 && az > -180)
{
yaw.setTargetDeg(-90 - az);
pitch.setTargetDeg(height);
}
if (az < 90 && az > -90)
{
yaw.setTargetDeg(90.0 - az);
pitch.setTargetDeg(180 - height);
}
}



Я не буду подробно разбирать каждую строчку, лишь остановлюсь на интересных моментах.

Урок астрономии


Про астротермины и еще кое-что
Если вы не знаете значение какого-либо термина, его можно узнать в википедии или спросить в комментариях – буду рад ответить.

Также, я не нашел в интернете алгоритма перевода координат из одной системы в другую.


… а момент здесь один – функция CalculateParams()
Что должна делать такая функция: принять на вход ко
ординаты звезды в экваториальной системе (прямое восхождение и склонение), время и координаты наблюдателя и выдать высоту и азимут объекта, т.е. по сути перевести координаты звезды из экваториальной системы(в которой звезды неподвижны) в горизонтальную (в которой звезды перемещаются в течение суток).

Реализовано это, используя формулы , а также в вакууме.

Алгоритм таков:

  1. вычислить эклиптическую долготы солнца
  2. вычислить склонение солнца
  3. вычислить прямое восхождение солнца
  4. вычислить часовой угол звезды
  5. вычислить высоту и азимут

Вот как это реализовано:


void CalculateParams()
{
float _time = (h + (float)m / 60 + (float)s / 3600) * 360.0 / 24.0;//вычисляем время в часах
float sunlambda = (float)Days(d, mo, yr) * 360 / 365; // вычисляем эклиптическую долготы солнца в градусах
sundelta = asin(0.398749 * sin(sunlambda * 2 * PI / 360));//вычисляем склонение солнца в радианах
sunalpha = asin(tan(sundelta) / 0.434812); // вычисляем прямое восхождение солнца в радианах
float tS = _time + lambda - 180 + sunalpha * 360 / (2 * PI) - alpha; //вычисляем часовой угол звезды в градусах
height = 360 / 2 / PI * asin(sin(phi * 2 * PI / 360) * sin(delta * 2 * PI / 360) + cos(phi * 2 * PI / 360) * cos(delta * 2 * PI / 360) * cos(tS * 2 * PI / 360));//вычисляем высоту
az = 360 / 2 / PI * asin(sin(tS * 2 * PI / 360) * cos(delta * 2 * PI / 360) / cos(height * 2 * PI / 360)); //вычисляем астрономический азимут (с юга по часовой)
if (tS > 90 || tS < -90)
{
az = 180 - az;
}
}


Оценка погрешности


Оценим вклад каждого фактора в погрешность (отсортировано по вкладу в неточность)

  1. Кривизна рук — у меня это самый главный фактор
  2. Неточное положение горизонта
  3. неточное положение нулевого азимута (напомню, что астрономы считают азимут с юга по часовой стрелке)
  4. Тот факт, что сервы не могут поворачиваться на дробный угол
  5. неточности в алгоритме перевода (положение солнца определяется не очень точно: например, эклиптическая долгота солнца считается гораздо сложнее (в коде упрощённый вариант); день весеннего равноденствия не всегда происходит в одно и тоже время;
    также, я не учитываю уравнение времени) но погрешность из-за этого натекает небольшая.

Что еще можно сделать (todo)


  1. Сменить сервы на шаговые двигатели с их микрошагами;
  2. Немного усовершенствовать алгоритм
  3. Есть метод полного устранения погрешности из-за кривого горизонта и азимута:
    существуют сервисы по «решению астрофото»: им загружаешь фотографию со звездами, а они вычисляют координаты центра кадра.

    Что мы делаем:
    1. прикрепляем к большому телескопу маленький телескопчик с камерой
    2. поворачиваем шаговики на 3 случайных точки на небе, делаем фотографии
    3. отправляем их на один из сервисов, используя его API и получаем координаты точек
    4. сложными программными методами избавляемся от погрешности установки основного телескопа
    5. PROFIT!!!

    На этом все, спасибо за внимание!

    С радостью отвечу на ваши вопросы в комментариях.
 
Сверху Снизу