落叶知秋 发表于 2018-5-10 12:03:41

GRBL源码简单分析[源码+图片+xmind文档]

本帖最后由 落叶知秋 于 2018-5-10 12:05 编辑

声明

好久没发技术贴,刚好前一阵子才把文档写完,就发到论坛上来,希望对坛友有用。
去年就开始在GitHub下载了源码开始看,但断断续续的看了将近一年才模模糊糊的看完!
也一边看一边做文档记录,用的XMind做记录,由于免费版,导不出来Word或者PDF,
只能导图片了,将就一下。

说明

文档内容是看代码过程中的理解,肯定有理解不透彻的地方,如果有坛友能指出,提前感谢。
由于软件限制和懒,文档内容以图片形式展示
附件提供楼主看代码的GitHub版本的源代码和网络上找的stm32移植版本的源代码,没有实际上机录验证过,此处仅讨论代码

简介
GRBL是基于Avr单片机的一个应用于3D打印机上的运动控制项目,由于楼主不会Avr,就不发在Avr版块,但不影响阅读源码
注意:个人认为GRBL是个简易型的数控系统,但仅此而已,其中有可以学习借鉴的地方,运动控制方面的。
GRBL里面有G代码解析、运动前瞻,T型加减速、直线插补、两轴圆弧插补等功能。

文档内容











附件





编辑原因:添加了XMind源文档文件

dreampet 发表于 2018-5-10 12:46:57

楼主很用心啊

honami520 发表于 2018-5-10 13:13:41

挺好的。如果做3D打印,可以去下载marlin,这个里面都给你做好了

落叶知秋 发表于 2018-5-10 13:42:53

honami520 发表于 2018-5-10 13:13
挺好的。如果做3D打印,可以去下载marlin,这个里面都给你做好了

marlin也是听说过的,里面也是引用了GRBL的代码,只不过marlin是专攻3D打印方面,grbl还可以用来做小型雕刻机

tear604922959 发表于 2018-5-10 13:47:43

3D打印机

liurangzhou 发表于 2018-5-10 13:54:56

之前看过,没看明白,采用DDA算法按理会造成脉冲不平滑啊,不平滑很容易造成电机失步,这里面是怎么解决的?

落叶知秋 发表于 2018-5-10 14:02:52

采用DDA算法按理会造成脉冲不平滑啊,不平滑很容易造成电机失步

不太明白你的意思?

eyancool 发表于 2018-5-10 14:08:24

感谢楼主的详细说明

iqxt88 发表于 2018-5-10 14:09:12

谢谢分享

HJJ2008 发表于 2018-5-10 14:10:06

楼主好人;

liurangzhou 发表于 2018-5-10 14:19:37

落叶知秋 发表于 2018-5-10 14:02
不太明白你的意思?

比如3hz的定时器基频,要输出2hz脉冲,采用dda算法之后:
主时基: 101010101010
输出   :   101000101000

一个10代表一个高低电平,这样输出不是平滑的

落叶知秋 发表于 2018-5-10 14:27:38

liurangzhou 发表于 2018-5-10 14:19
比如3hz的定时器基频,要输出2hz脉冲,采用dda算法之后:
主时基: 101010101010
输出   :   101000101 ...

3Hz的定时器基频,输出2Hz脉冲,平滑的波形应该是怎样的?能请你列一下吗?

liurangzhou 发表于 2018-5-10 14:30:35

落叶知秋 发表于 2018-5-10 14:27
3Hz的定时器基频,输出2Hz脉冲,平滑的波形应该是怎样的?能请你列一下吗? ...

我用步进电机时是一个定时器管一个电机,每一步去计算下一步的延时,然后改变时基,所以没搞懂它这种用一个固定时基的怎么输出平滑脉冲

ZMHCISS 发表于 2018-5-10 14:33:08

楼主厉害了,学习一下

落叶知秋 发表于 2018-5-10 14:45:41

liurangzhou 发表于 2018-5-10 14:30
我用步进电机时是一个定时器管一个电机,每一步去计算下一步的延时,然后改变时基,所以没搞懂它这种用一 ...

grbl脉冲输出不是固定时基的,定时器频率会随运动段变化的

suny1022 发表于 2018-5-10 14:48:48

好东西,学习一下

diandianer 发表于 2018-5-10 15:03:41

大赞!谢谢分享!

liurangzhou 发表于 2018-5-10 15:04:24

落叶知秋 发表于 2018-5-10 14:45
grbl脉冲输出不是固定时基的,定时器频率会随运动段变化的

多谢,看来我想多了

ardon 发表于 2018-5-10 15:28:30

佩服,有毅力,看完竟然还做了笔记!

advarx21ic 发表于 2018-5-10 16:04:04

谢谢楼主!有空研究一下,之前下载了源代码,一直没有时间看。

UCboy 发表于 2018-5-10 16:19:47

谢谢楼主的分享

a_2012_m 发表于 2018-5-10 17:09:04

楼主做了不少工作呀

liurangzhou 发表于 2018-5-10 17:12:15

tinyg2似乎是一个固定的时基,楼主有看过吗

落叶知秋 发表于 2018-5-10 17:39:10

liurangzhou 发表于 2018-5-10 17:12
tinyg2似乎是一个固定的时基,楼主有看过吗

听过TinyG,没有看过源码。

因为GRBL被引用的多,去年就决定先看这个,但工作原因断断续续的看,所以才有了这文档

王涛 发表于 2018-5-10 18:53:35

楼主可以重点讲一下Bresenham”直线算法吗?
怎么理解的
这个GRBL源码其他地方我都看懂了,就是这里一直不是很好的理解

sf49ers 发表于 2018-5-10 19:02:12

赞一个,一直想作这个工作,拖延症严重

落叶知秋 发表于 2018-5-10 20:19:49

王涛 发表于 2018-5-10 18:53
楼主可以重点讲一下Bresenham”直线算法吗?
怎么理解的
这个GRBL源码其他地方我都看懂了,就是这里一直不 ...

Bresenham算法就是一种改进的DDA算法,最大的特点就是计算过程不带浮点数,全用整型数

其实在GRBL里面Bresenham算法的应用就体现在stepper.c的T1中断ISR里面的脉冲输出条件判断
如果细心的话,可以留意到同一个文件的st_prep_buffer()函数里面有这样一段代码
for (idx=0; idx<N_AXIS; idx++) { st_prep_block->steps = (pl_block->steps << 1); }
          st_prep_block->step_event_count = (pl_block->step_event_count << 1);
其实就是把各轴的单次累加比例和脉冲输出的步数阈值都*2而已,然后在ISR里面,有如下代码
// Initialize Bresenham line and distance counters
      st.counter_x = st.counter_y = st.counter_z = (st.exec_block->step_event_count >> 1);
就是在取初始数据时,把计数数值降到之前计算的1/2,最后判断的代码是
// Execute step displacement profile by Bresenham line algorithm
#ifdef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING
    st.counter_x += st.steps;
#else
    st.counter_x += st.exec_block->steps;
#endif
if (st.counter_x > st.exec_block->step_event_count) {...}
这三处加起来就是Bresenham算法的所有了,细想一下的话可以发现,不就是下面这个图片嘛

当某个轴的计算counter大于翻转脉冲阈值的1/2时,就输出,否则就不输出,继续累加对应的比例

由于是脉冲计算,所以一般是不存在浮点输出的,Bresenham算法就是为了按照上面图片的方式取“非主要步进”轴的点
不知道这样能不能说清楚?

lilolog 发表于 2018-5-11 07:42:29

顶一个

PCBBOY1991 发表于 2018-5-11 08:32:28

楼主有心人啊!

amxx 发表于 2018-5-11 08:32:32

佩服楼主,学习一下!

liurangzhou 发表于 2018-5-11 08:45:35

加减速是怎么进行的?分段?还是每个脉冲都更新时基

落叶知秋 发表于 2018-5-11 11:18:08

liurangzhou 发表于 2018-5-11 08:45
加减速是怎么进行的?分段?还是每个脉冲都更新时基

加减速部分文档里面有稍微提及了一下,没有细述
一是因为加减速部分跟前瞻部分是有些关联的,比如前后运动段之前的速度限制和衔接,入口初速度和出口结束速度之类的
二是因为懒得写那么多,看起来就臃肿
既然坛友有意交流这部分,那我再整理一下吧

这里先说一下,T型加减速的具体计算是在stepper.c里面的st_prep_buffer()函数里面
待我整理一下之后再回复本帖把

linux-0405209 发表于 2018-5-11 11:36:21

挺详细的 楼主真用心谢谢

PICTURE 发表于 2018-5-11 11:50:19

之前也有分析过源代码,谢谢楼主资料,不知有没有上位机应用方面的知识可介绍?

elecfun 发表于 2018-5-11 12:27:35

这学习笔记真棒!

落叶知秋 发表于 2018-5-11 13:39:14

PICTURE 发表于 2018-5-11 11:50
之前也有分析过源代码,谢谢楼主资料,不知有没有上位机应用方面的知识可介绍? ...

GRBL有对应的通信交互协议,但没有项目直接配套的上位机,但有不少人为其开发了对应的上位机,也有开源的
我没有研究过GRBL配套的上位机,只是知道其有对应的通信机制。

落叶知秋 发表于 2018-5-11 16:30:14

RAMILE 发表于 2018-5-11 15:33
xmind 文件压缩包损坏

撸主重新下载并解压,文件无损坏。重新下载试下吧

落叶知秋 发表于 2018-5-11 16:31:39

liurangzhou 发表于 2018-5-11 08:45
加减速是怎么进行的?分段?还是每个脉冲都更新时基

结构体说明
GRBL里面的速度规划是带运动段前瞻的,所以有规划运动段数据和微小运动段的区分
这里的“规划运动段”对应的数据结构是plan_block_t,前瞻和加减速会使用到,也就是通过解析G代码后出来的直接直线数据或是圆弧插补出来的拟合直线数据
“微小运动段”对应的数据结构是segment_t,加减速的终端数据持有者,也就是plan_block_t数据经过了加减速后的计算数据
plan_block_t的数据还有一个临时的数据缓冲区,st_block_buffer,作用如上所述,其中存储的是脉冲发生ISR里面需要用到的数据
stepper.c里面有个静态变量static st_prep_t prep,这个变量是plan_block_t数据转换为segment_t数据时的一个缓冲,即从plan_block_t里面提取相关数据,然后生成新的segment_t数据

加减速处理
加减速处理在stepper.c的st_prep_buffer()函数里,流程为:
1.segment_t缓冲区有空余位置才会进行加减速数据生成,否则退出函数;有接收到停止运动指令,同样停止生成数据

2.从规划器的plan_block_t队列里面找到当前要进行分解的运动段,此处会分为“系统运动段”和“普通运动段”,就是一般操作和G代码解析的区分

3.判断prep里面的重新计算标志是否有效,有效则重新计算当前的运动段分解
什么时候会需要重新计算呢?一般是调整了速度修调(Ratio)之后,会对未被执行的运动段重新进行计算

4.如果不需要重新计算了,则直接把plan_block_t里面对应的Bresenham算法需要用到的脉冲输出阈值和总步数,记录到st_block_buffer里面

5.此时开始根据plan_block_t里面的数据和系统当前状态来确定加减速的状态,这里有好几个条件分支,其中牵扯到前瞻计算的初始速度和终止速度,以及系统的速度修调率。
5.1如果检测到系统有进给保持(HOLD)状态指令,则直接进入全减速状态(RAMP_DECEL)
5.2如果速度修调被降低了,则判断当前运动段的距离够不够减速到对应的终止速度,不够则进入全减速状态(RAMP_DECEL),如果是足够的,则进入半减速状态(RAMP_DECEL_OVERRIDE),如果是进入了规划的减速段,则也进入全减速状态(RAMP_DECEL)
5.3如果是速度修调被升高了,或是一开始进入加速运动,则要进入加速状态(RAMP_ACCEL)
5.4如果是加速到了目标速度,则应该进入匀速状态(RAMP_CRUISE)
5.5其中,在加速状态的时候,可以计算出要进行梯形加减速还是三角形加减速

6.在确定了当前运动段的加减速状态后,则根据该状态进行segment_t微小运动段的分解计算,分解计算是依靠一个固定的时基DT_SEGMENT来进行的
注意,该固定时基只在计算segment_t数据时使用,跟脉冲发生的时间没有直接关系

7.分解微小运动段的方法是:根据加减速状态和pl_block的速率,算出在DT_SEGMENT单元时间内的进给量(即应输出的脉冲量)
7.1半减速处理(RAMP_DECEL_OVERRIDE):首先计算减速到目标速度的时间,然后算出减速状态下的进给,再把加减速状态设置为匀速状态(RAMP_CRUISE),剩下的时间会在循环条件里再次进入匀速状态计算进给
7.2全加速处理(RAMP_ACCEL):计算全加速下,是否会到达运动段的加速段目标位置,如果还可以加速,则继续处于全加速状态,否则看加减速是梯形条件还是三角形条件,如果是梯形则进入匀速状态(RAMP_CRUISE),如果是三角形则进入全减速状态(RAMP_DECEL)
7.3匀速处理(RAMP_CRUISE):计算匀速段是否结束了,是则进入全减速状态(RAMP_DECEL),否则保持该状态;另外,如果匀速状态在DT_SEGMENT时间内就结束了,同样地,把剩余时间在全减速状态再计算一次进给量
7.4全减速处理(RAMP_DECEL):如果剩余的距离还可以继续减速,则保持该状态,然后计算出该次的进给量;如果剩余的距离可以在该次减速中完成,则要算出完成减速的时间量和进给量,然后退出该运动段pl_block的加减速
7.5另外,加减速处理中有“最小步”的输出要求,如果在1个DT_SEGMENT内,没有达到“最小步”的要求,则继续累加1个DT_SEGMENT的时间,知道满足“最小步”输出要求,才退出步骤7

8.要注意的是,步骤5是segment_t根据pl_block_t的状态初始化自己数据的流程,里面有步骤7计算时需要用到的初始数据
步骤7是计算一个完整segment_t数据的流程,里面有输出脉冲的条件数据,比如在多长的时间内输出多少个脉冲
然后就是初始化T1定时器计数,并且触发ISR,在ISR中计算脉冲输出条件

9.另外,1个pl_block_t可以分解成很多个segment_t,如果segment_t的缓冲满了,pl_block_t还没有结束,则暂停生成segment_t,等缓冲区空闲了再计算,否则会一直生成segment_t;
相反,也有可能1个pl_block_t就对应1个segment_t,且segment_t可能会在DT_SEGMENT内就完成了pl_block_t的目标距离,时间是以实际计算的为准

10.脉冲输出的时基,即T1定时器的溢出中断计数,是根据segment_t的需要输出脉冲数和segment_t使用的时间(不一定是DT_SEGMENT)来直接计算的。在单个segment_t的脉冲输出期间,T1的溢出中断时间间隔一致,但一般不同的segment_t会有不同的T1时间间隔

王涛 发表于 2018-5-11 17:05:01

落叶知秋 发表于 2018-5-10 20:19
Bresenham算法就是一种改进的DDA算法,最大的特点就是计算过程不带浮点数,全用整型数

其实在GRBL里面Br ...

非常感谢楼主的回复

pl_block->step_event_count 是完成这个block所需走的步数,就是steps, steps, steps, 的最大值,即是三个轴中的最长轴。如果某个轴是最长轴,意味着这个轴的步进电机每个中断都有脉冲输出。
其他两个轴的输出如何判断的这里没有理解。

落叶知秋 发表于 2018-5-11 18:00:43

王涛 发表于 2018-5-11 17:05
非常感谢楼主的回复

pl_block->step_event_count 是完成这个block所需走的步数,就是steps, steps ...

这个问题是属于DDA算法的范畴了,这样说吧:
假设从P0点(0,0,0)插补到P1点(2,4,8),坐标分别对应X,Y,Z,则最长轴是Z,每个周期应该Z步数增加1个单位,总共需要8个周期
再假设event_step = 8,step_x = 2,step_y = 4,step_z = 8,cnt_x = cnt_y = cnt_z = 0;
要Z每个周期都输出脉冲,则有:
cnt_x += step_x;
if (cnt_x >= event_step) {输出X脉冲,cnt_x -= event_step} //这里会在第4和第8个周期分别输出脉冲
cnt_y += step_y;
if (cnt_y >= event_step) {输出Y脉冲,cnt_y -= event_step} //这里会在第2/4/6/8个周期分别输出脉冲
cnt_z += step_z;
if (cnt_z >= event_step) {输出Z脉冲,cnt_z -= event_step} //每个周期都输出脉冲
你按照每个周期来算一下就知道,X/Y/Z会按照插值法来输出脉冲了,这个就是DDA的一个例子

王涛 发表于 2018-5-11 18:07:19

本帖最后由 王涛 于 2018-5-11 18:09 编辑

落叶知秋 发表于 2018-5-11 18:00
这个问题是属于DDA算法的范畴了,这样说吧:
假设从P0点(0,0,0)插补到P1点(2,4,8),坐标分别对应X,Y,Z, ...


谢谢,分析。
下面是Bresenham's line and circle algorithm数学模型
可是看了半天代码分析和这个模型就是关联不起来。
https://www.cs.helsinki.fi/group/goa/mallinnus/lines/bresenh.html


(x, y +ε)的下一个点为(x, y + ε + m),这里ε为累加误差。

可以看出,当ε+m < 0.5时,绘制(x + 1, y)点,否则绘制(x + 1, y + 1)点。

每次绘制后,ε将更新为新值:
            ε = ε + m ,如果(ε + m) < 0.5
            ε = ε + m – 1, 其他情况

这里判别条件的(ε + m) < 0.5

    即 2*(ε + m) < 1

    即 2*(ε + dy / dx) < 1(由于m = dy / dx)

    即 2*(ε* dx + dy) < dx (同乘以dx)

    令ξ = ε*dx 可得

            ξ = ξ + dy, 如果2*(ξ + dy) < dx
            ξ = ξ + dy – dx, 其他情况
    可以看到,此时运算已经全变为整数了。以下为算法的伪代码:
            ξ ← 0, y ← y1
            For x ← x1 to x2 do
                Plot Point at (x, y)
                If (2(ξ + dy) < dx)
                  ξ ←ξ + dy
                Else
                  y ← y + 1,ξ ←ξ + dy – dx
                End If
            End For

落叶知秋 发表于 2018-5-11 19:33:44

王涛 发表于 2018-5-11 18:07
谢谢,分析。
下面是Bresenham's line and circle algorithm数学模型
可是看了半天代码分析和这个模型就 ...

其实也没有很难理解,把你提到的公式里面的字母换一下就好结合GRBL的代码理解了

            ξ ← 0, y ← y1
            For x ← x1 to x2 do
                Plot Point at (x, y)
                If (2(ξ + dy) < dx)
                  ξ ←ξ + dy
                Else
                  y ← y + 1,ξ ←ξ + dy – dx
                End If
            End For
把上面代码里的ξ看做非主步进轴的计数值,比如X为主步进轴,dx是step_x,dy是step_y,那么event_step = max(step_x,step_y)
然后ξ是cnt_y,cnt_x就不用了,因为每次cnt_x都等于event_step,即X轴每次都输出脉冲
这样把上面的代码翻译一下

            For x ← x1 to x2 do //X主步进轴,每次都输出1个脉冲
                Plot Point at (x, y) //打印函数,此处无意义
                If (2(cnt_y + step_y) < event_step)
                  cnt_y += step_y
                Else
                  y ← y + 1 //输出1个脉冲
                   cnt_y+= step_y – event_step
                End If
            End For
是不是跟GRBL的T1的ISR函数代码片段差不多?

liurangzhou 发表于 2018-5-11 22:25:54

落叶知秋 发表于 2018-5-11 16:31
结构体说明
GRBL里面的速度规划是带运动段前瞻的,所以有规划运动段数据和微小运动段的区分
这里的“规划 ...

非常感谢楼主的详细解答,也就是说加减速是以DT_SEGMENT单元时间进行逼近,在这个单元时间内速度是一定的,加速过程都相当于走楼梯一步一步上去,而不是每一个脉冲输出之后即更新一个时基

propor123 发表于 2018-5-11 22:44:42

不错,好好研究下。

hxke 发表于 2018-5-11 22:50:01

不懂,顶下

wwk1996 发表于 2018-5-12 06:54:22

楼主很细心,也很乐于给大家分享自己的心得。这个帖子记下了。

NM2012 发表于 2018-5-12 07:39:57

不懂,顶下楼主细心热心

落叶知秋 发表于 2018-5-12 08:33:42

liurangzhou 发表于 2018-5-11 22:25
非常感谢楼主的详细解答,也就是说加减速是以DT_SEGMENT单元时间进行逼近,在这个单元时间内速度是一定的 ...

我看代码觉得差不多这个意思,但不一定是正确的哦{:titter:}

kk2008 发表于 2018-5-12 09:33:15

谢谢分享!

liang16888 发表于 2018-5-12 12:38:53

谢谢分享!

tianbianren 发表于 2018-5-12 15:10:14

谢楼主,需要仔细看

codefish 发表于 2018-5-12 15:54:27

不错,顶一下!

raydsp 发表于 2018-5-12 16:03:48

谢谢楼主分享,感觉有点复杂,慢慢看看。

donglaile 发表于 2018-5-12 18:41:57

感谢分享,顶一下

sunbest80 发表于 2018-5-12 22:35:58

谢谢分享

WITSOFT 发表于 2018-5-13 22:17:14

谢谢分享!

mypc16888 发表于 2018-5-13 22:32:52

感谢分享

chewy 发表于 2018-5-13 22:42:03

谢谢分享~

lichaoming520 发表于 2018-5-14 08:26:15

楼主,你用的是什么软件,生成的文件目录树?很直观逻辑很清晰

落叶知秋 发表于 2018-5-14 08:35:04

lichaoming520 发表于 2018-5-14 08:26
楼主,你用的是什么软件,生成的文件目录树?很直观逻辑很清晰

楼主位有说明,用XMind,一款思维导图软件

longjunyi 发表于 2018-5-14 08:51:55

感谢分享

gzyy2005 发表于 2018-5-14 13:26:17

阅读代码,理解框架,不错

王涛 发表于 2018-5-14 14:43:10

落叶知秋 发表于 2018-5-10 20:19
Bresenham算法就是一种改进的DDA算法,最大的特点就是计算过程不带浮点数,全用整型数

其实在GRBL里面Br ...

多谢楼主的分析,有点明白了。
可是下面的代码有什么意思呢。为什么要先扩大2倍后有缩小2倍?

for (idx=0; idx<N_AXIS; idx++) { st_prep_block->steps = (pl_block->steps << 1); }
          st_prep_block->step_event_count = (pl_block->step_event_count << 1);


其实就是把各轴的单次累加比例和脉冲输出的步数阈值都*2而已,然后在ISR里面,有如下代码
// Initialize Bresenham line and distance counters
      st.counter_x = st.counter_y = st.counter_z = (st.exec_block->step_event_count >> 1);


就是在取初始数据时,把计数数值降到之前计算的1/2,最后判断的代码是

落叶知秋 发表于 2018-5-14 14:46:49

王涛 发表于 2018-5-14 14:43
多谢楼主的分析,有点明白了。
可是下面的代码有什么意思呢。为什么要先扩大2倍后有缩小2倍?



因为GRBL里面的counter是无符号数值,所以在实际代码中是做了1/2 event_step的初值偏移和step_axis与event_step的2倍放大的,为了不出现负值运算
实际代码的意思是跟上面的伪代码差不多的

albert.hu 发表于 2018-5-14 15:08:11

楼主有心了,谢谢分享

王涛 发表于 2018-5-14 16:03:24

落叶知秋 发表于 2018-5-14 14:46
因为GRBL里面的counter是无符号数值,所以在实际代码中是做了1/2 event_step的初值偏移和step_axis与even ...


源代码中ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING 自适应多轴平滑步进(AMASS)是被定义过的的了,也就是step_event_count被扩大了 st_prep_block->step_event_count = pl_block->step_event_count << MAX_AMASS_LEVEL;多倍,
而后又 st.counter_x = st.counter_y = st.counter_z = (st.exec_block->step_event_count >> 1);然后在缩小    st.steps = st.exec_block->steps >> st.exec_segment->amass_level;倍。
实际上是这里只是赋值 st.counter_x = st.counter_y = st.counter_z = (st.exec_block->step_event_count >> 1)为什么要这样做


#ifndef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING
          for (idx=0; idx<N_AXIS; idx++) { st_prep_block->steps = (pl_block->steps << 1); }
          st_prep_block->step_event_count = (pl_block->step_event_count << 1);
      #else
          // With AMASS enabled, simply bit-shift multiply all Bresenham data by the max AMASS
          // level, such that we never divide beyond the original data anywhere in the algorithm.
          // If the original data is divided, we can lose a step from integer roundoff.
          for (idx=0; idx<N_AXIS; idx++) { st_prep_block->steps = pl_block->steps << MAX_AMASS_LEVEL; }
          st_prep_block->step_event_count = pl_block->step_event_count << MAX_AMASS_LEVEL;
      #endif

// Initialize Bresenham line and distance counters
      st.counter_x = st.counter_y = st.counter_z = (st.exec_block->step_event_count >> 1);
      }
      st.dir_outbits = st.exec_block->direction_bits ^ dir_port_invert_mask;

      #ifdef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING
      // With AMASS enabled, adjust Bresenham axis increment counters according to AMASS level.
      st.steps = st.exec_block->steps >> st.exec_segment->amass_level;
      st.steps = st.exec_block->steps >> st.exec_segment->amass_level;
      st.steps = st.exec_block->steps >> st.exec_segment->amass_level;
      #endif

落叶知秋 发表于 2018-5-14 16:10:15

王涛 发表于 2018-5-14 16:03
源代码中ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING 自适应多轴平滑步进(AMASS)是被定义过的的了,也就是ste ...

st.counter_x = st.counter_y = st.counter_z = (st.exec_block->step_event_count >> 1)为什么要这样做
这个是之前说过的,为了去掉负数运算

ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING 自适应多轴平滑步进(AMASS)
这个请看楼主位最后一张图片的右下角部分

王涛 发表于 2018-5-14 16:58:15

落叶知秋 发表于 2018-5-14 16:10
这个是之前说过的,为了去掉负数运算




ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING 自适应多轴平滑步进(AMASS)
这里是为了在低速时加快中断的处理速度这里我理解。我想问的是 st.counter_x = st.counter_y = st.counter_z 赋值为0行不行。 st.counter_x = st.counter_y = st.counter_z =0;

落叶知秋 发表于 2018-5-14 17:24:37

王涛 发表于 2018-5-14 16:58
ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING 自适应多轴平滑步进(AMASS)
这里是为了在低速时加快中断的处理速 ...

不行。。。字数补丁

jetta2014 发表于 2018-5-15 10:34:36

本帖最后由 jetta2014 于 2018-5-15 10:48 编辑

刚准备玩下GRBL,就看到楼主的大作了,先谢啦!
补充一下,grbl 1.1f XMind文档.rar无法打开!

liujingwei 发表于 2018-5-15 14:20:34

这个分析很强,图文并茂

cdlxzlp 发表于 2018-5-17 09:30:43

这个楼主确实是花了精力的!全顶起来

windancerhxw 发表于 2018-5-17 17:06:12

{:handshake:}很用心,学习了。多谢分享
mark一下:3D打印机流程图

leijiayou 发表于 2018-5-17 17:52:39

顶楼主{:victory:}

useronce 发表于 2018-5-17 18:11:54

这个必须赞

huanger 发表于 2018-5-18 00:04:44

楼主确实是花了精力的,也非常有心,赞!

lmhtz 发表于 2018-5-18 05:24:03

顶一个,楼主很有心花了不少精力!

xd785 发表于 2018-5-18 08:15:58

之前看过一段时间,只是用于应用,没去理解底层那么深,楼主很用心。顶~~~

落叶知秋 发表于 2018-5-18 12:02:18

jetta2014 发表于 2018-5-15 10:34
刚准备玩下GRBL,就看到楼主的大作了,先谢啦!
补充一下,grbl 1.1f XMind文档.rar无法打开! ...

重新下载试试?论坛只能上传压缩文件,源文件上传不了。还不行的话,我之后再重新上传一个

Chunfy 发表于 2018-5-18 14:32:04

做的好,做的好,做的好,做的好,

王涛 发表于 2018-5-19 12:01:19

本帖最后由 王涛 于 2018-5-19 12:29 编辑

落叶知秋 发表于 2018-5-14 17:24
不行。。。字数补丁



楼主你好这是一个关于marlin源码bresenham算法光栅化的画直线算法的分析。这个算法的误差E只和dx和dy有关系。

grbl1.1源代码中
      //Initialize Bresenham line and distance counters
      st.counter_x = st.counter_y = st.counter_z = (st.exec_block->step_event_count >> 1);
       我们这里假定X轴是最长轴恒为真,也就是么个每个中断都有脉冲输出,
       那么Y轴的判断就是根据
       ξ‘’+dy-0.5step_event_count >0

       这里 ξ‘’=0.5dX//ξ‘’   就是st.counter_y等于st.counter_x = st.counter_y = st.counter_z = (st.exec_block->step_event_count >> 1);

      st.counter_y=0.5st.exec_block->step_event_count//(st.exec_block->step_event_count >> 1);
      
      st.counter_y += st.steps;
      
   if (st.counter_y > st.exec_block->step_event_count) {
    st.step_outbits |= (1<<Y_STEP_BIT);
    st.counter_y -= st.exec_block->step_event_count;
    if (st.exec_block->direction_bits & (1<<Y_DIRECTION_BIT)) { sys_position--; }
    else { sys_position++; }
}
      
这里的判断条件为啥是if (st.counter_y > st.exec_block->step_event_count)而不是if (st.counter_y > 0.5st.exec_block->step_event_count) ?????

但是下面的逻辑又是对的
请教上面的问题
假设需要从点(0,0,0)到点(31,21,5),从(0,0,0)到(31,21,5)最终的执行结果是X轴步进电机移动31步、Y轴步进电机移动21步、Z轴步进电机移动了5步。
那么代码执行的详细情况如下

current_block->steps = 31;

current_block->steps = 21;

current_block->steps = 5;

current_block->step_event_count = 31;

//st.counter_x = st.counter_y = st.counter_z = (st.exec_block->step_event_count >> 1);
counter_x = (current_block->step_event_count>>1) = 15;

counter_y = counter_z = counter_e = counter_x;



第一步

Counter_x = counter_x + current_block->steps = 15 + 31 = 46;

因为条件counter_x > current_block->step_event_count为true, 所以X电机向前走一步

counter_x = counter_x - current_block->step_event_count = 46 - 31; = 15;



counter_y = counter_y + current_block->steps = 15 + 21 = 36;

因为条件counter_y > current_block->step_event_count为true,所以Y电机向前走一步

counter_y = counter_y - current_block->step_event_count = 36 - 31 = 5;



counter_z = counter_z + current_block->steps = 15 + 5 = 20;

因为条件counter_z > current_block->step_event_count为false,所以Z电机不动





第二步

Counter_x = counter_x + current_block->steps = 15 + 31 = 46;

因为条件counter_x > current_block->step_event_count为true, 所以X电机向前走一步

counter_x = counter_x - current_block->step_event_count = 46 - 31 = 15;



counter_y = counter_y + current_block->steps = 5 + 21 = 26;

因为条件counter_y > current_block->step_event_count为false,所以Y电机不动





counter_z = counter_z + current_block->steps = 20 + 5 = 25;

因为条件counter_z > current_block->step_event_count为false,所以Z电机不动






第三步

同理

……

落叶知秋 发表于 2018-5-19 16:26:35

王涛 发表于 2018-5-19 12:01
楼主你好这是一个关于marlin源码bresenham算法光栅化的画直线算法的分析。这个算法的误差E只和dx和dy有 ...

之前已经回复过你了,避免负数运算,且不可能是有浮点运算的。。。为了避免浮点,放大了两倍;为了避免负数运算,初始值做了1/2的偏置

王涛 发表于 2018-5-20 08:44:34

落叶知秋 发表于 2018-5-19 16:26
之前已经回复过你了,避免负数运算,且不可能是有浮点运算的。。。为了避免浮点,放大了两倍;为了避免负 ...

楼主你好,我太笨了,还是不能理解,楼主好人做到底能否结合源代码给我分析一下,先谢谢了。

zhousun 发表于 2018-5-20 09:04:25

楼主精神值得学习!

零妖 发表于 2018-5-20 09:51:57

楼主用心了。我之前研究过用Python源码,做一个小激光打印机,失败了,这个是好资料。

落叶知秋 发表于 2018-5-20 10:05:32

王涛 发表于 2018-5-19 12:01
楼主你好这是一个关于marlin源码bresenham算法光栅化的画直线算法的分析。这个算法的误差E只和dx和dy有 ...

st.counter_x = st.counter_y = st.counter_z = (st.exec_block->step_event_count >> 1);
这个是给计数器赋了个初值0.5*event_count,本来应该是0的,按照算法来写的话。
然后判断的时候,if (counter_y > event_count),本来按照算法应该是if (counter_y > 0.5*event_count)的,但结合上面的赋初值处理,可以变成这样:
if (counter_y+0.5*event_count > 0.5*event_count+0.5*event_count),
这里就跟按照算法来写是一样的,只不过两边都有了个0.5*event_count的初值。

王涛 发表于 2018-5-20 11:16:20

本帖最后由 王涛 于 2018-5-20 11:40 编辑

落叶知秋 发表于 2018-5-20 10:05
st.counter_x = st.counter_y = st.counter_z = (st.exec_block->step_event_count >> 1);
这个是给计数 ...

也就是说误差counter_y本来应该是0的,按照算法来写的话。这个里给计数器赋了个初值0.5*event_count。所以判断条件为是if (st.counter_y > st.exec_block->step_event_count)


落叶知秋 发表于 2018-5-20 11:25:56

王涛 发表于 2018-5-20 11:16
也就是说误差counter_y本来应该是0的,按照算法来写的话。这个里给计数器赋了个初值0.5*event_count。所 ...

counter_y是计数器,单次event_step多少就累加多少,为什么要0.5呢?这些比例换算之前已经算好了

王涛 发表于 2018-5-20 11:56:57

本帖最后由 王涛 于 2018-5-20 12:09 编辑

落叶知秋 发表于 2018-5-20 11:25
counter_y是计数器,单次event_step多少就累加多少,为什么要0.5呢?这些比例换算之前已经算好了 ...

对了误差counter_y本来应该是0的。这里给赋了个初值0.5*event_count。
谢谢了我明白了。
这个问题我迷糊了好几个月了

http://www.openedv.com/thread-230376-1-2.html

fengyunyu 发表于 2018-5-20 15:59:03

大神,厉害!

老蔡6198 发表于 2018-8-15 23:14:38

多谢分享

txl216 发表于 2018-8-16 10:48:52

楼主,这个给力

windancerhxw 发表于 2018-8-24 16:06:03

mark一下:小型雕刻机 3D打印机

huangguimina4 发表于 2018-8-24 20:44:07

楼主分析的真透彻,可厉害了!

yinian 发表于 2018-9-11 22:32:12

感谢分析,非常有帮助

bujie8010 发表于 2018-9-12 10:14:32

非常感谢分享,好好学习一下!

sanjianke 发表于 2018-9-17 23:24:36

刚好看了一阵子grbl,还不是很熟悉,正在摸索。等了解了再加入讨论。

jacktau 发表于 2018-9-18 13:02:43

谢谢分享~~~~~~~

windancerhxw 发表于 2018-9-27 10:55:47

mark:GRBL源码简单分析 支持

stevenh 发表于 2018-9-27 12:33:14

谢谢分享!
页: [1] 2
查看完整版本: GRBL源码简单分析[源码+图片+xmind文档]