Arduino经Zaber盾控制Zaber线性位移台的移动位置轮询与双台控制问题
双Zaber位移台并行控制+实时位置轮询实现方案
原代码存在的技术限制
- 采用串行阻塞流程:必须等1号位移台完全停止才启动2号,无法实现双台并行运动
- 总线收发绑定同步阻塞:发查询命令后立刻等待单条回复,占用总线期间无法处理另一台的指令/应答
- 轮询逻辑冗余:叠加固定
delay(),且每轮仅查询单台,效率极低 - 未利用Zaber总线的异步应答机制:所有操作按单台串行排队,总线资源浪费
实现方案
1. 核心逻辑优化(适配Zaber Ascii总线特性)
Zaber设备采用半双工RS485共享总线,需遵循「发指令→等应答→清总线」的时序,优化方向:
- 改用
millis()实现非阻塞定时轮询,取消所有阻塞式delay() - 同时向两台设备下发移动命令,不再串行等待
- 用非阻塞方式接收总线应答,按设备号分类处理
- 仅对仍在运动的设备发查询命令,减少总线负载
2. 优化后完整代码
#include <ZaberAscii.h> // 硬件初始化(Zaber盾固定地址为AA) ZaberShield shield(ZABERSHIELD_ADDRESS_AA); ZaberAscii za(shield); // 设备运动状态标记 bool dev1_moving = false; bool dev2_moving = false; // 轮询定时变量(避免阻塞) unsigned long last_poll = 0; const unsigned long POLL_INTERVAL = 20; // 20ms轮询一次,兼顾实时性与总线负载 void setup() { shield.begin(115200); Serial.begin(115200); delay(100); // 硬件上电缓冲 // --- 并行下发双台移动命令(原代码为串行下发) // 1号设备移动命令 za.send(1, 1, "move rel", 20000); za.receive(); // 仅接收命令确认应答,不阻塞后续 dev1_moving = true; // 立刻下发2号设备移动命令(无需等待1号停止) za.send(2, 1, "move rel", 20000); za.receive(); // 接收2号命令确认应答 dev2_moving = true; } void loop() { // 1. 非阻塞处理总线所有未读应答 while (za.available()) { // 库自带非阻塞总线检查 ZaberAscii::reply reply = za.receive(); if (reply.isReply) { // 按设备号分类处理 switch (reply.deviceNumber) { case 1: // 处理1号编码器位置返回 if (reply.responseType == "encoder.pos") { Serial.print("Dev1 Enc Pos: "); Serial.println(reply.responseData); } // 处理1号运动状态返回 if (reply.responseData == "IDLE") { dev1_moving = false; Serial.println("Dev1 Motion Finished"); } break; case 2: // 处理2号编码器位置返回 if (reply.responseType == "encoder.pos") { Serial.print("Dev2 Enc Pos: "); Serial.println(reply.responseData); } // 处理2号运动状态返回 if (reply.responseData == "IDLE") { dev2_moving = false; Serial.println("Dev2 Motion Finished"); } break; } } else if (reply.isRejected) { Serial.print("Dev"); Serial.print(reply.deviceNumber); Serial.println(" Rejected Command"); } } // 2. 定时轮询运动中设备的位置与状态 if (millis() - last_poll >= POLL_INTERVAL) { last_poll = millis(); // 仅对仍在运动的设备发查询,减少总线占用 if (dev1_moving) { za.send(1, "get encoder.pos"); // 查询1号编码器位置 za.send(1, "get status"); // 查询1号运动状态 } if (dev2_moving) { za.send(2, "get encoder.pos"); // 查询2号编码器位置 za.send(2, "get status"); // 查询2号运动状态 } } // 所有设备停止后,可扩展后续逻辑(如循环运动、数据存储) if (!dev1_moving && !dev2_moving) { // 示例:清空串口缓冲 while (Serial.available()) Serial.read(); } }
3. 总线时序关键注意事项
- 若出现应答混乱:检查Zaber总线两端的终端电阻是否开启(两台设备需在总线物理两端接120Ω终端电阻,避免信号反射)
- 若轮询过载:适当调大
POLL_INTERVAL(最大可设为100ms,仍满足实时位置采集需求) - 若命令失效:确认两台设备的总线地址为1、2(无重复地址,可通过库函数校验)
内容的提问来源于stack exchange,提问作者Henry
相关产品推荐
相关产品推荐

