Создание графика (эквидистанты и вот это вот всё)

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

Короче, нужна помощь зала. Граф - выходи на сцену, помнится, у тебя там профильное по математике, а у мну мозжечка не хватает.

Короче, есть вот такой вот девайс:

Тыкаем в экран, ставим 8 опорных точек, рисуется график белыми линиями. По тыку на кнопку "ВЫЧИСЛИТЬ" - при помощи эквидистант расставляются промежуточные опорные точки, их нужно 200 штук, они рисуются жёлтым. На этом этапе - всё норм, как видно.

Теперь вводные: из всего этого надо получить список интервалов для таймера, в условных единицах (в нашем случае - микросекунды, но это не суть). По оси X у нас - общее время, за которое должен отработать этот график, обозначим его T. По осу Y у нас - относительная ширина интервала, причём чем выше (к верхнему краю экрана) располагается точка на графике, тем интервал между импульсами должен быть короче.

Очевидно, что у нас есть общее время, и относительный размах времени интервала по Y, назовём его deltaY.

Задача: заставить всю эту херобору правильно просчитаться. Как это сделать весовыми долями по одной оси - я более-менее в курсе, применительно оси X - берём промежуток между двумя точками deltaX, и делим его на T, получаем относительный вес интервала в диапазоне [0,1]. Суммируем эти веса, получаем общее кол-во весов weightSumX. Отсюда средневзвешенный вес по оси X = T/weightSumX.

То же самое по Y: берём Y-координату опорной точки, и делим её на deltaY - получаем вес ширины интервала по Y. Если точка по Y самая высокая - это тупо минимальный интервал, константа.

Далее - собственно затык: нам надо, чтобы сумма величин всех рассчитанных интервалов была равна Т (с хорошим приближением), и учитывался вес по Y, наряду с весом по X.

Вопрос: как? Чувствую, истина где-то рядом, но математику в универе - прогуливал, цуко.

З.Ы. Для удобства понимания можно оперировать круглыми величинами, например: 10 опорных точек, распределены равномерно, общее время работы T - 10 секунд, первая точка - максимальная ширина импульса, вторая точка - 50% ширины импульса, третья - минимальный импульс, четвёрная - 50% ширины импульса, пятая - максимальный импульс, и т.п. (ну то есть - изображенная на картинке "пила").

Признателен за пинок в нужном направлении, ибо сейчас рассчитывается какая-то дичь :(

rkit
Offline
Зарегистрирован: 23.11.2016

Общее время делится на сумму Y всех точек. Полученное значение умножаем на Y конкретной точки.

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

Да, забыл - время t одной весовой доли по любой оси - просчитать не проблема:

      double oneWeightYTime = fullWorkTime/weightYSum; // время на одну весовую долю, по Y
      double oneWeightXTime = fullWorkTime/weightXSum; // время на одну весовую долю, по X

Но вот так вот рассчитывать - это дичь:

         double xWeight = pointsXWeight[z];
         double yWeight = pointsYWeight[z];

         uint32_t pulseWidth = ((xWeight*oneWeightXTime) + (yWeight*oneWeightYTime))/2;

Потому что вместо постепенного уменьшения величины импульса на первых точках графика приведённой выше "пилы" - получаем вот это:

Pulse width: 692
Pulse width: 883
Pulse width: 670
Pulse width: 863
Pulse width: 651
Pulse width: 841
Pulse width: 631
Pulse width: 822
Pulse width: 609

Видно, что через один интервал уменьшается, а должно - последовательно. И да, я знаю, что так суммировать веса - это дичь. Вопрос - как правильно ;)

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

rkit пишет:

Общее время делится на сумму Y всех точек. Полученное значение умножаем на Y конкретной точки.

Это ясно, собственно. Об этом и написано. Вопрос в том, что у нас есть ещё и вес точки по X, т.е. относительная ширина интервала. Одной Y-координатой тут не обойтись, ибо _нельзя_ равномерно раскидывать интервалы по X, они должны соответствовать весам временных долей изменения координаты X между двумя соседними точками.

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

Вдруг поможет - вот наброс на вентилятор, дикий и необузданный, не до причёсывания, жопа горит:


    int minY, maxY;
    minY = maxY = resultPoints[0].Y;

    for(size_t z=0;z<resultPoints.size();z++)
    {
      Point pt = resultPoints[z];
      minY = min(minY,pt.Y);
      maxY = max(maxY,pt.Y);

      DBG("pt.X="); DBG(pt.X); DBG(", pt.Y="); DBGLN(pt.Y);
    } // for

    // получили минимальную и максимальную координаты по Y, относительно которых будем потом рассчитывать длительность итерации цикла для одной опорной точки графика
    
    int fullYDia = maxY - minY; // полная дельта размаха по Y, 100% ширины одного самого длительного импульса
    double fullWorkTime = 1000.*(PULSE_CHART_WORK_TIME); // полное время работы графика (100%), микросекунд

    // теперь считаем сумму весов всех точек по Y
    double weightYSum = 0; // сумма весов всех точек, по Y
     Vector<double> pointsYWeight; // список весов каждой точки части графика, по Y
     
     for(size_t z=0;z<resultPoints.size();z++)
     {
        Point pt = resultPoints[z];
        double pointYWeight = (double(1.)*pt.Y)/fullYDia; // весовая доля точки по Y
        
        weightYSum += pointYWeight;
        pointsYWeight.push_back(pointYWeight);  
     } // for

      // теперь нам надо просчитать вес каждой точки по X. это можно сделать, просчитам дельту от следующей точки до текущей, это и будет ширина импульса по X,
      // следовательно, мы можем посчитать относительный вес этого импульса по X.      

      double weightXSum = 0; // сумма весов всех точек, по X
      Vector<double> pointsXWeight; // список весов каждой точки части графика, по X

      for(size_t z=0;z<resultPoints.size()-1;z++)
      {
        Point ptCur = resultPoints[z];
        Point ptNext = resultPoints[z+1];
        int dt = ptNext.X - ptCur.X;

        double pointXWeight = (double(1.)*dt)/totalDeltaX; // весовая доля точки по X
        weightXSum += pointXWeight;
        pointsXWeight.push_back(pointXWeight);  
      }

      // добавляем последний, нулевой вес, для крайней правой точки
      pointsXWeight.push_back(0.);

      // теперь у нас есть веса по X и по Y, и мы можем ими оперировать при расчёте

      // средневзвешенные веса для точек по X и Y
      double oneWeightYTime = fullWorkTime/weightYSum; // время на одну весовую долю, по Y
      double oneWeightXTime = fullWorkTime/weightXSum; // время на одну весовую долю, по X



      #ifdef _DEBUG
      uint32_t totalPulseWidth = 0;
      #endif

      // теперь бежим по всем точкам, получаем их веса, и преобразуем в длительность импульса
      for(size_t z=0;z<resultPoints.size();z++)
      {
         double xWeight = pointsXWeight[z];
         double yWeight = pointsYWeight[z];

         uint32_t pulseWidth = ((xWeight*oneWeightXTime) + (yWeight*oneWeightYTime))/2;

         if(pulseWidth < (PULSE_WIDTH)*2) // минимальная ширина импульса - двойная ширина высокого уровня, т.е. минимальное заполнение - 50%
         {
            pulseWidth = (PULSE_WIDTH)*2;
         }

          #ifdef _DEBUG
            totalPulseWidth += pulseWidth;
          #endif         

         // отнимаем от ширины импульса ширину высокого уровня, чтобы обеспечить правильность по длительности времени
         pulseWidth -= (PULSE_WIDTH);

         // печатаем для теста
         DBG("Pulse width: "); DBGLN(pulseWidth);

         // сохраняем в список
         pulsesList.push_back(pulseWidth);         
      } // for

    #ifdef _DEBUG
      DBG("TOTAL PULSES WIDTH: "); DBGLN(totalPulseWidth);
    #endif         

Строка 69, собственно, ставит в ступор, ибо неправильно.

b707
Онлайн
Зарегистрирован: 26.05.2017

DIYMan пишет:

ибо _нельзя_ равномерно раскидывать интервалы по X

а из чего следует, что "нельзя"? насколько я понял процедуру, сначала оператор ставит на графике некоторое число белых точек, а потом система заполняет промежутки большим количеством желтых точек. Откуда известно. что система не ставит точки через равные промежутки по Х?

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

b707 пишет:

DIYMan пишет:

ибо _нельзя_ равномерно раскидывать интервалы по X

а из чего следует, что "нельзя"? насколько я понял процедуру, сначала оператор ставит на графике некоторое число белых точек, а потом система заполняет промежутки большим количеством желтых точек. Откуда известно. что система не ставит точки через равные промежутки по Х?

Точки между двумя отрезками расставляются на одинаковых интервалах, но - кол-во точек между двумя отрезками графика рассчитывается, исходя из весовой доли этого участка графика к общему времени работы системы. Участок графика может иметь крутой наклон вверх или вниз, что значит, применительно к X-оси - что относительное время между импульсами на этом отрезке должно быть меньше, чем у более пологой части. А это вычисляется, исходя из deltaX, и deltaY - если deltaX мал, а deltaY велик - то это должно повлиять на величину относительной ширины интервала одного импульса.

Именно поэтому - и нельзя ;)

b707
Онлайн
Зарегистрирован: 26.05.2017

если желтые точки расставлять равномерно НЕЛЬЗЯ - то нужна табличка интервалов по Х. Дальше  считаем сумму произведений значений Y на ширину интервала Х, и на полученную сумму делим время T

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

b707 пишет:

если желтые точки расставлять равномерно НЕЛЬЗЯ - то нужна табличка интервалов по Х. Дальше  считаем сумму произведений значений Y на ширину интервала Х, и на полученную сумму делим время T

Всё есть, выше - кусок кода приведён. Строка 69, сейчас неправильная. Напиши формулу для одной точки, пж, известно: её вес по X и по Y, диапазон [0,1], известно также общее время работы. Известны также средневзвешенные времена по X и по Y. Как это всё запустить в полёт - туплю.

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

Короче - это эмуляция работы энкодера: чем круче график вверх - тем энкодер крутится быстрее, и задержка между импульсами меньше. Чем круче вниз - тем, соответственно, медленнее крутится энкодер.

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

Вот, до кучи, ещё расчёт промежуточных точек:

void creteLinePoints(int x1, int x2, int y1, int y2, uint16_t pointsCount, Points& resultPoints)
{

  // работаем с эквидистантами
  /*
   x = x1 + (x2-x1) * t
   y = y1 + (y2-y1) * t

   где t - шаг между точками, в диапазоне 0-1
   */

   double tStep = 1.0/pointsCount;
   double t = 0;
   
   int deltax = (x2-x1);
   int deltay = (y2-y1);

   for(uint16_t i=0;i<pointsCount;i++)
   {
     int x = x1 + t * deltax;
     int y = y1 + t * deltay;
     t += tStep;

     Point pt = {x,y};
     resultPoints.push_back(pt);
   } // for
  
}
//------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Вот как рассчитывается кол-во точек для отрезка графика:

   Vector<uint16_t> xDeltas;
      
      Point ptPrev = {START_POINT_X,START_POINT_Y};
      for(size_t i=0;i<chartPoints.size();i++)
      {
         Point ptNext = chartPoints[i];

          xDeltas.push_back(abs(ptNext.X - ptPrev.X));
         
         ptPrev = ptNext;
      }
      
     xDeltas.push_back(abs(END_POINT_X - ptPrev.X));

    #ifdef _DEBUG
     // выводим список всех дельт по X
     for(size_t i=0;i<xDeltas.size();i++)
     {
       DBG("X delta #"); DBG((i+1)); DBG(": "); DBGLN(xDeltas[i]);
     }
     #endif

     // теперь рассчитываем кол-во точек на каждом из отрезков
     Vector<uint16_t> xPoints; // кол-во точек на часть графика
 
     double deltaErr = 0.0; // ошибка накопления точек
 
     uint16_t pointsGenerated = 0;

    // 2. считаем процентный вес участка к общему времени
    // 3. считаем кол-во точек для каждого участка

     for(size_t i=0;i<xDeltas.size();i++)
     {
        uint16_t xDelta = xDeltas[i]; 

        double percents = (100.*xDelta)/totalDeltaX;

        // теперь считаем кол-во точек на отрезок

        double pointsPerPart = (percents*TOTAL_POINTS_IN_CHART)/100 + deltaErr;
        uint16_t pppInt = pointsPerPart;
        deltaErr = pointsPerPart - pppInt;

        pointsGenerated += pppInt;

        if(i == xDeltas.size() - 1) // добиваем до общего кол-ва точек в конце графика
        {
            while(pointsGenerated < TOTAL_POINTS_IN_CHART)
            {
              pointsGenerated++;
              pppInt++;
            }
        }

        xPoints.push_back(pppInt);
        
     } // for

 

 

b707
Онлайн
Зарегистрирован: 26.05.2017

А может, Дим, ты переусложняешь?

Вот смотри - по оси у тебя время последовательности, по Y - длительность импульса. По моему эти величины не связаны никак.

В момент времени Xn выставляешь длительность таймера, соответвующий точке Yn и все

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

1. У меня "корона". В легкой форме, но я сплю как кот, по 20 часов в сутки;

2. Мне вот-прям-щас надо ехать в аптеку и магаз. И да маску я все равно надену на подбородок! ;))) Гы! (это в качестве г.. на вентилятор).

3. Завтра возможно вникну, щаз я даже буквы не все узнаю ;)))).

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

b707 пишет:

А может, Дим, ты переусложняешь?

Вот смотри - по оси у тебя время последовательности, по Y - длительность импульса. По моему эти величины не связаны никак.

В момент времени Xn выставляешь длительность таймера, соответвующий точке Yn и все

Да может и переусложняю, хз. Но вопрос-то в том, что у нас нет известной величины длительности импульса, есть только константа минимальной длительности импульса. Сумма времён всех импульсов должна уложиться во время работы системы.

Подскажи пж, где я реально переусложняю, что-то я совсем уже мозг завернул. Допустим, мы вычислили средневзвешенное время импульса по X, предварительно просчитав веса всех отрезков по X. Очевидно, что из этого можно легко вычислить требуемую ширину импульса в нужной точке, чтобы сумма этих времён соответствовала общему времени работы. Но тут нет места весу импульса по Y, или я его не вижу. А оно должно там быть, ведь чем точка по Y выше - тем относительная ширина импульса должна быть меньше, при условии совпадения суммы всех времён импульсов с общим временем работы системы.

Да сто пудов переусложняю, понять бы, где.

Logik
Offline
Зарегистрирован: 05.08.2014

Та определи deltaX и deltaY и смотри что больше в каждой точке. По большему и строй шаг. А для "чтобы сумма величин всех рассчитанных интервалов была равна Т " то это вобще не проблема если число точек можно по ходу менять. Система ж у тя их сама тыкает, вот и дотыкает где нужно если нужно.

Но еще лучше будет, если тебе нормально описать задачу, а то там - энкодер, сям - таймер...

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

Logik пишет:

Та определи deltaX и deltaY и смотри что больше в каждой точке. По большему и строй шаг. А для "чтобы сумма величин всех рассчитанных интервалов была равна Т " то это вобще не проблема если число точек можно по ходу менять. Система ж у тя их сама тыкает, вот и дотыкает где нужно если нужно.

Но еще лучше будет, если тебе нормально описать задачу, а то там - энкодер, сям - таймер...

1. Задача описана нормально, про энкодер - это описание того, для чего задача предназначена, всего лишь.

2. Кол-во точек менять нельзя - они посчитаны. Это эталонное кол-во импульсов, которое должен выдать девайс.

3. Не уверен, что строительство шага по бОльшей дельте - правильно, обоснуй, пж. С доказательством того, что сумма времён интервалов будет приближенной к Т.

b707
Онлайн
Зарегистрирован: 26.05.2017

Если это эмулятор энкодера или, что проще - сигнал датчика холла с вращающегося вала - то тогда на графике - зависимость скорости вращения от времени, верно?

Мне как-то так понятнее.

Тогда количество импульсов в каждом интервале = произведение скорости (Y) на длительность интервала dt. А общее количество импульсов за все ремя будет сумма (Y * dt) по всем интервалам. А минимальная длина импульса - это отношение dT / Y в том интервале. где Y максимальный

 

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

b707 пишет:

Если это эмулятор энкодера или, что проще - сигнал датчика холла с вращающегося вала - то тогда на графике - зависимость скорости вращения от времени, верно?

Мне как-то так понятнее.

Тогда количество импульсов в каждом интервале = произведение скорости (Y) на длительность интервала dt. А общее количество импульсов за все ремя будет сумма (Y * dt) по всем интервалам. А минимальная длина импульса - это отношение dT / Y в том интервале. где Y максимальный

 

Да, всё верно, зависимость скорости вращения от времени. По остальному - спасибо, надо обмыслить, что-то я заработался по самые не балуйся :(

Так-то вроде всё понятно, но у нас кол-во импульсов - фиксировано, и должно быть разбросано по графику правильно. Надо мозговать, решение простое, очевидно я тупо перегрелся, сорри.

b707
Онлайн
Зарегистрирован: 26.05.2017

DIYMan пишет:

 у нас кол-во импульсов - фиксировано, и должно быть разбросано по графику правильно.

ИМХО, это ошибка. "Количество импульсов" и их "правильное положение" не могут быть фиксированы одновременно. Если это так, то никакого простора для изменения числа оборотов по графику Y уже не остается.

Logik
Offline
Зарегистрирован: 05.08.2014

1. Та нихрена не нормально. Постоянно то график, то таймер, то энкодер. Давай остановимся что это график. Ну с осью Х все вроде ясно, линейная, требуется поделить допустим на 200 интервалов, длительность каждого интервала (имеется в виду интервала на графике между соседними точками по оси Х) в диапазоне от 0 до...  а до скольких, есть ограничение? Ось У- полная лажа, минимальный интервал есть, но он вверху. Так? Или то интервал не между точками на графике, а у импульса (таймера, энкодера - ХЗ) Не линейно по оси У? есть ограничение на интервала на графике между соседними точками по оси У? 

Распиши нормально задачу, чисто в терминах графика. Точки понятно - линейная интерполяция кусочно-ломаной функции. Распиши ограничения на интервалы между точками по каждой оси.

2. Принимаем. Но в общем случае возможно что задача не будет иметь решения при некоторых сочетаниях.

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

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

b707 пишет:

DIYMan пишет:

 у нас кол-во импульсов - фиксировано, и должно быть разбросано по графику правильно.

ИМХО, это ошибка. "Количество импульсов" и их "правильное положение" не могут быть фиксированы одновременно. Если это так, то никакого простора для изменения числа оборотов по графику Y уже не остается.

Воот, и меня тут что-то смущает. Но кол-во импульсов - строго фиксировано. Ок, тогда зайдём с другой стороны: надо правильно раскидать это кол-во импульсов по графику, чтобы оперировать только весами по Y, если я всё правильно понял.

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

Logik пишет:

1. Та нихрена не нормально. Постоянно то график, то таймер, то энкодер. 

Не утруждайся в таком случае, просто пройди мимо, спасибо.

Logik
Offline
Зарегистрирован: 05.08.2014

b707 пишет:

DIYMan пишет:

 у нас кол-во импульсов - фиксировано, и должно быть разбросано по графику правильно.

ИМХО, это ошибка. "Количество импульсов" и их "правильное положение" не могут быть фиксированы одновременно. Если это так, то никакого простора для изменения числа оборотов по графику Y уже не остается.

Возможно что и так. Тут надо знать нахрена оно вобще. Если мы сами в начале интерполяции решаем что 200 точек - и хоть усрись, то ошибка, проще по ходу решать сколько удобней из условия "правильное положение". А если заказчик сказал нужно 200 иначе не заплачу, то куда дется.

Logik
Offline
Зарегистрирован: 05.08.2014

DIYMan пишет:

Logik пишет:

1. Та нихрена не нормально. Постоянно то график, то таймер, то энкодер. 

Не утруждайся в таком случае, просто пройди мимо, спасибо.

ок, согласовано.

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

b707, спасибо, стало доходить, я сам виноват. Это ж изменение скорости по времени, следовательно, надо просто раскидать правильно опорные точки на график. Сейчас просто неправильно рассчитывается кол-во точек на отрезок графика, как я понял.

b707
Онлайн
Зарегистрирован: 26.05.2017

DIYMan пишет:

b707, спасибо, стало доходить, я сам виноват. Это ж изменение скорости по времени, следовательно, надо просто раскидать правильно опорные точки на график. Сейчас просто неправильно рассчитывается кол-во точек на отрезок графика, как я понял.

 Число импульсов в единицу времени в каждой конкретной точке графика i, при условии что общее число точек 200 - будет ( Yi / 200) * СУММ (Y *dt)

Длительность импульса - обратная этой величине

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

b707 пишет:

 Число импульсов в единицу времени в каждой конкретной точке графика i, при условии что общее число точек 200 - будет ( Yi / 200) * СУММ (Y *dt)

Длительность импульса - обратная этой величине

Спасибо за помощь, надо со всем этим переспать.

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

b707, спасибо, понял свою ошибку, сейчас пересчитал всё ручками, на тестовом графике - вроде всё сошлось. Набросал код, проверить смогу только завтра, в целом же, кмк - задача почти решена, останется, как максимум - разобраться с кол-вом опорных точек для каждого из отрезков графика. Короче, вскрытие покажет. Собственно, говнокода там получилось немного, глянь, если несложно - вдруг я опять где-то что-то упустил:

    int fullXDia = maxX - minX; // полная дельта размаха по X
    double fullWorkTime = 1000.*(PULSE_CHART_WORK_TIME); // полное время работы графика (100%), микросекунд    

    // считаем веса точек по Y.
    uint32_t weightYSum = 0; // сумма весов всех точек, по Y

    // сумма весов считается как сумма Yi*dt, где dt = длительность участка графика по X
    Vector<double> xDeltasWeights; // список дельт по X
   
    for(size_t z=0;z<resultPoints.size()-1;z++)
    {
        Point ptCur = resultPoints[z];
        Point ptNext = resultPoints[z+1];

        double deltaX = ptNext.X - ptCur.X; // промежуток времени для отрезка
        double pointWeight = ptCur.Y; // вес точки по Y
        double dt = (deltaX/fullXDia);

        weightYSum += pointWeight*dt; // приплюсовали к сумме весов
        
        xDeltasWeights.push_back(deltaX); // запоминаем дельту по X для отрезка
        
    } // for

    // сумму весов высчитали, теперь считаем относительный вес каждой точки
    Vector<double> relativePointsWeight; // список относительных весов точек (импульсов на единицу времени)
    
    for(size_t z=0;z<resultPoints.size()-1;z++)
    {
       Point ptCur = resultPoints[z];
       double pointWeight = ptCur.Y; // вес точки по Y
       double pulsesPerTimeUnit = (pointWeight/resultPoints.size())*weightYSum; // импульсов на единицу времени для точки
       double pointResultWeight = pulsesPerTimeUnit * xDeltasWeights[z]; // результирующий вес точки
	   
       relativePointsWeight.push_back(pointResultWeight);
    } // for

    // относительные веса посчитали, теперь преобразовываем их к единицам времени.
    // для этого у нас есть weightYSum, и данные в массиве relativePointsWeight, которые показывают,
    // какую часть от weightYSum занимает в процентах каждая точка. Соответственно, мы можем высчитать время

    for(size_t z=0;z<relativePointsWeight.size();z++)
    {
      double w = relativePointsWeight[z]; // относительный вес точки, по времени
      double percents = (w*100)/weightYSum; // процентный вес точки, от общего времени срабатывания

      uint32_t pulseWidth = (fullWorkTime*percents)/100; // ширина импульса

     if(pulseWidth < (PULSE_WIDTH)*2) // минимальная ширина импульса - двойная ширина высокого уровня, т.е. минимальное заполнение - 50%
     {
        pulseWidth = (PULSE_WIDTH)*2;
     }

     // отнимаем от ширины импульса ширину высокого уровня, чтобы обеспечить правильность по длительности времени
     pulseWidth -= (PULSE_WIDTH);

     // печатаем для теста
     DBG("Pulse width: "); DBGLN(pulseWidth);

     // сохраняем в список
     pulsesList.push_back(pulseWidth);
     
      // всё, посчитали ширину импульса
      
    } // for

В целом же, по тестовому графику из 7 точек, желаемой длительностью в 4 секунды, получаю 6 импульсов суммарной длины в 4,1428 секунды, что очень даже неплохо, в целом. В общем, буду признателен, если глянешь глазком, комментарии есть, переменные тоже вроде понятно обозваны. Гран мерси заранее.

З.Ы. За корявые англицизмы в названиях - не бить, не до того щас :) Поправлю позже.

b707
Онлайн
Зарегистрирован: 26.05.2017

хм... непонятно, почему точек получилось 7, а интервалов 6.  Надо брать точку либо на нижней границе интервала, либо на верхней - но точек должно быть по числу интервалов.

К основной формуле. Прошу прощения, я там выше впопыхах не той стороной дробь повернул. Правильная формула

( Yi * 200) / СУММ (Y *dt)

Соответственно строка 32 правильно будет так:

double pulsesPerTimeUnit = (pointWeight * resultPoints.size())/weightYSum; // импульсов на единицу времени для точки

Если честно, я так и не понял, что ты делаешь в строках с 33 и до конца. Ведь если у тебя есть число импульсов в единицу времени для каждой точки, для получения длины импульса в данной точке достаточно взять обратную pulsesPerTimeUnit величину и отмасштабировать ее на общую продолжительность всей последовательности:

uint32_t pulseWidth = (fullWorkTime)/pulsesPerTimeUnit; // ширина импульса

 

вроде так... в 2 часа ночи голова уже не варит

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

b707 пишет:

вроде так... в 2 часа ночи голова уже не варит

Спасибо, будем посмотреть, у меня это пока, так сказать, набросок. Главное, уже понятно, что к чему.

DetSimen
DetSimen аватар
Онлайн
Зарегистрирован: 25.01.2017

тебе на выходе надо сигнал произвольной формы, которую ты на экране натыкал? 

а выход какой?  analogWrite?  Самое главное, машштаб времён какой?

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

Утро доброе! Я проснулся и  разглядел знакомые буквы ;))). Спал херово, но сам виноват - 0.7 водки не очень хорошо идут с антибиотиками и всякими иммуномодуляторами! 

-

Давай я пойму суть задачи, а то вас, народ, понесло в какие-то дебри? График показывает угловую скорость энкодера? Начальная и конечная w == 0, или это относительный график, показывающий крутильные колебания вращающегося с постоянной средней скоростью вала?

В любом случае - первичен график и рисовать его с острыми углами - не комильфо. Поставь 8 точек и соедини сплайном с нужной гладкостью. Это просто решение системы линейных уравнений. Нескольких систем. Если нужно - поясню, но я про это читал в книжке "Машинная графика" еще в 91 году. ;)) Тогда же и реализовывал. Сплайн - технически грамотное решение, если только внешний вид важен - безье подойдут.

Второе - симуляция импульсов энкодера. Вот просто бери и крути его виртуально с указанной угловой скоростью. Нет никакого смысла в аналитическом решении, если у тебя в руках компьютер мощнее кофемолки. Вот бери и крути с шагом времени в 100 раз мельче разрешения энкодера. У тебя энкодер 200 линий? ну делай шаг в 2*Pi/20000 радиан. Поскольку все величины угловые - все вычисления...ты не поверишь!... линейны!

На выходе у тебя будут все импульсы энкодера: и пересечение с белого на черное и с черного на белое и старт-стоповые линии, если они есть в твоей модели. Даже на СТМ, не говоря про Малино-Апельсино, такое вычисление произойдет по отжатию кнопки. На простой Ардуине - боюсь памяти не хватит, но можно постараться.

----------------------

Извини, если я чего не понял. Значит тупею от вируса! ;))))

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

DetSimen пишет:

тебе на выходе надо сигнал произвольной формы, которую ты на экране натыкал? 

а выход какой?  analogWrite?  Самое главное, машштаб времён какой?

Не, на выходе - паузы между импульсами, в условных единицах (считаем, что микросекунды). Импульсы - фиксированной ширины. Масштаб времён - настраиваемый, условно - общее время работы всего графика равно 120 мс.

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

wdrakula пишет:

График показывает угловую скорость энкодера? 

У тебя энкодер 200 линий? ну делай шаг в 2*Pi/20000 радиан. Поскольку все величины угловые - все вычисления...ты не поверишь!... линейны!

1. Да, зависимость скорости вращения по времени. Начальные и конечные точки графика - это тупо отсутствие импульсов. Подъём по Y - увеличение скорости вращения энкодера, размах по X - общее время работы системы. Кол-во импульсов, которыми надо отразить график - строго фиксировано.

2. Энкодер может быть любой. Кол-во импульсов, которое необходимо сформировать на выходе - тоже может меняться, например, не 200, а 400. Но по результату на выходе надо получить строго указанное кол-во импульсов с правильно рассчитанными временнЫми интервалами между ними. Длительность самого импульса - фиксирована, 10 мкс.

DetSimen
DetSimen аватар
Онлайн
Зарегистрирован: 25.01.2017

DIYMan пишет:

Не, на выходе - паузы между импульсами, в условных единицах (считаем, что микросекунды). Импульсы - фиксированной ширины. Масштаб времён - настраиваемый, условно - общее время работы всего графика равно 120 мс.

Ух ты ж, б-ть.  Опщем, я тоже матан прогуливал. 

DetSimen
DetSimen аватар
Онлайн
Зарегистрирован: 25.01.2017

wdrakula пишет:

тупею от вируса! ;))))

Это всё потому, что ты мало пьёшь. :) 

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

DIYMan пишет:

1. Да, зависимость скорости вращения по времени. Начальные и конечные точки графика - это тупо отсутствие импульсов. Подъём по Y - увеличение скорости вращения энкодера, размах по X - общее время работы системы. Кол-во импульсов, которыми надо отразить график - строго фиксировано.

2. Энкодер может быть любой. Кол-во импульсов, которое необходимо сформировать на выходе - тоже может меняться, например, не 200, а 400. Но по результату на выходе надо получить строго указанное кол-во импульсов с правильно рассчитанными временнЫми интервалами между ними. Длительность самого импульса - фиксирована, 10 мкс.

Ысчо раз: сделай нормальный график угловой скорости и крути виртуальный энкодер. То, что ты написал в 1. - это не технично. подъем, увеличение.. - штаны на лямках! - это уже не отличается от графика угловой скорости, так и сделай его точным. w(0) и w(T) == 0? В начальной и конечной точке омега равна 0?

-----------------------

То есть что сказать хочу? - любой способ расчета "количества импульсов" суть моделирование вращения. Проще пройти по шагам:

новое положение = старое положение + скорость_текущая(из графика) * шаг_времени;

ЕСЛИ новое положение >= линия энкодера ТО

      генерировать импульс();

      линия энкодера += 2*Пи / количество линий энкодера;

==============

;))))) и так далее ;)))) сделай шаг_времени маленьким, у тебя до п..зды ресурсов, и ошибок не будет. Оценочно - хватит 10 шагов на линию, но можно и 100 ;)).

И, заметь, само получится нужно количество импульсов, и все они будут соответствовать зонам графика! ;))))

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

wdrakula пишет:

 Проще пройти по шагам:

новое положение = старое положение + скорость_текущая(из графика) * шаг_времени;

Да рассматривал такой вариант, в общем-то. 

Ребята, всем спасибо, остальное, по сути, дело техники ;)

 

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

Собственно, что-то похожее уже получилось. Взял все точки нарисованного графика, тупо вбил их в наскоро написанную на C# приложуху, написал расчёт длительности задержки перед каждым импульсом, и построил потом по ним график. Виды графиков - совпадают, что и нужно было. Импульсов - сколько нужно на всё время работы. Осталось проверить на железе.

Скрины:

Всем большое спасибо за участие.

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

Так не пойдет! Я ж от любопытства умру! Для чего это?

mykaida
mykaida аватар
Offline
Зарегистрирован: 12.07.2018

Не, ну я еще выпью, но задача уважаемого DIYMan смутно напоминает интегрирование.

Да и график - линейные функции. Вот нахера их интегрировать?

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

wdrakula пишет:
Так не пойдет! Я ж от любопытства умру! Для чего это?

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

Вот для этого вся эта херобора. Построение графика на одной железке-генераторе - это просто, по сути, проверка железки, обрабатывающей энкодер: построил несколько графиков, и смотришь, правильно ли они отрабатываются рабочей железякой. Штанги огромные, тяжёлые, в комнату не помещаются :)

mykaida
mykaida аватар
Offline
Зарегистрирован: 12.07.2018

Дык ускорение это вторая степень от времени, а Вы его аппроксимируете в первую. Херня-с получается.

2 точки - прямая, 3 точки - парабола

b707
Онлайн
Зарегистрирован: 26.05.2017

mykaida пишет:
Не, ну я еще выпью, но задача уважаемого DIYMan смутно напоминает интегрирование.

Да и график - линейные функции. Вот нахера их интегрировать?

mykaida пишет:

Дык ускорение это вторая степень от времени, а Вы его аппроксимируете в первую. Херня-с получается.

а по-моему все нормально. На графике - зависимость скорости от времени, как известно интеграл по скорости - это пройденный путь. Для энкодера "пройденный путь" - это число импульсов, именно его в этой ветке Дийман и ищет.

Так что не зря вам решение напомнило интегрирование, это оно и есть. Все вполне соответствует теории.

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

mykaida пишет:

Дык ускорение это вторая степень от времени, а Вы его аппроксимируете в первую. Херня-с получается.

2 точки - прямая, 3 точки - парабола

Я всё прекрасно понимаю ;) Графики - совпадают, по величинам импульсов видно, как меняется скорость, ускорение на определённом участке - наблюдается. Если участок с ускорением будет занимать немного больше времени (от общего времени работы системы) - то это не сильная беда, тут главное - общая картина изменения скорости по времени. Главное пока, что основные критерии соблюдены - общее количество импульсов, общее время работы системы, и картина изменения скорости по времени. 

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

З.Ы. Это, по сути - просто проверочный инструмент, реальные графики будут сниматься с реальной системы, и записываться как эталонные, уже на другой железяке. Эта же железяка, которая эмулирует работу системы - просто для того, чтобы проверить правильность отработки рабочей железки. Так что прямо супер-пупер точность, а также сглаженность графиков - там не особо тарахтела ;)

sadman41
Offline
Зарегистрирован: 19.10.2016

Да, так движки большие проверяют на разрушение подшипника - дают частоту, снимают график и сравнивают картинки - если не совпадают, то подшипник под замену.

mykaida
mykaida аватар
Offline
Зарегистрирован: 12.07.2018

DIYMan пишет:

Я всё прекрасно понимаю ;) Графики - совпадают, по величинам импульсов видно, как меняется скорость, ускорение на определённом участке - наблюдается. Если участок с ускорением будет занимать немного больше времени (от общего времени работы системы) - то это не сильная беда, тут главное - общая картина изменения скорости по времени. Главное пока, что основные критерии соблюдены - общее количество импульсов, общее время работы системы, и картина изменения скорости по времени. 

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

З.Ы. Это, по сути - просто проверочный инструмент, реальные графики будут сниматься с реальной системы, и записываться как эталонные, уже на другой железяке. Эта же железяка, которая эмулирует работу системы - просто для того, чтобы проверить правильность отработки рабочей железки. Так что прямо супер-пупер точность, а также сглаженность графиков - там не особо тарахтела ;)

Так вот в этом и есть "нюансик". По двум точкам совпадает, а по трем - уже с погрешностью. А Вы это еще собрались интегрировать. Разойдется - зуб даю!

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

mykaida пишет:

Так вот в этом и есть "нюансик". По двум точкам совпадает, а по трем - уже с погрешностью. А Вы это еще собрались интегрировать. Разойдется - зуб даю!

Да и хрен с ним, что разойдётся, главное - общая картина ;) Я полностью осознаю, что оно разойдётся. Но на данном этапе совпадение общей картины - вполне устраивает. В понедельник посмотрим, что к чему.

mykaida
mykaida аватар
Offline
Зарегистрирован: 12.07.2018

DIYMan пишет:

Да и хрен с ним, что разойдётся, главное - общая картина ;) Я полностью осознаю, что оно разойдётся. Но на данном этапе совпадение общей картины - вполне устраивает. В понедельник посмотрим, что к чему.

Да это как раз понятно. Но если края попадут в "особенные" области функций, то Ваш анализ пойдет в шопу.

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

mykaida пишет:

Но если края попадут в "особенные" области функций, то Ваш анализ пойдет в шопу.

Поподробней, пж, про особенные области функций ;) Я ж говорю - прогуливал.

mykaida
mykaida аватар
Offline
Зарегистрирован: 12.07.2018

DIYMan пишет:

Поподробней, пж, про особенные области функций ;) Я ж говорю - прогуливал.

Прогугливать - это правильно! ВИ Вам рассказал про "я плакал". 

А теперь про Вашу задачу: Почему Вы считаете, что кривые между точками замеров - это прямые? Замерьте в 2 раза чаще и постройте параболу. Замерьте в 3 раза чаще и постройте кривую 3-го порядка. И т.д. Где сойдутся - там и правда. А в чем правда, брат...?