这个平台采用的开源的GRBL,基于单片机Arduino,三个轴的设备。
支持G代码,也就是CNC上面用的G代码。
这个平台如下面所示:

你可以用它来写字,效果还不错。
控制程序实现了基础的功能:
示教点
步进调试位置
回原点的控制功能

由于这个平台没有源点感应开关,因此它所谓的原点, 是在开机那一刻的当前位置设置为原点.
因此这个机器不能中途断电, 否则你得重新设置原点.
并且每次结束程序前都会自动回一次原点, 才能断电.
下面列出核心类的代码, 方便大家参考.
代码是需要netMarketing类的支持的.
using netMarketing.automation.communication;
using netMarketing.dataType;
using netMarketing.http;
using sharClass;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace xyzControlPanel
{
public struct ptStruct
{
public string x;
public string y;
public string z;
public string u;
public string explain;
}
public struct ptListStruct
{
public string ptName;
public string x;
public string y;
public string z;
public string u;
public string explain;
}
public struct axisPosStruct
{
public double x;
public double y;
public double z;
public double u;
}
public class motionControl : Settings
{
[Config, Description("串口名称,例如COM7"), Category("xyz平台参数"), DefaultValue("")]
public string 串口名称 { get; set; }
[Config, Description("步进距离x,单位mm"), Category("xyz平台参数"), DefaultValue("")]
public double 步进距离x { get; set; }
[Config, Description("步进距离y,单位mm"), Category("xyz平台参数"), DefaultValue("")]
public double 步进距离y { get; set; }
[Config, Description("步进距离z,单位mm"), Category("xyz平台参数"), DefaultValue("")]
public double 步进距离z { get; set; }
[Config, Description("步进距离u,单位deg"), Category("xyz平台参数"), DefaultValue("")]
public double 步进距离u { get; set; }
[Config, Description("连续运动增量,x,y,z, 单位mm"), Category("xyz平台参数"), DefaultValue("")]
public double 连续运动增量 { get; set; }
[Config, Description("连续运动角度增量,只能轴u, 单位deg"), Category("xyz平台参数"), DefaultValue("")]
public double 连续运动角度增量 { get; set; }
[Config, Description("步进模式,0连续运动,1步进运动"), Category("xyz平台参数"), DefaultValue("")]
public int 步进模式 { get; set; }
[Config, Description("步进速度"), Category("xyz平台参数"), DefaultValue("")]
public int 步进速度 { get; set; }
[Config, Description("示教点列表,每行之间用|分隔"), Category("xyz平台参数"), DefaultValue("")]
public string 示教点列表 { get; set; }
private CommPort serial = null;
private bool isInitFlag = false;
private Dictionary<string, ptStruct> ptList = new Dictionary<string,ptStruct>();
public axisPosStruct workPos = default(axisPosStruct);
private axisPosStruct orgiPos = default(axisPosStruct);
public motionControl()
{
try
{
init();
isInitFlag = true;
InitCMD();
}
catch(Exception ex)
{
throw ex;
}
}
/// <summary>
/// 外部更新点列表
/// </summary>
/// <param name="data"></param>
public void updatePtList(List<ptListStruct> data)
{
StringBuilder sb1 = new StringBuilder();
if (data.Count > 0)
{
ptList.Clear();
foreach (var m in data)
{
ptList.Add(m.ptName, new ptStruct() { x = m.x, y = m.y, z = m.z, u = m.u, explain = m.explain });
}
}
Save();
}
/// <summary>
/// 返回点列表,按数据结构 List<ptListStruct> ,主要给外部改新点列表使用。
/// </summary>
/// <returns></returns>
public List<ptListStruct> getPtList()
{
var res = new List<ptListStruct>();
foreach(var m in ptList)
{
res.Add(new ptListStruct()
{
ptName = m.Key,
x=m.Value.x,
y=m.Value.y,
z=m.Value.z,
u=m.Value.u,
explain=m.Value.explain
});
}
return res;
}
/// <summary>
/// 取指定示教点信息
/// </summary>
/// <param name="ptName">示教点的名字</param>
/// <returns></returns>
public ptStruct getPosInfo(string ptName)
{
if (ptList.ContainsKey(ptName))
return ptList[ptName];
return default(ptStruct);
}
public new void Save()
{
try
{
if (ptList.Count > 0)
{
StringBuilder sb1 = new StringBuilder();
var list1 = new List<string>();
foreach (var m in ptList)
{
list1.Clear();
list1.Add(m.Key);
list1.Add(m.Value.x);
list1.Add(m.Value.y);
list1.Add(m.Value.z);
list1.Add(m.Value.u);
list1.Add(m.Value.explain);
sb1.Append(list1.createCSVRow()); sb1.Append("|");
}
示教点列表 = sb1.ToString();
if (示教点列表.Length > 2)
{
示教点列表 = 示教点列表.Substring(0, 示教点列表.Length - 1);
}
}
base.Save();
}
catch(Exception ex)
{
int k1 = 0;
}
}
public new void Load()
{
try
{
base.Load();
if (示教点列表.Length > 0)
{
ptList.Clear();
var ary1 = 示教点列表.Split('|');
foreach (var m in ary1)
{
var ary2 = m.Split(',');
if (ary2.Length == 6)
ptList.Add(ary2[0], new ptStruct()
{
x = ary2[1],
y = ary2[2],
z = ary2[3],
u = ary2[4],
explain = ary2[5]
});
}
}
}
catch(Exception ex)
{
int k1 = 0;
}
}
/// <summary>
/// 把当前机器位置设置为原点位置,指令
/// </summary>
public void setOriPosCMD()
{
if (!isInit()) throw new ArgumentException("运动模板没有初始化(或者没有初始化成功!)");
try
{
var cmd = "G10 P0L20 X0.0Y0.0Z0.0\r";
serial.Write(binHelper.toBin<string>(cmd),cmd.Length);
orgiPos = getAxisMacPosCMD();
}
catch (Exception ex)
{
throw ex;
}
}
/// <summary>
/// SLP指令,关闭电机
/// </summary>
public void SLPCMD()
{
if (!isInit()) throw new ArgumentException("运动模板没有初始化(或者没有初始化成功!)");
try
{
var cmd = "SLP\r";
serial.Write(binHelper.toBin<string>(cmd), cmd.Length);
}
catch (Exception ex)
{
throw ex;
}
}
/// <summary>
/// x,y,z平台初始化指令
/// </summary>
public void InitCMD()
{
try
{
var buff = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 , 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
var msg = binHelper.toBin<string>("$X\r");
serial.Write(msg,msg.Length);
Thread.Sleep(50);
serial.Read(ref buff, buff.Length);
msg = binHelper.toBin<string>("G10 P0L20 X0.0Y0.0Z0.0\r");
serial.Write(msg,msg.Length);
Thread.Sleep(50);
//机器初始化时,把当前位置设置为原点位置
orgiPos = getAxisMacPosCMD();
serial.Read(ref buff, buff.Length);
msg = binHelper.toBin<string>("$ZL00418467\r");
serial.Write(msg,msg.Length);
Thread.Sleep(50);
serial.Read(ref buff, buff.Length);
}
catch(Exception ex)
{
throw ex;
}
}
/// <summary>
/// 解锁指令
/// </summary>
public void unlockCMD()
{
if (!isInit()) throw new ArgumentException("运动模板没有初始化(或者没有初始化成功!)");
try
{
var msg = binHelper.toBin<string>("$X\r");
serial.Write(msg,msg.Length);
}
catch (Exception ex)
{
throw ex;
}
}
/// <summary>
/// 所有轴停止运动
/// </summary>
public void axisStopCMD()
{
if (!isInit()) throw new ArgumentException("运动模板没有初始化(或者没有初始化成功!)");
}
/// <summary>
/// 去原点指令
/// </summary>
public void goOrgiCMD()
{
if (!isInit()) throw new ArgumentException("运动模板没有初始化(或者没有初始化成功!)");
try
{
var msg = binHelper.toBin<string>("G01 X0Y0Z0F20000\r");
serial.Write(msg, msg.Length);
}
catch (Exception ex)
{
throw ex;
}
}
/// <summary>
/// 示教用x轴步进运动
/// </summary>
/// <param name="供给量"></param>
/// <returns></returns>
public void tMoveXCMD(int speed,bool isForward=true, double 供给量= 0.08333)
{
//$J=G21G91X0Y0.08333Z0F500
if (!isInit()) throw new ArgumentException("运动模板没有初始化(或者没有初始化成功!)");
try
{
if (!isForward) 供给量 = 供给量 * -1;
string msg = string.Format("$J=G21G91X{0}Y0Z0F{1}\r",供给量, speed);
var strmsg = binHelper.toBin<string>(msg);
serial.Write(strmsg, strmsg.Length);
Stopwatch sw1 = new Stopwatch(); sw1.Start();
while (true)
{
Thread.Sleep(10);
var recvBuff = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
serial.Read(ref recvBuff, 10);
if (binHelper.InBin(recvBuff, new byte[] { (byte)'o', (byte)'k' }) >= 0)
break;
if (sw1.ElapsedMilliseconds >2000) { break; }
}
sw1.Stop();
}
catch (Exception ex)
{
throw ex;
}
}
/// <summary>
/// 示教用y轴步进运动
/// </summary>
/// <param name="供给量"></param>
/// <returns></returns>
public void tMoveYCMD(int speed, bool isForward = true, double 供给量 = 0.08333)
{
//$J=G21G91X0Y0.08333Z0F500
if (!isInit()) throw new ArgumentException("运动模板没有初始化(或者没有初始化成功!)");
try
{
if (!isForward) 供给量 = 供给量 * -1;
string msg = string.Format("$J=G21G91X0Y{0}Z0F{1}\r", 供给量, speed);
var strmsg = binHelper.toBin<string>(msg);
serial.Write(strmsg, strmsg.Length);
Stopwatch sw1 = new Stopwatch(); sw1.Start();
while (true)
{
Thread.Sleep(10);
var recvBuff = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
serial.Read(ref recvBuff, 10);
if (binHelper.InBin(recvBuff, new byte[] { (byte)'o', (byte)'k' }) >= 0)
break;
if (sw1.ElapsedMilliseconds > 2000) { break; }
}
sw1.Stop();
}
catch (Exception ex)
{
throw ex;
}
}
/// <summary>
/// 示教用Z轴步进运动
/// </summary>
/// <param name="供给量"></param>
/// <returns></returns>
public void tMoveZCMD(int speed, bool isForward = true, double 供给量 = 0.08333)
{
//$J=G21G91X0Y0.08333Z0F500
if (!isInit()) throw new ArgumentException("运动模板没有初始化(或者没有初始化成功!)");
try
{
if (!isForward) 供给量 = 供给量 * -1;
string msg = string.Format("$J=G21G91X0Y0Z{0}F{1}\r", 供给量, speed);
var strmsg = binHelper.toBin<string>(msg);
serial.Write(strmsg, strmsg.Length);
Stopwatch sw1 = new Stopwatch(); sw1.Start();
while (true)
{
Thread.Sleep(10);
var recvBuff = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
serial.Read(ref recvBuff, 10);
if (binHelper.InBin(recvBuff, new byte[] { (byte)'o', (byte)'k' }) >= 0)
break;
if (sw1.ElapsedMilliseconds > 2000) { break; }
}
sw1.Stop();
}
catch (Exception ex)
{
throw ex;
}
}
public bool tMoveUCMD(int speed, bool isForward = true, double 供给量 = 0.018)
{
return true;
}
public bool helpCMD()
{
if (!isInit()) throw new ArgumentException("运动模板没有初始化(或者没有初始化成功!)");
try
{
string msg = string.Format("$\r");
var strmsg = binHelper.toBin<string>(msg);
serial.Write(strmsg, strmsg.Length);
while (true)
{
Thread.Sleep(10);
var recvBuff = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
serial.Read(ref recvBuff, 10);
break;
}
return true;
}
catch (Exception ex)
{
throw ex;
}
}
/// <summary>
/// 取轴的工作位置
/// </summary>
/// <returns></returns>
public axisPosStruct getAxisWorkPosCMD()
{
axisPosStruct axispos = default(axisPosStruct);
var macpos = getAxisMacPosCMD();
axispos.x = macpos.x - orgiPos.x;
axispos.y = macpos.y - orgiPos.y;
axispos.z = macpos.z - orgiPos.z;
axispos.u = macpos.u - orgiPos.u;
return axispos;
}
/// <summary>
/// 取轴的机器位置
/// </summary>
/// <returns></returns>
public axisPosStruct getAxisMacPosCMD()
{
axisPosStruct axispos=default(axisPosStruct);
if (!isInit()) throw new ArgumentException("运动模板没有初始化(或者没有初始化成功!)");
try
{
string msg = string.Format("?\r");
var strmsg = binHelper.toBin<string>(msg);
serial.Write(strmsg, strmsg.Length);
Stopwatch sw1 = new Stopwatch(); sw1.Start();
while (true)
{
Thread.Sleep(10);
var recvBuff = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
serial.Read(ref recvBuff, recvBuff.Length);
if (binHelper.InBin(recvBuff, new byte[] { (byte)'P', (byte)'o', (byte)'s', (byte)':' }) >= 0)
{
var txt=Encoding.Default.GetString(recvBuff);
var ary1 = stringHelper.getMidString(txt, "MPos:", "|Bf:").Split(',');
if(ary1.Length==3)
{
axispos.x = double.Parse(ary1[0]);
axispos.y = double.Parse(ary1[1]);
axispos.z = double.Parse(ary1[2]);
axispos.u = 0;
}
break;
}
if (sw1.ElapsedMilliseconds > 2000) { sw1.Stop(); return axispos; }
}
sw1.Stop();
return axispos;
}
catch (Exception ex)
{
throw ex;
}
}
/// <summary>
/// xy轴以指定速度移动到绝对位置
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <param name="speed"></param>
public bool goxyzCMD(double x, double y,double z, int speed)
{
if (!isInit()) throw new ArgumentException("运动模板没有初始化(或者没有初始化成功!)");
try
{
//使用绝对坐标
string msg = string.Format("G90\r");
var strmsg = binHelper.toBin<string>(msg);
serial.Write(strmsg, strmsg.Length);
//齐步走
msg = string.Format("G1 X{0}Y{1}Z{2}F{3}\r",
x.ToString("0.000"), y.ToString("0.000"),z.ToString("0.000"), speed);
strmsg = binHelper.toBin<string>(msg);
serial.Write(strmsg, strmsg.Length);
Stopwatch sw1 = new Stopwatch(); sw1.Start();
while (true)
{
Thread.Sleep(10);
var recvBuff = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
serial.Read(ref recvBuff, recvBuff.Length);
if (binHelper.InBin(recvBuff, new byte[] { (byte)'o', (byte)'k' }) >= 0)
break;
if (sw1.ElapsedMilliseconds > 10000) { sw1.Stop(); return false; }
}
sw1.Stop();
return true;
}
catch (Exception ex)
{
throw ex;
}
}
/// <summary>
/// 运动控制类初始化成功标志
/// </summary>
/// <returns></returns>
public bool isInit()
{
return isInitFlag;
}
/// <summary>
/// 初始化运动控制类
/// </summary>
public void init()
{
try
{
serial = new CommPort(串口名称);
serial.Open();
示教点列表 = "";
}
catch(Exception ex)
{
throw ex;
}
}
}
}有空勇哥会把程序完善一下,写成一个tcp服务器的服务模块, 以方便视觉程序通讯调用.
目前只能用程序的调试运动功能来配合视觉模块做测试了.
---------------------
作者:hackpig
来源:www.skcircle.com
版权声明:本文为博主原创文章,转载请附上博文链接!


少有人走的路



















