GCode G02/3 Arc Implementation (Bresenham?)

Hello to all,

first of all: I will not use ready libraries for that because as my hobby I want to understand this on a deeper level and implement this by myself for my (real)diy laserengraver/cutter. I managed to implement a good behaivor on G01 Codes for linear movement. Now I'm on to implement G03 Arcs.

I read about the Bresenham Algorhitm but also saw that it is NOT implemented for instance in GRBL. They seem to use another alogrhitm. I guess for timing/performance reasons.

So my question is whether to use Bresenham or not and: Are there any other algorhitms I should take a closer look to? Another approach i could think of is to calculate a look up table via sin cos and then just move from point to point with some kind of step resolution.

Greetings!

I have a Python function to expand a GCode Arc instruction into a series of GCode straight-line moves. I will post it here if it would be useful.

...R

Bresenham is used for speed, when you're plotting pixels - potentially many millions per second, but if you're moving something mechanical, maybe a fixed-point DDA is more appropriate.

Robin2:
I have a Python function to expand a GCode Arc instruction into a series of GCode straight-line moves. I will post it here if it would be useful.

...R

Would love to see that!

TheMemberFormerlyKnownAsAWOL:
Bresenham is used for speed, when you're plotting pixels - potentially many millions per second, but if you're moving something mechanical, maybe a fixed-point DDA is more appropriate.

Will have a look into this. Thanks!

Below is my Python code for expanding a GCode arc into a series of straight lines.

The function call is like this
arcLines = expandArc(gCmd, prevXaxisVal, prevYaxisVal, xAxisVal, yAxisVal, iVal, jVal)
in which gCmd will be one of 'G02', 'G03', 'G2', 'G3' and the paramters xAxisVal, yAxisVal, iVal, jVal come from the line of GCode. For example G2 X41.1994 Y30.0377 Z1.0000 I-14.9907 J-14.9903

The variable arcLines will contain all the straight-lines that are needed to follow the arc

Note that this function does not prefix each line of GCode with 'G1' as the rest of my Python program takes that as given. It just produces the relevant X and Y values as they would appear in a line of GCode like this G1 Xaaa Ybbb

My notes tell me that I got the idea from this website

I hope, with that description, you will be able to follow the Python code

def expandArc(gCmd, prevXaxisVal, prevYaxisVal, xAxisVal, yAxisVal, iVal, jVal):
    arcMoveList = []
    dirn = 'CW'
    if gCmd in ['G03', 'G3']:
        dirn = 'CCW'
    
    startX = prevXaxisVal
    startY = prevYaxisVal
    
    centreX = startX + iVal
    centreY = startY + jVal
    
        # calculate angle to start point
    dxStart = startX - centreX
    dyStart = startY - centreY
    startAngle = math.atan2(dyStart, dxStart) #* 180 / math.pi

        # calculate angle to end point
    dxEnd = xAxisVal - centreX
    dyEnd = yAxisVal - centreY
    endAngle = math.atan2(dyEnd, dxEnd) #* 180 / math.pi
    
        # make sure direction works
    if endAngle > startAngle:
        endAngle = endAngle - (math.pi * 2)
    
    radius = math.sqrt((dyStart * dyStart) + (dxStart * dxStart));
    
    sweep = endAngle - startAngle
    if dirn == 'CCW':
        sweep = (math.pi * 2) + sweep # for 'CW' sweep will be negative

    # ~ arcLen = abs(sweep) * radius
    
    # ~ numSegments = int(arcLen / mD.arcSegmentLength)
    numSegments = int(abs(sweep / (math.pi * 2) * mD.circleDiv))

    for x in range(numSegments):
        fraction =  float(x) / numSegments
        stepAngle = startAngle + ( sweep * fraction ) 
        stepX = centreX + math.cos(stepAngle) * radius;
        stepY = centreY + math.sin(stepAngle) * radius;
        if dirn == 'CW':
            if sweep > 0:
                stepY = - stepY
        else:
            if sweep < 0:
                stepY = - stepY
        arcMoveList.append([round(stepX,4), round(stepY,4)])

    arcMoveList.append([xAxisVal, yAxisVal])
    
    # ~ print("ArcList  STARTx %s   STARTy %s" %(startX, startY))
    # ~ for m in arcMoveList:
        # ~ print (m)
        # ~ pass
    # ~ print ("--------\n")
    return arcMoveList

...R

Robin2:
Below is my Python code for expanding a GCode arc into a series of straight lines.

The function call is like this
arcLines = expandArc(gCmd, prevXaxisVal, prevYaxisVal, xAxisVal, yAxisVal, iVal, jVal)
in which gCmd will be one of 'G02', 'G03', 'G2', 'G3' and the paramters xAxisVal, yAxisVal, iVal, jVal come from the line of GCode. For example G2 X41.1994 Y30.0377 Z1.0000 I-14.9907 J-14.9903

The variable arcLines will contain all the straight-lines that are needed to follow the arc

Note that this function does not prefix each line of GCode with 'G1' as the rest of my Python program takes that as given. It just produces the relevant X and Y values as they would appear in a line of GCode like this G1 Xaaa Ybbb

My notes tell me that I got the idea from this website
How to Improve the 2-axis CNC GCODE Interpreter to Understand Arcs – Marginally Clever Robots

I hope, with that description, you will be able to follow the Python code

def expandArc(gCmd, prevXaxisVal, prevYaxisVal, xAxisVal, yAxisVal, iVal, jVal):

arcMoveList = []
   dirn = 'CW'
   if gCmd in ['G03', 'G3']:
       dirn = 'CCW'
   
   startX = prevXaxisVal
   startY = prevYaxisVal
   
   centreX = startX + iVal
   centreY = startY + jVal
   
       # calculate angle to start point
   dxStart = startX - centreX
   dyStart = startY - centreY
   startAngle = math.atan2(dyStart, dxStart) #* 180 / math.pi

# calculate angle to end point
   dxEnd = xAxisVal - centreX
   dyEnd = yAxisVal - centreY
   endAngle = math.atan2(dyEnd, dxEnd) #* 180 / math.pi
   
       # make sure direction works
   if endAngle > startAngle:
       endAngle = endAngle - (math.pi * 2)
   
   radius = math.sqrt((dyStart * dyStart) + (dxStart * dxStart));
   
   sweep = endAngle - startAngle
   if dirn == 'CCW':
       sweep = (math.pi * 2) + sweep # for 'CW' sweep will be negative

# ~ arcLen = abs(sweep) * radius
   
   # ~ numSegments = int(arcLen / mD.arcSegmentLength)
   numSegments = int(abs(sweep / (math.pi * 2) * mD.circleDiv))

for x in range(numSegments):
       fraction =  float(x) / numSegments
       stepAngle = startAngle + ( sweep * fraction )
       stepX = centreX + math.cos(stepAngle) * radius;
       stepY = centreY + math.sin(stepAngle) * radius;
       if dirn == 'CW':
           if sweep > 0:
               stepY = - stepY
       else:
           if sweep < 0:
               stepY = - stepY
       arcMoveList.append([round(stepX,4), round(stepY,4)])

arcMoveList.append([xAxisVal, yAxisVal])
   
   # ~ print("ArcList  STARTx %s   STARTy %s" %(startX, startY))
   # ~ for m in arcMoveList:
       # ~ print (m)
       # ~ pass
   # ~ print ("--------\n")
   return arcMoveList




...R

Thank you. Looks great and since i have my GCode Streamer for the Laser in Python this will be very easy to implement.

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.