ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

IgH EtherCAT Master 启用 DC 同步:从主站配置到多轴 FOC 纳秒级同步实践

IgH EtherCAT Master 启用 DC 同步:从主站配置到多轴 FOC 纳秒级同步实践 IgH EtherCAT Master 启用 DC 同步从主站配置到多轴 FOC 纳秒级同步实践在多轴机器人系统中仅仅保证 EtherCAT PDO “每 1 ms 发一次”并不能代表所有关节在同一时刻执行控制算法。Linux 主站的任务调度存在抖动EtherCAT 报文经过不同从站时也存在传播延迟。如果每个关节都使用自己的本地时钟运行控制周期那么随着运行时间增加各个节点之间的相位会逐渐产生偏差。这也是 EtherCAT Distributed ClocksDC机制存在的意义。DC 并不是简单让主站“定时发包”而是让整条 EtherCAT 网络中的从站建立一个统一的分布式时间基准并基于这个时间基准产生硬件级的SYNC0/SYNC1同步信号。这次项目中的目标比较明确RK3588 IgH EtherCAT Master ↓ 1 kHz DC ↓ 多个 EtherCAT 关节模组 ↓ SYNC0 对齐 ↓ 各关节 25 kHz FOC 相位同步最终使用逻辑分析仪测量两个从站的 FOC 执行时刻可以做到纳秒量级的同步。本文把整个实现过程记录下来包括EtherCAT DC 到底解决什么问题IgH Master 如何开启 DCecrt_slave_config_dc()每个参数是什么意思Application Time、Reference Clock、Slave Clock 之间是什么关系IgH 周期任务里 DC API 应该放在哪里为什么 DC 同步以后 FOC 仍然不会自动同步如何使用SYNC0 ELC GPT MTU3把 1 kHz DC 基准传递到 25 kHz FOC实际工程中容易踩的几个坑。1. 为什么多轴系统需要 EtherCAT DC先考虑一个最简单的场景。假设系统中有两个 EtherCAT 关节Joint A Joint B它们的控制周期都是1 ms如果没有 DC同样是 1 ms 周期并不意味着两个从站真的在同一个时刻执行任务。可能是时间 → Joint A |----Control----|----Control----|----Control----| 0 1ms 2ms Joint B |----Control----|----Control----|----Control----| 80us 1.08ms 2.08ms周期完全相同但相位差Δt 80 us而且如果两个从站使用独立晶振fA ≠ fB即使初始相位一致运行一段时间后也会逐渐漂移。对于普通 IO这几十微秒可能无所谓。但对于双足机器人 多轴伺服 电子凸轮 机器人关节 高精度轨迹控制不同轴在不同时间采样、计算和更新输出会直接影响整个控制系统的一致性。2. DC 的核心不是“通信同步”而是“时钟同步”很多人刚接触 EtherCAT DC 时很容易把它理解成主站每 1 ms 同时给所有从站发一个同步报文。实际上并不是这样。DC 的核心是让所有 DC 从站内部的本地时钟保持同步可以把网络想象成EtherCAT Master │ │ 校准 ▼ Reference Clock │ ┌─────────┼─────────┐ ▼ ▼ ▼ Slave 1 Slave 2 Slave 3 DC Clock DC Clock DC Clock最终Clock1 ≈ Clock2 ≈ Clock3每个从站再利用自己的 DC Clock在指定的绝对时间产生SYNC0 SYNC1因此即使 EtherCAT 报文到达不同从站的物理时刻存在差异也不会影响最终的同步触发时刻。换句话说EtherCAT DC 把“什么时候执行任务”从报文到达时刻中解耦出来了。这也是 DC 相比普通周期通信最重要的价值。3. 整个 DC 同步链路是什么样的在这次项目中整个同步链路可以拆成三层。第一层Linux Real-Time Cycle ↓ Application Time第二层Application Time ↓ Reference Clock ↓ 其他 DC Slave Clock第三层DC Clock ↓ SYNC0 ↓ FOC/PWM最终形成RK3588 Linux │ │ Application Time ▼ IgH EtherCAT Master │ │ DC 校准报文 ▼ Reference Slave Clock │ ├──────────────┐ ▼ ▼ Slave A DC Slave B DC │ │ SYNC0 SYNC0 │ │ ▼ ▼ FOC A FOC B这里需要特别注意DC Clock 同步并不等于应用层算法已经同步。DC 只能保证SYNC0_A ≈ SYNC0_B至于从站收到 SYNC0 以后到底做什么还需要应用程序自己设计。这也是后面 FOC 同步部分真正要解决的问题。4. 当前系统架构这次测试环境为RK3588 │ ├── Linux ├── PREEMPT_RT └── IgH EtherCAT Master │ ├── Topology ESC │ ├── Motor Slave 0 │ └── Motor Slave 1主站周期1 kHz即#definePERIOD_NS1000000SYNC0 同样配置为1 ms从站内部 FOC25 kHz即40 us因此1 个 SYNC0 周期 25 个 FOC 周期后面要解决的本质就是如何让这 25 个 FOC 周期的相位始终锁定在 EtherCAT SYNC0 上。5. IgH 开启 DC 最核心的 APIIgH 中配置从站 DC 的接口为ecrt_slave_config_dc()函数原型intecrt_slave_config_dc(ec_slave_config_t*sc,uint16_tassign_activate,uint32_tsync0_cycle,int32_tsync0_shift,uint32_tsync1_cycle,int32_tsync1_shift);项目中的调用为ecrt_slave_config_dc(sc[i],0x0300,PERIOD_NS,DC_SHIFT_NS,0,0);例如#definePERIOD_NS1000000#defineDC_SHIFT_NS300000对应SYNC0 Cycle 1 ms SYNC0 Shift 300 us6.ecrt_slave_config_dc()参数逐个理解6.1scec_slave_config_t*sc就是当前 EtherCAT Slave 的配置对象。通常通过sc[i]ecrt_master_slave_config(master,0,slave_position,VENDOR_ID,PRODUCT_ID);得到。6.2assign_activate第二个参数assign_activate用于设置从站 ESC 的 DC AssignActivate。例如项目中的从站 ESI 配置对应0x0300因此使用ecrt_slave_config_dc(sc,0x0300,...);这里有一个非常重要的点不要看到别人的代码用了0x0300就认为所有 EtherCAT 从站都应该写0x0300。IgH 官方文档明确说明AssignActivate 是 vendor-specific 的应从设备 ESI XML 中的Device - Dc - AssignActivate获取。因此最可靠的方式是打开从站 XMLDc...AssignActivate#x0300/AssignActivate/Dc然后将对应数值填入ecrt_slave_config_dc()而不是硬套固定值。7. SYNC0 Cycle 和 Shift 分别是什么意思这一部分非常关键。例如ecrt_slave_config_dc(sc,0x0300,1000000,300000,0,0);代表SYNC0 Cycle 1 ms SYNC0 Shift 300 us可以理解成EtherCAT DC 时间轴 → 周期边界 │ │----300 us----SYNC0-------------------------------│ │ │ 0 1ms下一个周期1ms 300us再次产生 SYNC0。因此SYNC0(n) StartTime n × Cycle ShiftShift 的作用不是改变周期而是调整SYNC0 在整个 EtherCAT 周期里的相位位置8. 为什么不一定把 SYNC0 放在周期起点直觉上很容易写sync0_shift0;也就是周期开始 ↓ SYNC0但工程中经常会留出一定 Shift。原因之一是整个周期里可能还有主站唤醒 ↓ 接收上一周期数据 ↓ 控制算法 ↓ 更新 PDO ↓ EtherCAT Send ↓ 报文传播如果从站的算法执行点和主站过程数据更新时间有关就需要设计好PDO 到达 SYNC0 控制计算 PWM 更新之间的相位关系。因此Cycle解决的是多久同步一次而Shift解决的是在这个周期里的哪个时间点同步这两个概念最好分开理解。9. 选择 DC Reference Clock配置完各从站以后需要选择一个参考时钟。项目中if(ecrt_master_select_reference_clock(master,sc[0])){printf(Select DC reference clock failed\n);return-1;}也就是Motor Slave 0作为 DC Reference Clock。整个网络变成Application Time ↓ Motor Slave 0 Reference Clock ↓ ┌─────┴─────┐ ▼ ▼ Slave 1 Slave 2 Clock ClockIgH 官方文档说明如果应用程序不主动调用ecrt_master_select_reference_clock()那么 Master 会选择第一个具备 DC 能力的从站作为 Reference Clock。显式指定的好处是网络行为更确定尤其是设备很多、拓扑复杂以后我更倾向于主动选择 Reference Clock而不是依赖默认行为。10. Application Time 是整个 DC 同步链中的关键配置 DC 后周期线程中还必须持续向 IgH 提供Application Time接口为ecrt_master_application_time(master,app_time);项目中staticuint64_tsystem_time_ns(void){structtimespects;clock_gettime(CLOCK_MONOTONIC,ts);return(uint64_t)ts.tv_sec*1000000000ULLts.tv_nsec;}周期中app_timesystem_time_ns();ecrt_master_application_time(master,app_time);这一句看起来很简单但它实际上建立了Linux 时间 ↓ IgH Application Time ↓ EtherCAT DC Reference Clock这条链路。11. 为什么这里用CLOCK_MONOTONIC也可以IgH 官方文档把 Application Time 定义为从 2000-01-01 开始计算的纳秒值但紧接着也说明绝对值本身并不重要。也就是说对 DC 同步真正重要的是时间连续 单调递增 周期一致因此在这种闭环控制场景下使用CLOCK_MONOTONIC作为 Application Time 基准是可以工作的。相比CLOCK_REALTIMECLOCK_MONOTONIC还有一个实际优势系统时间被NTP 手动校时 系统时间修改调整以后不会突然产生跳变。对于实时控制系统而言这一点很重要。12.ecrt_master_application_time()应该在周期中的固定位置调用这是 IgH DC 中一个非常容易被忽略的细节。官方文档特别强调ecrt_master_application_time()应该每个实时周期都在尽可能相同的位置调用。因为这个时间会被用来计算SYNC0 / SYNC1 phase如果有时在周期开始调用0 us有时到了200 us才调用那么主站自身就把执行抖动引入到了 DC 时间基准中。所以比较合理的结构是while(run){clock_nanosleep(...);app_timesystem_time_ns();ecrt_master_application_time(master,app_time);...}也就是周期唤醒 ↓ 立即更新时间 ↓ 再处理 EtherCAT 和控制算法项目中的实现正是这个结构。13. 周期线程为什么使用绝对时间睡眠代码中使用clock_nanosleep(CLOCK_MONOTONIC,TIMER_ABSTIME,wakeup_time,NULL);而不是简单usleep(1000);这是实时循环里一个非常重要的设计。如果使用usleep(1000)整个周期实际上是上一周期执行时间 1 ms sleep例如代码本身运行了100 us那么实际周期就是1.1 ms下一次又可能变成1.08 ms最终不断累计漂移。使用绝对时间TIMER_ABSTIME则每次唤醒目标为T0 1ms T0 2ms T0 3ms ...即使某一个周期多执行了几十微秒也不会把这几十微秒继续累计到后续周期。14. PREEMPT_RT 解决的不是 DC 精度而是主站实时性项目中的 RK3588 Linux 使用了 PREEMPT_RT。这里也需要区分两个问题Linux 实时性和EtherCAT DC 同步精度它们并不是同一件事。PREEMPT_RT 主要解决主站周期任务什么时候醒 PDO 什么时候计算 报文什么时候发送而 DC 主要解决各从站本地时钟是否一致 SYNC0 是否同步产生因此可以理解成PREEMPT_RT ↓ 保证主站周期尽量稳定 EtherCAT DC ↓ 保证从站硬件时间同步两者结合以后才更适合高性能运动控制。15. IgH 周期里的标准 DC 调用链项目中的核心周期结构可以压缩成while(run){/* 1. 周期唤醒 */clock_nanosleep(CLOCK_MONOTONIC,TIMER_ABSTIME,wakeup_time,NULL);/* 2. 更新时间基准 */app_timesystem_time_ns();ecrt_master_application_time(master,app_time);/* 3. 接收 EtherCAT 数据 */ecrt_master_receive(master);ecrt_domain_process(domain);/* 4. 读取 Rx 数据并计算 */read_process_data();control_algorithm();/* 5. 更新 Tx PDO */write_process_data();/* 6. Queue Domain */ecrt_domain_queue(domain);/* 7. DC 时钟同步 */ecrt_master_sync_reference_clock(master);ecrt_master_sync_slave_clocks(master);/* 8. 发送 EtherCAT Frame */ecrt_master_send(master);}把它画成时序就是Realtime Wakeup │ ▼ Application Time │ ▼ EtherCAT Receive │ ▼ Domain Process │ ▼ Read PDO │ ▼ Control Algorithm │ ▼ Write PDO │ ▼ Domain Queue │ ▼ Sync Reference Clock │ ▼ Sync Slave Clocks │ ▼ EtherCAT Send其中ecrt_master_sync_reference_clock(master);负责把 Reference Clock 向最近一次 Application Time 校准。而ecrt_master_sync_slave_clocks(master);负责让其他 DC 从站继续跟踪 Reference Clock。IgH 官方文档也是这样定义这两步Application Time ↓ Reference Clock ↓ Slave Clocks16. 完整初始化顺序把与 DC 有关的主站初始化流程整理一下ecrt_request_master() ↓ ecrt_master_create_domain() ↓ ecrt_master_slave_config() ↓ ecrt_slave_config_pdos() ↓ ecrt_slave_config_dc() ↓ ecrt_master_select_reference_clock() ↓ ecrt_domain_reg_pdo_entry_list() ↓ ecrt_master_activate() ↓ ecrt_domain_data() ↓ 进入实时循环对应关键代码masterecrt_request_master(0);domainecrt_master_create_domain(master);for(inti0;iSLAVE_NUM;i){sc[i]ecrt_master_slave_config(master,0,MOTOR_FIRST_SLAVE_POSi,VENDOR_ID,PRODUCT_ID);ecrt_slave_config_pdos(sc[i],EC_END,slave_syncs);ecrt_slave_config_dc(sc[i],0x0300,PERIOD_NS,DC_SHIFT_NS,0,0);}ecrt_master_select_reference_clock(master,sc[0]);ecrt_domain_reg_pdo_entry_list(domain,domain_regs);ecrt_master_activate(master);domain_pdecrt_domain_data(domain);需要注意ecrt_slave_config_dc()必须在ecrt_master_activate()之前配置。17. 如何判断 DC 到底有没有同步仅仅从站进入OP并不能证明 DC 已经同步得很好。因为OP只能说明 EtherCAT 状态机已经进入 Operational。更重要的是检查各个 DC Slave 之间的 System Time DifferenceIgH 本身提供ecrt_master_sync_monitor_queue()和ecrt_master_sync_monitor_process()用于监控 DC Synchrony。其本质是读取各个从站的System Time Difference从而得到当前网络同步误差的上界估计。因此实际项目里我更推荐不要只打印Slave OP还应该同时监控DC Difference WKC Cycle Jitter Slave State这样才能真正判断DC 是“已经启用了”还是“已经稳定锁定了”。18. 到这里还没有结束SYNC0 同步了但 FOC 并不会自动同步这是这次项目中非常关键的一点。DC 配置成功以后我们能够做到Slave A SYNC0 ≈ Slave B SYNC0但是关节内部真正执行 FOC 的节拍来自MTU3 PWM TimerFOC 周期40 us也就是25 kHz而 EtherCAT SYNC01 ms也就是1 kHz两个时间基准如果完全独立DC Clock ↓ SYNC0 1 kHz MCU Peripheral Clock ↓ MTU3 ↓ FOC 25 kHz即使一开始人工把它们对齐SYNC0 │ ▼ FOC valley由于两个时钟源存在 ppm 级频率误差运行一段时间以后仍然会产生漂移。例如初始 SYNC0 ↓ │ ▼ FOC Valley 运行一段时间 SYNC0 ↓ │ FOC Valley ↓所以真正要实现多关节 FOC 同步还需要建立SYNC0 ↓ PWM / FOC Phase Lock19. 当前 FOC 时序关节模组 FOC25 kHz周期40 usMTU3 工作在三角波 PWM 模式。一个 PWM 周期可以理解成Peak /\ / \ / \ / \ / \ Valley Valley系统设计为Valley / Underflow ↓ 触发 FOC ISR ↓ 电流采样 ↓ Clark / Park ↓ 控制算法 ↓ 反 Park ↓ 计算 PWMPWM 占空比则在Peak附近通过缓冲机制更新从而避免占空比在当前周期中间突然变化。我们的同步目标并不是让SYNC0 每一次 FOC因为SYNC0 1 kHz FOC 25 kHz而是每隔 1 ms用 SYNC0 对 FOC 的 25 kHz 本地节拍重新进行一次相位校准。本质上已经非常接近数字锁相环的思路。20. 如何测量 SYNC0 和 FOC 的相位差这里使用了一路独立 GPT Timer 做时间测量。同时借助 Renesas 的ELC Event Link Controller把两个硬件事件直接连接到 GPT。事件 1MTU Underflow ↓ ELC ↓ GPT Clear也就是每次 FOC 波谷出现时把 GPT 清零。事件 2EtherCAT SYNC0 ↓ ELC ↓ GPT Capture A也就是SYNC0 到来时把 GPT 当前计数自动锁存到 GTCCRA。整个过程全部由硬件完成FOC Valley │ └──────────→ GPT 0 │ │ count │ SYNC0 ────────────────┘ ↓ Capture ↓ GTCCRA于是GTCCRA里的值就代表最近一次 FOC Valley 到 当前 SYNC0之间的时间差。记为delta_t21. 为什么用 ELC 硬件捕获而不是在中断里读 Timer如果简单写成voidsync0_isr(void){delta_tGPT-CNT;}实际测到的是SYNC0 到来 ↓ GIC ↓ CPU 响应 ↓ 上下文保存 ↓ 进入 ISR ↓ 读取 GPT其中包含了IRQ Latency GIC Latency CPU Jitter 中断抢占因此测出来的其实是真实相位差 软件中断延迟而通过SYNC0 → ELC → GPT Capture捕获动作发生在外设硬件层。CPU 后面什么时候进入中断已经不重要。这也是硬件 Capture 在高精度时间测量中非常有价值的原因。22. FOC 锁相算法在每次 FOC 波谷中断中读取delta_tR_GPT0-GTCCR[0];之后进行相位补偿。整体流程读取 GPT Capture ↓ 判断本周期是否捕获到 SYNC0 ↓ 计算相位误差 ↓ 换算成 MTU Count ↓ 限制单周期补偿量 ↓ 修改 PWM Period ↓ 缓冲寄存器下周期生效 ↓ 继续测量 ↓ 逐渐锁定23. 第一步读取相位偏差delta_tR_GPT0-GTCCR[0];GTCCRA中保存的是FOC Valley → SYNC0之间经过的 GPT Tick 数。例如GPT Clock 100 MHz则1 count 10 ns假设读取delta_t 500则时间差5 us24. 第二步判断捕获是否有效项目中的设计是if(delta_t0){/* 当前周期没有新的 SYNC0 */skip;}因为 SYNC01 kHzFOC25 kHz25 个 FOC 周期里只有一个附近会出现 SYNC0。所以绝大多数 FOC 周期并不需要做同步修正。25. 第三步判断超前还是滞后因为 PWM 是周期信号相位误差不能只看delta_t本身。需要结合PWM Period判断 SYNC0 离哪一个 Valley 最近。例如FOC Period 40 us如果测到delta_t 2 us说明SYNC0发生在 Valley 后面 2 us。但如果delta_t 39 us实际上并不应该认为误差是39 us而应该把它理解成距离下一个 Valley 只有 1 us因此相位误差应该按环形时间轴处理0 Valley │ 39us │ 1us \ │ / \ │ / \│/ periodic这也是实现锁相时非常容易犯错的地方。26. 第四步把 GPT 时间转换为 MTU3 CountGPT 和 MTU3 的时钟频率并不一定相同。因此需要GPT Count ↓ Time ↓ MTU Count转换。公式corr_counts delta_t × f_mtu / f_gpt即corr_countsdelta_t*F_MTU/F_GPT;这样才能知道当前需要把 MTU3 周期调整多少个计数。27. 为什么不能一步把相位误差全部补掉例如发现误差5 us最直接的想法可能是下一周期直接缩短 5 us理论上下一次立刻就对齐。但对于电机控制来说这样做风险很大。因为 MTU3 控制的是PWM CarrierPWM Period 如果突然变化会直接影响电流采样位置 PWM 波形 FOC 离散时间 电压输出严重时会造成电流突变所以不能使用一次性跳相而应该渐进式拉频28. 使用限幅进行渐进补偿设计corrclamp(corr_counts,-MAX_STEP,MAX_STEP);然后new_TGRAnominal_TGRAcorr;再写到 MTU3 的缓冲寄存器TGRC于下一个 PWM 周期生效。例如每个周期最多允许±2 count那么当前误差 ↓ 100 count不会直接调整100 count而是2 2 2 2 ...慢慢把相位拉回来。这就是一个非常典型的Phase Error ↓ Limited Correction ↓ Frequency Adjustment ↓ Phase Convergence过程。29. 从控制角度看这其实就是一个简化的软件 PLL整个系统可以这样理解EtherCAT DC Reference │ ▼ SYNC0 │ │ phase compare ▼ Phase Error │ ▼ Correction Algorithm │ ▼ MTU3 Period │ ▼ 25 kHz FOC │ └──────────── feedback这与 PLLReference ↓ Phase Detector ↓ Loop Filter ↓ Oscillator ↓ Feedback在结构上非常相似。这里SYNC0相当于 Reference。GPT Capture相当于 Phase Detector。MAX_STEP Correction相当于一个非常简化的 Loop Filter。MTU3则相当于本地 Oscillator。所以它本质上可以理解为利用 EtherCAT SYNC0 对本地 PWM/FOC 时钟进行数字锁相。30. 锁相完成以后不能停止校正一个容易忽略的问题是即使现在测得Δt 0也不能从此停止同步。因为EtherCAT DC Clock和MCU Peripheral Clock来自不同振荡源。两个时钟永远存在 ppm 级频率差。因此如果停止补偿刚锁定 Δt ≈ 0 ↓ 运行一段时间 ↓ Δt 再次逐渐增大所以系统应该进入Tracking Mode而不是Correction Off即未锁定 ↓ 较强补偿 ↓ 进入阈值 ↓ Locked ↓ 小幅持续跟踪31. 锁相完成的判断项目中的思路是|corr_counts| threshold连续保持N 个周期则认为LOCKED例如if(abs(corr_counts)LOCK_THRESHOLD){lock_count;if(lock_countLOCK_COUNT){dc_lockedtrue;}}else{lock_count0;}比“某一次测到误差很小”更加可靠。因为一次误差为零可能只是偶然。32. 整个 FOC 同步流程最终流程可以压缩为EtherCAT SYNC0 │ ▼ GPT Hardware Capture │ ▼ Phase Error │ ▼ GPT Tick → MTU Tick │ ▼ Clamp(MAX_STEP) │ ▼ Update MTU TGRC │ ▼ Next PWM Period Apply │ ▼ FOC Valley │ └─────────┐ │ ▼ Next Comparison这也是原项目中最后采用的同步方案。33. 实际测量结果完成同步以后使用逻辑分析仪同时抓取两个 EtherCAT 从站进入 FOC 的 GPIO。测量的是Slave A FOC Entry Slave B FOC Entry而不是单纯测SYNC0_A SYNC0_B这一点非常重要。因为真正关心的是最终控制算法是否同步。原始测试中两个从站进入 FOC 的时间差已经达到纳秒量级。换句话说同步链路已经从EtherCAT DC Clock真正传递到了Motor Control Execution而不是仅仅停留在 ESC 的 SYNC0 引脚。34. 为什么“测 FOC GPIO”比只测 SYNC0 更有意义如果只抓SYNC0_A SYNC0_B只能证明EtherCAT DC 同步工作正常但后面还有SYNC0 ↓ MCU Interrupt ↓ Application ↓ FOC Timer ↓ FOC ISR任何一层都有可能重新引入误差。因此最好分别测试Level 1 SYNC0_A ↔ SYNC0_B Level 2 SYNC0 ↔ FOC_A Level 3 FOC_A ↔ FOC_B最终FOC_A ↔ FOC_B才是运动控制真正关心的系统级指标。35. 一个非常重要的坑SYNC0 通常是边沿中断这次 DC 开发中还遇到过一个很隐蔽的问题。从站在 SM 模式运行稳定但打开 DC 后运行一段时间程序会跑飞。最终定位到SYNC0属于边沿触发中断。程序原来在某个临界区通过GICD_ICENABLER直接关闭 GIC Distributor 中的 SYNC0 IRQ。恰好撞上SYNC0 已经 Pending ↓ 准备送往 CPU ↓ 软件突然 Disable ↓ CPU 读取 IAR ↓ Spurious Interrupt 1023再叠加 FSP 中断入口没有过滤INTID 1023最终导致 Vector Table 越界程序跑飞。所以在使用 DC SYNC0 时还需要非常重视Edge Trigger IRQ Pending Active IRQ Mask GIC Distributor CPU Interface这些底层中断机制。这个问题我另外做了一篇完整复盘这里不展开。36. 一个完整的工程检查清单如果重新从零开启 IgH DC我会按照下面顺序检查。主站侧□ Linux 是否具备足够实时性 □ 周期任务是否使用绝对时间唤醒 □ ecrt_slave_config_dc() 是否在 activate 前调用 □ AssignActivate 是否来自目标从站 ESI □ SYNC0 Cycle 是否正确 □ SYNC0 Shift 是否经过设计 □ 是否明确选择 Reference Clock □ ecrt_master_application_time() 是否每周期调用 □ application_time 是否在周期固定位置更新 □ sync_reference_clock 是否周期执行 □ sync_slave_clocks 是否周期执行从站侧□ ESI XML 是否声明 DC □ ESC 是否支持 Distributed Clocks □ SYNC0 是否实际输出 □ SYNC0 周期是否正确 □ SYNC0 中断触发方式是否正确 □ 中断优先级是否合理 □ 应用任务是否真正使用 SYNC0控制侧□ SYNC0 是否真的传递到 FOC □ PWM Timer 与 DC 时钟是否存在长期漂移 □ 是否有相位测量机制 □ 是否渐进补偿而不是直接跳相 □ 是否有 Lock 判定 □ Lock 后是否继续 Tracking测试侧□ 测 SYNC0_A ↔ SYNC0_B □ 测 SYNC0 ↔ FOC □ 测 FOC_A ↔ FOC_B □ 长时间运行观察漂移 □ 观察 DC System Time Difference □ 观察 Linux Cycle Jitter □ 同时监控 WKC 和 Slave State37. 总结这次 EtherCAT DC 的实现过程让我对“同步”这件事有了更完整的理解。最开始很容易认为打开 DC ↓ 配置 SYNC0 ↓ 同步完成真正做完整以后会发现系统里其实存在多层时间域Linux Scheduler ↓ Application Time ↓ EtherCAT Reference Clock ↓ Slave DC Clock ↓ SYNC0 ↓ MCU Timer ↓ PWM ↓ FOC任何一层没有处理好都可能让前面已经得到的同步精度重新丢掉。因此真正的多轴同步不是“所有 EtherCAT 从站都进入了 DC 模式。”而应该是统一时间基准最终传递到了真正需要同步执行的控制任务。在这次机器人关节系统里最终链路为RK3588 ↓ IgH Application Time ↓ DC Reference Clock ↓ Slave DC Clock ↓ 1 kHz SYNC0 ↓ GPT ELC Hardware Capture ↓ Phase Error ↓ MTU3 Period Correction ↓ 25 kHz FOC ↓ 多关节 FOC 相位锁定最终通过逻辑分析仪直接测量两个从站 FOC 执行时刻而不是只看 EtherCAT 状态或者 SYNC0 信号确认了整个同步链路确实已经传递到了电机控制层。对我来说这也是 EtherCAT DC 最值得理解的一点DC 的价值并不只是让网络里的时钟数字看起来一致而是给整个分布式实时控制系统提供一个可以真正落到执行层的统一时间基准。
返回列表