Marlin多轴同步算法

1、Bresenham直线插补(实现多轴同步)

1.1算法原理

Bresenham 是一个快速画直线的算法。在现实中,我们可以根据直线方程$ y = k x + b $画出任意直线,但在计算机中却稍有不同。由于计算机屏幕是由很多栅格(像素点)组成,所以计算机中的直线实际上是通过一系列离散化的像素点来近似表现这条直线。如下a,b两图所示:

根据这样的原理,假设线段端点为$(x_1,y_1),(x_2,y_2)$,$\Delta x,\Delta y$为偏移量,直线方程为$f(x) = kx+b$。则有:

K = \frac{y_2-y_1}{x_2-x_1} = \frac{\Delta y}{\Delta x}

假设已经确定了直线上某个点正好在像素 $P_i(x_i,y_i)$,我们要确定下一个像素$P_{i+1}$是$(x_i+1,y_i)$还是$(x_i+1,y_i+1)$。此时的情况如下:

由上图可知,在$x = x_i+1$处,直线上y的值$y = k(x_i+1)+b$,该点距离$(x_i+1,y_i)$和点$(x_i+1,y_i+1)$的距离分别为$d_1$$d_2$,我们选取距离直线上理论点更近的像素点作为下一步要到达的地方,则有:

d_1 = y-y_i = k(x_i+1)+b-y_i

d_2 = y_i+1-y = y_i+1-k(x_i+1)-b

根据上述两个式子,两距离的差值为:

D(x_i) = d_1-d_2 = 2k(x_i+1)-2y_i+2b-1

根据两点之间距离差值的大小判断,即$D(x)>0$?,可以作为计算机判断的决策,则有:\label{eq6} D(x_i)=\left\{ \begin{aligned} &y_i &D(x_i) < 0 \\ &y_i+1 &D(x_i) \geq 0 \end{aligned} \right.

将i+1代入式子(15)中,其中$x_{i+1} = x_i + 1$得:D(x_{i+1})- D(x_i) = 2k(x_{i+1}-x_i)-2(y_{i+1}+y_i) = 2k-2(y_{i+1}+y_i)

$ k = \frac{\Delta{y}}{\Delta{x}}$,代入式子(17),化简得:

D(x_{i+1}) = D(x_i) + 2\frac{\Delta{y}}{\Delta{x}}-2(y_{i+1}+y_i)

由于$\Delta{x} > 0$,对$D(x_i)$的大小判断无任何影响,所以,可以对式子(18)两边同时乘$\Delta{x}$进行进一步化简, 令$P_i = \Delta{x} * D(x_i) $,得:

P_{i+1} = \Delta x *D(x_{i+1}) = P_i + 2\Delta{y} - 2(y_{i+1}+y_i)\Delta{x}

令直线上初始端点为像素点坐标,则将$y_1 = Kx_1 + b$代入式子(17)中的$x_i,y_i$可得到判断条件$P_i$的初始值为:

P_1 = 2\Delta{y}-\Delta{x}

由判断条件(16),递推公式(19),初始值(20)可得:\label{eq6} P_{i+1}=\left\{ \begin{aligned} &y_i,P_{i+1} = P_i+2(\Delta{y}-\Delta{x}) &P_i < 0 \\ &y_i+1,P_{i+1} = P_i+2\Delta{y} &P_i \geq 0 \end{aligned} \right.

对平面坐标系所以象限进行bresenham的推导,结果如下所示:

1. x1 > x0, y1 > y0, dy <= dx : x = x0, y = y0, p = 2dy − dx
若 p < 0 : p = p + 2dy, y = y, x = x + 1
若 p > 0 : p = p + 2dy − 2dx, y = y + 1, x = x + 1
2. x1 > x0, y1 > y0, dy > dx : x = x0, y = y0, p = 2dx − dy
若 p < 0 : p = p + 2dx, x = x, y = y + 1
若 p > 0 : p = p + 2dx − 2dy, x = x + 1, y = y + 1
3. x1 > x0, y1 < y0, dy <= dx : x = x0, y = −y0, p = 2dy − dx
若 p < 0 : p = p + 2dy, y = y, x = x + 1
若 p > 0 : p = p + 2dy − 2dx, y = y + 1, x = x + 1
4. x1 > x0, y1 < y0, dy <= dx : x = x0, y = −y0, p = 2dx − dy
若 p < 0 : p = p + 2dx, x = x, y = y + 1
若 p > 0 : p = p + 2dx − 2dy, x = x + 1
5. x1 < x0, y1 > y0, dy <= dx : x = −x0, y = y0, p = 2dy − dx
若 p < 0 : p = p + 2dy, y = y, x = x + 1
若 p > 0 : p = p + 2dy − 2dx, y = y + 1, x = x + 1
6. x1 < x0, y1 > y0, dy > dx : x = −x0, y = y0, p = 2dx − dy
若 p < 0 : p = p + 2dx, x = x, y = y + 1
若 p > 0 : p = p + 2dx − 2dy, x = x + 1, y = y + 1
7. x1 < x0, y1 < y0, dy <= dx : x = −x0, y = −y0, p = 2dy − dx
若 p < 0 : p = p + 2dy, y = y, x = x + 1
若 p > 0 : p = p + 2dy − 2dx, y = y + 1, x = x + 1
8. x1 < x0, y1 < y0, dy > dx : x = −x0, y = −y0, p = 2dx − dy
若 p < 0 : p = p + 2dx, x = x, y = y + 1
若 p > 0 : p = p + 2dx − 2dy, x = x + 1, y = y + 1

1.2应用分析

Bresenham原理上是一个快速画直线的算法,Marlin根据其原理,进行了部分转化,从而实现多轴同步,实现打印机的打印操作。根据对Marlin中bresenham部分代码的分析,其中作了两点比较重要的转化。

1. 判断条件改为 P1 = Y − X/2 因为计算机中浮点数计算会占用比较多的资源, 所以 Marlin
中针对计算的优化采用了整数相加和移位的方法,实现快速计算。
2. 将长轴的判断整合在一起,当某个轴为长轴是,其轴的步进条件恒为真

1.3算法分析

//Marlin 1.x版本Bresenham算法 代码简化
/*----------------init---------------------*/
int step_even_count; // 步数计数
int step_x,step_y,step_z; //各轴输出的步数 ΔY
int count_x,count_y,count_z;//各轴的Pi
int step_completed;//步数计数器
step_even_count = max(step_x,step_y);
step_even_count = max(step_z,step_even_count);//取各轴最大值作为X(ΔX)
count_x = -(step_even_count>>1);//-ΔX/2 ΔX>>1,后边所有数据都是除2处理 
count_y = count_x;
count_z = count_x;
/*-------------bresenham-------------------*/
counter_X += current_block->steps_x; 
if(counter_x > 0) //pi>0?
{
    WRITE(X_STEP_PIN,!INVERT_X_STEP_PIN); //脉冲输出
    counter_x -= current_block->step_event_count;
    count_position[X_AXIS] += count_direction[X+AXIS]; //由于Pi + ΔY已经计算,此处直接减去ΔX即可
    WRITE(X_STEP_PIN,INVERT_X_STEP_PIN);//脉冲停止
}
counter_y +=current_block->steps_y;
if(counter_y > 0)
{
    WRITE(Y_STEP_PIN,!INVERT_Y_STEP_PIN);
    counter_y -= current_block->step_event_count;
    count_position[Y_AXIS] += count_direction[Y_AXIS];
    WRITE(Y_STEP_PIN,INVERT_Y_STEP_PIN);
}
counter_z += current_block->steps_z;
if(counter_z > 0)
{
    WRITE(Z_STEP_PIN,!INVERT_Z_STEP_PIN);
    #ifdef Z_DUAL_STEPPER_DRIVERS
        WRITE(Z2_STEP_PIN,!INVERT_Z_STEP_PIN)
    #endif
    counter_z -= current_block->step_event_count;
    count_position[Z_AXIS] += count_direction[Z+AXIS]
    WRITE(Z_STEP_PIN,INVERT_Z_STEP_PIN)
    #ifdef Z_DUAL_STEPPER_DRIVERS
        WRITE(Z2_STEP_PIN,INVERT_Z_STEP_PIN)
    #endif
}
#ifdef ADVANCE
    counter_e +=current_block->steps_e;
if(counter_e > 0)
{
    WRITE(!INVERT_E_STEP_PIN);
    counter_e -= current_block->step_event_count;
    count_position[E_AXIS] += count_direction[E_AXIS];
    WRITE(INVERT_E_STEP_PIN);
}
#endif
/*--------------------Marlin 2.x Bresenham--------------------*/
//(本质上没什么变化,只是优化了结构,改变变量,进行封装)
#define _APPLY_STEP(AXIS, INV, ALWAYS) AXIS ##_APPLY_STEP(INV, ALWAYS)   
#define _INVERT_STEP_PIN(AXIS) INVERT_## AXIS ##_STEP_PIN
//分成3个宏
#define PULSE_PREP(AXIS) do{\
    delta_error[AXIS] += advance_dividend[AXIS];\
    steep_needed[AXIS] = (delta_error[AXIS]>=0);\
    if( steep_needed[AXIS])\
            delta_error[AXIS] -= advance_divisor\
    }while(0)
#define PULSE_START(AXIS) do{ \
      if (step_needed[AXIS]) { \
        count_position[AXIS] += count_direction[AXIS]; \
        _APPLY_STEP(AXIS, !_INVERT_STEP_PIN(AXIS), 0); \
      } \
    }while(0)
 #define PULSE_STOP(AXIS) do { \
      if (step_needed[_AXIS(AXIS)]) { \
        _APPLY_STEP(AXIS, _INVERT_STEP_PIN(AXIS), 0); \
      } \
    }while(0)

1.4.小结

marlin使用的bresenham算法实现的直线插补,在实际执行上对于具体的运动拟合程度比较差。例如,在对于一个45度直线进行插补时,x和y两个分量实际的插补和使用bresenham算法在微观上是存在较大的差异。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值