Получение компьютера понять 360 градусов = 0 градусов, вращение орудийной башни

Недавно я обнаружил EQATEC Profiler http://www.eqatec.com/tools/profiler . Он работает с большинством версий .NET и на множестве платформ. Он прост в использовании, а его части бесплатны даже для коммерческого использования.

19
задан RCIX 26 November 2010 в 22:15
поделиться

12 ответов

Если вам нужно повернуть турель более чем на 180 градусов в одном направлении, тогда будет быстрее повернуть в другом направлении.

Я бы просто проверил это, а затем повернул в соответствующем направлении

if (objdeg != gundeg)
{
    if ((gundeg - objdeg) > 180)
       gundeg++;
    else
       gundeg--;
}

РЕДАКТИРОВАТЬ: Новое решение

Я усовершенствовал свое решение на основе отзывов в комментариях. Это определяет, находится ли цель «слева или справа» от турели, и решает, в какую сторону повернуть. Затем он меняет это направление, если цель находится на расстоянии более 180 градусов.

if (objdeg != gundeg)
{
  int change = 0;
  int diff = (gundeg - objdeg)%360;
  if (diff < 0)
     change = 1;
  else
     change = -1;

  if (Math.Abs(diff) > 180)
     change = 0 - change;

  gundeg += change;
 }
20
ответ дан 30 November 2019 в 01:53
поделиться

Вы можете полностью избежать деления (и модуляции), если представите свои углы в чем-то, называемом «BAMS», что означает двоичная система измерения углов. Идея состоит в том, что если вы сохраняете свои углы в N-битном целом числе, вы используете весь диапазон этого целого числа для представления угла. Таким образом, не нужно беспокоиться о переполнении после 360, потому что естественные свойства по модулю 2 ^ N вашего представления позаботятся об этом за вас.

Например, допустим, вы используете 8 бит. Это разрежет ваш круг на 256 возможных ориентаций. (Вы можете выбрать больше бит, но для примера удобно 8). Пусть 0x00 означает 0 градусов, 0x40 означает 90 градусов, 0x80 - 180 градусов, а 0xC0 - 270 градусов. Не беспокойтесь о «знаке», опять же, BAMS естественен для углов. Если вы интерпретируете 0xC0 как ' беззнаковый 'и масштабируется до 360/256 градусов на счет, ваш угол равен (+192) (360/256) = +270; но если вы интерпретируете 0xC0 как «подписанный», ваш угол будет (-64) (360/256) = -90. Обратите внимание, что -90 и +270 означают одно и то же в угловых терминах.

Если вы хотите применить триггерные функции к вашим углам BAMS, вы можете предварительно вычислить таблицы. Есть уловки, чтобы уменьшить столы, но вы можете видеть, что столы не такие уж большие. Для хранения всей таблицы синусов и косинусов двойной точности для 8-битных BAMS не требуется более 4 КБ памяти, что в сегодняшних условиях.

Поскольку вы упомянули об использовании этого в игре, вы, вероятно, могли бы уйти. с 8-битным или 10-битным представлением. Каждый раз, когда вы добавляете или вычитаете углы, вы можете преобразовать результат в N бит с помощью логической операции И, например, angle & = 0x00FF для 8 бит.

ЗАБЫЛИ НАИЛУЧШУЮ ЧАСТЬ (править)

Проблема поворота направо против поворота налево легко решается в системе BAMS. Просто обратите внимание на разницу и убедитесь, что вы сохранили только N значащих битов. Интерпретация MSB как знакового бита указывает, в какую сторону вам следует повернуть. Если разница отрицательная, поверните в противоположную сторону на abs () разницы.

Эта уродливая маленькая программа на языке C демонстрирует. Попробуйте сначала ввести 20 10 и 20 30. Затем попробуйте обмануть его, обернув вокруг нулевой точки. Дайте 20-10, он повернет налево. Дайте 20 350, он все равно поворачивает налево. Обратите внимание, что, поскольку это 8 бит, 181 неотличимо от 180, поэтому не удивляйтесь, если вы скармливаете ему 20 201, и он поворачивается вправо, а не влево - в разрешении, обеспечиваемом восемью битами, поворот налево и поворот направо в этом случае одинаковы. Введите 20 205, и он будет короче.

#include <stdio.h>
#include <math.h>

#define TOBAMS(x)    (((x)/360.0) * 256)
#define TODEGS(b)    (((b)/256.0) * 360)

int main(void)
{
    double a1, a2;     // "real" angles
    int b1, b2, b3;    // BAMS angles


    // get some input
    printf("Start Angle ? ");
    scanf("%lf", &a1);

    printf("Goal Angle ? ");
    scanf("%lf", &a2);

    b1 = TOBAMS(a1);
    b2 = TOBAMS(a2);

    // difference increases with increasing goal angle
    // difference decreases with increasing start angle
    b3 = b2 - b1;
    b3 &= 0xff;

    printf("Start at %7.2lf deg and go to %7.2lf deg\n", a1, a2);
    printf("BAMS   are 0x%02X and 0x%02X\n", b1, b2);
    printf("BAMS diff is 0x%02X\n", b3);

    // check what would be the 'sign bit' of the difference
    // negative (msb set) means turn one way, positive the other
    if( b3 & 0x80 )
    {
        // difference is negative; negate to recover the
        // DISTANCE to move, since the negative-ness just
        // indicates direction.

        // cheap 2's complement on an N-bit value:
        // invert, increment, trim
        b3 ^= -1;       // XOR -1 inverts all the bits
        b3 += 1;        // "add 1 to x" :P
        b3 &= 0xFF;     // retain only N bits

        // difference is already positive, can just use it
        printf("Turn left %lf degrees\n", TODEGS(b3));
        printf("Turn left %d counts\n", b3);
    }
    else
    {
        printf("Turn right %lf degrees\n", TODEGS(b3));
        printf("Turn right %d counts\n", b3);
    }

    return 0;
}
23
ответ дан 30 November 2019 в 01:53
поделиться

Просто сравните следующее:

gundeg - objdeg
objdeg - gundeg 
gundeg - objdeg + 360
objdeg - gundeg + 360

и выберите тот с минимальным абсолютным значением .

6
ответ дан 30 November 2019 в 01:53
поделиться

Вот рабочий пример C #, это верный путь. :

public class Rotater
{
    int _position;
    public Rotater()
    {

    }
    public int Position
    {
        get
        {
            return _position;
        }
        set            
        {
            if (value < 0)
            {
                _position = 360 + value;
            }
            else
            {
                _position = value;
            }
            _position %= 360;
        }
    }
    public bool RotateTowardsEx(int item)
    {
        if (item > Position)
        {
            if (item - Position < 180)
            {
                Position++;
            }
            else
            {
                Position--;
            }
            return false;
        }
        else if (Position > item)
        {
            if (Position - item < 180)
            {
                Position--;
            }
            else
            {
                Position++;
            }
            return false;
        }
        else
        {
            return true;
        }
    }
}

    static void Main(string[] args)
    {


        do
        {
            Rotater rot = new Rotater();
            Console.Write("Enter Starting Point: ");
            var startingPoint = int.Parse(Console.ReadLine());
            rot.Position = startingPoint;
            int turns = 0;

            Console.Write("Enter Item Point: ");
            var item = int.Parse(Console.ReadLine());
            while (!rot.RotateTowardsEx(item))
            {
                turns++;
            }
            Console.WriteLine(string.Format("{0} turns to go from {1} to {2}", turns, startingPoint, item));
        } while (Console.ReadLine() != "q");


    }

Благодарим Джона Пири за вдохновение.

Редактировать: Мне не понравилось мое средство определения позиции, поэтому я убрал его

5
ответ дан 30 November 2019 в 01:53
поделиться

Вам нужно решить, повернуть влево или вправо, в зависимости от того, какое расстояние меньше. Затем вам нужно будет взять модуль:

if (objdeg > gundeg)
{
    if (objdeg - gundeg < 180)
    {
        gundeg++;
    }
    else
    {
        gundeg--;
    }
}
if (objdeg < gundeg)
{
    if (gundeg - objdeg < 180)
    {
        gundeg--;
    }
    else
    {
        gundeg++;
    }
}
if (gundeg < 0)
{
    gundeg += 360;
}
gundeg = gundeg % 360;
3
ответ дан 30 November 2019 в 01:53
поделиться

Проблема заключается в том, чтобы найти направление, которое даст кратчайшее расстояние.

Однако вычитание может привести к отрицательным числам, и это необходимо учитывать.
Если вы перемещаете пистолет на один шаг при каждой проверке, я не знаю, когда вы сделаете модуль упругости.
И, если вы хотите переместить ружье за ​​один шаг, вы должны правильно добавить / вычесть дельту.

С этой целью Киршштейн , кажется, думает ближе всех ко мне.
Я работаю с целым числом в этом простом псудокоде.

if (objdeg != gundeg)
{
    // we still need to move the gun
    delta = gundeg - objdeg
    if (delta > 0)
        if (unsigned(delta) > 180)
           gundeg++;
        else
           gundeg--;
    else // delta < 0
        if (unsigned(delta) > 180)
           gundeg--;        
        else
           gundeg++;

    if (gundeg == 360)
        gundeg = 0;
    else if (gundeg == -1)
        gundeg = 359;
}

Попытайтесь работать с этим постепенно, используя gundeg = 10 и objdeg = 350, чтобы увидеть, как Gundeg будет перемещен с 10 на 0, а затем с 359 до 350.

0
ответ дан 30 November 2019 в 01:53
поделиться

Вот как я недавно реализовал нечто подобное в игре:

double gundeg;

// ...

double normalizeAngle(double angle)
{
    while (angle >= 180.0)
    {
        angle -= 360.0;
    }
    while (angle < -180.0)
    {
       angle += 360.0;
    }
    return angle;
}

double aimAt(double objAngle)
{
    double difference = normalizeAngle(objdeg - gundeg);
    gundeg = normalizeAngle(gundeg + signum(difference));
}

Все угловые переменные ограничены до -180 .. + 180, что упрощает такие вычисления.

0
ответ дан 30 November 2019 в 01:53
поделиться

Нормализовано до [0,360):

(т. Е. Полуоткрытый диапазон)

Используйте оператор модуля, чтобы выполнить «получение остатка от деления»:

361 % 360

будет 1.

В языках стиля C / C ++ / ... это будет

gundeg %= 360

Примечание (благодаря комментарию): если gundeg является типом с плавающей запятой, вам нужно будет либо использовать библиотечную функцию в C / C ++: fmod, или сделай сам (.NET):

double FMod(double a, double b) {
  return a - Math.floor(a / b) * b;
}

Куда повернуть?

Какой путь короче (и если поворот равен 180 °, то ответ будет произвольным) в C #, и предполагая, что направление измеряется анти- по часовой стрелке

TurnDirection WhichWayToTurn(double currentDirection, double targetDirection) {
  Debug.Assert(currentDirection >= 0.0 && currentDirection < 360.0
               && targetDirection >= 0.0 && targetDirection < 360.0);

  var diff = targetDirection - currentDirection ;
  if (Math.Abs(diff) <= FloatEpsilon) {
    return TurnDirection.None;
  } else if (diff > 0.0) {
    return TurnDirection.AntiClockwise;
  } else {
    return TurnDirection.Clockwise;
  }
}

Примечание. Это требует тестирования.

Обратите внимание на использование assert для подтверждения предварительного условия нормализованных углов, и я использую assert, потому что это внутренняя функция, которая не должна получать непроверенные данные. Если это обычно многоразовая функция, проверка аргумента должна вызывать исключение или возвращать ошибку (в зависимости от языка).

Также обратите внимание. для решения подобных задач нет ничего лучше карандаша и бумаги (моя первоначальная версия была неправильной, потому что я смешивал (-180,180] и [0,360).

12
ответ дан 30 November 2019 в 01:53
поделиться

Попробуйте разделить на 180, используя целочисленное деление и повернув на четный / нечетный результат?

749/180 = 4 Итак, вы поворачиваете по часовой стрелке на 29 градусов (749% 180)

719 / 180 = 3 Таким образом, вы поворачиваете против часовой стрелки на 1 градус (180–719% 180)

1
ответ дан 30 November 2019 в 01:53
поделиться

Я предпочитаю решение, в котором

  • не имеет большого количества вложенных if операторов
  • не предполагает, что любой из двух углов находится в конкретном диапазон, например [0, 360] или [-180, 180]
  • , имеет постоянное время выполнения

Решение перекрестного произведения, предложенное Krypes , соответствует этим критериям, однако необходимо сгенерировать векторы сначала с углов. Я считаю, что методика BAMS JustJeff также удовлетворяет этим критериям. Я предлагаю еще один ...

Как обсуждалось в Почему модуль различается на разных языках программирования? , который относится к превосходной статье Википедии , есть много способов выполнить операция по модулю. Обычные реализации округляют частное до нуля или отрицательной бесконечности.

Однако, если

11
ответ дан 30 November 2019 в 01:53
поделиться

На самом деле, есть более простой способ решить эту проблему. Перекрестное произведение двух векторов дает вам вектор, представляющий нормаль (например, перпендикуляр). В качестве артефакта этого, учитывая два вектора a, b, которые лежат на плоскости xy, axb = c подразумевает c = (0,0, + -1).

Знак компонента z вектора c (например. выходит ли он из плоскости xy или переходит в плоскость xy) зависит от того, будет ли это поворот влево или вправо вокруг оси z, чтобы a было равно b.

Vector3d turret
Vector3d enemy

if turret.equals(enemy) return;
Vector3d normal = turret.Cross(enemy);
gundeg += normal.z > 0 ? 1 : -1; // counter clockwise = +ve
3
ответ дан 30 November 2019 в 01:53
поделиться

Рискуя навесить велосипед, сохранение степеней в виде целого числа, а не в виде собственного класса может быть случаем «примитивной одержимости». Если я правильно помню, в книге «Программист-прагматик» предлагалось создать класс для хранения степеней и выполнения над ними операций.

0
ответ дан 30 November 2019 в 01:53
поделиться
Другие вопросы по тегам:

Похожие вопросы: