Linux IgH EtherCAT六轴机械臂实时控制与CiA402实践
简介围绕 EtherCAT 开源主站 IgH 在 Linux 平台上的工程实践这份 PDF 资料面向从事工业以太网、伺服运动控制与六轴机械臂开发的初中级工程师及学习者聚焦主站环境搭建、主从通信调试、实时性优化和轨迹规划等常见问题。压缩包内仅含 1 个 PDF 文件大小约 1.63MB篇幅集中便于直接阅读、检索与对照论文思路。内容结合汇川 SV630N 伺服电机与六自由度机械臂案例梳理 IgH 主站运行流程并涉及 Xenomai 实时内核增强、基于三次 B 样条插值的轨迹规划、改进 D-H 参数建模及逆运动学求解对于需要理解主站如何对接伺服从站、如何借助实时内核降低抖动、以及如何在笛卡尔空间生成平滑轨迹的读者具有较强参考价值。目前已有 5228 人学习适合用作开源主站入门、英文论文研读以及运动控制项目方案设计的参考资料。1. 从一台六轴机械臂说起主站为什么落在 IgH 上控制柜里一台工控机、六台国产伺服驱动器SV 系列、CiA402 标准从站中间只有一根网线。上位机要做的第一件事是把六台从站逐个认出来第二件事是让六轴的电流环在同一个节拍上闭合——ethercat linux igh 这套组合的价值就体现在这两件事上。开源主站有两条路SOEM 轻量、上手快但分布式时钟和状态机要自己补IgH 主站完整、内核态网卡驱动直通、能从 ESI 文件直接生成配置代码代价是流程长、坑多。这套方案面向的是要做六轴机械臂运动控制毕设、课程设计或自研控制器的读者从主站初始化一路讲到 CiA402 使能、改进 D-H 逆解和三次 B 样条轨迹最后给出可验收的判据。2. 总体架构设计四层职责、一个过程数据域和两条时间线在写第一行代码之前先把层次和数据流定下来。多关节机械臂的复杂度不在算法本身而在于“谁在什么时候把哪个字节放到哪个位置”。架构定错了后面调试会一直处于靠猜测的状况。2.1 四层结构与各自的边界整套系统我一般切成四层每层只暴露一个接口层与层之间不互相越界调用。层次主要职责关键接口或文件应用与规划层逆解求解、B 样条轨迹插值、使能流程调度用户态进程1000 Hz 周期任务主站层IgH 内核模块与用户态库过程数据域管理ec_master、ecrt_domain_*从站层伺服驱动器ESM 状态机、CiA402 状态机、DC 同步ESI 文件、0x6040/0x6041硬件层网线拓扑、电机、编码器反馈总线物理连接应用层拿到的是浮点关节角主站层拿到的是 16 位控制字和 32 位目标位置中间必须有人做单位换算和限幅。我的做法是在应用层和主站层之间放一个薄薄的“轴对象”每个轴结构体里存着偏移量、缩放系数、软限位、当前状态周期任务只调用axis_set_target()这类函数不直接碰指针运算。这样后面要换伺服型号或者改减速比只改轴对象主站流程一行不动。从站层要特别说明一点SV 系列这类国产伺服基本都遵循 CiA402 协议但 ESI 文件里给的默认 PDO 映射往往是“全量版”把电流、转矩、温度、报警码全塞进输入 PDO。对六轴 1 ms 周期来说这会白白吃掉带宽。我一般在设计阶段就把 RXPDO 砍到两项0x6040 控制字和 0x607A 目标位置TXPDO 砍到两项0x6041 状态字和 0x6064 实际位置。需要读电流或者报警时走 SDO 异步读不占周期带宽。2.2 过程数据域怎么划单 domain、六轴一张偏移表IgH 里所有周期数据都放在 domain 里一个 domain 对应一段连续内存主站每周期把这段内存打包进以太网帧发出去。有人习惯一轴一个 domain觉得“隔离性好”实际是给自己找麻烦多个 domain 意味着多次queue帧的拼接顺序和 DC 时间戳更难对齐出了问题也不好定位。六轴系统我的建议是一个 domain 装下全部过程数据注册时用一张表把六个从站的 PDO 项一次性登记完/* 六个从站的过程数据登记表位置、厂商、产品码、PDO 索引、偏移量指针 */ static ec_pdo_entry_reg_t domain_regs[] { /* 轴 10x6040 控制字 / 0x607A 目标位置 / 0x6041 状态字 / 0x6064 实际位置 */ { AXIS1_POS, VENDOR_ID, PRODUCT_CODE, 0x6040, 0x00, off_ctrl[0], NULL }, { AXIS1_POS, VENDOR_ID, PRODUCT_CODE, 0x607A, 0x00, off_tpos[0], NULL }, { AXIS1_POS, VENDOR_ID, PRODUCT_CODE, 0x6041, 0x00, off_stat[0], NULL }, { AXIS1_POS, VENDOR_ID, PRODUCT_CODE, 0x6064, 0x00, off_apos[0], NULL }, /* 轴 2 到轴 6 按同样四项重复仅从站位置递增 */ { AXIS2_POS, VENDOR_ID, PRODUCT_CODE, 0x6040, 0x00, off_ctrl[1], NULL }, /* ... */ { 0xFF, 0, 0, 0, 0, NULL, NULL } /* 表结束标记漏掉必然越界 */ };这张表的信息全部来自 ESI 文件厂商 ID 和产品码写错一个字节ecrt_domain_reg_pdo_entry_list()就会返回非零主站激活时直接报错。偏移量指针在注册成功后才会被赋值所以周期任务里读数据必须在ecrt_master_activate()之后、并且通过ecrt_domain_data()拿到的基址来算不能自己 malloc 一块内存硬凑。单 domain 的另一个好处是工作计数器WKC好对账。六个从站、每站四个 PDO 项理论上每周期 WKC 应该是 24实际值不等于 24 就说明有从站掉线或者映射对不上这个数字是排查时的第一线索比看状态机快得多。2.3 两条时间线1 ms 控制周期与 SYNC0 同步EtherCAT 的实时性来自两件事主站按固定周期发帧从站按分布式时钟在同一时刻锁存和输出。这两条时间线必须对齐否则“周期很稳但六轴不同步”表现在机械臂上就是走直线时末端画弧。控制周期由实时线程决定常见取值是 1 ms对应 1 kHz。周期越短对实时内核要求越高我一般先用 1 ms 把流程跑通再根据伺服电流环带宽往 500 µs 压。DC 同步则要靠主站做三件事选一台从站作参考时钟、把参考时钟的绝对时间广播给所有从站、让每个从站根据自己的传播延时做补偿。流程上对应ecrt_master_select_reference_clock()、ecrt_master_application_time()、ecrt_master_sync_reference_clock()、ecrt_master_sync_slave_clocks()四个调用顺序不能乱先给时间、再同步参考钟、最后同步从站钟且必须在ecrt_master_send()之前完成。有一个容易被忽略的细节application_time传的是纳秒时间戳取值应该和 SYNC0 的相位一致。我习惯把参考时钟的周期偏移设成半个周期让 SYNC0 中断落在主站发帧动作的中间位置这样从站采样时主站帧已经稳定在线上实测抖动会小一截。2.4 总线选型对比为什么不是 CAN 和 PROFINET有人会问CAN 也能控六轴为什么非得上 EtherCAT。把几种常见工业以太网和现场总线摆在一起看就清楚了。方案通信结构同步精度节点上限开放性六轴场景的实际感受CAN多主竞争毫秒级110 左右开放六轴 1 ms 周期带宽吃紧加轴就掉帧PROFINET IRT主从微秒级数百需授权同步好但从站芯片和开发授权是成本PowerLink主从微秒级数百需授权拓扑灵活生态偏小众EtherCAT主从百纳秒级65535完全开放帧在从站“飞过即处理”六轴几乎不占额外时间真正让 EtherCAT 在六轴机械臂上占优的是“on the fly”处理机制主站发一帧从站边转发边读写自己的那段数据整帧绕一圈回来只花几十微秒六个轴和六十个轴的时间开销差别很小。再加上 IgH 是纯软件主站、不需要专用主站芯片工控机的普通网口就能跑整套方案的硬件成本压得很低。代价是开发难度。商业方案在 Windows 下点几下鼠标就能配好从站Linux 下得自己写配置、自己推状态机、自己处理实时性这正是后面几章要填的坑。3. IgH 主站程序流程从请求主站到周期循环的七个步骤IgH 的调用顺序是固定的但它不会告诉你“为什么是这个顺序”。把七步拆开看每一步都在为下一步准备上下文少一步后面必然拿到空指针。3.1 七步初始化与最小可运行骨架下面这段是能编译运行的最小骨架省略了错误处理的分支打印实际项目里每个返回值都要判。/* main.c —— IgH 主站初始化七步骨架 */ #include ecrt.h #include stdio.h static ec_master_t *master; static ec_domain_t *domain; static uint8_t *domain_data; static unsigned int off_ctrl[6], off_stat[6], off_tpos[6], off_apos[6]; int main(void) { /* 第 1 步申请 0 号主站一台机器上多主站才需要区分编号 */ master ecrt_request_master(0); if (!master) return -1; /* 第 2 步创建过程数据域六轴共用一块 */ domain ecrt_master_create_domain(master); if (!domain) return -1; /* 第 3 步按表登记 PDO 项偏移量在这里被写回 */ if (ecrt_domain_reg_pdo_entry_list(domain, domain_regs)) { fprintf(stderr, PDO 登记失败检查 ESI 里的索引和子索引\n); return -1; } /* 第 4 步取从站配置句柄配置 DC 与看门狗可先跳过用默认 */ for (int i 0; i 6; i) { ec_slave_config_t *sc ecrt_master_slave_config( master, 0, i, VENDOR_ID, PRODUCT_CODE); if (!sc) return -1; /* SYNC0 周期 1 ms偏移 500 us让采样落在帧中间 */ ecrt_slave_config_dc(sc, 0x0300, 1000000, 500000, 0, 0); } /* 第 5 步激活主站此后不能再改配置 */ if (ecrt_master_activate(master)) return -1; /* 第 6 步拿域内存基址所有读写都基于它加偏移 */ domain_data ecrt_domain_data(domain); if (!domain_data) return -1; /* 第 7 步进入周期任务下一节展开 */ cyclic_loop(); return 0; }这段代码里有三个顺序陷阱。第一ecrt_master_slave_config()必须在activate()之前调用激活之后配置就冻结了再改会返回错误。第二ecrt_domain_data()也必须在激活之后拿激活前返回的地址是不确定的。第三从站位置参数传的是“环路上的顺序号”不是别名如果 ESI 里配了别名要用ecrt_master_get_slave()按别名反查别想当然写 0 到 5。3.2 从 ESI 到 C 代码cstruct 生成的配置怎么改手写ec_pdo_entry_info_t、ec_pdo_info_t、ec_sync_info_t三层结构很容易出错正确做法是让工具生成# 主站跑起来后哪怕只跑到 PREOP把 0 号从站的配置导成 C 代码 ethercat cstruct -p 0 sv_cfg.c # 顺便确认从站是否被识别、当前处在哪个状态 ethercat slaves ethercat pdos -p 0cstruct导出的是该从站 ESI 里的完整映射直接编译会让六个轴各带几十个 PDO 项。我的处理方式是只保留同步管理器 2 和 3 里的目标项其余删掉并把ec_sync_info_t数组的最后一个元素写成{ 0xff }作结束标记。删的时候要同步删掉domain_regs里对应的行两边不一致WKC 就会对不上。还有一种情况是同一型号伺服挂了六台cstruct只需要导一次然后ecrt_master_slave_config()里循环调用六次位置参数从 0 递增。别复制六份配置结构体改起来会疯。3.3 周期任务里的固定顺序周期任务的动作顺序是 IgH 最容易写反的地方。我把它固定成“收—处理—算—同步—发”五段/* 1 ms 周期任务运行在实时线程里 */ void cyclic_loop(void) { struct timespec next; clock_gettime(CLOCK_MONOTONIC, next); while (running) { /* 推进到下一个绝对时刻避免累积误差 */ next.tv_nsec 1000000; if (next.tv_nsec 1000000000) { next.tv_sec; next.tv_nsec - 1000000000; } clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, next, NULL); /* 收把上一周期从站回传的帧取进来 */ ecrt_master_receive(master); ecrt_domain_process(domain); /* 处理读状态字、判故障、算逆解与插值 */ for (int i 0; i 6; i) { uint16_t status EC_READ_U16(domain_data off_stat[i]); if (status 0x0008) { /* 故障位走复位流程 */ } int32_t target plan_next_position(i); /* 脉冲当量 */ EC_WRITE_U16(domain_data off_ctrl[i], cw_current[i]); EC_WRITE_S32(domain_data off_tpos[i], target); } /* 同步给时间、同步参考钟、同步从站钟三步顺序不能换 */ struct timespec now; clock_gettime(CLOCK_MONOTONIC, now); ecrt_master_application_time(master, (uint64_t)now.tv_sec * 1000000000ULL now.tv_nsec); ecrt_master_sync_reference_clock(master); ecrt_master_sync_slave_clocks(master); /* 发入队并发送 */ ecrt_domain_queue(domain); ecrt_master_send(master); } }几个参数要说清楚。clock_nanosleep用的是TIMER_ABSTIME传入绝对时刻这样即使某次循环被延迟下一个周期也会自动追赶不会像相对睡眠那样越跑越偏。周期值 1000000 纳秒必须和ecrt_slave_config_dc()里的 SYNC0 周期完全一致差一个纳秒都会让从站报同步错误。EC_WRITE_S32写目标位置时用的是脉冲当量缩放系数在轴对象里算好别在周期任务里做浮点乘除——1 ms 里六个轴做浮点运算在非实时内核上就是抖动的来源。ecrt_domain_process()和ecrt_domain_queue()是一对前者解析收到的数据后者把要发的内容挂到发送队列。中间这段是唯一允许读写过程数据的时间窗口窗口之外的读写不会生效也不会报错只会让人以为自己算错了。3.4 把 AL 状态从 INIT 推到 OP主站激活后从站停在 INIT 或者 PREOP得由程序主动往上推。ESM 有五个状态INIT、PREOP、SAFEOP、OP、BOOTSTRAP前四个是必须支持的。/* 状态机推进每周期检查一次按需写 AL Control */ static void advance_state(ec_master_t *master, ec_domain_t *domain) { ec_domain_state_t ds; ecrt_domain_state(domain, ds); for (int i 0; i 6; i) { ec_slave_config_state_t scs; ecrt_slave_config_state(sc[i], scs); if (scs.al_state 0x08) { /* OP正常 */ continue; } else if (scs.al_state 0x04) { /* SAFEOP 升 OP需要输入数据有效 */ if (ds.wc_state EC_WC_COMPLETE) request_state(i, 0x08); } else if (scs.al_state 0x02) { /* PREOP 升 SAFEOP */ request_state(i, 0x04); } } }推进过程中最常见的卡点是“停在 SAFEOP 上不去”。原因通常不是代码写错而是输入 PDO 的映射和实际数据长度不符主站检测到 WKC 不完整就拒绝升 OP。这时候用ethercat pdos -p N对比一下从站实际支持的 PDO 和程序里登记的项多半能看出差异。另外升 OP 之前必须先把 CiA402 的使能流程走完否则从站虽然进了 OP驱动器还是处于“未使能”状态写目标位置没有任何反应。这部分放到第 4 章。4. 伺服侧落地CiA402 使能、D-H 逆解与三次 B 样条轨迹主站通信跑通只是让数据能来回流动真正让机械臂动起来还需要三部分内容把伺服从“未使能”推到“运行使能”、把末端位姿换成六个关节角、把关节角变成平滑的位置序列。4.1 CiA402 状态字握手与使能序列CiA402 的使能流程是一套严格的状态迁移控制字和状态字必须一步步对应跳步会直接被拒绝。/* CiA402 控制字常量与使能流程 */ #define CW_DISABLE 0x0000 #define CW_SHUTDOWN 0x0006 /* 进入 Ready to switch on */ #define CW_SWITCH_ON 0x0007 /* 进入 Switched on */ #define CW_ENABLE_OP 0x000F /* 进入 Operation enabled */ #define CW_FAULT_RESET 0x0080 /* 故障复位上升沿有效 */ /* 每周期根据状态字决定下一步控制字 */ uint16_t cia402_next_cw(uint16_t status) { if (status 0x0008) /* bit3 故障 */ return CW_FAULT_RESET; switch (status 0x006F) { /* 取状态位段 */ case 0x0040: return CW_SHUTDOWN; /* Switch on disabled */ case 0x0021: return CW_SWITCH_ON; /* Ready to switch on */ case 0x0023: return CW_ENABLE_OP; /* Switched on */ case 0x0027: return CW_ENABLE_OP; /* Operation enabled保持 */ default: return CW_DISABLE; } }状态字 0x6041 的位定义要记牢bit0 就绪、bit1 合闸、bit2 运行使能、bit3 故障、bit5 快停、bit6 禁止合闸。上面代码里 0x006F是把无关位屏蔽掉再比对直接用等号判断会因为在不同时刻多出几个状态位而永远匹配不上这是新手最常踩的坑。使能过程中还有一个参数必须提前写好0x6060 运行模式。六轴机械臂一般用周期同步位置模式CSP值为 8这个值通过 SDO 写一次即可不需要每周期写。模式写错会出现“使能成功但电机不动”的现象且状态字一切正常非常难查。4.2 改进 D-H 建模与解析逆解六自由度机械臂逆解有数值法和解析法两条路。数值法通用但迭代次数不定在 1 ms 周期里跑不可控解析法要求构型满足 Pieper 准则后三轴交于一点工业六轴臂基本都满足算一次只要几十微秒。建模用改进 D-H 参数法每个连杆四个参数连杆扭角 α、连杆长度 a、关节角 θ、连杆偏距 d。相邻坐标系的变换矩阵为import numpy as np def mdh_transform(alpha, a, theta, d): 改进 D-H 单连杆变换先绕 X 转 alpha 并平移 a再绕 Z 转 theta 并平移 d ca, sa np.cos(alpha), np.sin(alpha) ct, st np.cos(theta), np.sin(theta) return np.array([ [ct, -st, 0, a], [st*ca, ct*ca, -sa, -sa*d], [st*sa, ct*sa, ca, ca*d], [0, 0, 0, 1] ])六个矩阵连乘得到末端位姿。逆解反过来推先用腕心位置解前三个关节再由末端姿态解后三个关节。前三个关节的求解本质是解一个三角形用余弦定理即可注意肘上肘下两组解要按关节限位和“离上一周期解最近”的原则挑一组否则机械臂会在两个构型之间来回跳。我做逆解时会在周期任务之外单独跑一个测试程序把正解算出来的位姿再喂回逆解比较六个关节角误差。误差在 1e-6 弧度以内才算通过。这个自检不过后面轨迹规划做得再漂亮也是错的。4.3 三次 B 样条插值让六个关节同时平滑起步笛卡尔空间的直线插补如果直接按时间均匀取点经过逆解后关节角会出现加速度突变机械臂走起来一顿一顿。三次 B 样条的好处是曲线二阶连续加速度不会跳变而且每一段只受相邻四个控制点影响改一段不会污染全局。def cubic_bspline(points, samples_per_seg20): 三次 B 样条给定路径控制点输出等间隔采样点含两端端点重复 n len(points) - 1 ts np.linspace(0, n, n * samples_per_seg 1) result [] for t in ts: i min(int(t), n - 1) # 落入的段号 u t - i # 段内归一化参数 p0, p1, p2, p3 points[i-1 if i 0 else 0], points[i], points[i1], points[i2 if i2 n else n] # 三次 B 样条基函数权重和为 1保证曲线落在控制点凸包内 b0 (1 - u) ** 3 / 6 b1 (3 * u ** 3 - 6 * u ** 2 4) / 6 b2 (-3 * u ** 3 3 * u ** 2 3 * u 1) / 6 b3 u ** 3 / 6 result.append(b0 * p0 b1 * p1 b2 * p2 b3 * p3) return np.array(result)这段代码里控制点索引的边界处理是关键。曲线起点和终点不经过控制点如果希望轨迹严格从起点出发到终点结束需要把首尾控制点各重复三次或者在两端补虚拟点。我第一次实现时没有处理机械臂起步位置偏了十几毫米查了很久才发现是曲线端点问题。采样密度samples_per_seg20是经验值1 ms 周期下一段 100 ms 的轨迹采 20 个点关节速度变化足够平滑插值计算量也压得住。实际部署时先把整条轨迹离线算好存进缓冲区周期任务只做查表和线性插值这样实时线程里只剩整数运算抖动会小很多。5. 常见问题与排查五个最容易导致失败的地方这套方案跑通的过程中出问题的地方高度集中。下面五条按“现象—原因—解决”列出来基本覆盖了八成故障。5.1 网卡被内核驱动接管主站起不来现象ethercat master显示主站已加载但ethercat slaves一台从站都看不到dmesg里出现网卡 link down 或者设备忙。原因IgH 的通用网卡驱动需要独占物理网口系统启动时自带的网卡驱动已经先一步绑定IgH 拿不到设备。解决开机脚本里先把目标网口从内核驱动解绑再加载主站模块。用ip link确认网口名然后echo 0000:03:00.0 /sys/bus/pci/drivers/r8169/unbindPCI 地址和驱动名按实际改再modprobe ec_master main_devicesXX:XX:XX:XX:XX:XX。MAC 地址一定要写对写错了主站会静默使用第一个网口现象是“能跑但数据不通”比直接报错还难查。5.2 WKC 不等于 0 但等于错的数现象主站能进 OP但ecrt_domain_state()返回的wc_state一直是EC_WC_INCOMPLETE周期任务里读到的状态字全 0 或者不变化。原因domain_regs里登记的 PDO 项数和从站实际支持的项数不一致。常见于删了cstruct生成的配置却没同步删登记表或者从站固件升级后 PDO 映射变了。解决先跑ethercat pdos -p N看从站当前实际映射再对照程序里的ec_pdo_entry_info_t数组逐项比。另外用ethercat slaves -v看每台从站的 WKC 计数值六台里面只有一台不对就是那一台的映射问题全部不对大概率是参考时钟那台没配对。5.3 周期抖动忽大忽小现象用cyclictest测出来最大延迟几百微秒机械臂低速运行时看不出问题速度一快就有异响。原因控制线程没有真正跑在实时核上或者周期任务里混进了会睡眠的调用printf、malloc、文件 IO。解决确认实时线程用的是实时调度策略检查/proc/xenomai/stat里的模式计数是否在涨。周期任务里只留整数运算和内存读写所有打印走环形缓冲区、由非实时线程输出。中断屏蔽也要处理网卡中断如果落在实时核上会打断周期用echo 1 /proc/irq/N/smp_affinity把它赶到非实时核。5.4 伺服使能不上状态字一直卡在 Switch on disabled现象控制字按序列写了状态字读到 0x0240 或 0x0250 就不动了。原因0x0240 表示“开关禁止”通常是驱动器侧还有未清除的故障、STO 信号没接、或者 0x6060 运行模式没写。还有一种可能是一次性写了控制字又立刻改回去上升沿没被采到。解决先用ethercat upload -p N 0x6041 0单独读状态字再用ethercat upload -p N 0x603F 0读错误码。确认没有故障后检查 STO 接线和 0x6060 的值。控制字的状态迁移必须一周期一步用状态机驱动而不是一次性跳到 0x000F每步之间至少留两个周期。5.5 轨迹末端抖一下关节电流有尖峰现象机械臂走到目标点附近时末端轻微抖动伺服电流波形上出现周期性尖峰。原因两种可能。一是 B 样条控制点首尾没做重复处理曲线端点不经过目标点逆解算出来的关节角在最后几个周期反复修正二是目标位置每周期都按浮点重算量化误差导致位置指令在小范围内来回跳。解决轨迹起点和终点各重复三次控制点保证曲线端点严格过点。位置指令做量化时用累积误差补偿记录本周期的理论值与实际下发值之差下一周期补上这样长期平均位置不会漂。另外在目标点附近加一个死区误差小于两个脉冲当量就不再更新指令电流尖峰基本能消掉。6. 验收与答辩用三张表和一台示波器证明这套主站真的跑通了这套方案做到最后最容易被问住的问题不是“逆解怎么推的”而是“你怎么知道它是同步的”。答辩或者交付评审时光说“能跑”没有说服力得拿出可复现的证据。我一般准备三张表加一次波形测量。第一张是通信验收表逐项列出主站状态、从站数量、每台从站的 AL 状态、WKC 理论值与实测值、DC 同步偏差取证方式是ethercat master、ethercat slaves、ethercat pdos三个命令的输出截图WKC 用程序打印。第二张是实时性表记录 1 ms 周期下连续跑十分钟的最大延迟、平均延迟、超时次数用cyclictest或者自己写的周期任务统计判据我一般定为最大延迟小于周期的 20%。第三张是运动精度表给定若干目标位姿记录正解回代的关节角误差、重复定位误差判据是关节角误差小于 1e-4 弧度、重复定位误差小于 0.05 mm。波形测量这一步最能打动人。用示波器一路探 SYNC0 信号从站调试口或者 IO 引出另一路探某台伺服的电流输出让机械臂走一段直线。如果 DC 同步正常六轴的 SYNC0 应该完全重合电流波形平滑无台阶如果某台从站的 SYNC0 比其他晚了几十微秒波形上立刻能看出来不用猜。这个方法比任何软件打印都直观评审现场演示一次后面基本没人再质疑同步性。答辩讲架构的时候控制住时间三分钟只讲三件事为什么用 IgH内核态驱动加完整 DC、数据是怎么流的一个 domain 六轴一张偏移表、同步是怎么保证的参考时钟加 SYNC0。逆解和 B 样条的公式放在备用页被问到再翻主动展开容易把自己讲进细节里出不来。有一个习惯我保持了几年每次换伺服型号或者换工控机都先把“单轴空载跑 1 ms 周期十分钟不超时”当成第一个里程碑通过了再上六轴。刚开始做这套系统时我图快直接把六轴代码烧进去调试结果一台伺服的 DC 参数不对整个环路的抖动被放大到整机查了两天才定位到是那一台的 SYNC0 偏移配错了。从那以后我每次上电都强制走一遍单轴空载加 DC 偏差打印宁可多花半天也不在六个轴同时转的时候靠猜。希望帮到你。本文还有配套的精品资源点击获取