Raspberry PI 3 + OpenCV + Arduino UNO + ......(поледнее решение и вопрос в последнем сообщении)

Тема в разделе "Флудилка", создана пользователем Igor68, 21 ноя 2016.

  1. Igor68

    Igor68 Гуру

    Плюсы понятны... а методика использования нет. Курю, пью, решаю...
     
    Mitrandir нравится это.
  2. Mitrandir

    Mitrandir Гуру

    Методик работы с гитом куча))
     
  3. Igor68

    Igor68 Гуру

    Доброго времени суток!
    ...вот тихо-тихо работаем с программой sonar:
    в цикле main:
    Код (C++):
    ...
    if(fshm > 0)
            {
                sem_wait(sem);
                switch (addr->scntrl)    //источник управления
                {
                    case _scntrl_free:    //нет источника управления
                    case _scntrl_wsbot:    //источник wsbot через веб интерфейс
                        scntrl.rspL        = addr->wrmotors[0];
                        scntrl.rspR        = addr->wrmotors[1];
                    case _scntrl_sonar:    //источник управления sonar
                        scntrl.spL        = addr->wrmotors[0];
                        scntrl.spR        = addr->wrmotors[1];
                        if(disctLimCntrl())    //функция пересчёта и режима
                        {    //проиошло изменение задания и оно персчитано пропорционально расстоянию
                            addr->wrmotors[0]     = scntrl.spL;
                            addr->wrmotors[1]    = scntrl.spR;
                            addr->scntrl        = _scntrl_sonar;    //захватываем управление
                        }
                        else
                        {
                            if(addr->scntrl == _scntrl_sonar)
                                addr->scntrl = _scntrl_free;    //отпускаем управление
                        }
                        break;
                    default:
                        break;
                }
                //
                //
                addr->sonardistance    = dist;
                addr->sonarradius    = radius.radius;
                addr->timeSonar        = timeSonar;
                sem_post(sem);
            }
    ...
    и функция пересчёта:
    Код (C++):
    //приближение к препятствию - пересчёт
    uint8_t disctLimCntrl(void)
    {
        float    prcDist;
        //
        if(!(dist > _dist_Hcntrl))
        {
            prcDist    = (float)(dist - _dist_Lcntrl) / (float)(_dist_Hcntrl - _dist_Lcntrl);
            if(prcDist < 0.0)
                prcDist = 0;
            scntrl.spL = (int16_t)((float)(scntrl.rspL) * prcDist);
            scntrl.spR = (int16_t)((float)(scntrl.rspR) * prcDist);
            printf("dist=%i  prcDist=%0.2f\n", dist, prcDist);
            return 0xFF;
        }
        else
            return 0;
    }
    А в чём смысл? Есть некий источник управления ну wsbot... кстати servoANDmotor умеет ограничивать всё сам. И вдруг при управлении через браузер мы приближаемся к препятствию и исходя из:
    Код (C++):
    //параметры для управления от сонара
    #define    _dist_Hcntrl    105    //максимальная дистанция контроля
    #define _dist_Lcntrl    35    //минимальная дистанция контроля
    на дистанции менее 105 см мы фиксируем задание, реагируем, и пересчитываем исходя из того что за 0% дистанции взято 35 см, а за 100% взято 105 см. Ну и по процентам изменяем задание для правых и левых колёс индивидуально исходя из зафиксированного ранее задания.
    Это попытка снизить скорость пропорционально, даже если движение не по линии, а по радиусу. Правда совсем забыл, если разворот на месте, когда одни колёса вперёд, другие назад с одинаковой величиной. Когда первичный источник управления заменяется программой sonar... а потом сам сонар отпускает управление, то задание остаётся прежним и требуется другой программе захватить управление. Иначе servoANDmotor при критическом приближении (менее 35 см) захватит управление и остановит движение...
     
  4. Igor68

    Igor68 Гуру

    Когда первичный источник управления заменяется программой sonar(sonar перехватывает управление меняя задание в разделяемой памяти в ней же и выставляет параметр addr->scntrl =_scntrl_sonar, при этом servoANDmotor определяя приоритет источника решает реагировать ли на этот параметр имея своё "мнение") ... а потом сам сонар отпускает управление(выставляя параметр addr->scntrl=_scntrl_free, сто означает "управление свободно" и любой источник управления(программа) может захватить его), то задание остаётся прежним и требуется другой программе захватить управление. Иначе servoANDmotor при критическом приближении (менее 35 см) захватит управление(установив параметр addr->scntrl на своё имя) и остановит движение. После чего вновь освободит управление.
    Каждая из программ анализирует параметр addr->scntrl в разделяемой памяти и так же анализирует приоритеты. Пример:
    1) servoANDmotor собственно сам управляет приводами, но при этом имеет узкую специализацию. Он так же принимает управление от захватившего его источника. И если любой из источников не противоречит заданным правилам, то выставит задание для приводов по ModbusRTU от него. servoANDmotor принимает дистанцию до препятствия и сам определяет действия. Правила для servoANDmotors:
    - нельзя ехать если сонар смотрит не вперёд
    - нельзя ехать вперёд при достижении <35 см до препятствия
    - можно делать разворот на месте
    В других случаях имя источника задания=СВОБОДНО, если другими не занято

    2) sonar - если управление свободно или его захватил wsbot(командами от браузера), но при этом дистанция имеет ограничения по своим (sonar)правилам... то он меняет задание(ранее читая его из разделяемой памяти) и выставляет источником управления своё имя. Пока что он замедляет движение колёс по мере приближения к препятствию (рассматривалось ранее). Но предполагается что он будет выставляя задание для приводов поворачивать и наклонять сонар для построения участка пространства, разумеется остановив движение вперёд. Правила:
    - нельзя менять задание если оно занято servoANDmotor

    3) wsbod - собственно ручное управление через браузер на ПК по websocket. Если источник задания не занят, то имеется возможность управлять по веб интерфейсу. Так же он организует сервер для доступа по websocket, читает глобальную картинку, накладывает на неё индикацию и элементы управления... после чего всё это передаёт клиенту (браузеру). От браузера он принимает клик мышкой и координаты этого клика на картинке(холсте), после чего анализирует это исходя из элементов управления... ну и изменяет задания(для слайдеров) либо выполняет некую функцию(для кнопок). А потом елси он захватил управление (прочитав имя источника) выставляет задания для servoANDmotor, сопровождая своим именем.
    wsbot так же имеет правила:
    - разрешено захватить управление указав своё имя если источник СВОБОДЕН(не занят)
    - если источником является он сам(wsbot) можно так же как и СВОБОДЕН выставлять задания для servoANDmotors

    4) camwork - принимает данные из USB камеры, формирует картинку в формате BMP и выкладывает её в разделяемую память. Пока этим всё ограничивается, но предполагается сохранять ещё и массив, который ещё до BMP сформирован средствами OpenCV для предоставления данных ещё и другим программам... и не обязательно на этой именно малине. Интервал(время сна) в цикле съёмки кадров 50000мкС. Реально же мерить частоту кадров не довелось. Пока не время.
     
  5. Igor68

    Igor68 Гуру

    Доброго времени суток!
    Сонар... ох уж этот сонар. Устройство имел ввиду. Думал что, по другому... но вот:
    Код (Text):
    www-data@pi02:~/ramdisk/mainwork_13_10_2018/sonar/work$ ./sonar
    MB1242 - Testing... (use "--help" for info in new starting)
    Input mode for 5V:
    1 = diagremm A
    2 = diagremm B
    3 = diagremm C
    4 = diagremm D
    ?1
    distance:222
    distance:222
    distance:-32546
    distance:222
    distance:222
    distance:-32546
    distance:222
    distance:222
    distance:222
    distance:222
    distance:222
    distance:222
    distance:-32546
    distance:222
    distance:222
    distance:-32546
    distance:-32546
    distance:222
    distance:-32546
    Видите значение -32546 отрицательое число... да это старший байт имеет значение 0x80, а младший соответственно значение в сантиметрах. И если сделать не так:
    Код (C++):
    dist    = ((dat[0] << 8) | dat[1]);
    а так:
    Код (C++):
    dist    = ((dat[0] << 8) | dat[1]) & 0x7FFF;
    то будет 222. Крутил, читал, искал и видел тут:


    и тут:


    Особенно первый просмотр и не увидел что бы было большее значение. Выходит не та модель. Ну да ладно... есть ещё и камера. Но какого ляда надо читать именно два байта? А данные только в первом. А знак я так понимаю недосягаемость. Ну тогда хоть в милиметрах были данные, ну и минимальная дистанция менее 20 см. Либо делаю что не так... либо слаб сонар. К сожалению на Али в продаже нашёл только одну модель... и то далеко не копейки.
     
  6. Igor68

    Igor68 Гуру

    Вот тут:
    http://forum.amperka.ru/threads/Монитор-питания-по-usb.16566/page-3#post-186152
    попытка регистрации просадки напряжения питания на малине... к сожалению пока без успеха:(. Но... Но управление подачи питания на USB устройство от малины... чисто программно... РЕШЕНО:). Респект... РЕСПЕКТ РЕСПЕКТ АВТОРУ(АВТОРАМ) темы питания по USB от малины. :):):):):):):):):):):):):):):):):):):):):):):):):):):)
     
  7. Igor68

    Igor68 Гуру

    Доброго времени суток!
    Вот так странно выглядит результат работы ультразвукового сканера. Не сканера конечно, а поворот сонара с веб-камерой на 45 градусов вправо и влево от центра... это по горизонтали. По вертикали сканирование не велось. Шаг перемещения 5 градусов:
    snapshot21.png
    сейчас сонар смотрит вперёд. Полукруг внизу это ограничение 20см (сонар выдаёт минимальную дистанцию 20 см) "кривая" слева это момент поворота к стене. В некоторых местах, которые определены углом поворота по отношению к стене, где звук отражается куда-то и не попадает обратно в сонар. По аналогии угол справа сверху это продолжение стены и горизонтальная часть это начало перпендикулярной стены. Там же дверь и сетка от комаров(надо либо склеить либо ...). Вертикальная правая линия отражение от шкафа и наклонная завершающая линия это открытая дверь того шкафа. Тонкие линии от начала это соответственно точки регистрации сонара.

    Вывод аварийных сообщений:
    snapshot19.png
    Тут специально нажата кнопка сброса на ComMotion и Arduino UNO R3. Да в этот момент пропадает связь от малины с управлением. Восстановление связи так же сопровождается сообщением. (Вот блин, а откуда "s" добавилось к ОК). Окно со скролингом и дополнением сообщений снизу и уползанием всего вверх. Но к краю экрана строки не подойдут. Данные затираются.

    Индикация поворота сонара и камеры производится если поворот происходит, но не достиг задания
    snapshot20.png
    Аналогично и с моторами движения. Введёно время индикации - если состояние не меняется индикация исчезает. При этом элементы управления остаются, как индикация дистанции и источника управления. При приближении на некую дистанцию управление перехватывает сонар. И кроме управления движением на достижении некой дистанции происходит остановка движения и включается сканирование - программа sonar сама управляет поворотами касеры и сонара...

    Пока ничего не сделал... надо допилить и приступить к добавлению элементов кластера.
    Что-то моторы никак не приедут вместе с аккумуляторами...

    Вот этот черновик. Рабочий, но черновик:
    mainwork_24_10_2018.zip
     

    Вложения:

    Последнее редактирование: 25 окт 2018
  8. Igor68

    Igor68 Гуру

    Всплыл косяк. В файле main.c части wsbot вместо этого:
    Код (C++):
    SHCMD        Mshcmd;
    ...
    ...
    int main(int argc, char *argv[])
    {
    ....
    ....
    shcmd = (SHCMD*)(&Mshcmd);
    memset(shcmd, 0, sizeof(SHCMD));
    ....
    ....
    }
     
    было это:
    Код (C++):
    ...
    ...
    int main(int argc, char *argv[])
    {
    ....
    ....
    shcmd = (SHCMD*)(addrCMD);
    ....
    ....
    }
    Когда делал наверное что-то делать заставили... ну и с какого-то хрена подставил как указатель на разделяемую память.
    Теперь отработка команд 100%:)
     
  9. Igor68

    Igor68 Гуру

    Доброго времени суток!
    Вот решил убрать фон от слайдера (чёрный фон как ранее)
    snapshot22.png
    Теперь элемент "ползунковый резистор" не в чёрном прямоугольнике. Но можно и по желанию так:
    Код (C++):
    ...
    slidersINIT();
        sliderCREATE(38,455,200,20, 45,-45, (int16_t*)(&motCntrl.camHposition), 255,255,255, 127,127,127, 0);
        sliderCREATE(3, 250,20,200, -30, 40, (int16_t*)(&motCntrl.camVposition), 255,255,255, 127,127,127, 0);
        sliderCREATE(3, 30, 20,150, 0,255, (int16_t*)(&motCntrl.motSpeed), 255,255,255, 127,127,127, 0);
        sliderCREATE(145,5,200,20, -255,255, (int16_t*)(&motCntrl.motTurn), 255,255,255, 127,127,127, 0);
    ...
    Это в файле main.c программы wsbot. А элементы управления slider получили и флаг фона:
    Код (C++):
    //отображение горизонтального слайдера
    __inline void sliderH(int16_t x, int16_t y, int16_t w, int16_t h,
        int16_t min, float k, int16_t cur,
        uint8_t RF, uint8_t GF, uint8_t BF,
        uint8_t RB, uint8_t GB, uint8_t BB,
        uint8_t fback)
    {
        int16_t    x1_clip_old, y1_clip_old, x2_clip_old, y2_clip_old;
        int16_t    X, szX;
        int16_t    h_4    = h/4;
        int16_t    h_2    = h/2;
        if(w < 20)
            szX = 4;
        else if(w < 51)
            szX = 6;
        else
            szX = 8;
        pthread_mutex_lock(&BMPmutex);
        //сохраним регион
        x1_clip_old = x1_clip;
        y1_clip_old = y1_clip;
        x2_clip_old = x2_clip;
        y2_clip_old = y2_clip;
        //установим регион
        BMP_GSetClip(x, y, x + w, y + h);
        //отрисовка фона
        if(fback)
            BMP_rect_filled(x, y, x + w, y + h, 0, 0, 0);
        BMP_rect(x, y + h_4, x + w - 1, y + h - h_4, (RF >> 1), (GF >> 1), (BF >> 1));
        BMP_rect(x + 1, y + h_4 + 1, x + w - 2, y + h - h_4 - 1, RF, GF, BF);
        BMP_rect_filled(x + 2, y + h_4 + 2, x + w - 3, y  + h - h_4 - 2, RB, GB, BB);
        BMP_rect_filled(x + 3, y + h_2 - 1, x + w - 4, y + h_2 + 1, (RB >> 2), (GB >> 2), (BB >> 2));
        //отрисовка ползунка
        X = x + (int16_t)((float)(cur - min) / k);
        if(X > (x + w - szX))
            X = (x + w - szX);
        else if(X < x)
            X = x;
        //BMP_rect_filled(X, y, szX, h, RF, GF, BF);
        BMP_rect_filled(X, y, X + szX/2, y + h, RF, GF, BF);

        //восстановим регион
        BMP_GSetClip(x1_clip_old, y1_clip_old, x2_clip_old, y2_clip_old);
        pthread_mutex_unlock(&BMPmutex);
    }
    это в файле slider.c
    Ну и текущий черновик:
    mainwork_30_10_2018.zip

    Вот думаю про "узкие места". Думаю что в сетевом соединении... ну и так ещё допилить элементы. Я передаю BMP... А вот если бы PNG? Прорисовка картинки идёт в BMP, но передать можно наверное и PNG.
    Подскажите, если можно как реализовать преобразование BMP в PNG внутри ОЗУ в буфере.
    Есть ли примеры? не файл-файл а в ОЗУ. Имею поверхностное представление про SDL... и пока нет решения.
    Спасибо!!!
     

    Вложения:

  10. Igor68

    Igor68 Гуру

    Доброго времени суток! И... простите если повторяюсь!
    Если вдруг надо будет не этот проект, а просто применение картинки и управление через браузер то для анализа и перерисовки элементов управления... функция в object.c как раз занимается этим:
    Код (C++):

    #include <stdio.h>
    #include <stdint.h>
    #include "workfunc.h"
    #include "objects.h"
    #include "config.h"

    void objsCNRTL(SHCMD *pc)
    {
        uint8_t    res1 = 0;
        uint8_t    res2 = 0;
        res1 = buttonCTRL(pc->mX, pc->mY, &pc->cmd); //обработка всех кнопок в buttons.c
        res2 = sliderCTRL(pc->mX, pc->mY, &pc->cmd); //обработка всех слайдеров в slider.c
        if(res1 | res2)
            pc->Cmd    = 0xFF;
        pc->cmd    = 0;
        #if(_objsCNRTL_dbg > 0)
        if(pc->Cmd)
            printf("objsCNRTL: pc->Cmd=%02X\n", pc->Cmd);
        #endif
    }
    И она вызывается из main основного цикла программы например так:
    Код (C++):
    while(1)
        {
            mainTime    = time(NULL); //мы тут берём время системы 64бита
            pthread_mutex_lock(&MainMutex); //захватываем мютекс и запрещаем другом потокам доступ к нашим данным
            MainBMP(); //тут читаем картинку от камеры(кадр) из разделяемой памяти
            rectangle(0, 0, 639, 479, 64, 64, 64); //просто рисуем контур - прямоугольник
            objsCNRTL(shcmd); //собственно принимаем события от кнопок и слайдеров и перерисовываем их прямо на принятой картинке
            manInd(); //функция рисует нашу произвольную индикацию на картинке
            pthread_mutex_lock(&serverBMPmutex); //захватывам мютекс сервера для передачи ему картинки
            memcpy(&BMP_DUMPsend[0], &BMP_DUMP[0], bhfi.bfSize); //копируем картику серверу - пусть отдаёт клиентам
            pthread_mutex_unlock(&serverBMPmutex); //отпускаем мютекс сервера
            pthread_mutex_unlock(&MainMutex); //отпускаем свой мютех для всех потоков кому надо
            rwSHMcntrl(); //реализуем управление приводами читая и записывая параметры в разделяемую память
            usleep(_MainSleep); //спим, сто бы потом начать снова
        }
    Но для реализации элементов управления их надо сначала сделать до цикла main:
    КНОПКА:
    Код (C++):
    buttonsINIT(); //инициализируем структуру кнопок в файле buttons.c и там всё для кнопок
        buttonCREATE(55, 3, 80, 20, "NO TURN\0", 192, 255, 255, 0,0,0, &cmdNoTurn);
        buttonCREATE(3, 3, 50, 20, "STOP\0", 192, 255, 255, 0,0,0, &cmdSTOP);
        buttonCREATE(3, 455, 30, 20, ">0<\0", 192, 255, 255, 0,0,0, &cmdPosNull); //на примере этой кнопки "позиция всех сервомашин в исходное положение"
    //3,455 - положение на картинке левого нижнего угла кнопки X,Y
    //30,20 - размер кнопки Zx,Zy
    //">0<" - текст на кнопке (будет по её центру)
    //192,255,255 - цвет кнопки R,G.B
    //0,0,0 - цвет текста на кнопке
    //&cmdPosNull - адрес функции, которая устанавливает в 0 задания для сервомашин

    ...
    //вот эта функция
    void cmdPosNull(void)
    {
       motCntrl.camHposition   = 0; //позиция по горизонтали в структуре
       motCntrl.camVposition   = 0; //позиция по вертикали в структуре
    }
     
    Нажатие на кнопку (местом на слайдере) в сервере:
    Код (C++):
    default:
                            #if(_dbg_msg_server_con > 0)
                            printf("Поток клиента, данные от клиента приняты - флаг _cntWS_WORK\n");
                            #endif
                            res = wsin(&wsdat);
                            res = wsfunc(&wsdat);
                            switch(res)
                            {
                                case -1:
                                    goto mexit;
                                default:
                                    //pthread_mutex_lock(&MainMutex);
                                    wsworkfunc((char*)(&rdbuf[0]));
                                    //pthread_mutex_unlock(&MainMutex);
                                    break;
                            }
                            break;
    Вызывает регистрацию координат и нажатия (далее)........
     
  11. Igor68

    Igor68 Гуру

    ....которая в workfunc.c:
    Код (C++):
    void wsworkfunc(char * indat)
    {
        char    wb[64];
        int    X,Y;
        int    resx, resy;
        //
        if(!(shcmd->cmd))  //если прежнее событие от браузера отработано из main через objsCNRTL (предыдущий пост) то обрабатываем новое событие
    {
        memset(&wb[0], 0, 64); //чистим буфер дла параметра
        resx = strval(indat, "mouse_X:", &wb[0]); //ищем в принятых о браузера данных строку "mouse_X:" после которой должно быть значение от нашей веб страницы
        if(resx) //если строка со значением найдены, то значение уже в буфере
            X    = atoi(&wb[0]); //фиксируем значение координаты X клика мышкой
    //так же и и Y
        memset(&wb[0], 0, 64);
        resy = strval(indat, "mouse_Y:", &wb[0]);
        if(resy)
            Y    = atoi(&wb[0]);
        if((resx) && (resy)) //если координаты X и Y обе есть то заносим их в рабочую структуру и устанавливаем флаг события
            shcmd->mX    = (int16_t)(X);
            shcmd->mY    = (int16_t)(Y);
            shcmd->cmd    = 0xFF;
        }
    //обратно уходим в сервер
    }
    СЛАЙДЕР (ползунок) его(их) так же как и кнопку создать надо до цикла main:
    Код (C++):
    slidersINIT(); //инициализация структуры слайдеров
        sliderCREATE(38,455,200,20, 45,-45, (int16_t*)(&motCntrl.camHposition), 255,255,255, 127,127,127, 0); //слайдер горизонтального позиционирования камеры и сонара
        sliderCREATE(3, 250,20,200, -30, 40, (int16_t*)(&motCntrl.camVposition), 255,255,255, 127,127,127, 0); //слайдер вертикального позиционирования камеры и сонара
        sliderCREATE(3, 30, 20,150, 0,255, (int16_t*)(&motCntrl.motSpeed), 255,255,255, 127,127,127, 0); //слайдер задания скорости вперёд (назад нам запрещено)
        sliderCREATE(145,5,200,20, -255,255, (int16_t*)(&motCntrl.motTurn), 255,255,255, 127,127,127, 0);//слайдер значения поворота и разворота и его рассмотрим
    //145,5 - координаты X и Y положения нижнего левого угла слайдера
    //200,20 - размер слайдера по X и Y. Кстати если размер по Y будет больше, чем по X то слайдер будет создан вертикаьным
    //-255,255 - минимальное и максимальное значение изменяемого слайдером задания. Кстати если их менять местами то управление станет так же наоборот (было слева направо, станет справа на лево)
    //(int16_t*)(&motCntrl.motTurn) - указатель на параметр, который сможет изменять слайдер
    //255,255,255 - цвет контура и бегунка соайдера
    //127,127,127 - цвет продольной полоски хода бегунке слайдера
    //0 - слайдер будет нарисован поверх картинки без чёрной закраски его места. Иначе слайдер на чёрном месте вместо фрагмента картинки
     
    Всё, что касается текста:
    Знакогенератор (таблица) взята от моих старых деяний и русский там отсутствует. Да и в кириллице такое богатое количество вариантов кода одного и того же символа:(, что ограничился латиницей, где для любой кодировки (почти все) коды символов идентичны.
    То что касается управления через разделяемую память. Память так же создаётся как файл в /dev/shm ещё до цикла main:
    Код (C++):
    int createSHMcntrl(void)
    {
        //
        //память - создание
            if ( (shmCMD = shm_open(gdatSHARED_MEMORY_OBJECT_NAME, O_CREAT|O_RDWR, S_IRWXO|S_IRWXG|S_IRWXU)) == -1 )
                    return 1;
        if ( ftruncate(shmCMD, gdatSHARED_MEMORY_OBJECT_SIZE + 1) == -1 )
                    return 2;
        if ( (addrCMD = mmap(0, gdatSHARED_MEMORY_OBJECT_SIZE + 1, PROT_WRITE|PROT_READ, MAP_SHARED, shmCMD, 0)) == (u_int8_t*)-1 )
                    return 3;
        //семафор - создание
        if ( (semCNTRL = sem_open(gdatSEMAPHORE_NAME, O_CREAT, 0777, 0)) == SEM_FAILED )
            return 4;
        sem_post(semCNTRL);
        usleep(500000);
        sem_wait(semCNTRL);
        //
        memset(addrCMD, 0, gdatSHARED_MEMORY_OBJECT_SIZE);
        sem_post(semCNTRL);
        return 0;
    }
    Видите addrCMD - это как раз адрес на общую структуру (файл maindat.h) которая едина для всех запущенных в одном проекте программ. Создаются ведь с единым именем. Семафор синхронизации делается тут же. Для обмена картинками всё идентично.
    .....
     
  12. Igor68

    Igor68 Гуру

    ...таким образом обращение к этой структуре происходит одинаково из разных программ.
    Например wsbot получив данные от браузера меняет задание в этой самой памяти, а программа servoANDmotor обратившись к этой памяти получает задание на позиционирование камеры и вращение моторов перемещения. Она (servoANDmotor) передаёт нужные значения ардуине по USB-SERIAL и получает от неё значения исполняемых параметров. И сразу в разделяемую память, где wsbot получив новые состояния нарисует на картинке индикацию и всё это отдаст серверу, через который клиент получит данные и отправит новые события пользователя мышкой. Конечно клиентом может быть и планшет и смартфон... главное подсоединиться к серверу.......
    И всё же скорость реакции не в этом пути. Узкое место как говорил это:
    ПК<--wlan0-->RPi1<--wlan0--> RPi2.
    Потму как:
    ПК<--wlan0-->RPi1<--ethernet--> RPi2.
    работает гораздо быстрее.
    Собственно RPi1 элемент неподвижной связи - организатор сети wifi и один из элементов кластера, а RPi2 это подвижная автономная часть на батарейках с камерой и т.п.
    И встаёт вопрос в сжатии картинки со стороны сервера вполне понятной браузеру.
    Помогите подсказкой к PNG!
     
    Последнее редактирование: 31 окт 2018
  13. Igor68

    Igor68 Гуру

    ...работа servoANDmotor заключается в получении данных из разделяемой памяти. Это организовано отдельным потоком от цикла main... ещё до начала собственно цикла main:
    Код (C++):
    ....
    res = gdmemstart();
        if(res != 0)
            return res;
    ....
    в файле gdat.c. Вот начало обращения к этой памяти(разделяемой) и запуск потока работы с ней:
    Код (C++):
    //старт доступа к разделякмой памяти
    int    gdmemstart(void)
    {
        //память - создание
        if ( (shm = shm_open(gdatSHARED_MEMORY_OBJECT_NAME, O_CREAT|O_RDWR, S_IRWXO|S_IRWXG|S_IRWXU)) == -1 )
        {
                perror("shm_open");
                return 1;
            }
        if ( ftruncate(shm, gdatSHARED_MEMORY_OBJECT_SIZE+1) == -1 )
        {
            perror("ftruncate");
            return 2;
            }
        if ( (addr = mmap(0, gdatSHARED_MEMORY_OBJECT_SIZE+1, PROT_WRITE|PROT_READ, MAP_SHARED, shm, 0)) == (GDATA1*)-1 )
        {
                perror("mmap");
                return 3;
            }
        //семафор - создание
        if ( (sem = sem_open(gdatSEMAPHORE_NAME, O_CREAT, 0777, 0)) == SEM_FAILED )
        {
            perror("sem_open");
            return 4;
        }
        sem_post(sem);
        //mutex create
            if(!(pthread_mutex_init(&gdmemMutex,NULL) == 0))
                    return 5;
        pthread_create(&hgdmemnProcess,NULL,(void*) gdmemProcess,NULL);
        //
        return 0;
    }
    Если посмотреть в файле maindat.h то увидим, что имена доступа к разделяемой памяти и семафоров:
    Код (C++):
    //SHM приводов
    #define gdatSEMAPHORE_NAME            "/gdat_semaphore"
    #define gdatSHARED_MEMORY_OBJECT_NAME        "gdat_memory"
    #define gdatSHARED_MEMORY_OBJECT_SIZE         sizeof(GDATA1)
    //SHM картинки BMP
    #define bmpSEMAPHORE_NAME            "/gdat_bmp"
    #define bmpSHARED_MEMORY_OBJECT_NAME        "bmp_memory"
    Тут же указано и имя для картинки BMP. Этот файл одинаковый для всех программ проекта.
    И если обратить внимание ещё и на это:
    Код (C++):
    //описание scntrl
    #define _scntrl_free        0x00                //никем не управляется
    #define    _scntrl_servoANDmotors    0x01                //локальное управление самих приводов
    #define    _scntrl_wsbot        0x02                //управление по websocket
    #define _scntrl_sonar        0x03                //управление по sonars
    #define _cntnrl_camcntrl    0x04                //управление от камеры локально
    #define _scntrl_cluster        0x05                //управление от кластера
    то увидим коды источников управления приводами. Каждый элемент(программа) системы анализирует кто сейчас управляет приводами.

    .....
     
  14. Igor68

    Igor68 Гуру

    ....
    Вот анализ в потоке доступа к этой памяти:
    Код (C++):
    //процесс доступа к разделяемой памяти
    void gdmemProcess(void)
    {
        while(1)
        {
            pthread_mutex_lock(&gdmemMutex);
            sem_wait(sem);
            //
            gdat.timeSonar            = addr->timeSonar;
            gdat.timeCamwork        = addr->timeCamwork;
            gdat.timeWSbot            = addr->timeWSbot;
            gdat.scntrl            = addr->scntrl;
            gdat.statSonar            = addr->statSonar;
            gdat.statCamwork        = addr->statCamwork;
            gdat.statWsbot            = addr->statWsbot;
            gdat.sonardistance        = addr->sonardistance;
            //
            switch(gdat.scntrl)
            {
                case _scntrl_servoANDmotors:    //управление от приводов - выключение движения, но разрешение разворота на месте
                    //разрешение по малой дистанции и по развороту              
                    if((gdat.sonardistance < _distance_min_lim) &&
                    (((addr->wrmotors[0] + addr->wrmotors[1]) == 0) && (addr->wrspeed_motors[0] == addr->wrspeed_motors[1])))
                    {
                        gdat.wrmotors[0]        = addr->wrmotors[0];
                        gdat.wrmotors[1]        = addr->wrmotors[1];
                        gdat.wrspeed_motors[0]        = addr->wrspeed_motors[0];
                        gdat.wrspeed_motors[1]        = addr->wrspeed_motors[1];
                        gdat.wrservo[_MwrservoH]    = addr->wrservo[_MwrservoH];
                        gdat.wrservo_speed[_MwrservoH]    = addr->wrservo_speed[_MwrservoH];
                        gdat.wrservo[_MwrservoV]    = addr->wrservo[_MwrservoV];
                        gdat.wrservo_speed[_MwrservoV]    = addr->wrservo_speed[_MwrservoV];
                        addr->scntrl            = _scntrl_free;
                    }
                    //разрешение по большой дистанции и горизонтальному нулевому положению сенсоров
                    else if((gdat.sonardistance >= _distance_min_lim) &&
                    (gdat.rdservo[0] == 0))
                    {
                        gdat.wrmotors[0]        = addr->wrmotors[0];
                        gdat.wrmotors[1]        = addr->wrmotors[1];
                        gdat.wrspeed_motors[0]        = addr->wrspeed_motors[0];
                        gdat.wrspeed_motors[1]        = addr->wrspeed_motors[1];
                        gdat.wrservo[_MwrservoH]    = addr->wrservo[_MwrservoH];
                        gdat.wrservo_speed[_MwrservoH]    = addr->wrservo_speed[_MwrservoH];
                        gdat.wrservo[_MwrservoV]    = addr->wrservo[_MwrservoV];
                        gdat.wrservo_speed[_MwrservoV]    = addr->wrservo_speed[_MwrservoV];
                        addr->scntrl            = _scntrl_free;
                    }
                    else if(((addr->wrmotors[0] + addr->wrmotors[1]) == 0) && (addr->wrspeed_motors[0] == addr->wrspeed_motors[1]))
                    {
                        gdat.wrmotors[0]        = addr->wrmotors[0];
                        gdat.wrmotors[1]        = addr->wrmotors[1];
                        gdat.wrspeed_motors[0]        = addr->wrspeed_motors[0];
                        gdat.wrspeed_motors[1]        = addr->wrspeed_motors[1];
                        gdat.wrservo[_MwrservoH]    = addr->wrservo[_MwrservoH];
                        gdat.wrservo_speed[_MwrservoH]    = addr->wrservo_speed[_MwrservoH];
                        gdat.wrservo[_MwrservoV]    = addr->wrservo[_MwrservoV];
                        gdat.wrservo_speed[_MwrservoV]    = addr->wrservo_speed[_MwrservoV];
                        addr->scntrl            = _scntrl_free;
                    }
                    else    //ограничение
                    {
                        gdat.wrservo[_MwrservoH]    = addr->wrservo[_MwrservoH];
                        gdat.wrservo_speed[_MwrservoH]    = addr->wrservo_speed[_MwrservoH];
                        gdat.wrservo[_MwrservoV]    = addr->wrservo[_MwrservoV];
                        gdat.wrservo_speed[_MwrservoV]    = addr->wrservo_speed[_MwrservoV];
                        gdat.wrmotors[0]        = 0;
                        gdat.wrmotors[1]        = 0;
                        gdat.wrspeed_motors[0]        = 0;
                        gdat.wrspeed_motors[1]        = 0;
                        addr->wrmotors[0]        = 0;
                        addr->wrmotors[1]        = 0;
                        addr->wrspeed_motors[0]        = 0;
                        addr->wrspeed_motors[1]        = 0;
                        addr->scntrl            = _scntrl_servoANDmotors;
                    }
                    break;
                case _scntrl_wsbot:    //управление от wsbot
                    //разрешение по малой дистанции и по развороту              
                    if((gdat.sonardistance < _distance_min_lim) &&
                    (((addr->wrmotors[0] + addr->wrmotors[1]) == 0) && (addr->wrspeed_motors[0] == addr->wrspeed_motors[1])))
                    {
                        .......
                    }
                    //разрешение по большой дистанции и горизонтальному нулевому положению сенсоров
                    else if((!(gdat.sonardistance < _distance_min_lim)) &&
                    (gdat.rdservo[0] == 0))
                    {
                       ......
                    }
                    else if(((addr->wrmotors[0] + addr->wrmotors[1]) == 0) && (addr->wrspeed_motors[0] == addr->wrspeed_motors[1]))
                    {
                        .....
                    }
                    else    //запрет
                    {
                        .....
                    }
                    break;
                case _scntrl_sonar:
                    //разрешение по малой дистанции и по развороту              
                    if((gdat.sonardistance < _distance_min_lim) &&
                    (((addr->wrmotors[0] + addr->wrmotors[1]) == 0) && (addr->wrspeed_motors[0] == addr->wrspeed_motors[1])))
                    {
                        .....
                    }
                    //разрешение по большой дистанции и горизонтальному нулевому положению сенсоров - норма
                    else if((!(gdat.sonardistance < _distance_min_lim)) &&
                    (gdat.rdservo[0] == 0))
                    {
                        .......
                    }
                    else if(((addr->wrmotors[0] + addr->wrmotors[1]) == 0) && (addr->wrspeed_motors[0] == addr->wrspeed_motors[1]))
                    {
                        .....
                    }
                    else    //запрет
                    {
                        gdat.wrservo[_MwrservoH]    = addr->wrservo[_MwrservoH];
                        gdat.wrservo_speed[_MwrservoH]    = addr->wrservo_speed[_MwrservoH];
                        gdat.wrservo[_MwrservoV]    = addr->wrservo[_MwrservoV];
                        gdat.wrservo_speed[_MwrservoV]    = addr->wrservo_speed[_MwrservoV];
                        gdat.wrmotors[0]        = 0;
                        gdat.wrmotors[1]        = 0;
                        gdat.wrspeed_motors[0]        = 0;
                        gdat.wrspeed_motors[1]        = 0;
                        addr->wrmotors[0]        = 0;
                        addr->wrmotors[1]        = 0;
                        addr->wrspeed_motors[0]        = 0;
                        addr->wrspeed_motors[1]        = 0;
                        addr->scntrl            = _scntrl_servoANDmotors;
                    }
                    break;
                   
                default:
                    break;
            }
            //
            addr->timeMotorANDservo        = gdat.timeMotorANDservo;
            addr->rdmotors[0]        = gdat.rdmotors[0];
            addr->rdmotors[1]        = gdat.rdmotors[1];
            addr->rdservo[0]        = gdat.rdservo[0];
            addr->rdservo[1]        = gdat.rdservo[1];
            addr->statServoANDmotor        = gdat.statServoANDmotor;
            //printf("addr->scntrl %02X   gdat.scntrl %02X\n", addr->scntrl, gdat.scntrl);
            //
            sem_post(sem);
            pthread_mutex_unlock(&gdmemMutex);
            usleep(_gmemSleep);
        }
        pthread_exit(0);
    }
    Как видно сама программа тут ещё и сообщает своё время
    ....
     
  15. Igor68

    Igor68 Гуру

    ....
    А управление приводами через Arduino UNO R3 по USB-SERIAL(протокол близкий к Modbus RTU) осуществляется ещё одним отдельным потоком программы, который запускается до цикла main:
    Код (C++):
    res = startwork(&serialname[0], baudrate, wait_dev);
        if(res != 0)
            return res
    Тут ещё указывается скорость связи и интервал доступа. Ну и поток с запуском в файле motors.c:
    Код (C++):
    //запуск рабочего потока
    int    startwork(char * serial, int baudrate, int symwait)
    {
        int res;
        res = MRTU_Connect(serial, baudrate, symwait);
        if(res != 0)
            return res;
        //mutex create
            if(!(pthread_mutex_init(&motorsMutex,NULL) == 0))
                    return 4;
        pthread_create(&hmotorsProcess,NULL,(void*) motorsProcess,NULL);
        return 0;
    }

    //рабочий поток
    void    motorsProcess(void)
    {
        int    cntcntrl = 0;
        memset(&wrmovectl, 0, sizeof(MOVECTRL));
        memset(&rdmovectl, 0, sizeof(MOVECTRL));
        while(1)
        {
            timeMotorANDservo    = time(NULL);
            pthread_mutex_lock(&motorsMutex);
            motorsCntrl();
            cntcntrl = movecntrl(cntcntrl,0);
            pthread_mutex_unlock(&motorsMutex);
            usleep(_wmotorsSleep);
        }
        pthread_exit(0);
    }

    //обмен состояниеми и параметрами
    void    motorsCntrl(void)
    {
        pthread_mutex_lock(&gdmemMutex);
        //фиксация задания для сервомашин без ограничений в управлении
        wrmovectl.servo[0]         = gdat.wrservo[0];        //задания положения сервомашин
        wrmovectl.servo[1]         = gdat.wrservo[1];        //
        wrmovectl.servo_speed[0]     = gdat.wrservo_speed[0];    //задания скорости положения
        wrmovectl.servo_speed[1]     = gdat.wrservo_speed[1];    //
        //status1                = gdat.status1;            //
        //
        wrmovectl.motors[0]         = gdat.wrmotors[0];        //задания моторов
        wrmovectl.motors[1]         = gdat.wrmotors[1];        //
        wrmovectl.speed_motors[0]    = gdat.wrspeed_motors[0];    //задание скорости моторов
        wrmovectl.speed_motors[1]    = gdat.wrspeed_motors[1];    //
        //отдаём состояния
        gdat.timeMotorANDservo        = timeMotorANDservo;
        gdat.rdmotors[0]        = rdmovectl.motors[0];
        gdat.rdmotors[1]        = rdmovectl.motors[1];
        gdat.rdservo[0]            = rdmovectl.servo[0];
        gdat.rdservo[1]            = rdmovectl.servo[1];
        //параметры времени
        if(timeMotorANDservo > (gdat.timeSonar + _timeDelta))
            gdat.statServoANDmotor    &= (~(_statServoANDmotor_timeSonar));
        else
            gdat.statServoANDmotor    |= _statServoANDmotor_timeSonar;
        if(timeMotorANDservo > (gdat.timeCamwork + _timeDelta))
            gdat.statServoANDmotor    &= (~(_statServoANDmotor_timeCamwork));
        else
            gdat.statServoANDmotor    |= _statServoANDmotor_timeCamwork;
        if(timeMotorANDservo > (gdat.timeWSbot + _timeDelta))
            gdat.statServoANDmotor    &= (~(_statServoANDmotor_timeWsbot));
        else
            gdat.statServoANDmotor    |= _statServoANDmotor_timeWsbot;
        //параметры связи
        if(res3 == 0)
            gdat.statServoANDmotor    |= _statServoANDmotor_MB03conn;
        else
            gdat.statServoANDmotor    &= (~(_statServoANDmotor_MB03conn));
        if(res16 == 0)
            gdat.statServoANDmotor    |= _statServoANDmotor_MB10conn;
        else
            gdat.statServoANDmotor    &= (~(_statServoANDmotor_MB10conn));
        pthread_mutex_unlock(&gdmemMutex);
    }
    Как видно тут фиксируются ещё и состояния как времени от каждой программы, так и состояния связи. Все ответы возвращаются в разделяемую память
    ....
     
  16. Igor68

    Igor68 Гуру

    ....
    доступ к Arduino UNO R3 происходит через функцию:
    Код (C++):
    //управление перемещением
    int    movecntrl(int numwork, uint8_t fp)
    {
        switch(numwork)
        {
            case 0:    //задание параметров
                res16 = MRTU_CMD_16(1, _MoveControl, (u_int16_t*)(&wrmovectl), 8);
                if(fp > 0)
                {
                    printf("send res:%i\n", res16);
                    printf("send %05i %05i %05i %05i %05i %05i %05i %05i\n",
                    wrmovectl.motors[0], wrmovectl.motors[1],
                    wrmovectl.speed_motors[0], wrmovectl.speed_motors[1],
                    wrmovectl.servo[0], wrmovectl.servo[1],
                    wrmovectl.servo_speed[0], wrmovectl.servo_speed[1]);
                }
                numwork++;
                break;
            case 1:    //чтение параметров
                res3 = MRTU_CMD_3(1, _MoveControl, (u_int16_t*)(&rdmovectl), 8);
                if(fp > 0)
                {
                    printf("recv res:%i\n", res3);
                    printf("recv %05i %05i %05i %05i %05i %05i %05i %05i\n",
                    rdmovectl.motors[0], rdmovectl.motors[1],
                    rdmovectl.speed_motors[0], rdmovectl.speed_motors[1],
                    rdmovectl.servo[0], rdmovectl.servo[1],
                    rdmovectl.servo_speed[0], rdmovectl.servo_speed[1]);
                }
                numwork = 0;
                break;
            default:
                numwork = 0;
                break;
        }
        return numwork;
    }
    которая так же в этом файле. А вот собственно обращения с командами Modbus RTU реализованы в файле mrtu.c

    А Arduino просто выполняет свой скетч, осуществляя доступ по I2C к Multiservo(Amperka) для позиционирования сонара и вебкамеры и ComMotion Driver For 4 motors для управления 4-мя двигателями движения Арматурчика (Таково имя устройства на колёсах). И скетч на ардуине ещё реализует скорость изменения задания. Если Вы обратили внимание в maindat.h есть:
    Код (C++):
    int16_t        wrmotors[_Mcnt_motmove];        //задание для моторов колёс
        uint16_t    wrspeed_motors[_Mcnt_motmove];        //задание времени реакции моторов
        int16_t        wrservo[_Mcnt_servomove];        //задание позиционирования
        uint16_t    wrservo_speed[_Mcnt_servomove];        //задание втемени позиционирования
    А именно допустим время реакции моторов... задаётся задержка в количестве циклов когда задание от текущего значения увеличит/уменьшит своё значение в сторону заданной величины на 1. А читается из ардуины значение, которое в текущий момент подаётся на приводы. Таким образом задаётся не только скорость, но и ускорение/торможение. Это же касается и позиционирования.

    ....
     
    Последнее редактирование: 31 окт 2018
  17. Igor68

    Igor68 Гуру

    ... программа sonar так же имеет кроме цикла main ещё и отдельный поток. Собственно этот поток только и занимается тем, что читает данные от сонара по I2C. Он расположен в файле main.c:
    Код (C++):
    void MainPth(void)
    {
        while(1)
        {
            pthread_mutex_lock(&MainMutex);
            dist        = mb1242read_sensorR(_slave_addr, &radius);
            pthread_mutex_unlock(&MainMutex);
            usleep(_MainPthSleep);
        }
        pthread_exit(0);
    }
    Так же вычисляется предполагаемый радиус "пятна отражения". Обработка событий активируется этим (пока что только так и в main.c):
    Код (C++):
    ...
    //приближение к препятствию - пересчёт
    uint8_t disctLimCntrl(void)
    {
        float    prcDist;
        //
        if((dist < _dist_Lcntrl_scznH) && (scntrl.rposH == 0))
        {
            scntrl.maincntrl = _maincntrl_scanmapH;
            return 0;
        }
        else if((!(dist > _dist_Hcntrl)) && (scntrl.rposH == 0))
        {
            prcDist    = (float)(dist - _dist_Lcntrl) / (float)(_dist_Hcntrl - _dist_Lcntrl);
            if(prcDist < 0.0)
                prcDist = 0;
            scntrl.spL = (int16_t)((float)(scntrl.rspL) * prcDist);
            scntrl.spR = (int16_t)((float)(scntrl.rspR) * prcDist);
            printf("dist=%i  prcDist=%0.2f\n", dist, prcDist);
            return 0xFF;
        }
        else
            return 0;
    }
    А вот реализация управления в основном цикле main(фрагмент):
    Код (C++):
    scntrl.timeMotorANDservo    = addr->timeMotorANDservo;
                scntrl.timeCamwork        = addr->timeCamwork;
                scntrl.timeWSbot        = addr->timeWSbot;
                scntrl.rposH            = addr->rdservo[0];
                switch (addr->scntrl)    //источник управления
                {
                    case _scntrl_free:    //нет источника управления
                    case _scntrl_wsbot:    //источник wsbot через веб интерфейс
                        scntrl.rspL        = addr->wrmotors[0];
                        scntrl.rspR        = addr->wrmotors[1];
                    case _scntrl_sonar:    //источник управления sonar
                        scntrl.spL        = addr->wrmotors[0];
                        scntrl.spR        = addr->wrmotors[1];
                        if(scntrl.maincntrl != _maincntrl_no)
                            break;
                        else if(disctLimCntrl())    //функция пересчёта и режима
                        {    //проиошло изменение задания и оно персчитано пропорционально расстоянию
                            addr->wrmotors[0]     = scntrl.spL;
                            addr->wrspeed_motors[0]    = 1;
                            addr->wrmotors[1]    = scntrl.spR;
                            addr->wrspeed_motors[1]    = 1;
                            addr->scntrl        = _scntrl_sonar;    //захватываем управление
                        }
                        else
                        {
                            if(addr->scntrl == _scntrl_sonar)
                                addr->scntrl = _scntrl_free;    //отпускаем управление
                        }
                        break;
                    default:
                        break;
                }
                //
    ...
    А вот цикл, когда до препятствия меньше некоторой величины, работы сканирования по горизонтали:
    Код (C++):
    /выполнение сканирования по горизонтали
                    if(scntrl.maincntrl == _maincntrl_scanmapH)
                    {
                        switch(addr->scntrl)
                        {
                            case _scntrl_free:    //нет источника управления
                            case _scntrl_wsbot:    //источник wsbot через веб интерфейс
                                addr->scntrl    = _scntrl_sonar;
                                break;
                            case _scntrl_sonar:    //источник управления sonar
                                if(!(addr->statSonar & _statSonar_msg_work)) //начало
                                {
                                    addr->wrmotors[0]    = 0;
                                    addr->wrspeed_motors[0]    = 0;
                                    addr->wrmotors[1]    = 0;
                                    addr->wrspeed_motors[1]    = 0;
                                    memset(&addr->ppd[0], 0, sizeof(PPD[_maxPPD]));
                                    addr->statSonar     |= _statSonar_msg_work;
                                    scntrl.setposH        = _setHposH;
                                    scntrl.cntsetposH    = 0;
                                    addr->wrservo[0]    = scntrl.setposH;
                                    addr->wrservo_speed[0]    = 20;
                                    addr->wrservo[1]    = 0;
                                    addr->wrservo_speed[1]    = 5;
                                }
                                else
                                {
                                    addr->wrmotors[0]    = 0;
                                    addr->wrspeed_motors[0]    = 0;
                                    addr->wrmotors[1]    = 0;
                                    addr->wrspeed_motors[1]    = 0;
                                    addr->wrservo[0]    = scntrl.setposH;
                                    addr->wrservo_speed[0]    = 150;
                                    addr->wrservo[1]    = 0;
                                    addr->wrservo_speed[1]    = 150;
                                    addr->statSonar     |= _statSonar_msg_work;
                                    if(addr->rdservo[0] == addr->wrservo[0])
                                    {
                                        addr->ppd[scntrl.cntsetposH].PH    = addr->rdservo[0];
                                        addr->ppd[scntrl.cntsetposH].PV    = addr->rdservo[1];
                                        addr->ppd[scntrl.cntsetposH].D    = dist;
                                        if(!(scntrl.setposH < _endHposH))
                                        {
                                            addr->statSonar     &= (~(_statSonar_msg_work));
                                            scntrl.maincntrl    = _maincntrl_no;
                                            addr->wrservo[0]    = 0;
                                            addr->wrservo_speed[0]    = 10;
                                            addr->wrservo[1]    = 0;
                                            addr->wrservo_speed[1]    = 10;
                                        }
                                        scntrl.cntsetposH++;  
                                        scntrl.setposH            += _stepHposH;
                                    }
                                }
                                break;
                            default:
                                break;
                        }
                    }
    ....
     
  18. Igor68

    Igor68 Гуру

    ... предполагается ещё сканирование и по вертикали. Если заметили в захвате управления сформирован ряд правил, о которых говорил ранее. К примеру вариант:
    - Когда происходит управление от wsbot (собственно управляет клиент из браузера на ПК/Праншете и т.п.) достигли некой дистанции до преграды. servoANDmotor не против (ему надо меньше), а вот sonar захватил упралвение и снизил скорость(Процедура не доработана и не продумана) при этом сообщил, что он захватил управление и выставил новое задание для перемещения. А почему не останов? Если заметили, то задания для правых и левых моторов могут быть и разными ну и движение может происходить по радиусу и за какое-то время препятствие давно не на пути. Как только дистанция станет больше sonar скажет, что управление свободно. servoANDmotor не против и для него такая дистанция это ещё далеко. А вот если дистанция станет ниже какой-то величины (всё определено в заголовочных файлах), но больше чем возмутиться servoANDmotor, сонар при захвате управления остановит движение и приступит к горизонтальному сканированию.
    Метод не отработан и нужные правила ещё далеко не определены.
    Как только управление будет свободным клиент вновь может захватить управление. Для этого надо только с экрана задать скорость или позицию камеры и сонара. Источник управления индицируется в нижнем правом углу(надпись). Там же и показывается дистанция.

    Для PNG остаётся непонятным метод сжатия. С элементами файла и контрольной суммой всё ясно стало сегодня.
     
  19. Igor68

    Igor68 Гуру

    Про PNG. Вот пытаюсь по этому:
    https://habr.com/post/130472/
    Что тут получилось?! Это функция для построения таблицы для подсчёта контрольной суммы:
    Код (C++):
    void make_crc_table(void)
    {
        unsigned long c;
        int n, k;
     
        for (n = 0; n < 256; n++) {
            c = (unsigned long) n;
            for (k = 0; k < 8; k++) {
                if (c & 1)
                    c = 0xedb88320L ^ (c >> 1);
                else
                    c = c >> 1;
            }
        crc_table[n] = c;
        }
        crc_table_computed = 1;
    }
    Правда пользуюсь готовой... это без разницы либо брать готовую таблицу либо заранее сгенерировать. А это собственно нахождение этой суммы:
    Код (C++):
    //контрольная сумма по таблице
    /*
    для чанков:
        контрольная сумма вычисляется для всего блока данных а это:
        (stop_addr - 4) - (start_addr + 4) т.е в подсчёт не входит размер
        чанка(4 байта) и контрольная сумма(4 байта)

    */

    uint32_t    Fcrc32(uint8_t * dat, uint32_t len)
    {
        uint32_t    crc32    = 0xFFFFFFFF;
        uint32_t    cnt     = 0;
        while(cnt < len)
        {
            crc32 = crc_table[(uint8_t)(crc32 ^ (*(uint8_t*)(dat + cnt))) & 0xFF] ^ (crc32 >> 8);
            cnt++;
        }
        return ~crc32;
    }
    Проверял по картинке из этого:
    https://habr.com/post/130472/
    там эта картинка:
    snapshot23.png
    Всё понятно и проверяется нормально. А методом тыка определил что первые 4 байта(размер) чанка не подсчитывется в определении контрольной суммы, ну и понятно, что последние 4 байта сама эта сумма. А вот IDAT - содержимое данных(содержимое картинки) архивировано. Так вот сейчас в этом и вопрос. По наблюдению загрузка wifi около 1Мб/сек и более, а браузер даже с ухищрениями все равно накапливает кеш - смотрю по загрузке памяти. Даже swap раздел начинает заполняться......
     
  20. Igor68

    Igor68 Гуру

    ....даже делал это в программе wsbot в файле bmpfunc.c:
    Код (C++):
    /*
    формирование данных для отсылки картинки для передачи
    по websocket
    с использованием сравнения картинки
    */

    int wsscreen(uint8_t * wsd, uint8_t *bd)
    {
        int64_t    wsz     = 0;
        uint8_t    flg    = 0;
        uint8_t    dbyte;
        uint8_t    *ad;
        if(flagNew)
        {
            wsz = (int64_t)(bhfi.bfSize);    //размер файла BMP
            (*(uint8_t*)(wsd))    = WS_OPCODE_BINARY | FIN1;
            if(wsz < 126)
            {
                int8_t    cnt8     = 0;
                (*(uint8_t*)(wsd + 1))    = (uint8_t)(wsz);
                while(cnt8 < wsz)
                {
                    dbyte  = (*(uint8_t*)(bd + cnt8));
                    ad    = cnt8 + wsd + 2;
                    if(!flg)
                    {
                        if(dbyte != (*(uint8_t*)(ad)))
                        {
                            flg = 0xFF;
                            (*(uint8_t*)(ad))    = dbyte;
                        }
                    }
                    else
                        (*(uint8_t*)(ad))    = dbyte;
                    cnt8++;
                }
                if(flg)
                    return (wsz + 2);
            }
            else if(wsz < 65535)
            {
                int32_t    cnt16     = 0;
                (*(uint8_t*)(wsd + 1))     = 126;
                (*(uint8_t*)(wsd + 2))     = (uint8_t)((wsz >> 8) & 0xFF);
                (*(uint8_t*)(wsd + 3))     = (uint8_t)(wsz & 0xFF);
                while(cnt16 < wsz)
                {
                    dbyte  = (*(uint8_t*)(bd + cnt16));
                    ad    = cnt16 + wsd + 4;
                    if(!flg)
                    {
                        if(dbyte != (*(uint8_t*)(ad)))
                        {
                            flg = 0xFF;
                            (*(uint8_t*)(ad))    = dbyte;
                        }
                    }
                    else
                        (*(uint8_t*)(ad))    = dbyte;
                    cnt16++;
                }
                if(flg)
                    return (wsz + 4);
            }
            else
            {
                int64_t    cnt64     = 0;
                (*(uint8_t*)(wsd + 1))    = 127;
                (*(uint8_t*)(wsd + 2))    = (uint8_t)((wsz >> 56) & 0xFF);
                (*(uint8_t*)(wsd + 3))    = (uint8_t)((wsz >> 48) & 0xFF);
                (*(uint8_t*)(wsd + 4))    = (uint8_t)((wsz >> 40) & 0xFF);
                (*(uint8_t*)(wsd + 5))    = (uint8_t)((wsz >> 32) & 0xFF);
                (*(uint8_t*)(wsd + 6))    = (uint8_t)((wsz >> 24) & 0xFF);
                (*(uint8_t*)(wsd + 7))    = (uint8_t)((wsz >> 16) & 0xFF);
                (*(uint8_t*)(wsd + 8))    = (uint8_t)((wsz >> 8) & 0xFF);
                (*(uint8_t*)(wsd + 9))    = (uint8_t)(wsz & 0xFF);
                while(cnt64 < wsz)
                {
                    dbyte   = (*(uint8_t*)(bd + cnt64));
                    ad    = cnt64 + wsd + 10;
                    if(!flg)
                    {
                        if(dbyte != (*(uint8_t*)(ad)))
                        {
                            flg = 0xFF;
                            (*(uint8_t*)(ad))    = dbyte;
                        }
                    }
                    else
                        (*(uint8_t*)(ad))    = dbyte;
                    cnt64++;
                }
                if(flg)
                    return (wsz + 10);
            }
        }
        return 0;
    }
    тут если картинка не совпадает с прежней (хотя бы 1 пиксель), то она отсылается клиенту, а иначе нет. Да что значит совпадает или нет?! А шумы камеры? А изменение индикации? А что будет если будет на колёсах?... там картинка динамичная. Правда применено непрерывное соединение TCP может UDP выгоднее даже с BMP? Но как будет двусторонняя связь? Данные должны быть достоверны! Особенно в управлении.