0

I am using a Zaber linear stage controlled by an Arduino using the Zaber shield. I try to do something similar to this question (Track position of a Zaber device as it moves), but using the Arduino language instead of Labview.

In the answer 3 options were provided: interpolate from the start and end points, poll the position using a timer, or turn on a device mode that reports the position every 250 ms.

The device mode does not seem to exist for my stage (X-LSQ-075B-E01) and I don't want to rely on interpolation. The stage is fitted with an encoder and I can easily get its readback for the exact position; I just don't know how to poll the stage while moving. I came up with the following code (a bit simplified), but it is relatively slow and only gives the readback from 1 stage (we are actually using 2) and sending the command to both does not really work.

#include <ZaberAscii.h>

ZaberShield shield(ZABERSHIELD_ADDRESS_AA);
ZaberAscii za(shield);

void setup() {
  shield.begin(115200);
  Serial.begin(115200);
  za.send(1, 1, "move rel", 20000);
  za.receive();

  while (za.isIdle(1) == false) {
    za.send(1, "get encoder.pos");
    ZaberAscii::reply reply = za.receive();
    if (!reply.isReply) {
      Serial.println("*** Received a non-reply message from device " + String(reply.deviceNumber) + ".");
    }
    else if (reply.isRejected) {
      Serial.println("*** A command was rejected by device " + String(reply.deviceNumber) + ".");
    }
    else {
      Serial.println( String(reply.deviceNumber) + ": " + String(reply.responseData));
    }
    delay(5);
  }

  za.pollUntilIdle(1);
  Serial.println("1 finished");
  za.send(2, 1, "move rel", 20000);

  while (za.isIdle(2) == false) {
    za.send(2, "get encoder.pos");
    Serial.println("Device 2 not idle");
    ZaberAscii::reply reply = za.receive();
    if (!reply.isReply) {
      Serial.println("*** Received a non-reply message from device " + String(reply.deviceNumber) + ".");
    }
    else if (reply.isRejected) {
      Serial.println("*** A command was rejected by device " + String(reply.deviceNumber) + ".");
    }
    else {
      Serial.println( String(reply.deviceNumber) + ": " + String(reply.responseData));
    }
    //delay(10);
  }
  Serial.println("2 finished");
}

void loop() {}
ocrdu
  • 2,172
  • 6
  • 15
  • 22
Henry
  • 27
  • 4

1 Answers1

0

To have it work on both devices, you would need to send both movement commands before starting the while loop, and then for the while loop you would want the condition to repeat until both axes are idle. Within the while loop you would read the position of both axes.

On the timing, can you say approximately what frequency you're getting now? The timing per communication is typically about 5-10ms. With the current functions, the isIdle() call takes it's own command, so I'd expect the above loop to be about 20-40ms. You could cut it in half by pulling the .IsBusy information from the 'get encoder.pos' reply.

Mike McDonald mike@zaber.com

ZaberCS
  • 76
  • 4
  • Thank you so much for your answer. I don't want to move both axes at the same time, but after each other, so I don't necessarily need both readbacks when only one is moving, but I thought it would be handy for post processing. I was getting a position app. every 70 ms. Applying your changes, I'm down to 35ms, but all positions are send twice. – Henry Feb 05 '21 at 11:14
  • I'm glad to assist. 35ms sounds about right for sending two separate commands to ask for the encoder.pos. One way you could half this again is to send a single command to device 0: za.send(0, "get encoder.pos"); This will prompt both devices in the chain to reply. You would then need to do two .receives to consume both replies. There wouldn't be a guarantee on the order of which device replied first, so you would also need to inspect the device number of each reply to ensure you are matching the correct device and position. – ZaberCS Feb 05 '21 at 17:42