Скетч для солнечного трекера.

Тема в разделе "Моторы, сервоприводы, робототехника", создана пользователем intrtrade, 15 дек 2016.

  1. intrtrade

    intrtrade Нуб



    это сам трекер, а скетч слишком длинный, ругается..как вставить?
     
  2. intrtrade

    intrtrade Нуб

    int xSize; // автор программы Торопов Андрей. пишите мне на ящик: intrtrade@gmail.com
    int ySize;
    int xPos;
    int yPos;
    unsigned long pereriv=300000; // время между проверками положения панелей в миллисекундах
    int pauz=4000; //для срабатывания геркона
    int centrX=0; // поправка для азимута в градусах, числа "-" или "+"
    int grad=200; // длина дуги по горизонтали в градусах



    #include <DS1302.h> // библиотеку часов подключаем
    DS1302 rtc(2, 3, 4); // на этих пинах "сидит" плата часов
    Time t; // тип переменной такой, да?


    int getXpos(int m, int d, int h, int mi){ // начало функции
    // Данные за 1,11 и 21 числа каждого месяца. Время по гринвичу, в минутах от 00:00. 37я ячейка повторяет 1ю
    int voshod[37]={326,323,316,303,287,270,254,234,212,189,168,148,130,114,100,90,85,85,89,97,108,122,136,150,166,180,195,209,224,240,257,273,289,303,315,323,326};
    int zahod[37]={822,834,848,866,882,899,912,928,943,960,975,990,1005,1020,1034,1046,1055,1059,1059,1055,1046,1032,1016,997,975,954,933,911,890,871,857,835,823,815,812,814,822};
    int azimutV[37]={125,123,120,115,110,105,100,94,88,82,76,70,65,60,56,53,51,51,51,53,56,60,65,70,76,81,87,93,99,105,111,115,120,123,125,126,125};
    int azimutZ[37]={234,236,239,244,249,254,259,265,271,277,283,289,294,298,302,306,308,308,308,306,303,299,294,289,283,277,272,266,260,254,248,243,239,236,234,233,234};

    int v; //время восхода
    int z; //время захода
    int azV; // азимут восхода
    int azZ; // азимут захода
    int v2;
    int z2;
    int azV2;
    int azZ2;
    float x;

    if (d==1){
    m=m*3;
    m=m-3;
    v=voshod[m];
    z=zahod[m];
    azV=azimutV[m];
    azZ=azimutZ[m];
    goto label;
    }
    if (d==11){
    m=m*3;
    m=m-2;
    v=voshod[m];
    z=zahod[m];
    azV=azimutV[m];
    azZ=azimutZ[m];
    goto label;
    }
    if (d==21){
    m=m*3;
    m=m-1;
    v=voshod[m];
    z=zahod[m];
    azV=azimutV[m];
    azZ=azimutZ[m];
    goto label;
    }
    // теперь с остальными датами. Вычисляем восход как средний меж двух дат. Определяем между каких мы дат (3 варианта)
    if (d<11){
    d=d-1;
    m=m*3;
    m=m-3;
    v=voshod[m];
    z=zahod[m];
    azV=azimutV[m];
    azZ=azimutZ[m];
    m=m+1; //следующая ячейка в массивах
    v2=voshod[m];
    z2=zahod[m];
    azV2=azimutV[m];
    azZ2=azimutZ[m];
    if (v>v2){
    x=v-v2;
    x=x/9;
    x=x*d;
    v=v-x; // среднее время восхода вычислено !!!!
    }
    else {
    x=v2-v;
    x=x/9;
    x=x*d;
    v=v+x; // среднее время восхода вычислено
    }
    if (z>z2){
    x=z-z2;
    x=x/9;
    x=x*d;
    z=z-x; // среднее время захода вычислено !!!!
    }
    else {
    x=z2-z;
    x=x/9;
    x=x*d;
    z=z+x; // среднее время захода вычислено
    }
    if (azV>azV2){
    x=azV-azV2;
    x=x/9;
    x=x*d;
    azV=azV-x; //!!!
    }
    else {
    x=azV2-azV;
    x=x/9;
    x=x*d;
    azV=azV+x;
    }
    if (azZ>azZ2){
    x=azZ-azZ2;
    x=x/9;
    x=x*d;
    azZ=azZ-x; //!!!
    }
    else {
    x=azZ2-azZ;
    x=x/9;
    x=x*d;
    azZ=azZ+x;
    }
    goto label;
    }

    if (d<21){
    d=d-11; // переменная больше не понадобится, по этому приводим дату к удобному числу
    m=m*3;
    m=m-2;
    v=voshod[m];
    z=zahod[m];
    azV=azimutV[m];
    azZ=azimutZ[m];
    m=m+1; //следующая ячейка в массивах
    v2=voshod[m];
    z2=zahod[m];
    azV2=azimutV[m];
    azZ2=azimutZ[m];
    if (v>v2){
    x=v-v2;
    x=x/10;
    x=x*d;
    v=v-x; // среднее время восхода вычислено !!!
    }
    else {
    x=v2-v;
    x=x/10;
    x=x*d;
    v=v+x; // среднее время восхода вычислено
    }
    if (z>z2){
    x=z-z2;
    x=x/10;
    x=x*d;
    z=z-x; // среднее время захода вычислено !!!
    }
    else {
    x=z2-z;
    x=x/10;
    x=x*d;
    z=z+x; // среднее время захода вычислено
    }
    if (azV>azV2){
    x=azV-azV2;
    x=x/10;
    x=x*d;
    azV=azV-x; //!!!!
    }
    else {
    x=azV2-azV;
    x=x/10;
    x=x*d;
    azV=azV+x;
    }
    if (azZ>azZ2){
    x=azZ-azZ2;
    x=x/10;
    x=x*d;
    azZ=azZ-x; //!!!!
    }
    else {
    x=azZ2-azZ;
    x=x/10;
    x=x*d;
    azZ=azZ+x;
    }
    goto label;
    }

    if (d>21){
    d=d-21; // переменная больше не понадобится, по этому приводим дату к удобному числу
    m=m*3;
    m=m-1;
    v=voshod[m];
    z=zahod[m];
    azV=azimutV[m];
    azZ=azimutZ[m];
    m=m+1; //следующая ячейка в массивах
    v2=voshod[m];
    z2=zahod[m];
    azV2=azimutV[m];
    azZ2=azimutZ[m];
    if (v>v2){
    x=v-v2;
    x=x/10;
    x=x*d;
    v=v-x; // среднее время восхода вычислено !!!!
    }
    else {
    x=v2-v;
    x=x/10;
    x=x*d;
    v=v+x; // среднее время восхода вычислено
    }
    if (z>z2){
    x=z-z2;
    x=x/10;
    x=x*d;
    z=z-x; // среднее время захода вычислено !!!!
    }
    else {
    x=z2-z;
    x=x/10;
    x=x*d;
    z=z+x; // среднее время захода вычислено
    }
    if (azV>azV2){
    x=azV-azV2;
    x=x/10;
    x=x*d;
    azV=azV-x; //!!!!
    }
    else {
    x=azV2-azV;
    x=x/10;
    x=x*d;
    azV=azV+x;
    }
    if (azZ>azZ2){
    x=azZ-azZ2;
    x=x/10;
    x=x*d;
    azZ=azZ-x; //!!!!
    }
    else {
    x=azZ2-azZ;
    x=x/10;
    x=x*d;
    azZ=azZ+x;
    }
    goto label;
    }

    // преобразуем время в минуты
    label:
    h=h*60;
    mi=mi+h;

    float az; // вычисляемый азимут
    x=z-v; // сколько времени от меньшего азимута до большего
    if (mi<v){ // солнце еще не взошло возвращаем азимут восхода
    return azV;
    }
    if (mi>z){ //солнце уже зашло за горизонт
    return azZ;
    }
    mi=mi-v; // время от восхода
    az=azZ-azV;
    az=az/x;
    az=az*mi;
    az=azV+az; //азимут вычислен.
    return az;

    } // конец функции




    int getUgolY(int m, int d){ // начало функции
    int sunUgol[13]={0,2087,3021,4100,5263,6096,6421,6124,5289,4148,3009,2087,1734}; // 21 число каждого месяца в полдень

    // дата= дата-21 день,из-за того что массив углов на 21 число
    d=d-21;
    if (d<1){
    d=30+d;
    if (m<1){
    m=12;
    }
    m=m-1;
    } // теперь дата преобразована
    int x;
    if (m!=11){
    x=sunUgol[m]-sunUgol[m+1];
    }
    else {
    x=sunUgol[m]-sunUgol[0];
    }
    x=x/30;
    x=x*d;
    x=sunUgol[m]-x;
    x=x/100;
    return x;
    } // конец функции
     
  3. intrtrade

    intrtrade Нуб

    // вторая часть


    int motorX (int obr) { // функция начинается тут
    unsigned long msec;
    int pauza =0;
    int z=0;
    int x;
    int y;
    y=digitalRead (6); // значение геркона
    msec = millis();

    if (obr>0) // крутим в +
    {
    digitalWrite (10,HIGH);
    digitalWrite (11,LOW);

    while(pauza < pauz){ // цикл продолжаем пару секунд если не срабатывает геркон
    x=digitalRead (6); // геркон горизонтали
    if (x!=y){ // если не равны значит геркон сработал и мотор крутится
    msec = millis(); // обновляем точку отсчета
    y=x; // теперь значение геркона является "устаревшим"
    z ++;
    if (z >=obr){
    digitalWrite (10,LOW);
    digitalWrite (11,LOW);
    return z;
    }
    }

    else {
    pauza= (millis()-msec); // выясняем сколько времени выполняется цикл
    }
    }
    digitalWrite (10,LOW); // сработала пауза
    digitalWrite (11,LOW);
    return z;
    }
    if (obr<0) // крутим в -
    {
    digitalWrite (10,LOW);
    digitalWrite (11,HIGH);

    while(pauza < pauz){ // цикл продолжаем пару секунд если не срабатывает геркон
    x=digitalRead (6); // геркон горизонтали
    if (x!=y){ // если не равны значит геркон сработал и мотор крутится
    msec = millis(); // обновляем точку отсчета
    y=x; // теперь значение геркона является "устаревшим"
    z --;
    if (z <=obr){
    digitalWrite (10,LOW);
    digitalWrite (11,LOW);
    return z;
    }
    }
    else {
    pauza= (millis()-msec); // выясняем сколько времени выполняется цикл
    }
    }
    digitalWrite (10,LOW); // сработала пауза
    digitalWrite (11,LOW);
    return z;
    }
    return z; // на входе был "0"
    } // конец тела функции



    int motorY (int obr) { // функция начинается тут
    unsigned long msec;
    int pauza =0;
    int z=0;
    int x;
    int y;
    y=digitalRead (5); // значение геркона
    msec = millis();

    if (obr>0) // крутим в +
    {
    digitalWrite (8,HIGH);
    digitalWrite (9,LOW);

    while(pauza < pauz){ // цикл продолжаем пару секунд если не срабатывает геркон
    x=digitalRead (5); // геркон горизонтали
    if (x!=y){ // если не равны значит геркон сработал и мотор крутится
    msec = millis(); // обновляем точку отсчета
    y=x; // теперь значение геркона является "устаревшим"
    z ++;
    if (z >=obr){
    digitalWrite (8,LOW);
    digitalWrite (9,LOW);
    return z;
    }
    }

    else {
    pauza= (millis()-msec); // выясняем сколько времени выполняется цикл
    }
    }
    digitalWrite (8,LOW); // сработала пауза
    digitalWrite (9,LOW);
    return z;
    }
    if (obr<0) // крутим в -
    {
    digitalWrite (8,LOW);
    digitalWrite (9,HIGH);

    while(pauza < pauz){ // цикл продолжаем пару секунд если не срабатывает геркон
    x=digitalRead (5); // геркон горизонтали
    if (x!=y){ // если не равны значит геркон сработал и мотор крутится
    msec = millis(); // обновляем точку отсчета
    y=x; // теперь значение геркона является "устаревшим"
    z --;
    if (z <=obr){
    digitalWrite (8,LOW);
    digitalWrite (9,LOW);
    return z;
    }
    }
    else {
    pauza= (millis()-msec); // выясняем сколько времени выполняется цикл
    }
    }
    digitalWrite (8,LOW); // сработала пауза
    digitalWrite (9,LOW);
    return z;
    }
    return z; // на входе был "0"
    } // конец тела функции



    int letY(int ugol){ //функция начало
    float f;
    int x;
    int y;
    f= (float)ySize/90;
    ugol=ugol*f; // угол перевели в нужные обороты актуатора от "0"
    if (yPos>ugol){
    x=yPos-ugol;
    x=x*-1; //знак "-"
    y= motorY(x);
    yPos=yPos+y; // позиция поменялась
    return x;
    }
    if (yPos<ugol){
    x=ugol-yPos;
    y=motorY(x);
    yPos=yPos+y; // позиция поменялась
    return x;
    }
    return 0; // позиция равна углу
    } //конец функции


    int letX(int azimut){ //функция начало
    float f;
    int x;
    int y;
    int p;
    p=360-grad;
    p=p/2;
    p=p+centrX; // дополнительная поправка
    azimut=azimut-p; // попытка номер III
    f= (float)xSize/grad;
    azimut=(float)azimut*f; // угол перевели в нужные обороты актуатора от "0"

    if (xPos>azimut){
    x=xPos-azimut;
    x=x*-1; //знак "-"
    y= motorX(x);
    xPos=xPos+y; // позиция поменялась
    return x;
    }
    if (xPos<azimut){
    x=azimut-xPos;
    y=motorX(x);
    xPos=xPos+y; // позиция поменялась
    return x;
    }
    return 0; // позиция равна углу
    } // конец функции





    void setup()
    {
    Serial.begin(9600);
    //rtc.setTime(19, 33, 0); // Set the time to 12:00:00 (24hr format)
    //rtc.setDate(27, 11, 2016); // Set the date to August 6th, 2010

    pinMode (8,OUTPUT);
    pinMode (9,OUTPUT);
    pinMode (10,OUTPUT);
    pinMode (11,OUTPUT);
    t = rtc.getTime(); // получаем текущее время

    Serial.print("mouns: ");
    Serial.print(t.mon,DEC);
    Serial.print(" date: ");
    Serial.println(t.date, DEC);
    Serial.print(" ");
    Serial.print(t.hour, DEC);
    Serial.print(" : ");
    Serial.print(t.min, DEC);
    Serial.println();


    // устанавливаем панели в крайнее верхнее положение для безопасного прогона горизонтали и вертикали последующими шагами.
    ySize= motorY (9999);

    // прогоняем панели на восток и считаем количество оборотов двигателя
    xPos= motorX (9999);
    xSize= motorX (-9999);
    xSize= xSize*-1; //меняю знак на "+"
    int xmedium;
    xmedium=xSize/2;
    xPos= motorX (xmedium); // устанавливаем (горизонталь) в среднее положение (на ЮГ)


    // переводим панели в вертикальное положение и считаем обороты в вертикальной оси
    ySize= motorY(-9999);
    ySize= ySize*-1; //меняю знак на "+"
    yPos=0;
    }

    void loop()
    {

    // вычисляем нужный угол по вертикали
    int m;
    int d;
    m=t.mon;
    d=t.date;


    int ugolY;
    ugolY=getUgolY(m,d);
    letY(ugolY); // функция установит панели в положение угол ugolY
    int h;
    int mi;
    t = rtc.getTime(); // получаем текущее время
    h=t.hour;
    mi=t.min;
    int azimut;
    azimut=getXpos(m,d,h,mi);
    letX(azimut); // функция установит панели по азимуту

    unsigned long stopTime;
    stopTime=millis();
    unsigned long pauza=0;
    while (pauza<pereriv){ // ждем
    pauza= (millis()-stopTime);
    }
    }
     
  4. Arkad_snz

    Arkad_snz Гик

    - Как сделать часы на Ардуино?
    - Легко! Быстренько строишь на двух сервах солнечный трекер, потом вооружаешься астрономическим справочником, и по Солнцу вычисляешь местное солнечное время.
     
  5. intrtrade

    intrtrade Нуб

    А я бы предпочел для этой цели установить камеру с фильтром типа затемненного стекла, и исходя из положения солнца в кадре вычислять время.
     
  6. Kopilov

    Kopilov Гик

    Вставляйте в тег CODE!
    Не знаю, перестанет ли ругаться движок, но читатели -- точно!
     
  7. intrtrade

    intrtrade Нуб

    да нет тут никого.
     
  8. Arkad_snz

    Arkad_snz Гик

    Не-е! Это не решение!
    А вот камера "смотрящая" на циферблат часов - в духе многих здешних решений.
     
  9. intrtrade

    intrtrade Нуб

    Расшифровывается как "тут все дауны, один я умный, и ты тоже, давай дружить"
    Да, конечно.
     
  10. mcureenab

    mcureenab Гуру

    Можно по механику подробнее написать? Какие компоненты используются? Монтировка экваториальная как я понимаю.
    В заполярных широтах будет работать? Сейчас, правда, солнце не восходит - ночь полярная.
    Функции тоже надо коментировать.
    Код (C++):
    } // конец функции
    Это для кого?

    И принцип работы хорошо бы увидеть. Для чего геркон, rtc, моторы?
    Иначе как было "нет тут никого" так никого и не будет.
     
  11. intrtrade

    intrtrade Нуб

    Про механику думаю ролик отдельный снять, тот что выложен это так, для затравки, оценить интерес.
    Для разных широт отдельно рассчитывается угол для "горизонтальной" оси, т.е. сам трекер изготавливается для конкретных широт. Но принцип один, работать будет как надо везде.
    В зависимости от координат также вносятся свои данные в теле программы, их можно брать из онлайн калькуляторов время восхода, захода, азимут и угол солнца. Если кто-то задастся целью собрать такой, могу оказать всестороннюю помощь, правда за деньги.

    " } // конец функции " -это для меня, чтоб быстрее ориентироваться в программе, мне.
    Герконы считают обороты актуаторов, по срабатыванию герконов программа ориентируется, под каким углом находятся панели в данный момент. На вертикальной оси стоит заводской актуатор, на горизонтальной самодельный, хотя ничто не мешает поставить тоже заводской. Кстати, благодаря особенностям конструкции, тому что горизонтальная ось находится под определенным углом к горизонту, актуатор для вертикальной оси не обязателен, его можно заменить ручной регулировкой, и корректируя угол всего один раз в 2 недели, погрешность составит всего 5 градусов.. а программа остается та же, ничего менять не надо. Но у " ручного управления" вертикальным углом есть один недостаток, заключается он в том, что нельзя будет убирать панели в горизонтальное положение в автоматическом режиме, что было бы полезно на случай урагана, шторма..
     
  12. mcureenab

    mcureenab Гуру

    Герконы наверное лучше заменить датчиками Холла. Надежнее будет.
    " } // конец функции " - не стоит писать функцию конца которой не видно на странице. Сделайте декомпозицию и пользуйтесь отступами для блоков кода разного уровня вложенности.

    И вообще код нужно причесать. Тогда он в один пост влезет. За 10 минут функция ополовинилась в объеме.

    Код (C++):
    int letX(
        int azimut
      ){
      int p = (360 - grad) / 2 + centrX;
      float f = (float)xSize / grad;
        azimut = (float)(azimut - p) * f; // угол перевели в нужные обороты актуатора от "0"
        if ( azimut == xPos )
            return 0;
        int x = azimut - xPos;
        xPos = xPos + motorX(x);
        return x;
    } // letX
     
     
  13. mcureenab

    mcureenab Гуру

    Ролик, это ладно. Для привлечения интереса нужен внятный текст. Туманные фразы "благодаря особенностям конструкции, тому что горизонтальная ось находится под определенным углом к горизонту," нужно заменить на общепринятые определения. Добавить фотки, на которых будет сразу понятно о чем речь. Назвать области применения устройства.
     
  14. intrtrade

    intrtrade Нуб

    Геркон стоит в заводском актуаторе. Это моя первая программа на С для ардуины, я вообще не программист, по мне так работает как надо -и ладно. Охотно верю что вы сможете ее улучшить. Можно даже выбросить данные и вставить формулы для вычисления положения солнца, что пусть не сократит размеры проги, зато сделает ее универсальней. И внятный текст пишите сами, если есть к этому способности, у меня их нет, и желания расписывать нет, и стимула нет. А на нет и суда нет, а еще "дареному коню в зубы не смотрят", так что сами разберетесь что со всем этим делать, хоть удаляйте вовсе тему-мне пофиг.
     
  15. intrtrade

    intrtrade Нуб

    Угол горизонтали на трекере( тот который наводит по азимуту) рассчитывается следующим образом. Зайдите в онлайн калькулятор расчета времени восхода и заката по координатам GPS, введите там свои координаты и дату равноденствия, либо 21 марта или 23 сентября, и узнайте для этих дат истинный полдень, затем введите полученное время, и к результату прибавьте 90 градусов, это и будет нужный угол для введенных вами координат. Если непонятно объяснил, то простите, понятнее только за деньги либо к mcureenab
     
  16. intrtrade

    intrtrade Нуб

    IMG_20161230_081237_504.jpg
    на вертикальной оси использованы обыкновенные воротные петли, 3 штуки.
     
    Последнее редактирование: 30 дек 2016
  17. intrtrade

    intrtrade Нуб

    IMG_20161230_081500_566.jpg блок управления. 4 реле, часы реального времени и ардуино уно.
     
  18. intrtrade

    intrtrade Нуб

    IMG_20161230_081146_184.jpg актуатор вертикальной оси.
     
  19. intrtrade

    intrtrade Нуб

    IMG_20161230_081206_848.jpg Он же с другой стороны
     
  20. intrtrade

    intrtrade Нуб

    IMG_20161230_080859_089.jpg Это поворотный узел "горизонтальной оси".
    Мотор крутит резьбовую шпильку, на шпильке накручена корретка
    которая состоит из пары гаек и корпуса. Корретка тянет цепь которая жестко закреплена на трубе.
     
    Последнее редактирование: 30 дек 2016