Hello Guest it is December 12, 2019, 04:05:37 AM

Author Topic: Mach4 Spindle Speed Linearity Hack  (Read 806 times)

0 Members and 1 Guest are viewing this topic.

Mach4 Spindle Speed Linearity Hack
« on: November 21, 2017, 07:32:06 PM »
Nice to be able to contribute something instead of asking everyone else for help solving my problems.
For those of us that don’t have VFD’s or spindle motors that are particularly linear in their speed ramps this will get you a lot closer.  Much like Mach3’s linearity.dat. 

You must have a spindle speed encoder for this to generate the linearity.csv file by running one of the scripts but you could generate one by hand as well.  The format of the file consists of 4 columns: Pulley/Gear, Speed you request, value from 0 to 100, Speed you need to enter to get the speed you want.  The value between 0 to 100 is not used by my script but could be used by those with modbus.  The entries in the file look like this

0,49,6,105
0,84,8,140
0,119,10,175
0,153,12,210
0,189,14,245
0,223,16,280
0,259,18,315
0,294,20,350
0,330,22,385
0,365,24,420
This was done on Mach4 build 3481 on the wxLathe screenset but I believe it will work for the mill as well.
The process consists of a few steps. 

1.     CREATE A PACKAGE of your current profile: Help->Support->Package Current Profile

2.   Create a button somewhere on your screen and copy the script below into the Left Up Script.  It is currently set to run from 6% to 80% by steps of 2 and spins at those steps for 5 seconds each to get the spindle to equilibrium.  You can modify those settings if you wish.  I don’t use my lathe at more than 80% power and it doesn’t really turn at less than 6%. Save the screen.  This script will generate a list of settings for the gear and put it in the History Log.  Clear the history log. This will lock up your screen while it runs!  Do this for each pulley/gear you have.  Save the file to ‘Linearity.csv’.  Open it with excel and save it.  You should be able to now open it with notepad and get the 4 columns for each gear.

3.   Copy the ‘spindlespeed.mcs to your profiles macro folder.  You’re now ready to go.  If you run an MDI of m3 s400 you should see the actual RPM very close to 400 but the command RPM will be something else.

4.   If you want to have the command and requested RPM’s visible do the following.  Create a register in gRegs0 called ssCMND, give it a value of one and I made it persistent.  I moved/sized the Spindle Tab items so that I could fit another DRO above the existing ‘PGM RPM’ dro.  Set the PG RPM dro to read only with a gray background and a black foreground set its height to 20 so that it looks more like a text than a dro.  Name the new DRO ‘ssCMND’.  Select it’s ‘Register’ property to the ‘gRegs0/ssCMND’  from the drop down list box.  In its Left Up Script enter this code:  mc.mcCntlMdiExecute(mc.mcGetInstance(),'s'..tostring(scr.GetProperty('ssCMND','Value')))

Now when you change the spindle speed through the mdi, gcode or through the ssCMND dro the old PGM RPM dro will show the speed necessary to get the requested speed and the ssCMND dro will show what speed was commanded.  If anyone knows of a way to improve it please feel free to help.   Hope this is clear enough and of use to someone else.

Code: [Select]
--spindlspeed.mcs
local inst = mc.mcGetInstance()
  local profile = mc.mcProfileGetName(inst)
  local mcPath = mc.mcCntlGetMachDir(inst)
  local nGears=10
  local tblSS = {}
  local gear=1
  local cmdSS=2
  local pctSS=3 
  local newSS=4
 
  function parseLine(s)
    local gr,cmd,pc,nw
    local i=1
for w in s:gmatch("([^,]+)") do
      if i==1 then
        gr=tonumber(w)
  elseif i==2 then
    cmd=tonumber(w)
  elseif i==3 then
    pc=tonumber(w)
  elseif i==4 then
    nw=tonumber(w)
      end
  i=i+1
    end  
    return gr,cmd,pc,nw
  end
 
  function interpolate(x,x0,y0,x1,y1)
      return y0+(x-x0)*(y1-y0)/(x1-x0)
  end
 
  function ssmLoadTable()
    local fName=''..mcPath.."\\Profiles\\"..profile.."\\Modules\\Linearity.csv"
local ssFile=io.open(fName,"r")     
    if ssFile==nil then
      wx.wxMessageBox("File: "..fName.." Not Found")
  return 0
    end
for i=1, nGears do
  tblSS[i]={}
end
  i=1
    local gr,cmd,pc,nw
local currGR=1
for line in ssFile:lines() do
      gr,cmd,pc,nw=parseLine(line)
  if currGR~=gr then
    i=1
currGR=gr
  end
  tblSS[gr+1][i]={}
      tblSS[gr+1][i][1]=gr
      tblSS[gr+1][i][2]=cmd
  tblSS[gr+1][i][3]=pc
  tblSS[gr+1][i][4]=nw
  i=i+1
    end
    ssFile:close()
-- wx.wxMessageBox("end of read")
return 1
  end

  function ssmGetNewSS(gear,cmd)
    if cmd==0 then
      return cmd
    end
    for i=1,#tblSS[gear+1] do
      if tblSS[gear+1][i][2]== cmd then
--      wx.wxMessageBox('equal:'..tostring(tblSS[gear+1][i][4]))-- case of exact match
      return tblSS[gear+1][i][4]
  end
  if tblSS[gear+1][i][2]>cmd then
        if i==1 then
--        wx.wxMessageBox('first entry')--case of first entry , can't go lower
      return tblSS[gear+1][i][4]
    end
--wx.wxMessageBox('interpolate')
        return interpolate(cmd,tblSS[gear+1][i-1][2],tblSS[gear+1][i-1][4],tblSS[gear+1][i][2],tblSS[gear+1][i][4])     
      end
    end
--  wx.wxMessageBox('over max: '..tostring(tblSS[gear+1][#tblSS[gear+1]][4]))-- case over the limit return limit
    return tblSS[gear+1][#tblSS[gear+1]][4]  --change 4 to 3 to return percent for a modbus number?
  end

function spindlespeed(rpm)
  local inst = mc.mcGetInstance();
  rpm = mc.mcSpindleGetCommandRPM(inst);
 
  local rslt=ssmLoadTable()
  if rslt~=1 then
    wx.wxMessageBox("load failure: "..tostring(rslt))
    return 0
  end
  local gear=mc.mcSpindleGetCurrentRange(inst)
  rslt=ssmGetNewSS(gear,rpm)
  mc.mcSpindleSetCommandRPM(inst,rslt)
  local hReg, rc=mc.mcRegGetHandle(inst,'gRegs0/ssCMND')
  if hReg~=nil then
    mc.mcRegSetValue(hReg,rpm)
  end
--  wx.wxMessageBox(tostring(rslt))
  mc.mcCntlSetLastError(inst,string.format("New SS: %.0f",rslt))
end

if (mc.mcInEditor() == 1) then
  spindlespeed();
end

Spindle Speed Macro

Code: [Select]
[/--spindlspeed.mcs
local inst = mc.mcGetInstance()
  local profile = mc.mcProfileGetName(inst)
  local mcPath = mc.mcCntlGetMachDir(inst)
  local nGears=10
  local tblSS = {}
  local gear=1
  local cmdSS=2
  local pctSS=3 
  local newSS=4
 
  function parseLine(s)
    local gr,cmd,pc,nw
    local i=1
for w in s:gmatch("([^,]+)") do
      if i==1 then
        gr=tonumber(w)
  elseif i==2 then
    cmd=tonumber(w)
  elseif i==3 then
    pc=tonumber(w)
  elseif i==4 then
    nw=tonumber(w)
      end
  i=i+1
    end  
    return gr,cmd,pc,nw
  end
 
  function interpolate(x,x0,y0,x1,y1)
      return y0+(x-x0)*(y1-y0)/(x1-x0)
  end
 
  function ssmLoadTable()
    local fName=''..mcPath.."\\Profiles\\"..profile.."\\Modules\\Linearity.csv"
local ssFile=io.open(fName,"r")     
    if ssFile==nil then
      wx.wxMessageBox("File: "..fName.." Not Found")
  return 0
    end
for i=1, nGears do
  tblSS[i]={}
end
  i=1
    local gr,cmd,pc,nw
local currGR=1
for line in ssFile:lines() do
      gr,cmd,pc,nw=parseLine(line)
  if currGR~=gr then
    i=1
currGR=gr
  end
  tblSS[gr+1][i]={}
      tblSS[gr+1][i][1]=gr
      tblSS[gr+1][i][2]=cmd
  tblSS[gr+1][i][3]=pc
  tblSS[gr+1][i][4]=nw
  i=i+1
    end
    ssFile:close()
-- wx.wxMessageBox("end of read")
return 1
  end

  function ssmGetNewSS(gear,cmd)
    if cmd==0 then
      return cmd
    end
    for i=1,#tblSS[gear+1] do
      if tblSS[gear+1][i][2]== cmd then
--      wx.wxMessageBox('equal:'..tostring(tblSS[gear+1][i][4]))-- case of exact match
      return tblSS[gear+1][i][4]
  end
  if tblSS[gear+1][i][2]>cmd then
        if i==1 then
--        wx.wxMessageBox('first entry')--case of first entry , can't go lower
      return tblSS[gear+1][i][4]
    end
--wx.wxMessageBox('interpolate')
        return interpolate(cmd,tblSS[gear+1][i-1][2],tblSS[gear+1][i-1][4],tblSS[gear+1][i][2],tblSS[gear+1][i][4])     
      end
    end
--  wx.wxMessageBox('over max: '..tostring(tblSS[gear+1][#tblSS[gear+1]][4]))-- case over the limit return limit
    return tblSS[gear+1][#tblSS[gear+1]][4]  --change 4 to 3 to return percent for a modbus number?
  end

function spindlespeed(rpm)
  local inst = mc.mcGetInstance();
  rpm = mc.mcSpindleGetCommandRPM(inst);
 
  local rslt=ssmLoadTable()
  if rslt~=1 then
    wx.wxMessageBox("load failure: "..tostring(rslt))
    return 0
  end
  local gear=mc.mcSpindleGetCurrentRange(inst)
  rslt=ssmGetNewSS(gear,rpm)
  mc.mcSpindleSetCommandRPM(inst,rslt)
  local hReg, rc=mc.mcRegGetHandle(inst,'gRegs0/ssCMND')
  if hReg~=nil then
    mc.mcRegSetValue(hReg,rpm)
  end
--  wx.wxMessageBox(tostring(rslt))
  mc.mcCntlSetLastError(inst,string.format("New SS: %.0f",rslt))
end

if (mc.mcInEditor() == 1) then
  spindlespeed();
end
[code]

[code]

HTH

RT[/code][/code]

Offline smurph

*
  • *
  •  1,161 1,161
  • "That there... that's an RV."
    • View Profile
Re: Mach4 Spindle Speed Linearity Hack
« Reply #1 on: November 22, 2017, 01:53:35 AM »
Bravo!!!!  I love it when a plan comes together!

Steve
Re: Mach4 Spindle Speed Linearity Hack
« Reply #2 on: November 22, 2017, 05:22:43 AM »
Looking good.

Mike
We never have the time or money to do it right the first time, but we somehow manage to do it twice and then spend the money to get it right.