Алгоритм равномерной расстановки шагов в 2-х направлениях

В настоящее время я программирую контроллер для станка с ЧПУ, и поэтому мне нужно получить количество шагов шагового двигателя в каждом направлении, когда я доберусь от точки A до точки B. Например, координаты точки A равны x = 0 и y = 0, а координаты точки B равны x = 15 и y = 3. Итак, мне нужно сделать 15 шагов по оси x и 3 - по оси y. Но как мне смешать эти два значения таким образом, чтобы получилось гладко (иначе сначала x, а затем y, это приводит к действительно уродливым линиям)? В моем примере с x = 15 и y = 3 я хочу, чтобы все было так:

for 3 times do:    
    x:4 steps  y:0 steps
    x:1 steps  y:1 step

Но как я могу получить эти числа из алгоритма? Надеюсь, вы понимаете, в чем моя проблема, спасибо за ваше время, Лука

stackoverflow.com/questions/924598/…
jrook 17.11.2018 20:46
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
1
288
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

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

Вот реализация на JavaScript - с использованием только простейшего синтаксиса:

function steps(a, b) {
    const dx = Math.abs(b.x - a.x);
    const dy = Math.abs(b.y - a.y);
    const sx = Math.sign(b.x - a.x); // sign = -1, 0, or 1
    const sy = Math.sign(b.y - a.y);
    const longest = Math.max(dx, dy);
    const shortest = Math.min(dx, dy);
    const ratio = shortest / longest;
    const series = [];

    let longDone = 0;
    let remainder = 0;
    for (let shortStep = 0; shortStep < shortest; shortStep++) {
        const steps = Math.ceil((0.5 - remainder) / ratio);
        if (steps > 1) {
            if (dy === longest) {
                series.push( {x: 0, y: (steps-1)*sy} );
            } else {
                series.push( {x: (steps-1)*sx, y: 0} );
            }
        }
        series.push( {x: sx, y: sy} );
        longDone += steps;
        remainder += steps*ratio-1;
    }
    if (longest > longDone) {
        if (dy === longest) {
            series.push( {x: 0, y: longest-longDone} );
        } else {
            series.push( {x: longest-longDone, y: 0} );
        }   
    }
    
    return series;
}

// Demo
console.info(steps({x: 0, y: 0}, {x: 3, y: 15}));

Обратите внимание, что первый сегмент короче всех остальных, поэтому он более симметричен тому, как заканчивается последовательность около второй точки. Если вам это не нравится, замените вхождение 0.5 в коде на 0 или 1.

Рассмотрим Алгоритм рисования линий Брезенхемаm - он изобрел его для плоттеров много лет назад. (Также DDA one)

В вашем случае смещения X / Y имеют общий делитель GCD=3 > 1, поэтому шаги должны меняться равномерно, но в общем случае они не будут распределяться так равномерно.

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

Более длительное движение следует программировать как можно быстрее (то есть, если двигатель может делать 100 шагов в секунду, пульсировать его каждые 1/100 секунды), а другое движение - с более длинными интервалами.

Обновлено: в приведенном выше абзаце предполагается, что вы хотите переместить инструмент как можно быстрее. Обычно это не так. Обычно указывается скорость инструмента, поэтому вам нужно рассчитать скорость по осям X и Y (и, возможно, также Z) отдельно от этого. Вы также должны знать, какое расстояние перемещения инструмента соответствует одному шагу двигателя. Таким образом, вы можете рассчитать количество шагов, которые вам нужно сделать за единицу времени, а также продолжительность всего движения и, следовательно, временные интервалы между последовательными импульсами шагового двигателя по каждой оси.

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

Это упрощение, потому что двигатели, как и все физические объекты, обладают инерцией и им требуется время для ускорения / замедления. Так что вам нужно принять это во внимание, если вы хотите добиться плавного движения. Следует принять во внимание и другие соображения. Но это больше о физике, чем о программировании. Модель программирования остается прежней. Вы моделируете свою машину как физический объект, который каким-то известным образом реагирует на известные стимулы (шаговые импульсы). Ваша программа вычисляет тайминги для импульсов шагового двигателя от модели и находится в цикле событий, ожидая, когда произойдет следующее событие времени.

@Spektre ты прав конечно, обновлю ответ.

n. 1.8e9-where's-my-share m. 18.11.2018 08:24

Я вижу обновление ... удалил комментарии, а также я опубликовал свой собственный ответ, поскольку ни один из других ответов не совсем сокращает задачу, с которой имеет дело OP (по крайней мере, для тех, кто не знает, что за этим стоит), и я думаю, что вещи это должно быть указано прямо в начале проекта, иначе время будет потеряно на стороне OP ...

Spektre 18.11.2018 09:12
Ответ принят как подходящий

Здесь есть 2 основные проблемы:

  1. траектория

    с этим можно справиться любой интерполяцией / растеризацией, например:

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

    и даже если у вас есть только 2D-машина, сама интерполяция, скорее всего, будет многомерной, поскольку вы, вероятно, добавите другие вещи, такие как: удерживающая сила, частота вращения инструмента, предварительные настройки для чего-либо и т. д. Это должно быть интерполировано вдоль вашей линии в так же.

  2. скорость

    Это намного сложнее. Вам необходимо плавно управлять двигателями от начального положения до конечного в следующих случаях:

    • начальная / конечная скорость линии, чтобы вы могли плавно соединять вместе больше линий
    • максимальная скорость (в зависимости от производственного процесса обычно постоянна для каждого инструмента)
    • мотор / механический резонанс
    • ограничения скорости двигателя: пуск / останов и верх

    Говоря о скорости, я имею в виду частоту [Hz] для шагов двигателя или физическую скорость инструмента [m/s] или [mm/2].

    Линейная интерполяция для этого не подходит. Вместо этого я использую кубики, поскольку они могут плавно соединяться и обеспечивать хорошую форму для изменения скорости. Видеть:

    Кубическая интерполяция (форма CATMUL ROM) - это именно то, что я использую для подобных задач (и я получил ее именно для этой цели)

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

    Итак, обычное регулирование скорости для одиночной линии старт / стоп выглядит так:

    speed

    Таким образом, у вас должно быть 2 куба, по одному на каждый запуск и один на остановку, чтобы разделить вашу линию на 2 соединенных. Вы должны сделать это, чтобы можно было настраивать начальную и конечную частоту ...

    Как теперь совместить скорость и время? Я использую для этого дискретное нелинейное время:

    это тот же процесс, но вместо времени есть угол. Частота синусоидальной волны изменяется линейно, поэтому вам нужно изменить ее с помощью кубической. Также у вас нет синусоиды, поэтому вместо этого используйте полученный time в качестве параметра интерполяции для DDA ... или сравните его со временем следующего шага, и, если он больше или равен, сделайте шаг и вычислите следующий ...

    Вот еще один пример этой техники:

    Этот на самом деле делает именно то, что вы должны делать ... интерполировать DDA со скоростью, управляемой кубической кривой.

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

Чтобы показать вам, что вас ждет впереди, когда я собираю все это вместе, мой интерполятор с ЧПУ имеет ~ 166 Кбайт чистого кода C++, не считая зависимых библиотек, таких как векторная математика, динамические списки, связь и т. д. Весь управляющий код составляет ~ 2,2 МБ

Другие вопросы по теме