http://www.gkong.com 2026-07-22 12:01 深圳市正运动技术有限公司
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库之外,提供一个对照结果。
验证器可以帮助确认:
?文件是否能打开。
?是否包含有效几何实体。
?各类实体数量是多少。
?图形边界范围是否合理。
?文件是否可能是格式或实体类型问题。
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
done
2.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图纸到运动加工(一):Qt Demo的CAD导入

正运动技术专注于运动控制技术研究和通用运动控制软硬件产品的研发,是国家级高新技术企业。正运动技术汇集了来自华为、中兴等公司的优秀人才,在坚持自主创新的同时,积极联合各大高校协同运动控制基础技术的研究。主要业务有:运动控制卡_运动控制器_EtherCAT运动控制卡_EtherCAT控制器_运动控制系统_视觉控制器__运动控制PLC_运动控制_机器人控制器_视觉定位_XPCIe/XPCI系列运动控制卡等等。