Перейти к содержанию
    

Усреднение значений энкодера

Добрый день!

 

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

 

Собственно, вопрос: как лучше всего обойти эту ситуацию?

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

 

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

 

Заранее спасибо.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

может быть что то типа этого

 

код содран от сюда

#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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

может быть что то типа этого

круто!

 

однако, у ТС целочисленные значения, дабл ему не уперся ни в куда (да еще cos с sin, да еще деление...), да и копипастить тут все могут

осмыслите, что пишет человек - значения от 0 до 16384. Логический 0 - это около 16384 и 0. Т.е. 3-2-1-0-16384-16383-16382

 

кстати.. я тут тоже маху дал.

не будет моя идея работать.. надо еще подумать :)

 

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

наиболее просто сделать исключение и принять 2-1-0-16384-16383 за 0.

 

или сделать матрицу на 16383 значений. тогда будет известно с какой стороны приближение к 0.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

наиболее просто сделать исключение и принять 2-1-0-16384-16383 за 0.

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

 

или сделать матрицу на 16383 значений. тогда будет известно с какой стороны приближение к 0.

отлично. Отдать 32К контроллера просто под таблицу?

(с) Поллитра? Поллитра! Вдребезги? Вдребезги! ДА Я ТЯ!!

 

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

 

 

Да, точно.

Усредняем как знаковое, а результат берем как беззнаковое с той же разрядностью

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

 

 

может быть что то типа этого

Жесть.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

 

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

 

BvDV

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

 

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

 

Про медианную фильтрацию - можно, конечно. Но, неужели, нет более элегантного и простого решения? :wacko:

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

У вас же не на большую велечину прыгают, если после вычислений далее идет например воздействие на привод, то интегратор привода это сгладит, ну

или если на ЦАП идет, то на вых. можно поставить фильтр.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Ввод цифр от 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;
	}
}

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

А если по-простому? У Вас же не бывает скачков на четверть оборота? Сначала по первому принятому значению оцениваете квадрант, затем применяете знаковое или беззнаковое усреднение. Должно получиться достаточно компактно.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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-х значений. Дальше в цикл и готово :biggrin:

 

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

 

Прогнал в экселе, вроде всё корректно. Ещё раз всем спасибо.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Просто используйте числа со знаком. Тогда 16384 преобразуется в -1, и усреднение будет работать.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Просто используйте числа со знаком. Тогда 16384 преобразуется в -1, и усреднение будет работать.

Уже предлагали выше, это не устраняет проблему, а сдвигает её на пол-оборота.

Около нуля будет хорошо: -1,0,+1, а середине будет +8191,-8192,-8191, тот же самый скачок на 16384.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...