Streams provide a way to execute a sequence of actions on the device without a communication delay.
An example of an action sequence may be:
Move two axes to coordinates (10 cm, 20 cm)
Wait 1 second
Set digital output 1 to high
Move single axis by 5 cm
Wait until digital input 1 is low
Do a circular movement with radius 1 cm
Stream movement differs from successive command execution because stream movement transitions smoothly between each path segment.
In addition, stream movement includes methods for multi-axis movement, such as moving in lines, arcs, or circles.
You can read more about interpolated, multi-axis motion in this article.
Much of the presented API's behavior originates in the underlying Zaber ASCII Protocol.
Refer to the stream section in the ASCII Protocol Manual for a detailed explanation of the behaviour.
MATLAB specific considerations
This API often uses arrays as arguments.
Sometimes you may need to create an array of a complex type with size of 1.
Such an array must be allocated using the javaArray function.
The function takes two arguments: fully qualified name of the type and the size of the array.
The example follows.
To start using streams, get a stream from the device.
In this example we get the first stream.
Python
C#
C++
JavaScript
Java
MATLAB (legacy)
stream = device.streams.get_stream(1)
const stream = device.streams.getStream(1);
var stream = device.Streams.GetStream(1);
Streamstream= device.getStreams().getStream(1);
stream = device.getStreams().getStream(1);
Stream stream = device.getStreams().getStream(1);
The instance returned represents a handle for stream number 1 on the device.
The stream needs to be set up before it can be used.
Once the stream is set up in either “Live” mode or “Store” mode, the methods of this instance allow you to append stream actions onto the stream queue.
Live Mode
When a stream is in Live mode, actions in the stream's queue will immediately execute.
To set up a stream in Live mode, call the
setupLive
method
with an arguments containing the axis numbers of the axes that the stream will target.
You can provide a variable number of arguments (axes numbers).
Python
C#
C++
JavaScript
Java
MATLAB (legacy)
stream.setup_live(1, 2)
await stream.setupLive(1, 2);
stream.SetupLive(1, 2);
stream.setupLive(1, 2);
stream.setupLive([1, 2]);
stream.setupLive(1, 2);
Store Mode
Streams set up in store mode do not execute the actions in their queue. Instead, they store them to a stream buffer on the device.
Once some actions are stored, stream buffers can be played back by Live streams via the
call
method,
so that the stored actions are added to the Live stream's queue.
Stored actions may also be set up to trigger if a device reaches a condition, such as when an I/O port equals a value.
Read more about triggers here.
Buffers on a device are identified by numbers starting from 1.
The total number of buffers available is determined by stream.numbufs setting.
Since Store streams cannot be set up to use non-empty stream buffers, it is recommended to erase the buffer before setting up.
See the following code for an example of setting up a stream in Store mode:
Python
C#
C++
JavaScript
Java
MATLAB (legacy)
stream_buffer = device.streams.get_buffer(1)
stream_buffer.erase()
# set up stream to store actions to stream buffer 1 and# to use the first two axes for unit conversion
stream.setup_store(stream_buffer, 1, 2)
# append stream actions...
const streamBuffer = device.streams.getBuffer(1);
await streamBuffer.erase();
// set up stream to store actions to stream buffer 1 and// to use the first two axes for unit conversionawait stream.setupStore(streamBuffer, 1, 2);
// append stream actions...
var streamBuffer = device.Streams.GetBuffer(1);
streamBuffer.Erase();
// set up stream to store actions to stream buffer 1 and// to use the first two axes for unit conversion
stream.SetupStore(streamBuffer, 1, 2);
// append stream actions...
StreamBufferstreamBuffer= device.getStreams().getBuffer(1);
streamBuffer.erase();
// set up stream to store actions to stream buffer 1 and// to use the first two axes for unit conversion
stream.setupStore(streamBuffer, 1, 2);
// append stream actions...
streamBuffer = device.getStreams().getBuffer(1);
streamBuffer.erase();
% set up stream to store actions to stream buffer 1 and% to use the first two axes for unit conversion
stream.setupStore(streamBuffer, [1, 2]);
% append stream actions...
StreamBuffer streamBuffer = device.getStreams().getBuffer(1);
streamBuffer.erase();
// set up stream to store actions to stream buffer 1 and// to use the first two axes for unit conversion
stream.setupStore(streamBuffer, 1, 2);
// append stream actions...
Once you have appended some actions to the stream buffer, you can print it out, or call it from a live stream.
Before calling the stream buffer ensure that the live stream is set up on the same set of axes as the original store stream.
If the axes are different, the unit conversions applied when storing the commands may result in incorrect motion.
You can ignore this condition if the conversion factors of your axes are the same (e.g. the peripherals are the same product).
Python
C#
C++
JavaScript
Java
MATLAB (legacy)
content = stream_buffer.get_content()
print(content)
# example output:# ['setup store 1 2',# 'line abs 1000 100',# 'wait 1000',# 'line abs 20000 29999',# 'setup disable']# change the streams mode from Store to Live
stream.disable()
stream.setup_live(1, 2)
# the stream will now execute the stored actions
stream.call(stream_buffer)
std::vector<std::string> content = streamBuffer.getContent();
for (auto line : content) {
std::cout << line << std::endl;
}
// example output:// 'setup store 1 2',// 'line abs 1000 100',// 'wait 1000',// 'line abs 20000 29999',// 'setup disable'
stream.disable();
stream.setupLive(1, 2);
stream.call(streamBuffer);
Note that the actions printed are in native units and their format is described in the stream buffer print command documentation in the ASCII Protocol Manual.
Store (Arbitrary axes)
There is a variant of Store mode that the library provides, called StoreArbitrary mode.
StoreArbitrary mode works as Store mode, but it does not target specific axes on the device.
Streams setup in this mode only require that the number of axes in the stream be specified.
See the following code for an example:
Python
C#
C++
JavaScript
Java
MATLAB (legacy)
stream_buffer = device.streams.get_buffer(1)
stream_buffer.erase()
# set up a StoreArbitrary stream for any two axes
stream.setup_store_arbitrary(stream_buffer, 2)
const streamBuffer = device.streams.getBuffer(1);
await streamBuffer.erase();
// set up a StoreArbitrary stream for any two axesawait stream.setupStoreArbitrary(streamBuffer, 2);
var streamBuffer = device.Streams.GetBuffer(1);
streamBuffer.Erase();
// set up a StoreArbitrary stream for any two axes
stream.SetupStoreArbitrary(streamBuffer, 2);
StreamBufferstreamBuffer= device.getStreams().getBuffer(1);
streamBuffer.erase();
// set up a StoreArbitrary stream for any two axes
stream.setupStoreArbitrary(streamBuffer, 2);
streamBuffer = device.getStreams().getBuffer(1);
streamBuffer.erase();
% set up a StoreArbitrary stream for any two axes
stream.setupStoreArbitrary(streamBuffer, 2);
StreamBuffer streamBuffer = device.getStreams().getBuffer(1);
// set up a StoreArbitrary stream for any two axes
streamBuffer.erase();
stream.setupStoreArbitrary(streamBuffer, 2);
The benefit of StoreArbitrary mode is that you can later call the stream buffer from any live stream, provided it has same the number of axes.
The downside of this mode is that it does not allow for unit conversion, as unit conversion is usually dependent on the conversion factor of a specific axis on the device.
Movement
Stream movement actions can be relative or absolute. If a movement is relative, the device treats the current position of the axes as the origin. If a movement is absolute, the device treats the position zero of the axes as origin.
Parameters for stream movement actions make use of
Measurement
objects.
Measurement objects are simple objects which contains a
value
field as well as an optional
unit
field.
The next couple of examples show how to do stream movement.
Line Movement
Python
C#
C++
JavaScript
Java
MATLAB (legacy)
stream.line_absolute(
Measurement(29.0047, Units.LENGTH_MILLIMETRES),
Measurement(40.49, Units.LENGTH_MILLIMETRES)
)
stream.line_relative(
Measurement(0),
Measurement(50.5, Units.LENGTH_MILLIMETRES)
)
# or, to write less, iterate over an array of points and convert to cm once
path_in_cm = [(0.00, 3.00), (2.25, 7.10), (5.35, 0.15), (1.45, 10.20), (9.00, 9.00)]
for point in path_in_cm:
stream.line_absolute(
Measurement(point[0], Units.LENGTH_CENTIMETRES),
Measurement(point[1], Units.LENGTH_CENTIMETRES)
)
await stream.lineAbsolute({
value: 29.0047, unit: Length.mm,
}, {
value: 40.49, unit: Length.mm,
});
await stream.lineRelative({
value: 0,
}, {
value: 50.5, unit: Length.mm,
});
// or, to write less, iterate over an array of points and convert to cm onceconst pathInCm = [[0.00, 3.00], [2.25, 7.10], [5.35, 0.15], [1.45, 10.20], [9.00, 9.00]];
for (const point of pathInCm) {
await stream.lineAbsolute(
{ value: point[0], unit: Length.cm },
{ value: point[1], unit: Length.cm },
);
}
stream.LineAbsolute(
new Measurement { Value = 29.0047, Unit = Units.Length_Millimetres },
new Measurement { Value = 40.49, Unit = Units.Length_Millimetres }
);
stream.LineRelative(
new Measurement { Value = 0 },
new Measurement { Value = 50.5, Unit = Units.Length_Millimetres }
);
// or, to write less, iterate over an array of points and convert to cm oncedouble[,] pathInCm = newdouble[,] { { 0.00, 3.00 }, { 2.25, 7.10 }, { 5.35, 0.15 }, { 1.45, 10.20 }, { 9.00, 9.00 } };
for (int i = 0; i < pathInCm.GetLength(0); i++)
{
stream.LineAbsolute(
new Measurement { Value = pathInCm[i, 0], Unit = Units.Length_Centimetres },
new Measurement { Value = pathInCm[i, 1], Unit = Units.Length_Centimetres }
);
}
stream.lineAbsolute(
newMeasurement(29.0047, Units.LENGTH_MILLIMETRES),
newMeasurement(40.49, Units.LENGTH_MILLIMETRES)
);
stream.lineRelative(
newMeasurement(0),
newMeasurement(50.5, Units.LENGTH_MILLIMETRES)
);
// or, to write less, iterate over an array of points and convert to cm oncedouble[][] pathInCm = { {0.00, 3.00}, {2.25, 7.10}, {5.35, 0.15}, {1.45, 10.20}, {9.00, 9.00} };
for (double[] point : pathInCm) {
stream.lineAbsolute(
newMeasurement(point[0], Units.LENGTH_CENTIMETRES),
newMeasurement(point[1], Units.LENGTH_CENTIMETRES)
);
}
stream.lineAbsolute([
Measurement(29.0047, Units.LENGTH_MILLIMETRES)
Measurement(40.49, Units.LENGTH_MILLIMETRES)
]);
stream.lineRelative([
Measurement(0)
Measurement(50.5, Units.LENGTH_MILLIMETRES)
]);
% or, to write less, iterate over an array of points and convert to cm once
points = [0.003.00; 2.257.10; 5.350.15; 1.4510.20; 9.009.00];
for row=1:size(points,1)
stream.lineAbsolute([
Measurement(points(row, 1), Units.LENGTH_CENTIMETRES)
Measurement(points(row, 2), Units.LENGTH_CENTIMETRES)
]);
end
stream.lineAbsolute(
Measurement(29.0047, Units::LENGTH_MILLIMETRES),
Measurement(40.49, Units::LENGTH_MILLIMETRES)
);
stream.lineAbsolute(
Measurement(0),
Measurement(50.5, Units::LENGTH_MILLIMETRES)
);
// or, to write less, iterate over an array of points and convert to cm oncedouble pathInCm[5][2] = { {0.00, 3.00}, {2.25, 7.10}, {5.35, 0.15}, {1.45, 10.20}, {9.00, 9.00} };
for (double* point : pathInCm) {
stream.lineAbsolute(
Measurement(point[0], Units::LENGTH_CENTIMETRES),
Measurement(point[1], Units::LENGTH_CENTIMETRES)
);
}
Note that line movement methods accept a variable number of arguments and therefore support motion on different numbers of axes.
Circular Movement
Python
C#
C++
JavaScript
Java
MATLAB (legacy)
# assuming current position is (9cm, 9cm)# do a clockwise rotation with radius 4, starting at 0 degrees
circle_centre_abs = ( # X,Y absolute position of circle's centre
Measurement(5, Units.LENGTH_CENTIMETRES),
Measurement(9, Units.LENGTH_CENTIMETRES)
)
stream.circle_absolute(RotationDirection.CW, circle_centre_abs[0], circle_centre_abs[1])
# do a counter-clockwise rotation with radius 4, starting at 0 degrees
circle_centre_rel = ( # X,Y relative position of circle's centre
Measurement(-4, Units.LENGTH_CENTIMETRES),
Measurement(0, Units.LENGTH_CENTIMETRES)
)
stream.circle_relative(RotationDirection.CCW, circle_centre_rel[0], circle_centre_rel[1])
// assuming current position is (9cm, 9cm)// do a clockwise rotation with radius 4, starting at 0 degreesconst circleCentreAbs = [ // X,Y absolute position of circle's centre
{ value: 5, unit: Length.cm },
{ value: 9, unit: Length.cm },
];
await stream.circleAbsolute(RotationDirection.CW, circleCentreAbs[0], circleCentreAbs[1]);
// do a counter-clockwise rotation with radius 4, starting at 0 degreesconst circleCentreRel = [ // X,Y relative position of circle's centre
{ value: -4, unit: Length.cm },
{ value: 0, unit: Length.cm },
];
await stream.circleRelative(RotationDirection.CCW, circleCentreRel[0], circleCentreRel[1]);
// assuming current position is (9cm, 9cm)// do a clockwise rotation with radius 4, starting at 0 degreesvar circleCentreAbs = new Measurement[] // X,Y absolute position of circle's centre
{
new Measurement { Value = 5, Unit = Units.Length_Centimetres },
new Measurement { Value = 9, Unit = Units.Length_Centimetres }
};
stream.CircleAbsolute(RotationDirection.CW, circleCentreAbs[0], circleCentreAbs[1]);
// do a counter-clockwise rotation with radius 4, starting at 0 degreesvar circleCentreRel = new Measurement[] // X,Y relative position of circle's centre
{
new Measurement { Value = -4, Unit = Units.Length_Centimetres },
new Measurement { Value = 0, Unit = Units.Length_Centimetres }
};
stream.CircleRelative(RotationDirection.CCW, circleCentreRel[0], circleCentreRel[1]);
// assuming current position is (9cm, 9cm)// do a clockwise rotation with radius 4, starting at 0 degrees
Measurement[] circleCentreAbs = { // X,Y absolute position of circle's centrenewMeasurement(5, Units.LENGTH_CENTIMETRES),
newMeasurement(9, Units.LENGTH_CENTIMETRES),
};
stream.circleAbsolute(RotationDirection.CW, circleCentreAbs[0], circleCentreAbs[1]);
// do a counter-clockwise rotation with radius 4, starting at 0 degrees
Measurement[] circleCentreRel = { // X,Y relative position of circle's centrenewMeasurement(-4, Units.LENGTH_CENTIMETRES),
newMeasurement(0, Units.LENGTH_CENTIMETRES),
};
stream.circleRelative(RotationDirection.CCW, circleCentreRel[0], circleCentreRel[1]);
% assuming current position is (9cm, 9cm)% do a clockwise rotation with radius 4, starting at 0 degrees
circleCentreAbs = [ % X,Y absolute position of circle's centre
Measurement(5, Units.LENGTH_CENTIMETRES)
Measurement(9, Units.LENGTH_CENTIMETRES)
];
stream.circleAbsolute(RotationDirection.CW, circleCentreAbs(1), circleCentreAbs(2));
% do a counter-clockwise rotation with radius 4, starting at 0 degrees
circleCentreRel = [ % X,Y relative position of circle's centre
Measurement(-4, Units.LENGTH_CENTIMETRES)
Measurement(0, Units.LENGTH_CENTIMETRES)
];
stream.circleRelative(RotationDirection.CCW, circleCentreRel(1), circleCentreRel(2));
// assuming current position is (9cm, 9cm)// do a clockwise rotation with radius 4, starting at 0 degrees
Measurement circleCentreAbs[] = { // X,Y absolute position of circle's centreMeasurement(5, Units::LENGTH_CENTIMETRES),
Measurement(9, Units::LENGTH_CENTIMETRES),
};
stream.circleAbsolute(RotationDirection::CW, circleCentreAbs[0], circleCentreAbs[1]);
// do a counter-clockwise rotation with radius 4, starting at 0 degrees
Measurement circleCentreRel[] = { // X,Y relative position of circle's centreMeasurement(-4, Units::LENGTH_CENTIMETRES),
Measurement(0, Units::LENGTH_CENTIMETRES),
};
stream.circleRelative(RotationDirection::CCW, circleCentreRel[0], circleCentreRel[1]);
Arc Movement
Python
C#
C++
JavaScript
Java
MATLAB (legacy)
# assuming current position is (9cm, 9cm)
arc_circle_centre_rel = ( # X,Y relative position of arc's centre
Measurement(-4, Units.LENGTH_CENTIMETRES),
Measurement(0, Units.LENGTH_CENTIMETRES)
)
arc_end_rel = ( # X,Y relative position of arc's end
Measurement(-8, Units.LENGTH_CENTIMETRES),
Measurement(0, Units.LENGTH_CENTIMETRES)
)
# move from 0 degrees to 180 degrees of circle with radius 4
stream.arc_relative(
RotationDirection.CCW,
arc_circle_centre_rel[0],
arc_circle_centre_rel[1],
arc_end_rel[0],
arc_end_rel[1]
)
arc_circle_centre_abs = ( # X,Y absolute position of arc's centre
Measurement(5, Units.LENGTH_CENTIMETRES),
Measurement(9, Units.LENGTH_CENTIMETRES)
)
arc_end_abs = ( # X,Y absolute position of arc's end
Measurement(9, Units.LENGTH_CENTIMETRES),
Measurement(9, Units.LENGTH_CENTIMETRES)
)
# backtrace last arc movement by moving from 180 degrees to 0 degrees
stream.arc_absolute(
RotationDirection.CW,
arc_circle_centre_abs[0],
arc_circle_centre_abs[1],
arc_end_abs[0],
arc_end_abs[1]
)
// assuming current position is (9cm, 9cm)const arcCircleCentreRel = [ // X,Y relative position of arc's centre
{ value: -4, unit: Length.cm },
{ value: 0, unit: Length.cm },
];
const arcEndRel = [ // X,Y relative position of arc's end
{ value: -8, unit: Length.cm },
{ value: 0, unit: Length.cm },
];
// move from 0 degrees to 180 degrees of circle with radius 4await stream.arcRelative(
RotationDirection.CCW,
arcCircleCentreRel[0], arcCircleCentreRel[1],
arcEndRel[0], arcEndRel[1],
);
const arcCircleCentreAbs = [ // X,Y absolute position of arc's centre
{ value: 5, unit: Length.cm },
{ value: 9, unit: Length.cm },
];
const arcEndAbs = [ // X,Y absolute position of arc's end
{ value: 9, unit: Length.cm },
{ value: 9, unit: Length.cm },
];
// backtrace last arc movement by moving from 180 degrees to 0 degreesawait stream.arcAbsolute(
RotationDirection.CW,
arcCircleCentreAbs[0], arcCircleCentreAbs[1],
arcEndAbs[0], arcEndAbs[1],
);
// assuming current position is (9cm, 9cm)var arcCircleCentreRel = new Measurement[] // X,Y relative position of arc's centre
{
new Measurement { Value = -4, Unit = Units.Length_Centimetres },
new Measurement { Value = 0, Unit = Units.Length_Centimetres }
};
var arcEndRel = new Measurement[] // X,Y relative position of arc's end
{
new Measurement { Value = -8, Unit = Units.Length_Centimetres },
new Measurement { Value = 0, Unit = Units.Length_Centimetres }
};
// move from 0 degrees to 180 degrees of circle with radius 4
stream.ArcRelative(
RotationDirection.CCW,
arcCircleCentreRel[0], arcCircleCentreRel[1],
arcEndRel[0], arcEndRel[1]
);
var arcCircleCentreAbs = new Measurement[] // X,Y absolute position of arc's centre
{
new Measurement { Value = 5, Unit = Units.Length_Centimetres },
new Measurement { Value = 9, Unit = Units.Length_Centimetres }
};
var arcEndAbs = new Measurement[] // X,Y absolute position of arc's end
{
new Measurement { Value = 9, Unit = Units.Length_Centimetres },
new Measurement { Value = 9, Unit = Units.Length_Centimetres }
};
// backtrace last arc movement by moving from 180 degrees to 0 degrees
stream.ArcAbsolute(
RotationDirection.CW,
arcCircleCentreAbs[0], arcCircleCentreAbs[1],
arcEndAbs[0], arcEndAbs[1]
);
// assuming current position is (9cm, 9cm)
Measurement[] arcCircleCentreRel = { // X,Y relative position of arc's centrenewMeasurement(-4, Units.LENGTH_CENTIMETRES),
newMeasurement(0, Units.LENGTH_CENTIMETRES),
};
Measurement[] arcEndRel = { // X,Y relative position of arc's endnewMeasurement(-8, Units.LENGTH_CENTIMETRES),
newMeasurement(0, Units.LENGTH_CENTIMETRES),
};
// move from 0 degrees to 180 degrees of circle with radius 4
stream.arcRelative(
RotationDirection.CCW,
arcCircleCentreRel[0], arcCircleCentreRel[1],
arcEndRel[0], arcEndRel[1]
);
Measurement[] arcCircleCentreAbs = { // X,Y absolute position of arc's centrenewMeasurement(5, Units.LENGTH_CENTIMETRES),
newMeasurement(9, Units.LENGTH_CENTIMETRES),
};
Measurement[] arcEndAbs = { // X,Y absolute position of arc's endnewMeasurement(9, Units.LENGTH_CENTIMETRES),
newMeasurement(9, Units.LENGTH_CENTIMETRES),
};
// backtrace last arc movement by moving from 180 degrees to 0 degrees
stream.arcAbsolute(
RotationDirection.CW,
arcCircleCentreAbs[0], arcCircleCentreAbs[1],
arcEndAbs[0], arcEndAbs[1]
);
% assuming current position is (9cm, 9cm)
arcCircleCentreRel = [ % X,Y relative position of arc's centre
Measurement(-4, Units.LENGTH_CENTIMETRES)
Measurement(0, Units.LENGTH_CENTIMETRES)
];
arcEndRel = [ % X,Y relative position of arc's end
Measurement(-8, Units.LENGTH_CENTIMETRES)
Measurement(0, Units.LENGTH_CENTIMETRES)
];
% move from 0 degrees to 180 degrees of circle with radius 4
stream.arcRelative( ...
RotationDirection.CCW, ...
arcCircleCentreRel(1), arcCircleCentreRel(2), ...
arcEndRel(1), arcEndRel(2));
arcCircleCentreAbs = [ % X,Y absolute position of arc's centre
Measurement(5, Units.LENGTH_CENTIMETRES)
Measurement(9, Units.LENGTH_CENTIMETRES)
];
arcEndAbs = [ % X,Y absolute position of arc's end
Measurement(9, Units.LENGTH_CENTIMETRES)
Measurement(9, Units.LENGTH_CENTIMETRES)
];
% backtrace last arc movement by moving from 180 degrees to 0 degrees
stream.arcAbsolute( ...
RotationDirection.CW, ...
arcCircleCentreAbs(1), arcCircleCentreAbs(2), ...
arcEndAbs(1), arcEndAbs(2));
// assuming current position is (9cm, 9cm)
Measurement arcCircleCentreRel[] = { // X,Y relative position of arc's centreMeasurement(-4, Units::LENGTH_CENTIMETRES),
Measurement(0, Units::LENGTH_CENTIMETRES),
};
Measurement arcEndRel[] = { // X,Y relative position of arc's endMeasurement(-8, Units::LENGTH_CENTIMETRES),
Measurement(0, Units::LENGTH_CENTIMETRES),
};
// move from 0 degrees to 180 degrees of circle with radius 4
stream.arcRelative(
RotationDirection::CCW,
arcCircleCentreRel[0], arcCircleCentreRel[1],
arcEndRel[0], arcEndRel[1]
);
Measurement arcCircleCentreAbs[] = { // X,Y absolute position of arc's centreMeasurement(5, Units::LENGTH_CENTIMETRES),
Measurement(9, Units::LENGTH_CENTIMETRES),
};
Measurement arcEndAbs[] = { // X,Y absolute position of arc's endMeasurement(9, Units::LENGTH_CENTIMETRES),
Measurement(9, Units::LENGTH_CENTIMETRES),
};
// backtrace last arc movement by moving from 180 degrees to 0 degrees
stream.arcAbsolute(
RotationDirection::CW,
arcCircleCentreAbs[0], arcCircleCentreAbs[1],
arcEndAbs[0], arcEndAbs[1]
);
Target Specific Axes
For all movement methods, you may also specify a subset of axes which the movement should target. This is useful if, for instance, you want to do an arc or circular movement on a stream set up for more than two axes, or if you do not want to specify all the points for a line movement.
To do this, call the on version of the method, specifying the axes to target as the first parameter. Note that these axes are zero-based indices into the streams axes, not axis numbers on the device.
Below is an example of using the on version of the line method.
Python
C#
C++
JavaScript
Java
MATLAB (legacy)
# move only the first axis in the stream
stream.line_absolute_on([0], [Measurement(1, Units.LENGTH_CENTIMETRES)])
// move only the first axis in the streamawait stream.lineAbsoluteOn([0], [{ value: 1, unit: Length.cm }]);
// move only the first axis in the stream
stream.LineAbsoluteOn(
newint[] { 0 },
new Measurement[] { new Measurement { Value = 1, Unit = Units.Length_Centimetres } }
);
// move only the first axis in the stream
stream.lineAbsoluteOn(
newint[] {0},
newMeasurement[] { newMeasurement(1, Units.LENGTH_CENTIMETRES) }
);
% move only the first axis in the stream
axes = [ 0 ];
endpoints = javaArray('zaber.motion.Measurement', 1);
endpoints(1) = Measurement(1, Units.LENGTH_CENTIMETRES);
stream.lineAbsoluteOn(axes, endpoints);
// move only the first axis in the stream
stream.lineAbsoluteOn({ 0 }, { Measurement(1, Units::LENGTH_CENTIMETRES) });
Helix Movement
Helix combines an arc and a line in a single movement.
Two axes perform the arc movement, while any number of other stream axes perform the line movement.
The helix method
helixRelativeOn
is similar to
only with additional arguments specifying the endpoint of the line movement.
The target axes must always be specified.
To perform a complete revolution (circle), specify 0 for the first two arguments of a relative movement.
To perform multiple revolutions, chain multiple helix calls.
Python
C#
C++
JavaScript
Java
MATLAB (legacy)
# assuming current position is (1cm, 2cm, 0cm)
helix_circle_centre = ( # X,Y relative position of helix's centre
Measurement(0, Units.LENGTH_CENTIMETRES),
Measurement(-1, Units.LENGTH_CENTIMETRES)
)
helix_end = ( # X,Y,Z relative position of helix's end
Measurement(-1, Units.LENGTH_CENTIMETRES),
Measurement(-1, Units.LENGTH_CENTIMETRES),
Measurement(3, Units.LENGTH_CENTIMETRES) # line movement
)
# move from 0 degrees to 270 degrees of circle with radius 1
stream.helix_relative_on(
[0, 1, 2], # first three axes of the stream
RotationDirection.CCW,
helix_circle_centre[0],
helix_circle_centre[1],
helix_end[0],
helix_end[1],
helix_end[2]
)
// assuming current position is (1cm, 2cm, 0cm)const helixCircleCentre = [ // X,Y relative position of helix's centre
{ value: 0, unit: Length.cm },
{ value: -1, unit: Length.cm },
];
const helixEnd = [ // X,Y,Z relative position of helix's end
{ value: -1, unit: Length.cm },
{ value: -1, unit: Length.cm },
{ value: 3, unit: Length.cm }, // line movement
];
// move from 0 degrees to 270 degrees of circle with radius 1await stream.helixRelativeOn(
[0, 1, 2], // first three axes of the streamRotationDirection.CCW,
helixCircleCentre[0], helixCircleCentre[1],
helixEnd[0], helixEnd[1], helixEnd[2],
);
// assuming current position is (1cm, 2cm, 0cm)var helixCircleCentre = new Measurement[] // X,Y relative position of helix's centre
{
new Measurement { Value = 0, Unit = Units.Length_Centimetres },
new Measurement { Value = -1, Unit = Units.Length_Centimetres }
};
var helixEnd = new Measurement[] // X,Y,Z relative position of helix's end
{
new Measurement { Value = -1, Unit = Units.Length_Centimetres },
new Measurement { Value = -1, Unit = Units.Length_Centimetres },
new Measurement { Value = 3, Unit = Units.Length_Centimetres } // line movement
};
// move from 0 degrees to 270 degrees of circle with radius 1
stream.HelixRelativeOn(
newint[] { 0, 1, 2 }, // first three axes of the stream
RotationDirection.CCW,
helixCircleCentre[0], helixCircleCentre[1],
helixEnd[0], helixEnd[1], helixEnd[2]
);
// assuming current position is (1cm, 2cm, 0cm)
Measurement[] helixCircleCentre = { // X,Y relative position of helix's centrenewMeasurement(0, Units.LENGTH_CENTIMETRES),
newMeasurement(-1, Units.LENGTH_CENTIMETRES),
};
Measurement[] helixEnd = { // X,Y,Z relative position of helix's endnewMeasurement(-1, Units.LENGTH_CENTIMETRES),
newMeasurement(-1, Units.LENGTH_CENTIMETRES),
newMeasurement(3, Units.LENGTH_CENTIMETRES), // line movement
};
// move from 0 degrees to 270 degrees of circle with radius 1
stream.helixRelativeOn(
newint[] {0, 1, 2}, // first three axes of the stream
RotationDirection.CCW,
helixCircleCentre[0], helixCircleCentre[1],
helixEnd[0], helixEnd[1], helixEnd[2]
);
% assuming current position is (1cm, 2cm, 0cm)
helixCircleCentre = [ % X,Y relative position of helix's centre
Measurement(0, Units.LENGTH_CENTIMETRES)
Measurement(-1, Units.LENGTH_CENTIMETRES)
];
helixEnd = [ % X,Y,Z relative position of helix's end
Measurement(-1, Units.LENGTH_CENTIMETRES)
Measurement(-1, Units.LENGTH_CENTIMETRES)
Measurement(3, Units.LENGTH_CENTIMETRES) % line movement
];
% move from 0 degrees to 270 degrees of circle with radius 1
stream.helixRelativeOn( ...
[0, 1, 2], ... % first three axes of the stream
RotationDirection.CCW, ...
helixCircleCentre(1), helixCircleCentre(2), ...
helixEnd(1), helixEnd(2), helixEnd(3));
// assuming current position is (1cm, 2cm, 0cm)
Measurement helixCircleCentre[] = { // X,Y relative position of helix's centreMeasurement(0, Units::LENGTH_CENTIMETRES),
Measurement(-1, Units::LENGTH_CENTIMETRES),
};
Measurement helixEnd[] = { // X,Y,Z relative position of helix's endMeasurement(-1, Units::LENGTH_CENTIMETRES),
Measurement(-1, Units::LENGTH_CENTIMETRES),
Measurement(3, Units::LENGTH_CENTIMETRES), // line movement
};
// move from 0 degrees to 270 degrees of circle with radius 1
stream.helixRelativeOn(
{ 0, 1, 2 }, // first three axes of the stream
RotationDirection::CCW,
helixCircleCentre[0], helixCircleCentre[1],
helixEnd[0], helixEnd[1], helixEnd[2]
);
Movement Settings
You may set the maximum speed, maximum tangential acceleration, and maximum centripetal acceleration of a stream. Setting these settings is also considered an action by the stream, so it will append the action to the action queue.
The device will always move such that it maintains the conditions of the movement settings. For example, if an action increases the maximum speed the device will accelerate to that maxspeed after executing that action. When an action decreases the maximum speed, the device will slow down to that speed before executing the action.
Note that the units are converted to the device native units using the first axis of the stream.
If the other axes have different conversion factors, the settings will not apply correctly.
See the stream set command documentation in the ASCII Protocol Manual for a detailed description of each setting.
Max Speed
To set the maximum speed use the
setMaxSpeed
method.
The maximum tangential acceleration controls the maximum acceleration and deceleration used for linear segments as well as the actual tangential acceleration and deceleration for curve segments. To set it use the
setMaxTangentialAcceleration
method.
The maximum centripetal acceleration controls the maximum centripetal acceleration and deceleration used for curves (including sharp corners between path segments). To set it use the
setMaxCentripetalAcceleration
method.
Streams can set the digital or analog output ports of a device as a stream action. You can also wait until digital or analog input ports of a device satisfy a certain condition. See the
StreamIo
class for a full list of available functions.
Python
C#
C++
JavaScript
Java
MATLAB (legacy)
# wait for digital input to become high
stream.io.wait_digital_input(1, True)
# wait for digital input to become low
stream.io.wait_digital_input(1, False)
stream.io.set_digital_output(1, DigitalOutputAction.TOGGLE)
stream.io.set_digital_output_schedule(1, DigitalOutputAction.ON, DigitalOutputAction.OFF, 100)
stream.io.set_analog_output(1, 0.42) # in Volts# condition can be one of '!=', '<=', '>=', '==', '>', '<'
stream.io.wait_analog_input(1, '>=', 0.50)
// wait for digital input to become highawait stream.io.waitDigitalInput(1, true);
// wait for digital input to become lowawait stream.io.waitDigitalInput(1, false);
await stream.io.setDigitalOutput(1, DigitalOutputAction.TOGGLE);
await stream.io.setDigitalOutputSchedule(1, DigitalOutputAction.ON, DigitalOutputAction.OFF, 100);
await stream.io.setAnalogOutput(1, 0.42);
// condition can be one of '!=', '<=', '>=', '==', '>', '<'await stream.io.waitAnalogInput(1, '>=', 0.50); // in Volts
// wait for digital input to become high
stream.Io.WaitDigitalInput(1, true);
// wait for digital input to become low
stream.Io.WaitDigitalInput(1, false);
stream.Io.SetDigitalOutput(1, DigitalOutputAction.Toggle);
stream.Io.SetDigitalOutputSchedule(1, DigitalOutputAction.On, DigitalOutputAction.Off, 100);
stream.Io.SetAnalogOutput(1, 0.42);
// condition can be one of '!=', '<=', '>=', '==', '>', '<'
stream.Io.WaitAnalogInput(1, ">=", 0.50); // in Volts
// wait for digital input to become high
stream.getIo().waitDigitalInput(1, true);
// wait for digital input to become low
stream.getIo().waitDigitalInput(1, false);
stream.getIo().setDigitalOutput(1, DigitalOutputAction.TOGGLE);
stream.getIo().setDigitalOutputSchedule(1, DigitalOutputAction.ON, DigitalOutputAction.OFF, 100);
stream.getIo().setAnalogOutput(1, 0.42);
// condition can be one of '!=', '<=', '>=', '==', '>', '<'
stream.getIo().waitAnalogInput(1, ">=", 0.50); // in Volts
% wait for digital input to become high
stream.getIo().waitDigitalInput(1, true);
% wait for digital input to become low
stream.getIo().waitDigitalInput(1, false);
stream.getIo().setDigitalOutput(1, DigitalOutputAction.TOGGLE);
stream.getIo().setDigitalOutputSchedule(1, DigitalOutputAction.ON, DigitalOutputAction.OFF, 100);
stream.getIo().setAnalogOutput(1, 0.42);
% condition can be one of '!=', '<=', '>=', '==', '>', '<'
stream.getIo().waitAnalogInput(1, ">=", 0.50); % in Volts
// wait for digital input to become high
stream.getIo().waitDigitalInput(1, true);
// wait for digital input to become low
stream.getIo().waitDigitalInput(1, false);
stream.getIo().setDigitalOutput(1, DigitalOutputAction::TOGGLE);
stream.getIo().setDigitalOutputSchedule(1, DigitalOutputAction::ON, DigitalOutputAction::OFF, 100);
stream.getIo().setAnalogOutput(1, 0.42);
// condition can be one of '!=', '<=', '>=', '==', '>', '<'
stream.getIo().waitAnalogInput(1, ">=", 0.50); // in Volts
Corking
Corking a stream blocks the execution of commands in a Live stream's action queue. Execution resumes when the stream is uncorked, or when the number of queued actions reaches its limit.
Corking is useful when you are doing a large number of fast actions, such as moving in small incremements or interacting with I/O multiple times. If actions are executed faster than they are received, the action queue will empty and may cause discontinuities in motion. This may also cause irregularities in timing of actions such as setting I/O, or waiting for small intervals.
Corking reduces the likelihood of running out of actions by first populating the action queue on the device, and then telling the device to execute it.
Python
C#
C++
JavaScript
Java
MATLAB (legacy)
# block action execution
stream.cork()
# ... add some actions to queue# unblock action execution
stream.uncork()
// block action executionawait stream.cork();
// ... add some actions to queue// unblock action executionawait stream.uncork();
// block action execution
stream.Cork();
// ... add some actions to queue// unblock action execution
stream.Uncork();
// block action execution
stream.cork();
// ... add some actions to queue// unblock action execution
stream.uncork();
% block action execution
stream.cork();
% ... add some actions to queue% unblock action execution
stream.uncork();
// block action execution
stream.cork();
// ... add some actions to queue// unblock action execution
stream.uncork();
Note that the stream must be idle when issuing the cork command.
See the stream fifo command documentation in the ASCII Protocol Manual for more details.
Stream Information
You can get information about a stream, such as textual representation, axes, and movement settings.
See the following example:
A stream is busy if it is currently executing actions in its action queue.
You can check if a Live stream is busy through the
isBusy
method.
The
waitUntilIdle
method waits until the stream becomes idle.
Here is an example of their usage:
Python
C#
C++
JavaScript
Java
MATLAB (legacy)
if stream.is_busy():
print("Stream is busy.")
stream.wait_until_idle()
if (await stream.isBusy()) {
console.log('Stream is busy.');
}
await stream.waitUntilIdle();
if (stream.IsBusy())
{
Console.WriteLine("Stream is busy.");
}
stream.WaitUntilIdle();
if (stream.isBusy()) {
System.out.println("Stream is busy.");
}
stream.waitUntilIdle();
if stream.isBusy()
fprintf("Stream is busy.")
end
stream.waitUntilIdle();
if (stream.isBusy()) {
std::cout << "Stream is busy." << std::endl;
}
stream.waitUntilIdle();
Disabling
To disable a stream that has been setup call the
disable
method.
Python
C#
C++
JavaScript
Java
MATLAB (legacy)
stream.disable()
await stream.disable();
stream.Disable();
stream.disable();
stream.disable();
stream.disable();
Discontinuities
If you exceed communication bandwidth with your device or your program does not generate movements fast enough,
you may encounter a movement discontinuity. When the device runs out of movements in the stream queue,
it slows down, possibly stopping completely. Once the device receives more movements, it starts moving again,
resulting in a jerking movement.
If your application requires smooth, precise movement, jerking can be an issue.
In such a case, we recommend enabling treating discontinuities as errors. When enabled,
the stream throws a PvtDiscontinuityException exception every time it encounters a discontinuity.
Once you detect the discontinuity, you can optimize the motion or lower your speed to prevent it in future runs.
Python
C#
C++
JavaScript
Java
MATLAB (legacy)
stream.treat_discontinuities_as_error()
stream.treatDiscontinuitiesAsError();
stream.TreatDiscontinuitiesAsError();
stream.treatDiscontinuitiesAsError();
stream.treatDiscontinuitiesAsError();
stream.treatDiscontinuitiesAsError();
If you interrupt your streaming intentionally, call
before starting a new movement to avoid the exception.
Lockstep
On devices with firmware version 7 and higher, the streams can also include axes in lockstep groups.
Use
setupLiveComposite
method to setup such streams.
In the following example the stream is set up on a lockstep group with number 1 and a single physical axis with number 3.