Corvus 1 14 июня, 2013 Опубликовано 14 июня, 2013 · Жалоба Добрый день! Есть инкрементный энкодер - разрядность 14 бит. Из-за несовершенства механики значения немного дрожат. Примерно на 1-3 значения. Чтобы избавиться от этого вычисляю арифметическое среднее для нескольких измерений. И всё бы хорошо, но в окрестности нуля такой путь не работает. Т.е. имеем набор значений 0, 0, 1, 16383, 0. После усреднения получим 3277 :( Собственно, вопрос: как лучше всего обойти эту ситуацию? Пока вижу единственный путь - считать обороты и использовать их как старшие биты для значения текущего положения. А потом усреднять. Но может быть есть способ проще? Задача выглядит стандартной, но что-то ничего путного не нагуглилось. Может не так ищу... Заранее спасибо. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
toweroff 1 14 июня, 2013 Опубликовано 14 июня, 2013 · Жалоба Я бы поступил так. Если значение от предыдущего усредненного (или просто предыдущего) "скачет" гораздо больше, чем величина "люфта", то ее вообще не воспринимать Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
BvDV 0 14 июня, 2013 Опубликовано 14 июня, 2013 · Жалоба может быть что то типа этого код содран от сюда #include<math.h> #include<stdio.h> double meanAngle (double *angles, int size) { double y_part = 0, x_part = 0; int i; for (i = 0; i < size; i++) { x_part += cos (angles[i] * M_PI / 180); y_part += sin (angles[i] * M_PI / 180); } return atan2 (y_part / size, x_part / size) * 180 / M_PI; } int main () { double angleSet1[] = { 350, 10 }; double angleSet2[] = { 90, 180, 270, 360}; double angleSet3[] = { 10, 20, 30}; printf ("\nMean Angle for 1st set : %lf degrees", meanAngle (angleSet1, 2)); printf ("\nMean Angle for 2nd set : %lf degrees", meanAngle (angleSet2, 4)); printf ("\nMean Angle for 3rd set : %lf degrees\n", meanAngle (angleSet3, 3)); return 0; } Output: Mean Angle for 1st set : -0.000000 degrees Mean Angle for 2nd set : -90.000000 degrees Mean Angle for 3rd set : 20.000000 degrees Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
toweroff 1 14 июня, 2013 Опубликовано 14 июня, 2013 · Жалоба может быть что то типа этого круто! однако, у ТС целочисленные значения, дабл ему не уперся ни в куда (да еще cos с sin, да еще деление...), да и копипастить тут все могут осмыслите, что пишет человек - значения от 0 до 16384. Логический 0 - это около 16384 и 0. Т.е. 3-2-1-0-16384-16383-16382 кстати.. я тут тоже маху дал. не будет моя идея работать.. надо еще подумать :) ТС, объясните задачу по-конкретнее. Есть у устройства этот самый "0" или нет, должен ли счет остановиться на этом "0" или спокойно может идти в одну или другую сторону Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
cant_101 0 14 июня, 2013 Опубликовано 14 июня, 2013 · Жалоба наиболее просто сделать исключение и принять 2-1-0-16384-16383 за 0. или сделать матрицу на 16383 значений. тогда будет известно с какой стороны приближение к 0. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
toweroff 1 14 июня, 2013 Опубликовано 14 июня, 2013 · Жалоба наиболее просто сделать исключение и принять 2-1-0-16384-16383 за 0. да не должно быть сингулярностей никаких, тогда в районе нуля постоянно будет "прыгать" значение или сделать матрицу на 16383 значений. тогда будет известно с какой стороны приближение к 0. отлично. Отдать 32К контроллера просто под таблицу? (с) Поллитра? Поллитра! Вдребезги? Вдребезги! ДА Я ТЯ!! Кстати. Если это число воспринимать как знаковое, а не как unsigned? Да, точно. Усредняем как знаковое, а результат берем как беззнаковое с той же разрядностью Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
RabidRabbit 0 14 июня, 2013 Опубликовано 14 июня, 2013 · Жалоба Может медианная фильтрация подойдёт? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
slavka012 0 15 июня, 2013 Опубликовано 15 июня, 2013 · Жалоба MixedSigns = (V1 | V2 | V3 | V4) ^ (V1 & V2 & V3 & V4) & 0x2000; If (MixedSigns) { V1 = (V1+0x1000) & 0x3FFF; V2 = (V2+0x1000) & 0x3FFF; V3 = (V3+0x1000) & 0x3FFF; V4 = (V4+0x1000) & 0x3FFF; }; AVG = (V1+V2+V3+V4)>>2; If (MixedSigns) AVG = (AVG - 0x1000) & 0x3FFF; Усредняем как знаковое, а результат берем как беззнаковое с той же разрядностью Не получится, вы просто разрыв в другое место передвинули - из 0x3FFF-0 в 0x2FFF-0x3000 может быть что то типа этого Жесть. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Corvus 1 15 июня, 2013 Опубликовано 15 июня, 2013 · Жалоба ТС, объясните задачу по-конкретнее. Есть у устройства этот самый "0" или нет, должен ли счет остановиться на этом "0" или спокойно может идти в одну или другую сторону Устройство - это энкодер. Ноль, разумеется, есть. Крутиться может в любую сторону. Значение считывается в любой произвольный момент. BvDV Вот это близко к тому, что нужно. :a14: Но сложность вычисления убивает. Попробую поискать в этом направлении, может есть более простые способы. Про знаковые-беззнаковые была идея, но это, действительно, только перемещает проблему по оси в другую точку. Про медианную фильтрацию - можно, конечно. Но, неужели, нет более элегантного и простого решения? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
khlenar 5 15 июня, 2013 Опубликовано 15 июня, 2013 · Жалоба У вас же не на большую велечину прыгают, если после вычислений далее идет например воздействие на привод, то интегратор привода это сгладит, ну или если на ЦАП идет, то на вых. можно поставить фильтр. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
adnega 11 15 июня, 2013 Опубликовано 15 июня, 2013 · Жалоба Ввод цифр от 0 до 9 при помощи энкодера (по типу сейфового замка: "5 по часовой, 3 против часовой"). Может, идея Вам поможет. pos = TIM3->CNT; // считываем положение энкодера if((pos > (encoder_position + (1<<ENCODER_SENCE) - 1)) &&((pos - encoder_position)<((ENCODER_MAX<<ENCODER_SENCE)>>1))) { //pos if(enc_dir == -1) { #ifdef ENCODER_DEBUG tx0_push('#'); tx0_push(' '); #endif //ENCODER_DEBUG tx0_dec(enc_num); #ifdef ENCODER_DEBUG tx0_push(' '); tx0_push('#'); #endif //ENCODER_DEBUG tx0_start(); enc_num = 0; } enc_time = 0; enc_dir = 1; enc_num += (pos - encoder_position)>>ENCODER_SENCE; if(enc_num >= 10) { enc_num = 0; } #ifdef ENCODER_DEBUG tx0_push('['); tx0_dec(enc_num); tx0_push(']'); tx0_start(); #endif //ENCODER_DEBUG encoder_position = pos; }else if((pos < (encoder_position - (1<<ENCODER_SENCE) + 1)) &&((encoder_position - pos)>((ENCODER_MAX<<ENCODER_SENCE)>>1))) { //pos if(enc_dir == -1) { #ifdef ENCODER_DEBUG tx0_push('#'); tx0_push(' '); #endif //ENCODER_DEBUG tx0_dec(enc_num); #ifdef ENCODER_DEBUG tx0_push(' '); tx0_push('#'); #endif //ENCODER_DEBUG tx0_start(); enc_num = 0; } enc_time = 0; enc_dir = 1; enc_num += (pos + (ENCODER_MAX<<ENCODER_SENCE) - encoder_position)>>ENCODER_SENCE; if(enc_num >= 10) { enc_num = 0; } #ifdef ENCODER_DEBUG tx0_push('<'); tx0_dec(enc_num); tx0_push('>'); tx0_start(); #endif //ENCODER_DEBUG encoder_position = pos; }else if((pos < (encoder_position - (1<<ENCODER_SENCE) + 1)) &&((encoder_position - pos)<((ENCODER_MAX<<ENCODER_SENCE)>>1))) { //neg if(enc_dir == 1) { #ifdef ENCODER_DEBUG tx0_push('#'); tx0_push(' '); #endif //ENCODER_DEBUG tx0_dec(enc_num); #ifdef ENCODER_DEBUG tx0_push(' '); tx0_push('#'); #endif //ENCODER_DEBUG tx0_start(); enc_num = 0; } enc_time = 0; enc_dir = -1; enc_num += (encoder_position - pos)>>ENCODER_SENCE; if(enc_num >= 10) { enc_num = 0; } #ifdef ENCODER_DEBUG tx0_push('('); tx0_dec(enc_num); tx0_push(')'); tx0_start(); #endif //ENCODER_DEBUG encoder_position = pos; }else if((pos < (encoder_position + (ENCODER_MAX<<ENCODER_SENCE) - (1<<ENCODER_SENCE) + 1)) &&((pos - encoder_position)>((ENCODER_MAX<<ENCODER_SENCE)>>1))) { //neg if(enc_dir == 1) { #ifdef ENCODER_DEBUG tx0_push('#'); tx0_push(' '); #endif //ENCODER_DEBUG tx0_dec(enc_num); #ifdef ENCODER_DEBUG tx0_push(' '); tx0_push('#'); #endif //ENCODER_DEBUG tx0_start(); enc_num = 0; } enc_time = 0; enc_dir = -1; enc_num += (encoder_position + (ENCODER_MAX<<ENCODER_SENCE) - pos)>>ENCODER_SENCE; if(enc_num >= 10) { enc_num = 0; } #ifdef ENCODER_DEBUG tx0_push('{'); tx0_dec(enc_num); tx0_push('}'); tx0_start(); #endif //ENCODER_DEBUG encoder_position = pos; } if(enc_time < (TO_SEC * 2)) { enc_time++; if(enc_time == ((TO_SEC * 2))) { #ifdef ENCODER_DEBUG tx0_push('#'); tx0_push(' '); #endif //ENCODER_DEBUG tx0_dec(enc_num); #ifdef ENCODER_DEBUG tx0_push(' '); tx0_push('#'); #endif //ENCODER_DEBUG tx0_str("\n\r"); tx0_start(); enc_dir = 0; enc_num = 0; } } Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Alex11 6 15 июня, 2013 Опубликовано 15 июня, 2013 · Жалоба А если по-простому? У Вас же не бывает скачков на четверть оборота? Сначала по первому принятому значению оцениваете квадрант, затем применяете знаковое или беззнаковое усреднение. Должно получиться достаточно компактно. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Corvus 1 15 июня, 2013 Опубликовано 15 июня, 2013 · Жалоба adnega Спасибо. Сейчас попробую разобраться. А если по-простому? У Вас же не бывает скачков на четверть оборота? Сначала по первому принятому значению оцениваете квадрант, затем применяете знаковое или беззнаковое усреднение. Должно получиться достаточно компактно. На четверть не бывает. Первое значение и все последующие могут быть в разных квадрантах. Спасибо всем за идеи :a14: Краткое резюме: Вариант нахождения среднего угла. В лоб - через синусы с арктангенсами. Алгоритм красивый - не требует введения разнообразных граничных условий и возможность ошибки минимальна. На вычислительная сложность высокая. Наверняка, можно упростить вычисления. Нашёл ветку на stackoverflow http://stackoverflow.com/questions/491738/...a-set-of-angles разбираюсь. Вариант второй - анализ входных данных и их фильтрация. Вычислительная сложность - низкая. Но вероятность ошибки, и возможность нарваться на такой набор данных, где алгоритм даст ошибку - высока. Так что требуется тщательное тестирование. Буду думать и проверять :smile3046: UPD Нашёл, как эта задача называется по-аглицки. Mean of circular quantities http://en.wikipedia.org/wiki/Mean_of_circular_quantities Из обсуждения на SO выбрал следующий код для среднего 2-х значений. Дальше в цикл и готово diff = (a-b+24576)%16384-8192; avg = (16384 + b + (diff/2)) % 16384; Прогнал в экселе, вроде всё корректно. Ещё раз всем спасибо. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Fujitser 0 16 июня, 2013 Опубликовано 16 июня, 2013 · Жалоба Просто используйте числа со знаком. Тогда 16384 преобразуется в -1, и усреднение будет работать. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
ae_ 2 16 июня, 2013 Опубликовано 16 июня, 2013 · Жалоба Просто используйте числа со знаком. Тогда 16384 преобразуется в -1, и усреднение будет работать. Уже предлагали выше, это не устраняет проблему, а сдвигает её на пол-оборота. Около нуля будет хорошо: -1,0,+1, а середине будет +8191,-8192,-8191, тот же самый скачок на 16384. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться