【问题标题】:Algorithm for evenly arranging steps in 2 directions2个方向均匀排列步数的算法
【发布时间】:2018-11-17 19:22:06
【问题描述】:

我目前正在为 CNC 机床编程控制器,因此当我从 A 点到 B 点时,我需要获取每个方向上的步进电机步数。 例如点 A 的坐标是 x=0 和 y=0,而 B 的坐标是 x=15 和 y=3。所以我必须在 x 轴上走 15 步,在 y 轴上走 3 步。 但是我如何以一种平滑的方式将这两个值混合在一起(也就是不是先 x 再 y,这会导致非常难看的线条)? 在我的 x=15 和 y=3 示例中,我希望它像这样排列:

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

但是我怎样才能从算法中得到这些数字呢? 我希望你能明白我的问题,谢谢你的时间, 卢卡

【问题讨论】:

标签: algorithm geometry line rasterizing cnc


【解决方案1】:

这里有两个主要问题:

  1. 轨迹

    这可以通过任何插值/光栅化处理,例如:

    • DDA
    • 布雷森纳姆

    DDA 是您的最佳选择,因为它可以轻松处理任意数量的维度,并且可以在整数和浮点算术上进行计算。它也更快(在 x386 时代并非如此,但现在 CPU 架构改变了一切)

    即使您只有 2D 机器,插值本身也很可能是多维的,因为您可能会添加其他东西,例如:夹持力、工具转速、压力等...必须沿着您的以同样的方式排列。

  2. 速度

    这个要复杂得多。您需要平稳地驱动电机从开始位置到结束位置:

    • 线路开始/结束速度,因此您可以将更多线路顺利连接在一起
    • 最高速度(取决于制造过程,每个工具通常保持不变)
    • 电机/机械共振
    • 电机速度限制:启动/停止和最高

    当写到速度时,我指的是频率[Hz] 表示电机的步数或工具的物理速度[m/s][mm/2]

    线性插值对此不好,我使用三次,因为它们可以平滑连接并为速度变化提供良好的形状。见:

    插值立方(CATMUL ROM 的形式)正是我用于此类任务的(我为此目的派生了它)

    主要问题是电机的启动。您需要从0 Hz 驱动到某个频率,但通常的步进电机在较低频率下会产生共振,并且对于多维机器来说,它们无法避免,您需要在这些频率上花费尽可能少的时间。还有另一种方法可以通过添加重量或改变形状以及在电机本身上添加惯性阻尼器(仅限旋转电机)来处理运动学的这种移动共振

    所以通常的单启动/停止线速度控制是这样的:

    因此,您应该有 2 个立方体,每次启动一个,每个停止一个,将您的生产线分成 2 个连接的生产线。你必须这样做,所以开始和停止频率是可配置的......

    现在如何合并速度和时间?我为此使用离散非线性时间

    它的过程相同,但不是时间而是角度。正弦波的频率呈线性变化,因此您需要随立方变化的部分。另外,您没有正弦波,因此请使用生成的 time 作为 DDA 的插值参数...或将其与下一步的时间进行比较,如果更大或相等,请执行步骤并计算下一个 ...

    这里是这种技术的另一个例子:

    这个实际上做了你应该做的......用三次曲线控制的速度插入 DDA。

完成后,您需要在此之上构建另一个层,该层将为每条轨迹线配置速度,以便结果尽可能快,并匹配您的机器速度限制,并尽可能匹配工具速度。这部分是最复杂的...

为了向您展示当我将所有这些放在一起时会发生什么,我的 CNC 插值器有大约 166KByte 的纯 C++ 代码,不包括依赖于向量数学、动态列表、通信等库的库......整个控制代码是~2.2 兆字节

【讨论】:

    【解决方案2】:

    如果您的控制器发出命令的速度比步进器实际转动的速度快,那么您可能希望使用某种基于事件驱动的基于计时器的系统。您需要计算何时触发每个电机,以便运动在两个轴上均匀分布。

    较长的运动应该尽可能快地进行编程(也就是说,如果电机每秒可以执行 100 步,则每 1/100 秒脉冲一次),而另一个运动则以较长的间隔进行。

    编辑:上面的段落假设您想尽快移动工具。通常情况并非如此。通常,工具速度是给定的,因此您需要分别计算沿 X 和 Y(也可能是 Z)轴的速度。您还应该知道电机的一步对应的刀具移动距离。因此,您可以计算每个时间单位需要执行的步数,以及整个运动的持续时间,以及沿每个轴的连续步进脉冲之间的时间间隔。

    因此,您可以对计时器进行编程,使其在计算出的最小时间间隔后触发,为相应的电机提供脉冲,为下一个脉冲设置计时器,等等。

    这是一种简化,因为电机与所有物理对象一样,具有惯性并且需要时间来加速/减速。所以如果你想产生平滑的运动,你需要考虑到这一点。还有更多的考虑因素需要考虑。但这更多是关于物理而不是编程。编程模型保持不变。您将机器建模为以某种已知方式对已知刺激(步进脉冲)做出反应的物理对象。您的程序根据模型计算步进脉冲的时间,并位于事件循环中,等待下一次事件发生。

    【讨论】:

    • @Spektre 你是对的,当然会更新答案。
    • 我看到了更新...删除了 cmets 并且我发布了我自己的答案,因为其他答案都没有完全削减 OP 正在处理的任务(至少对于不知道什么的人)在它背后),我认为应该在项目开始时直接说明这样的事情,否则 OP 方面会浪费时间......
    【解决方案3】:

    考虑一下Bresenham's line drawing algorithm - 他在多年前为绘图员发明了它。 (也是 DDA 之一)

    在您的情况下,X/Y 位移有公约数 GCD=3 > 1,因此步数应该均匀变化,但一般情况下它们不会分布得如此均匀。

    【讨论】:

      【解决方案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.log(steps({x: 0, y: 0}, {x: 3, y: 15}));

      请注意,第一段比所有其他段都短,因此它与序列在第二点附近结束的方式更加对称。如果您不喜欢这样,请将代码中出现的 0.5 替换为 0 或 1。

      【讨论】:

        猜你喜欢
        • 2015-02-24
        • 1970-01-01
        • 2017-06-28
        • 1970-01-01
        • 1970-01-01
        • 2021-02-05
        • 1970-01-01
        • 1970-01-01
        • 2021-08-17
        相关资源
        最近更新 更多