[Home]JMKsFusee

LinuxCNCKnowledgeBase | RecentChanges | PageIndex | Preferences | LinuxCNC.org

The following is taken (with permission) from http://www.jmkasunich.com/cgi-bin/blosxom/shoptask/fusee-1.html

Variable-pitch, variable-diameter threadcutting

In this post I mentioned making a 'fusee', as part of a rat-trap powered vehicle. The trap pulls a wire, which unwinds off of a threaded spool. The spool starts out large, to provide a lot of torque, then as the vehicle gets moving, the diameter drops to get more distance. The idea is the same as shifting gears in a car. The picture below shows the spool with the wire wound up, ready to go.

I promised a post and maybe a video about how that part was made, so here it is. First the video (on You Tube): http://www.youtube.com/watch?v=ACvRilmIKDQ&eurl=http://www.jmkasunich.com/cgi-bin/blosxom/shoptask/fusee-1.html

Below is the G-code that was used to cut the spool. I've broken it up into chunks so that I can explain what is going on.

Comments in g-code are in parenthesis. I tend to use a lot of comments - it's not the most transparent language, and I sometimes can't even read my own code a few weeks or months after I wrote it. I added even more comments when writing this post.

The lines after the comments are the data that defines the shape of the spool. I used a spreadsheet to calculate the shape I needed, based on how much wire I had, how far the vehicle had to go, and so on. Every project will need something different. When I was done, I had three columns with the Z (length), X (radius) and K (pitch) values. I exported that chunk of the spreadsheet to a text file, and massaged it into what you see below.

EMC2s g-code has a bit of a split-personality as programming languages go. It is a complete programming language, with flow control, variables, etc. In some ways it is quite high-level. You can cut a circle with a single line of code, the flow control is structured (no GOTO), etc. But there are no data structures, there are no arrays, in fact, there aren't even named variables. The statement '#1100 = 0.3200' assigns the value 0.3200 to the variable at location 1100. There 5000 possible variables, plus some dedicated ones above 5000. So what I'm doing here is making three "arrays", each with 28 entries. I'm wasting a bunch of variable space, from 1028 through 1099, 1128 thru 1199, etc, but it doesn't matter.

    (program to cut a variable-pitch, variable-diameter threaded pulley)

    (the profile - each segment is defined by its ending Z,X coordinates)
    (and by the distance per rev along that path - the pitch)
    (note that even though the profile is defined from left to right, )
    (the part will be cut from right to left)

    (Z values stored in #1000 and up)
    (X values stored in #1100 and up)
    (K values - pitch - stored in #1200 and up)

    #1000 = 0.0000   #1100 = 0.3200   #1200 = 0.0450
    #1001 = 0.0450   #1101 = 0.2560   #1201 = 0.0450
    #1002 = 0.1530   #1102 = 0.2560   #1202 = 0.0450
    #1003 = 0.2190   #1103 = 0.2406   #1203 = 0.0440
    #1004 = 0.2620   #1104 = 0.2189   #1204 = 0.0430
    #1005 = 0.3040   #1105 = 0.1920   #1205 = 0.0420
    #1006 = 0.3450   #1106 = 0.1651   #1206 = 0.0410
    #1007 = 0.3850   #1107 = 0.1480   #1207 = 0.0400
    #1008 = 0.4240   #1108 = 0.1390   #1208 = 0.0390
    #1009 = 0.4620   #1109 = 0.1330   #1209 = 0.0380
    #1010 = 0.4990   #1110 = 0.1280   #1210 = 0.0370
    #1011 = 0.5350   #1111 = 0.1280   #1211 = 0.0360
    #1012 = 0.5700   #1112 = 0.1280   #1212 = 0.0350
    #1013 = 0.6040   #1113 = 0.1280   #1213 = 0.0340
    #1014 = 0.7294   #1114 = 0.1280   #1214 = 0.0330
    #1015 = 0.7644   #1115 = 0.1219   #1215 = 0.0350
    #1016 = 0.7994   #1116 = 0.1132   #1216 = 0.0350
    #1017 = 0.8324   #1117 = 0.1025   #1217 = 0.0330
    #1018 = 0.8634   #1118 = 0.0918   #1218 = 0.0310
    #1019 = 0.8924   #1119 = 0.0831   #1219 = 0.0290
    #1020 = 0.9194   #1120 = 0.0790   #1220 = 0.0270
    #1021 = 0.9584   #1121 = 0.0770   #1221 = 0.0260
    #1022 = 1.4334   #1122 = 0.0770   #1222 = 0.0250
    #1023 = 1.4604   #1123 = 0.0770   #1223 = 0.0260
    #1024 = 1.4894   #1124 = 0.0770   #1224 = 0.0280
    #1025 = 1.5204   #1125 = 0.0770   #1225 = 0.0300
    #1026 = 1.6854   #1126 = 0.0770   #1226 = 0.0320
    #1027 = 1.7154   #1127 = 0.0770   #1227 = 0.0320

Now that I have my profile, there is some preamble code - every program needs to start off with a few lines of this. Select the units to be used, set blending mode, turn off tool length offsets and tool shape compensation, etc. See the EMC2 G-code documentation for the details of these commands. Finally, start the spindle, at a speed of 580 RPM.

    G20 (inches)
    G64 P0.002 (round corners with tolerance)
    G18 (XZ plane)
    G40 G49 (cancel compensation)
    G92.1 (cancel offsets)

    M3 S580 (start spindle)

The first step is to rough out the part from a solid cylinder to the tapered shape. The traditional way to do this is a CAM (Computer Aided Machining) program, but those are expensive. EMC2's 'O-word' extensions make g-code into a complete programming language, and it can do a lot of things that would normally be considered CAM. I was able to write code that roughs out the shape using the profile data stored in variables #1000-1027 and #1100-1127. The following code was written by a programmer who happens to be a hobby machinist. It will probably make more sense to programmers (even those who have never seen g-code) than it will to a machinist who isn't a programmer. This ain't your father's g-code.

A key thing to remember when looking at the rest of the g-code - this is generic code to rough and thread a part, based only on the profile data above, and on a few control variables. If you have a different profile, this code would be able to cut it with minimal changes. I've basically embedded the CAM right into the part program.

First, I set a number of variables to tell the code what it is supposed to do:

    #100 = 0.330    (initial blank radius)
    #101 = 0.050    (material to leave during roughing)
    #102 = 0.075    (depth per pass during roughing)
    #106 = 0.500    (Z offset, per inch of X offset - sets infeed angle)
    #107 = 0.400    (safe X - beyond OD of workpiece)
    #108 = 1.900    (safe Z - beyond end of workpiece)
    #109 = 6.0      (roughing feed)

The first step is to find out what part of the profile is the deepest. In my case, it is the first part to be cut (last entry in the profile data), but the program doesn't know that, or care. If the shape was an hourglass, this loop would find the skinniest part. The radius (X coordinate) of the skinniest part is stored in variable #103.

    (find #103 = X coordinate of deepest part of profile)
    #200 = 26  (loop counter)
    #103 = 100 (this will be the deepest cut)
    O100 while [ #200 GE 0 ]   (loop through all segments of the profile)
        O101 if [#[#200+1100] LT #103]
            #103 = #[#200+1100]
        O101 endif
        #200 = [#200-1]  (decrease loop counter by 1)
    O100 endwhile

The code above includes two of EMC2's 'O-word' extensions, a while loop and an if statement. The while loop starts at "O100 while" and ends at "O100 endwhile". I indented the loop body to make it easier to read, EMC2 ignores indenting. The if statement starts at "O101 if" and ends at "O101 endif". O-numbers must be unique - they are used by the interpreter to match up the beginning and ends of statements. The while loop starts at segment 26, and loops until it get to zero. "GE" means "greater than or equal".

"#[#200+1100]" might need some explaining. #200 is the loop variable - it starts at 26 and counts down to zero. 1100 is the location of the first X value. "[#200+1100]" adds the loop counter to 1100 to get the location of a specific X value, and the outer "#" looks at that location to get the actual X value. So in the first pass, when #200 is 26, [#200+1100] is 1126, and #[#200+1100] is the same as #1126, which is 0.0770.

The next step is to figure out how many roughing passes are needed. The code below does that by adding #102 (the depth per pass) and checking to see if the skinniest spot is still inside the diameter of the blank. If it is, it adds another pass.

    (find #104 = offset for first pass, and #105 pass count)
    #104 = [#101+#102]
    #105 = 1
    O102 while [[#103+#104] LT #100]
        #104 = [#104+#102]
        #105 = [#105+1]
    O102 endwhile

Now that those preliminary calculations are out of the way, its time to cut some metal. This next chunk of code is pretty complex. When I started writing this post, I was planning on explaining everything in lots of detail. That was an hour ago, and its getting late. So instead, I'm going to rely on the comments in the g-code. If anyone has any questions, please email me. If people want it, I'll come back and add more details later.

I will mention one detail here: the "if" statements at O105 and O106 are used to speed up the program by rapiding though any segements that are outside the blank. If you watch the first and second roughing passes in the video you will see the tool speed up. Those rapids were computed automatically by the g-code - there was no CAM involved, and no manual conversion of slow moves to rapids.

    (go to safe start point)
    G0X#107Z#108
    #104 = [#104-#102]  (move in by one step, so first pass will remove some metal)
    (loop through roughing passes)
    O103 while [#105 GT 0]  (#105 is the number of passes, it counts down)
        #200 = 26 (loop counter - counts through the segments on each pass)  
        #110 = [#[#200+1100]+#104]              (X at start of first segment)
        #112 = [#[#200+1000]+[#104*#106]]       (Z at start of first segment)
        (rapid to #102 outside start of pass)
        G0X[#110+#102]Z[#112+#102]
        (is start of segment inside the blank?)
        O105 if [#110 LE #100]
            (yes, cut to that point)
            G1F#109X#110Z#112
        O105 else
            (no, rapid to that point)
            G0X#110Z#112
        O105 endif
        (loop thru rest of segments)
        #200 = [#200-1]
        O104 while [ #200 GE 0 ]
            #111 = [#[#200+1100]+#104]          (X at end of segment)
            #113 = [#[#200+1000]+[#104*#106]]   (Z at start of segment)
            (is either start or end of segment inside the blank?)
            O106 if [[#110 LE #100] OR [#111 LE #100]]
                (yes, cut to the end)
                G1F#109X#111Z#113
            O106 else
                (no, rapid to the end)
                G0X#111Z#113
            O106 endif
            (previous end becomes next start)
            #110 = #111
            #112 = #113
            (next segment)
            #200 = [#200-1]
        O104 endwhile
        (move out to safe X if not already out)
        O107 if [#111 LT #107]
            G0X#107
        O107 endif
        (move over to safe Z)
        G0X#107Z#108
        (next pass)
        #104 = [#104-#102]
        #105 = [#105-1]
    O103 endwhile
    (roughing complete)

After roughing out the part, the next step is the threading. The code to do the threading looks very similar to the roughing code, except that it uses G33 spindle-synchronized moves instead of ordinary G1 linear moves.

Another detail: the code after the comment "(next pass)" is designed to reduce the depth of cut as it gets closer to the finished size, so the final cuts will be very light and avoid tool deflection. Once the amount of metal to be removed is less than the initial depth per pass, the O118 and O119 "if" statements reduce the depth of cut in half every time, until it reaches the minimum that was specified in #116.

    #101 = 0.100 (space to allow for accel)
    #104 = [#104+#102] (reset to offset of last pass)
    #102 = 0.007 (depth per pass during threading)
    #116 = 0.0004 (smallest finish pass)
    #105 = 1 (pass counter)
    (loop through threading passes)
    O113 while [#104 GE 0]
        #200 = 26 (loop counter)
        #110 = [#[#200+1100]+#104]		(X at start of first segment)
        #112 = [#[#200+1000]+[#104*#106]]	(Z at start of first segment)
        #114 = [#[#200+1200]]			(K value for first segment)
        (rapid start of pass)
        G0XZ[#112+#101]
        (start sync move to segment start)
        G33K#114X#110Z#112
        (loop thru rest of segments)
        #200 = [#200-1]
        O114 while [ #200 GE 0 ]
            #111 = [#[#200+1100]+#104]		(X at end of this segment, start of next)
            #113 = [#[#200+1000]+[#104*#106]]	(Z at end of this segment, start of next)
            #115 = [#[#200+1200]]		(K value for next segment)
            (cut this segment)
            G33K#114X#111Z#113
            (previous end becomes next start)
            #110 = #111
            #112 = #113
            #114 = #115
            (next segment)
            #200 = [#200-1]
        O114 endwhile
        (move out to safe X if not already out)
        O117 if [#111 LT #107]
            G0X#107
        O117 endif
        (move over to safe Z)
        G0X#107Z#108
        (next pass)
        O118 if [#104 LE [2*#102]]
            O119 if [#102 GT #116]
                #102 = [#104/2]
            O119 endif
        O118 endif
        #104 = [#104-#102]
        #105 = [#105+1]
    O113 endwhile

    M5 (stop spindle)
    G0X1Z3 (move clear of work)
    M2 (end program)
    %


LinuxCNCKnowledgeBase | RecentChanges | PageIndex | Preferences | LinuxCNC.org
This page is read-only. Follow the BasicSteps to edit pages. | View other revisions
Last edited May 3, 2008 6:39 pm by KennethLerman (diff)
Search:
Published under a Creative Commons License