我目前正在为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
但是如何从算法中获得这些数字? 希望您能解决我的问题,谢谢您的宝贵时间, 路卡
答案 0 :(得分:3)
这里有2个主要问题:
轨迹
这可以通过任何插值/栅格化处理,例如:
DDA是您最好的选择,因为它可以轻松处理任意数量的维,并且可以使用整数和浮点运算进行计算。它的速度也更快(在x386时代并非如此,但如今CPU架构已全部改变)
即使您只有2D机器,插值本身也很可能是多维的,因为您可能还会添加其他内容,例如:夹持力,工具rpm,对任何东西的压力等等……这必须沿着您的位置进行插值行以相同的方式。
速度
这要复杂得多。您需要从以下位置开始平稳地驱动电动机:
在写速度时,我指的是电机步进频率[Hz]
或工具[m/s]
或[mm/2]
的物理速度。
对此,线性插值不好。我使用三次方,因为它们可以平滑连接并为速度变化提供良好的形状。参见:
插值三次方(CATMUL ROM的形式)正是我用于此类任务的(出于这个目的,我派生了它)
主要问题是电动机的启动。您需要从0 Hz
驱动到某个频率,但是普通的步进电机在较低的频率下会产生共振,并且由于无法避免在多维机器上出现,因此您需要在这些频率上花费尽可能少的时间。还有另一种方法可以通过增加重量或改变形状,并在电动机本身(仅用于旋转电动机)上添加惯性阻尼器来处理运动学的这种移动共振。
因此,单个起停线通常的速度控制如下所示:
因此,每次启动时应有2个立方体,而每次停止时应有2个立方体,将您的管线分成2个相连的管线。您必须这样做,因此开始和停止频率是可配置的...
现在如何合并速度和时间?我为此使用了离散非线性时间:
具有相同的过程,但不是时间,而是角度。正弦波的频率呈线性变化,因此您需要随立方变化。另外,您也没有正弦波,因此可以使用所得的time
作为DDA的插值参数...或将其与下一步的时间进行比较,如果大于或等于则进行下一步,然后计算下一个... < / p>
这是该技术的另一个示例:
实际上,这正是您应该做的事情...用三次曲线控制的速度对DDA进行插值。
完成后,您需要在此之上构建另一个层,该层将配置轨迹的每条线的速度,以使结果尽可能快,并与您的机器速度限制相匹配,并尽可能与工具速度相匹配。这部分是最复杂的部分。
为了向您展示当我将所有这些结合在一起时,我前面的事情是我的CNC内插器具有〜166KByte的纯C ++代码,不包括依赖于矢量数学,动态列表,通信等库的数量,因此整个控制代码是约2.2 MByte
答案 1 :(得分:2)
如果您的控制器发出的命令比步进器实际转动的速度快,则可能要使用某种基于事件的基于计时器的系统。您需要计算何时触发每个电动机,以便运动在两个轴上均匀分布。
应将尽可能长的运动编程为可以进行的最快速度(也就是说,如果电动机每秒可以执行100步,则应每1/100秒对它进行一次脉冲),而另一个运动则应以更长的间隔进行。
编辑:上面的段落假定您要尽快移动工具。通常情况并非如此。通常,工具速度是给定的,因此您需要分别计算沿X和Y(可能还有Z)轴的速度。您还应该知道什么刀具行进距离对应于电机的一步。因此,您可以计算每个时间单位所需的步数,以及整个运动的持续时间,从而计算沿每个轴的连续步进脉冲之间的时间间隔。
因此,您可以将计时器编程为在计算出的最小时间间隔后触发,对相应的电动机进行脉冲控制,为下一个脉冲编程计时器,依此类推。
这是一种简化,因为电动机与所有物理对象一样,都具有惯性并且需要时间来加速/减速。因此,如果要产生平稳的运动,则需要考虑到这一点。还有更多需要考虑的因素。但这更多的是关于物理而不是编程。编程模型保持不变。您将机器建模为以某种已知方式对已知刺激(步进脉冲)做出反应的物理对象。您的程序将根据模型计算步进脉冲的时序,并置于事件循环中,等待下一次事件发生。
答案 2 :(得分:1)
考虑Bresenham's line drawing algorith m-他是多年前为绘图员发明的。 (也是DDA之一)
在您的情况下,X / Y位移具有公共除数GCD=3 > 1
,因此步长应均匀变化,但通常情况下步长不会均匀分布。
答案 3 :(得分:0)
您应该获取每个坐标上的距离之比,然后在距离最长的坐标上的步与在两个坐标上执行单个单位步的步之间交替。
这是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或1替换代码中0.5
的出现。