Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Усреднение значений энкодера
Форум разработчиков электроники ELECTRONIX.ru > Сайт и форум > В помощь начинающему > Программирование
Corvus
Добрый день!

Есть инкрементный энкодер - разрядность 14 бит. Из-за несовершенства механики значения немного дрожат. Примерно на 1-3 значения. Чтобы избавиться от этого вычисляю арифметическое среднее для нескольких измерений. И всё бы хорошо, но в окрестности нуля такой путь не работает. Т.е. имеем набор значений 0, 0, 1, 16383, 0. После усреднения получим 3277 sad.gif

Собственно, вопрос: как лучше всего обойти эту ситуацию?
Пока вижу единственный путь - считать обороты и использовать их как старшие биты для значения текущего положения. А потом усреднять.

Но может быть есть способ проще? Задача выглядит стандартной, но что-то ничего путного не нагуглилось. Может не так ищу...

Заранее спасибо.
toweroff
Я бы поступил так. Если значение от предыдущего усредненного (или просто предыдущего) "скачет" гораздо больше, чем величина "люфта", то ее вообще не воспринимать
BvDV
может быть что то типа этого

код содран от сюда
Код
#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
Цитата(BvDV @ Jun 14 2013, 22:50) *
может быть что то типа этого

круто!

однако, у ТС целочисленные значения, дабл ему не уперся ни в куда (да еще cos с sin, да еще деление...), да и копипастить тут все могут
осмыслите, что пишет человек - значения от 0 до 16384. Логический 0 - это около 16384 и 0. Т.е. 3-2-1-0-16384-16383-16382

кстати.. я тут тоже маху дал.
не будет моя идея работать.. надо еще подумать sm.gif

ТС, объясните задачу по-конкретнее. Есть у устройства этот самый "0" или нет, должен ли счет остановиться на этом "0" или спокойно может идти в одну или другую сторону
cant
наиболее просто сделать исключение и принять 2-1-0-16384-16383 за 0.

или сделать матрицу на 16383 значений. тогда будет известно с какой стороны приближение к 0.
toweroff
Цитата(cant @ Jun 14 2013, 23:21) *
наиболее просто сделать исключение и принять 2-1-0-16384-16383 за 0.

да не должно быть сингулярностей никаких, тогда в районе нуля постоянно будет "прыгать" значение

Цитата(cant @ Jun 14 2013, 23:21) *
или сделать матрицу на 16383 значений. тогда будет известно с какой стороны приближение к 0.

отлично. Отдать 32К контроллера просто под таблицу?
(с) Поллитра? Поллитра! Вдребезги? Вдребезги! ДА Я ТЯ!!

Кстати. Если это число воспринимать как знаковое, а не как unsigned?


Да, точно.
Усредняем как знаковое, а результат берем как беззнаковое с той же разрядностью
RabidRabbit
Может медианная фильтрация подойдёт?
ar__systems
Код
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;


Цитата(toweroff @ Jun 14 2013, 15:00) *
Усредняем как знаковое, а результат берем как беззнаковое с той же разрядностью

Не получится, вы просто разрыв в другое место передвинули - из 0x3FFF-0 в 0x2FFF-0x3000


Цитата(BvDV @ Jun 14 2013, 13:50) *
может быть что то типа этого

Жесть.
Corvus
Цитата(toweroff @ Jun 14 2013, 23:17) *
ТС, объясните задачу по-конкретнее. Есть у устройства этот самый "0" или нет, должен ли счет остановиться на этом "0" или спокойно может идти в одну или другую сторону


Устройство - это энкодер. Ноль, разумеется, есть. Крутиться может в любую сторону. Значение считывается в любой произвольный момент.

BvDV
Вот это близко к тому, что нужно. a14.gif Но сложность вычисления убивает. Попробую поискать в этом направлении, может есть более простые способы.

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

Про медианную фильтрацию - можно, конечно. Но, неужели, нет более элегантного и простого решения? wacko.gif
Iptash
У вас же не на большую велечину прыгают, если после вычислений далее идет например воздействие на привод, то интегратор привода это сгладит, ну
или если на ЦАП идет, то на вых. можно поставить фильтр.
adnega
Ввод цифр от 0 до 9 при помощи энкодера (по типу сейфового замка: "5 по часовой, 3 против часовой").
Может, идея Вам поможет.
CODE

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
А если по-простому? У Вас же не бывает скачков на четверть оборота? Сначала по первому принятому значению оцениваете квадрант, затем применяете знаковое или беззнаковое усреднение. Должно получиться достаточно компактно.
Corvus
adnega
Спасибо. Сейчас попробую разобраться.

Цитата(Alex11 @ Jun 15 2013, 13:15) *
А если по-простому? У Вас же не бывает скачков на четверть оборота? Сначала по первому принятому значению оцениваете квадрант, затем применяете знаковое или беззнаковое усреднение. Должно получиться достаточно компактно.


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

Спасибо всем за идеи a14.gif

Краткое резюме:
Вариант нахождения среднего угла. В лоб - через синусы с арктангенсами. Алгоритм красивый - не требует введения разнообразных граничных условий и возможность ошибки минимальна. На вычислительная сложность высокая. Наверняка, можно упростить вычисления.
Нашёл ветку на stackoverflow http://stackoverflow.com/questions/491738/...a-set-of-angles
разбираюсь.

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

Буду думать и проверять smile3046.gif

UPD
Нашёл, как эта задача называется по-аглицки. Mean of circular quantities
http://en.wikipedia.org/wiki/Mean_of_circular_quantities

Из обсуждения на SO выбрал следующий код для среднего 2-х значений. Дальше в цикл и готово biggrin.gif

Код
diff = (a-b+24576)%16384-8192;
avg = (16384 + b + (diff/2)) % 16384;


Прогнал в экселе, вроде всё корректно. Ещё раз всем спасибо.
Fujitser
Просто используйте числа со знаком. Тогда 16384 преобразуется в -1, и усреднение будет работать.
ae_
Цитата(Fujitser @ Jun 16 2013, 19:14) *
Просто используйте числа со знаком. Тогда 16384 преобразуется в -1, и усреднение будет работать.

Уже предлагали выше, это не устраняет проблему, а сдвигает её на пол-оборота.
Около нуля будет хорошо: -1,0,+1, а середине будет +8191,-8192,-8191, тот же самый скачок на 16384.
АНТОН КОЗЛОВ
Обрабатывая сигналы от ветряка, аналогичные экондерным, усредняли синус уголка, и вычисляли арксинус.
MaslovVG
В промышленных CNC Для определения положения по датчику применяются коррекции записанные в файлы (таблицы). Для движения вперёд, назад, температурная и прочие. Шаг таблицы и значения определяются при калибровке станка. (например лазерным имерителем). Используется кусочно линейная апроксимация.
ViKo
Весь диапазон значений энкодера 0...16384 разбиваем на 2 участка, на одном считаем, что число беззнаковое, на другом переводим в число со знаком, усредняем, и затем преобразуем обратно. Участки - поделим диапазон пополам:
0...4096 - со знаком
4096...12288 - без знака
12288...16384 - со знаком (-4096...0)
Границы выбраны условно. Важно работать только по одному варианту для значений, находящихся на границе. Поэтому, как только значение дошло до границы, принудительно переключаемся на другой вариант расчета.
Например, значение приближается к 12288 - переключаемся на расчет со знаком, превращаем значение в -4096. Вряд ли погрешность энкодера составит 1/4 оборота, так, что текущее значение выскочит за пределы -8192.
maksimp
Код
#define N 5
uint16_t x[N]; // их будем усренять

int16_t s;
int16_t i;
s=0;
for(i=1;i<N;i++)
{
  s+=(int16_t)((x[i]-x[0]+0x2000)&0x3fff)-0x2000;
}
if(s>0) s=(s+N/2)/N; // деление с округлением к ближайшему
else s=(s-N/2)/N;
r=x[0]+s; // результат

Для правильной работы при очень быстром вращении лучше сделать int32_t s;
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.