(обновление: я разместил решение и код в качестве ответа, а не редактировал вопрос снова)
Идеальная линия (пунктирная красная линия) — это график от начальной точки со средним повышением, добавленным с каждым углом измерения; это я получаю через среднее. Я измерил данные теста черным цветом. Как я могу измерить площадь падения синим цветом? Ось X унифицирована, поэтому уклоны и математика упрощены.
Я мог бы определить отсечение по размеру таких областей, как эта, а затем пометить эту часть для повторного тестирования или неудачи. Редко бывает еще один провал, который появляется ближе к праву, но установка предельного значения для стандартного отклонения обычно не дает этих частей.
Ответ Диего помог мне визуализировать это. Теперь, когда я вижу, что я пытаюсь сделать, я буду работать над алгоритмом для реализации «самодельного погружного детектора». 🙂
Я создал испытательный стенд для проверки датчиков положения дроссельной заслонки продаю. Я пытаюсь количественно определить, насколько прямолинейен график, анализируя собранные данные. Эта одна конкретная модель раздражает меня.
Пример участка, который я предпочитаю не продавать:
Ось X — это равномерно распределенные углы открытия дроссельной заслонки. Шаговый двигатель поворачивает входной вал, останавливаясь каждые 0,75 °, чтобы измерить выходной сигнал на 10-битном АЦП, который переводится в ось Y. Сюжет является переводом data[idx]
в idx,value
сопоставлены с (x,y)
растровые координаты. Затем я рисую линии между точками внутри растрового изображения, используя алгоритм Брезенхэма.
Мои другие продукты TPS производят удивительно линейный выход.
Нижняя (левая) часть участка имеет решающее значение для нормального использования любого транспортного средства; это когда вы едете по городу, въезжаете на стоянки и т. д. Эта конкретная часть имеет тенденцию к развороту на 15 °, и я хочу использовать программу для количественной оценки этого «наклона» на кривой и меньше полагаться на интуиция тестера. В приведенном выше примере график наклоняется, но не возвращается к идеальной линии.
Несмотря на то, что это встроенное приложение, печать отчета занимает 10 секунд, поэтому я не считаю, что проходить массив из 120 точек данных несколько раз — пустая трата циклов. Кроме того, так как я использую микроконтроллер uC32 PIC32, там много памяти, поэтому я могу позволить себе эту проблему в контроллере.
Массив подъема между контрольными точками: Я полностью отклоняю ось X, считая ее унифицированной, и затем делаю массив изменений от одного чтения к другому. Этот массив способствует тому, что в отчете «Минимальное повышение между точками: 0 Максимальное: 14». Я называю этот массив дельт.
Я пытался использовать стандартное отклонение на дельт, однако, во время тестирования я обнаружил, что низкий Std Dev не является надежной мерой для этой части. Если провал быстро возвращается к исходной линии, подразумеваемой ранними точками данных, Std Dev может быть обманчиво низким (наблюдалось, что он составляет всего 2,3), но эту часть я все еще не хотел бы использовать. Я попытался установить отсечку в 2,6, но она провалила слишком много частей с отличными графиками. Другая, более линейная часть, связанная с вышеупомянутым, может надежно рассчитывать на Std Dev по качеству.
эксцесс кажется, не подходит для этой ситуации вообще. Я узнал о эксцесс сегодня и нашел Библиотека статистики который включает в себя куртоз и асимметрию. Во время продолжительного тестирования я обнаружил, что в этих двух измерениях не было тенденции к положительному, отрицательному или амплитудному значению, которое соответствовало бы либо прохождению, либо провалу. Тот же самый джентльмен имеет библиотеку линейной регрессии, но я считаю, что Лин Рег не имеет отношения к моей ситуации, так как я согласен с предположением о AVG дельт быть моей идеальной линией. Линейная регрессия и R ^ 2 больше для нахождения линии из менее идеальных данных или гораздо больших наборов.
Сравнивая каждую дельту с AVG и Std Dev Я настроил монитор, чтобы проверить каждую дельту относительно окончательного среднего значения дельтданные. Здесь я тоже не смог найти надежную метрику. Слишком много хороших деталей не пройдет тест, ограничивающий любую дельту в пределах 2x стандартного отклонения от среднего. В конечном счете, единственное отличие от AVG, на котором я мог бы остановиться — это быть в пределах AVG+Std Dev
отличие от самой AVG. Что-то более ограничительное могло бы потерпеть неудачу, иначе хорошие части. И неуловимое падение на 15 ° может пройти через этот тест.
Самодельный DIP-детектор При кормлении дельт к последовательному монитору компьютера я заметил последовательный негатив дельт во время провала, так что я запрограммировал детектор провала, но мне это кажется очень грубым. Если есть 5 или более отрицательных дельт подряд я их суммирую. Я видел, что если я возьму эту сумму отличий провала от AVG, а затем разделю на число отрицательных дельт, значение более 2,9 или 3 может означать сбой. Я наблюдал провалы продолжительностью от 6 до 15 дельт. Легко наблюдаемые провалы будут отличаться от суммы AVG до -35.
Тенденция накопленного отклонения от AVG Вышеизложенное заставило меня задуматься, наблюдая за суммированием дельт поскольку это уходит от AVG, может быть ответом. То есть я перебираю массив и суммирую отличия каждой дельты от AVG. Я думал, что был к чему-то, пока большая часть не подорвала эту теорию. Я наблюдал тенденцию изменения количества бегущих сумм от AVG
менее чем 2x AVG
, тем более прямая линия появилась. Многие идеальные части показывают только 8 или менее дельта-точек, где sumOfDiffs
будет уходить от AVG очень далеко.
float sumOfDiffs=0.0;
for( int idx=0; idx<stop; idx++ ){
float spread = deltas[idx] - line->AdcAvgRise;
sumOfDiffs = sumOfDiffs + spread;
...
testVal = 2*line->AdcAvgRise;
if( sumOfDiffs > testVal || sumOfDiffs < -testVal ){
flag = 'S';
}
...
}
А затем появилась часть с фантастическим линейным сюжетом с 58 точками данных, где sumOfDiffs
был более чем в два раза больше AVG! Я нахожу это удивительным, так как в конце ~ 120 точек данных, sumOfDiffs
значение -0,000057.
Во время тестирования финал sumOfDiffs
результат часто регистрируется как 0.000000, и только на очень плохих деталях он будет больше, чем .000100. На самом деле я нахожу это довольно удивительным: как «плохая часть» могла накопить большую точность.
Пример вывода из мониторинга sumOfDiffs Этот вывод ниже показывает падение. Тест наблюдает, как работает sumOfDiffs
более чем в 2 раза больше AVG от AVG для всего теста. Это падение длится от дельт idx
от 23 до 49; начинается с 17,25 ° и длится 19,5 °.
Avg rise: 6.75 Std dev: 2.577
idx: delta diff from avg sumOfDiffs Flag
23: 5 -1.75 -14.05 S
24: 6 -0.75 -14.80 S
25: 7 0.25 -14.55 S
26: 5 -1.75 -16.30 S
27: 3 -3.75 -20.06 S
28: 3 -3.75 -23.81 S
29: 7 0.25 -23.56 S
30: 4 -2.75 -26.31 S
31: 2 -4.75 -31.06 S
32: 8 1.25 -29.82 S
33: 6 -0.75 -30.57 S
34: 9 2.25 -28.32 S
35: 8 1.25 -27.07 S
36: 5 -1.75 -28.82 S
37: 15 8.25 -20.58 S
38: 7 0.25 -20.33 S
39: 5 -1.75 -22.08 S
40: 9 2.25 -19.83 S
41: 10 3.25 -16.58 S
42: 9 2.25 -14.34 S
43: 3 -3.75 -18.09 S
44: 6 -0.75 -18.84 S
45: 11 4.25 -14.59 S
47: 3 -3.75 -16.10 S
48: 8 1.25 -14.85 S
49: 8 1.25 -13.60 S
Final Sum of diffs: 0.000030
RunningStats analysis:
NumDataValues= 125
Mean= 6.752
StandardDeviation= 2.577
Skewness= 0.251
Kurtosis= -0.277
Отрезвляющая записка о качестве: В этом путешествии я узнал, что крупные поставщики автомобильных комплектующих считают стандартным показателем этих деталей 4-точечный тест. мой первый испытательный стенд использовал Arduino с 8 КБ ОЗУ, не имел ни TFT-дисплея, ни принтера, а механическое разрешение составляло всего 3 °! Тогда я просто проверил дельт находясь в произвольных общих пределах и выбирая предел того, насколько большой может быть любая отдельная дельта. Мой тест на 120 баллов кажется более высоким по сравнению с предыдущим тестом на 30 баллов, но этот тест не имел понятия об этих провалах.
Y_dev = Y_data - Y_straight
математически то же самое) с этой процедурой:
PositiveMax = 0; NegativeMax = 0;
tmp_Area
tmp_Area = Y_dev;
к текущему значению, начиная таким образом, новое накоплениеY_dev
ниже порога вы его не накапливаете.Оказывается, результат моей интуиции, а метод Диего — это среднее от интеграла. Мне все еще не нравится это имя, поэтому я описал алгоритм и спросил на Math.SE, как это назвать, и он был перенесен в «Cross Validated», Stats.SE .
Я обновил графики после массового редактирования моего вопроса по Math.SE. Оказывается, я беру среднее из замкнутого интеграла от производной данных. : P Сначала мы собираем данные:
Далее идет «производная»: пошагово пройдитесь по исходному массиву данных, чтобы сформировать дельт массив, который представляет собой повышение значений АЦП от одного шага 0,75 ° до следующего. «Подъем» или «наклон» — это то, что является производной: dy / dx.
С «уклоном» или средним уровнем, я могу найти несколько отрицательных дельт подряд сложите их, затем разделите на число в конце падения. Сумма является интегралом площади между средним и дельт и когда провал становится положительным, я могу разделить сумму на количество провалов.
Во время тестирования я придумал значение отсечения для этого среднего значения интеграла в 2,6. Это была отличная мера моего «внутреннего инстинкта», когда я смотрел на сюжет, думая, что часть была хорошей или плохой.
На случай, если кто-то другой попытается измерить это, вот код, который я реализовал. Обратите внимание, что он ищет только отрицательные падения. Кроме того, dipCountLimit определено в другом месте как 5. В дополнение к детектору / аккумулятору провалов (т.е. числовому интегратору) у меня также есть детектор спайков, который произвольно помечает тест как плохой, если какие-либо точки данных отклоняются от среднего на величину среднего + стандартного отклонение. AVG + STD DEV в качестве предела шипа был выбран произвольно на основе наблюдаемых графиков частей, которые он потерпит неудачу.
int dipdx=0;
// inDipFlag also counts the length of this dip
int inDipFlag=0;
float dips[140] = { 0.0 };
for( int idx=0; idx<stop; idx++ ){
const float diffFromAvg = deltas[idx] - line->AdcAvgRise;
// state machine to monitor dips
const int _stop = stop-1;
if( diffFromAvg < 0 && idx < _stop ) {
// check NEXT data point for negative diff & set dipFlag to put state in dip
const float nextDiff = deltas[idx+1] - line->AdcAvgRise;
if( nextDiff < 0 && inDipFlag == 0 )
inDipFlag = 1;
// already IN a dip, and next diff is negative
if( nextDiff < 0 && inDipFlag > 0 ) {
inDipFlag++;
}
// accumulate this dip
dips[dipdx]+= diffFromAvg;
// next data point ends this dip and we advance dipdx to next dip
if( inDipFlag > 0 && nextDiff > 0 ) {
if( inDipFlag < dipCountLimit ){
// reset the accumulator, do not advance dipdx to next entry
dips[dipdx]=0.0;
} else {
// change this entry's value from dip sum to its ratio
dips[dipdx] = -dips[dipdx]/inDipFlag;
// advance dipdx to next entry
dipdx++;
}
// Next diff isn't negative, so the dip is done
inDipFlag = 0;
}
}
}