EtherCAT总线型运动控制器-ZMC600M
ZMC600M系列高性能多轴运动控制器是一款EtherCAT总线立式运动控制器,控制器本身最多支持6-32轴的复杂的控制需求。
ZMC600M是基于“ARM架构+纯国产Linux运动控制实时内核MotionRT750+ EtherCAT”的总线型运动控制器,打破平台壁垒,实现了嵌入式系统与运动控制和实时内核技术的无缝衔接。
ZMC600M硬件功能特性:
● 内置Linux系统,搭载强实时内核MotionRT750;
● 可选6-32轴运动控制,EtherCAT总线最快通讯周期可达250μs;
● 24路高速IO输入,2路高速锁存,3路单端编码器接口;
● 16路高速IO输出,4路PWM/PSO,4路单端脉冲轴接口;
● 2路AD,2路DA(0-10V,12bit);
● 支持EtherNET、CAN、RS232、RS485通讯;
● 1路MPG手轮接口;
● 支持掉电存储;
● 特殊型号支持5V/24V推挽输出功能;
● 支持多文件多任务编程,可支持PC程序和控制器内置程序同时工作。
更多关于ZMC600M详情点击→基于Linux的强实时EtherCAT运动控制器ZMC600M。
ZMC600M介绍视频请点击→基于Linux的强实时EtherCAT总线型运动控制器ZMC600M
基于linux平台的ZMotionCadEx和Qt/C++,把CAD文件导入、DXF验证、图形预览、轴参数配置和运动控制流程串成一个可演示、可参考复用的小型验证DEMO。
一、痛点及工程价值
在真实的CAD/CAM或运动控制项目里,问题通常不是单点出现的,而是出现在一条很长的链路中:
CAD文件:
CAD库导入
几何数据解析
图形预览
坐标和参数转换
控制器连接
运动执行
任何一层出现问题,最后用户看到的可能是:
“这个图纸跑不了”
“导入以后没图形”
“图形看起来对,但设备动作不对”
“控制器能连上,但加工效果异常”
所以这个demo的目标,不是做一个简单的文件打开工具,而是把“CAD文件→图形预览→参数配置→控制器执行”这条链路串起来,方便开发、联调、演示和现场排障。
二、CAD图形的校验优势
这个Demo主要用来验证从图纸到设备执行之间的关键环节。
对开发来说,它可以验证CAD导入逻辑和图形数据处理是否正确。
对排障来说,它可以帮助判断问题到底出在文件、导入库、参数还是设备层。
对演示来说,它可以比较完整地展示从CAD文件到运动控制执行的过程。
三、Demo已实现的功能模块
当前版本已实现的核心功能如下:
支持DXF/PLT/AI/EPS等CAD文件导入。
基于正运动ZMotionCadEx库解析CAD文件。
支持导入后的图形实时预览。
支持X/Y双轴脉冲当量、最大速度、最大加速度、回零速度配置。
支持控制器IP连接。
支持回零、开始加工、暂停、停止等运动控制操作。
支持系统日志输出,方便查看导入、连接和运动控制过程。
支持加工用红线覆盖,显示加工进度。
额外加入了一个基于dxflib的DXF验证器,用来排查导图失败问题。
四、Demo的实现流程
CAD导入核心逻辑代码如下:
CadData CadImporter::importFile(const QString& filePath, double unit, double precision) { CadData result; result.success = false; // option = 0:强制将实体转换为线段,便于后续预览和运动控制 int option = 0; // 原始 precision = 0.1 时,部分 SPLINE 曲线转换效果不稳定 // 调整到 0.01,提高曲线离散和导入成功率 double adjustedPrecision = 0.01; Struct_ZCad_VectGroup* vectGroup = ZMotionCad3_ImportVectGraph( filePath.toUtf8().constData(), unit, option, adjustedPrecision, 0 ); if (!vectGroup) { result.errorMessage = "导入失败:ZMotionCad3_ImportVectGraph 返回空指针"; return result; } double minX = 1e9; double minY = 1e9; double maxX = -1e9; double maxY = -1e9; int layerIndex = 0; Struct_ZCad_VectGroup* currentGroup = vectGroup; while (currentGroup) { layerIndex++; Struct_ZCad_Vect* vect = currentGroup->m_pVect; while (vect) { CadEntity entity; entity.layer = layerIndex; entity.color = currentGroup->m_ColorPen; entity.isClosed = vect->IfCloseLine; // 优先读取转换后的线段数据 Struct_ZCad_Seg* firstSeg = vect->m_pLineSeg; // 如果转换后的线段为空,则 fallback 到原始线数据 if (!firstSeg) { firstSeg = vect->m_pLine; } if (firstSeg) { Struct_ZCad_Seg* seg = firstSeg; int segCount = 0; do { QPointF point(seg->x1, seg->y1); entity.points.append(point); minX = qMin(minX, seg->x1); minY = qMin(minY, seg->y1); maxX = qMax(maxX, seg->x1); maxY = qMax(maxY, seg->y1); seg = seg->m_pnext; segCount++; // 防止链表异常导致死循环 if (segCount > 10000) { break; } } while (seg && seg != firstSeg); result.entities.append(entity); } Struct_ZCad_Vect* nextVect = (Struct_ZCad_Vect*)vect->m_pnext; if (nextVect == currentGroup->m_pVect) { break; } vect = nextVect; } Struct_ZCad_VectGroup* nextGroup = (Struct_ZCad_VectGroup*)currentGroup->m_pnext; if (nextGroup == vectGroup) { break; } currentGroup = nextGroup; } if (!result.entities.isEmpty()) { result.boundingRect = QRectF(QPointF(minX, minY), QPointF(maxX, maxY)); } ZMotionCad3_DeleteChain((Struct_ZCad_Item*)vectGroup); result.success = true; return result; }这里的关键点有两个:
1.导入时使用option=0,强制将CAD实体转换为线段,方便后续预览和运动控制。
②读取数据时不只检查m_pLineSeg,还有fallback检查m_pLine,避免库已经返回了原始线数据,但业务代码没有读取到。
这也是后面解决SPLINE样条曲线导入为空问题的关键。
CAD文件不是简单“打开一下”,而是要经过导入库解析、图层遍历、矢量对象读取、坐标提取,最后才能进入图形预览和运动控制流程。
五、DXF验证流程
在实际调试中,经常会遇到一种情况:
CAD软件里能正常打开的DXF文件,导入程序后实体数却是0。
这时候不能直接判断文件坏了,也不能直接判断CAD库有问题。
更合理的做法是先把问题拆开:
文件里到底有没有几何实体?
实体类型是什么?
是LINE、CIRCLE、ARC、LWPOLYLINE,还是SPLINE?
是文件格式问题,还是导入参数问题?
是CAD库没有解析出来,还是业务代码没有读取到?
所以这个demo里额外加了一个基于dxflib的第三方验证器。
它不参与正式加工流程,只负责独立检查DXF文件内容。
这样可以在正运动CAD库之外,提供一个对照结果。
验证器可以帮助确认:
文件是否能打开。
是否包含有效几何实体。
各类实体数量是多少。
图形边界范围是否合理。
文件是否可能是格式或实体类型问题。
1.脚本里的关键检查逻辑。
DXF_FILE="$1" if [ ! -f "$DXF_FILE" ]; then echo "错误: 文件不存在: $DXF_FILE" exit 1 fi if grep -q "ENTITIES" "$DXF_FILE"; then echo "找到 ENTITIES 段" else echo "未找到 ENTITIES 段,文件可能不包含几何实体" fi echo "实体类型统计:" for entity_type in LINE CIRCLE ARC LWPOLYLINE SPLINE ELLIPSE TEXT DIMENSION; do count=$(grep -A1 "^0$" "$DXF_FILE" | grep -c "^${entity_type}$") if [ $count -gt 0 ]; then printf " %-15s %d\n" "$entity_type:" $count fi done2.Qt验证器入口。
#include #include #include "dxflibverifier.h" int main(int argc, char *argv[]) { QCoreApplication app(argc, argv); if (argc < 2) { qDebug() << "用法: ./test_verifier "; return 1; } QString filePath = QString::fromLocal8Bit(argv[1]); DxflibVerifier verifier; DxfVerifyResult result = verifier.verifyFile(filePath); if (result.success && result.entities.size() > 0) { qDebug() << "验证成功,找到" << result.entities.size() << "个实体"; return 0; } qDebug() << "验证失败,未找到有效几何实体"; return 1; }3.验证结果数据结构。
struct DxfEntity { QVector points; bool isClosed; QString layer; // 0=LINE, 1=CIRCLE, 2=ARC, 3=LWPOLYLINE, 4=SPLINE int entityType; }; struct DxfVerifyResult { bool success; QString errorMessage; QVector entities; int totalLines; int totalCircles; int totalArcs; int totalPolylines; int totalSplines; double minX; double minY; double maxX; double maxY; }; DxfVerifyResult DxflibVerifier::verifyFile(const QString& filePath) { DxfVerifyResult result; if (!parseDxfFile(filePath, result)) { result.success = false; result.errorMessage = "解析失败或文件不存在"; return result; } result.success = true; printSummary(result); return result; }4.解析DXF实体类型。
bool DxflibVerifier::parseDxfFile(const QString& filePath, DxfVerifyResult& result) { QFile file(filePath); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { return false; } QTextStream in(&file); in.setCodec("UTF-8"); QStringList allCodes; QStringList allValues; // DXF 文本格式通常是“组码 + 值”成对出现 while (!in.atEnd()) { QString codeLine = in.readLine().trimmed(); if (in.atEnd()) break; QString valueLine = in.readLine().trimmed(); if (!codeLine.isEmpty() && !valueLine.isEmpty()) { allCodes.append(codeLine); allValues.append(valueLine); } } file.close(); if (allCodes.isEmpty()) { return false; } for (int i = 0; i < allCodes.size(); i++) { if (allCodes[i] != "0") { continue; } QString entityType = allValues[i].toUpper(); if (entityType == "LINE") { parseLine(allCodes.mid(i), allValues.mid(i), result); } else if (entityType == "CIRCLE") { parseCircle(allCodes.mid(i), allValues.mid(i), result); } else if (entityType == "ARC") { parseArc(allCodes.mid(i), allValues.mid(i), result); } else if (entityType == "LWPOLYLINE") { parseLWPolyline(allCodes.mid(i), allValues.mid(i), result); } else if (entityType == "SPLINE") { parseSpline(allCodes.mid(i), allValues.mid(i), result); } } return true; }六、这个Demo如何帮助排障?
加入DXF验证之后,整个排障路径会清晰很多。
如果第三方验证器能读到实体,但正运动CAD库导入为空,说明文件本身大概率不是空的。
这时候应该重点检查:
导入参数是否合适。
精度设置是否过粗。
是否存在 SPLINE、ELLIPSE等复杂实体。
是否需要将曲线转换成线段。
程序是否漏读了某些返回字段。
如果第三方验证器也读不到实体,那排查方向就应该回到DXF文件本身。比如:
文件是否为二进制DXF。
是否只有BLOCK定义但没有实际插入。
是否只有文字、标注等非几何实体。
是否需要另存为ASCII DXF R12/R14。
是否需要在CAD软件里先执行炸开或转换。
如果图形预览正常,但设备执行异常,那问题就不应该继续纠结在文件层。
这时候应该看:
X/Y轴脉冲当量是否正确。
最大速度和加速度是否合理。
回零速度是否匹配当前设备。
进给速度和主轴转速是否符合工艺。
控制器IP和连接状态是否正常。
运动控制指令是否正确执行(检查函数返回值是否为零)。
这样一来,原本模糊的“跑不了”,就可以被拆成更明确的问题类型。
教学视频:
ARM工控机Linux下从CAD图纸到运动加工(一)