Cycling Applications: Creating Precision Frequency Control and Vibration
By Mike McDonald, Applications Engineering Team
Last updated on Mar. 29, 2017
Introduction
A common use for actuators and stages is cycling, or moving back and forth between two points either continuously or for a set number of cycles. Typically these cycling applications have a number of characteristics, including the profile of the movement, the amplitude or distance of the travel, and the frequency or period of the cycle. Depending on the requirements, there are different optimal strategies for getting the best results from Zaber devices. There are also limitations to what the devices can do.
Common Profile Types
Triangle Wave
This is the simplest type of waveform, where the actuator moves at a constant speed between two points. It assumes that rapid acceleration and deceleration are occurring when the direction changes, producing the sharp points. It's a convenient waveform to use because the amplitude and frequency are very easy to control when you assume a rapid acceleration.

from zaber_motion import Units, Library
from zaber_motion.ascii import Connection
from zaber_motion.gcode import axis_definition
Library.enable_device_db_store()
with Connection.open_serial_port("COM5") as connection: # confirm that this is the right serial port
device_list = connection.detect_devices()
print("Found {} devices".format(len(device_list)))
device = device_list[0]
axis = device.get_axis(1)
if not axis.is_homed():
axis.home()
# triangle waveform example for X-LSM100A for 1 hz, 0.5 mm amplitude
# microstep size 0.047625 µm
axis_definition.microstep_resolution(0.047625, Units.LENGTH_MICROMETRES)
# set the speed to 34402, which works out to 1mm/s
axis.settings.set("maxspeed", 34402)
# set the acceleration to infinite
axis.settings.set("accel", 0)
# 100 cycles with 0.5 mm amplitude
for i in range(100):
axis.move_relative(10499)
axis.move_relative(-10499)
An example of a triangle wave program written for Zaber Launcher using the ASCII command protocol. More information on scripting for Zaber Motion Library can be found at https://software.zaber.com/motion-library/docs/.
While it is theoretically the easiest waveform, infinite acceleration is typically not practical. Devices have limited thrust to accelerate a load, which means there will be some maximum acceleration that can be set before the motor stalls.
Having to decelerate to a stop and accelerate again will have the effect of rounding the peaks of the triangular wave. As the acceleration is lowered, the peaks will become more rounded. To maintain the same period and amplitude, the target speed must be adjusted to compensate for the acceleration time. Every cycle will have three regions of movement - acceleration, deceleration, and constant speed. For a given acceleration, kinematic equations must be used for each region to determine the target speed to achieve the desired period and frequency.
Rounded Triangle Wave

As the target speed increases for a given acceleration, it will eventually get to a point at which the target speed is never reached. The first half of the travel will be constant acceleration, and the second half will be constant deceleration. With 2 regions, this makes the motion much easier to determine:
acceleration = amplitude / (period / 4)2The entire motion will be made up of acceleration as long as the target speed is:
speed > acceleration * (period / 4)In this case, the acceleration value was rounded to 14 (the acceleration setting must be an integer value). This actually works out to an acceleration of ~4.07 mm/s2 instead of 4 mm/s2, and a frequency of ~0.504 Hz. The resolution of the acceleration for the device may be a limiting factor for accuracy with this method.
from zaber_motion import Units, Library
from zaber_motion.ascii import Connection
from zaber_motion.gcode import axis_definition
Library.enable_device_db_store()
with Connection.open_serial_port("COM5") as connection: # confirm that this is the right serial port
device_list = connection.detect_devices()
print("Found {} devices".format(len(device_list)))
device = device_list[0]
axis = device.get_axis(1)
if not axis.is_homed():
axis.home()
# rounded triangle waveform example for X-LSM100A for 1 hz, 1 mm amplitude
# microstep size 0.047625 µm
axis_definition.microstep_resolution(0.047625, Units.LENGTH_MICROMETRES)
# set the speed to be above (4 mm/s^2 * 0.5 s) = 4 mm/s
axis.settings.set("maxspeed", 137608)
# set the acceleration to (1 mm / (0.5s)^2) = 4 mm/s^2
axis.settings.set("accel", 14)
# 100 cycles with
for i in range(100):
axis.move_relative(20997)
axis.move_relative(-20997)
An example of a rounded triangle wave program written for Zaber Launcher using the Zaber ASCII protocol. More information on scripting for Zaber Launcher can be found at https://software.zaber.com/motion-library/docs/.
Sawtooth
The sawtooth profile is very similar to the triangle wave, but different speeds are used during the periods of extension and retraction. It's a practical wave to achieve a slow but accurate speed in one direction then rapid movement in the other. For example, it could be used in a syringe application, where a low speed, high precision flow rate is needed for injection, and then the syringe should rapidly reset the position. It's another easy waveform to control and only involves adding commands to change the speed prior to movement commands. It has the same acceleration limitations as the triangle wave.

from zaber_motion import Units, Library
from zaber_motion.ascii import Connection
from zaber_motion.gcode import axis_definition
Library.enable_device_db_store()
with Connection.open_serial_port("COM5") as connection: # confirm that this is the right serial port
device_list = connection.detect_devices()
print("Found {} devices".format(len(device_list)))
device = device_list[0]
axis = device.get_axis(1)
if not axis.is_homed():
axis.home()
# sawtooth waveform example for X-LSM100A for 1 hz, 0.5 mm amplitude
# microstep size 0.047625 µm
axis_definition.microstep_resolution(0.047625, Units.LENGTH_MICROMETRES)
# set the speed to be above (4 mm/s^2 * 0.5 s) = 4 mm/s
axis.settings.set("maxspeed", 137608)
# set the acceleration to infinite
axis.settings.set("accel", 0)
# 100 cycles with 0.5mm amplitude, switching between 4 mm/s and 0.57 mm/s
for i in range(100):
axis.settings.set("maxspeed", 137608)
axis.move_relative(10499)
axis.settings.set("maxspeed", 19609)
axis.move_relative(-10499)
An example of a sawtooth wave program written for Zaber Launcher using the ASCII protocol. More information on scripting for Zaber Launcher can be found at https://software.zaber.com/motion-library/docs/.
Sinusoidal Wave
Sinusoidal motion is a useful and common pattern for cycling between two points. The waves have a constantly varying speed and acceleration, offering a smooth and predictable motion.
Because it is a frequently used profile, Zaber devices feature the move sin command in the Zaber ASCII protocol. This command allows a user to define an amplitude and period, and the device will begin to cycle based on these inputs. The user can also specify the number of cycles to execute. This command greatly simplifies a complex task, while also offering the best solution for timing and positional accuracy.
The ASCII protocol is available to all A-Series and X-Series devices. To simulate sinusoidal motion using T-Series devices (which cannot use the ASCII protocol), using the previously mentioned rounded triangle wave can give a reasonable approximation.

from zaber_motion import Units, Library
from zaber_motion.ascii import Connection
from zaber_motion.gcode import axis_definition
Library.enable_device_db_store()
with Connection.open_serial_port("COM5") as connection: # confirm that this is the right serial port
device_list = connection.detect_devices()
print("Found {} devices".format(len(device_list)))
device = device_list[0]
axis = device.get_axis(1)
if not axis.is_homed():
axis.home()
# sinusoidal waveform example for X-LSM100A for 1 hz, 0.5 mm amplitude
# microstep size 0.047625 µm
axis_definition.microstep_resolution(0.047625, Units.LENGTH_MICROMETRES)
# Run 200 cycles of sinusoidal motion on an X-LSM100A with 1 hz, 0.5mm amplitude
axis.generic_command("move sin 10499 1000 200")
An example of a program written for Zaber Launcher using the ASCII protocol to begin a sinusoidal motion. More information on scripting for Zaber Launcher can be found at https://software.zaber.com/motion-library/docs/.
Accurate Amplitude vs. Accurate Timing
A consideration to keep in mind is whether timing accuracy or positional accuracy is more important in the application. One of the reasons the sinusoidal motion command, move sin, is useful is that accuracy of both are ensured. Other solutions may be limited to achieving one or the other.
The source of this limitation is the need for communication between the device and the software when a desired position is reached to instruct it to move to the next. The communication can take anywhere from 5 - 50 ms depending on the device configuration, connection, commands, and software. The above example scripts have focused on achieving positional accuracy; the program waits until a movement is complete before communicating the next movement to the device. This ensures the end points are reached but introduces some delay and uncertainty in the timing. Adjusting the target speed and acceleration settings can be used to partially compensate for this.
The alternative to using precise movements and relying on speed and acceleration for frequency is to use precise timing and rely on speed and acceleration for amplitude. The easiest way to control timing for most motion commands is to use the system time of a computer. With this method, instead of sending point to point movement commands and waiting for them to complete, send constant speed commands in alternating directions with delays in between.
import time
from zaber_motion import Units, Library
from zaber_motion.ascii import Connection
from zaber_motion.gcode import axis_definition
Library.enable_device_db_store()
with Connection.open_serial_port("COM5") as connection: # confirm that this is the right serial port
device_list = connection.detect_devices()
print("Found {} devices".format(len(device_list)))
device = device_list[0]
axis = device.get_axis(1)
if not axis.is_homed():
axis.home()
# accurate amplitude & accurate timing
# triangle waveform example for X-LSM100A for 1 hz, 0.5 mm amplitude
# microstep size 0.047625 µm
axis_definition.microstep_resolution(0.047625, Units.LENGTH_MICROMETRES)
# set the acceleration to infinite
axis.settings.set("accel", 0)
# 100 cycles with 0.5 mm amplitude, 0.5 s wait between movements (speed 1 mm/s)
for i in range(100):
axis.move_velocity(34402)
time.sleep(0.5)
axis.move_velocity(-34402)
time.sleep(0.5)
An example of a triangle waveform that uses accurate timing for Zaber Launcher in the ASCII protocol. More information on scripting for Zaber Launcher can be found at https://software.zaber.com/motion-library/docs/.
The same method can be applied to produce any of the other waveforms as well. The drawback of this method is that some certainty on the positioning is lost because there is no condition in the program that a precise position must be met. Also there is still some small variation from command to command of how quickly it will be sent, so the timing doesn't have millisecond accuracy.
In addition to the move sin command, triggers and streamed commands are other tools available in the ASCII protocol that help to address potential timing errors from communication. The trigger tools are used to set up conditions on the device that will automatically cause an action to occur. An example of this is when a certain position is reached, the device should then move to another location. Read the technical article Using Triggers to Simplify Automation to learn more about triggers. Stream commands are added to a queue as they are sent, and the device will run the queued commands in order. This means that there is no communication time between two movements. A user can continually send streamed motion commands instructing the device to move between two points, and as long as the user keeps the queue full, the device will move back and forth. Streams can also co-ordinate multiple axes, so it's a useful tool for cycling two axes together. Read the Streamed and Interpolated Multi-axis Motion technical article for more information on streams.
High Frequency Limitations
Cycling applications will often have a range of frequencies that need to be met. In order to ensure the best device is selected, the user must know the maximum frequency a device can achieve. There are a number of factors to consider when determining this.
The first consideration is the aforementioned communication time. For example, for some devices and commands it may take about 10 ms to send a command to the device and receive a response, so a maximum of 100 commands can be sent every second (this rate varies based on the connection type, device version, and command, and is best found by testing). Each cycle requires at least 2 commands (depending on the waveform selected) which means that 50 Hz is an upper limit of what can be achieved with a computer controlling the device. If the move sin, stream, or trigger tools described above are used, then communication time will not be a factor.
For small amplitude applications, the maximum acceleration will be the next limiting factor. It doesn't matter what the maximum speed of a device is if the acceleration doesn't allow it to reach that speed in the distance available in a cycle. The maximum acceleration of a stage is not a listed specification because it will depend on both the speed to which the device is accelerating, as well as the loading on the device. Trial and error is the best way to find the maximum achievable acceleration for a specific application. Zaber's Applications Engineering Team is happy to help recommend a device and test for the maximum acceleration if there is a specific acceleration or frequency required.
As an example, the X-LSQ150B stage was tested with a 1 kg load on it (mounted horizontally) to have a maximum acceleration of about 10,000 mm/s 2. If the stage was cycling with 0.50 mm amplitude at that acceleration, it would reach a peak speed of 70 mm/s, and the time spent accelerating/decelerating in each cycle would be ~28 ms. With ~20 ms of communication time per cycle as well, this works out to a maximum frequency of around 20 Hz.
As the travel length increases, the maximum speed also starts to be a factor. Going back to the above example, the X-LSQ150B would reach the maximum speed of 280 mm/s in about 3.9 mm when accelerating at 10,000 mm/s 2. This means that over 10 mm, there would be 7.8 mm of acceleration and deceleration and 2.2 mm of constant speed. This works out to about 128 ms per cycle of movement and ~20 ms of communication time. In this case, the maximum frequency would be about 6 Hz.
The larger the load on the stage, the lower the acceleration can be set, decreasing the maximum frequency. If the stage is near the limit of the maximum acceleration, the stepper motor may stall, and some cycles could be skipped.
For most cases, the best solution is to be conservative when selecting a device and to contact our Applications Engineering Team to verify that the frequency is achievable.