This jupyter notebook shows how to work with 8SMC5 Standa Motor Controllers over the libximc python binding.
By default this notebook uses virtual controller. So you don’t need any real hardware. But if you have one, you can also test it with this notebook.
In case you are not familiar with Jupyter Notebooks, check official documentation.
!pip install libximc
It’s good practice to use a virtual environment - see this guide for details on using virtual environments.
If you have faced any issues while installing the library and you have no idea how to solve them, contact our technical support via:
import pathlib
import os
import time
import libximc.highlevel as ximc
Virtual device is just a binary file with a number of settings. In case there is no such file, it will be created automatically with default settings in current notebook working directory during the first device access.
In case you are going to work with a real device, you can skip this step.
virtual_device_filename = "virtual_motor_controller_1.bin"
virtual_device_file_path = os.path.join(pathlib.Path().cwd(), virtual_device_filename)
Now we will look for real 8SMC5 devices on your PC.
# Devices search
devices = ximc.enumerate_devices(ximc.EnumerateFlags.ENUMERATE_NETWORK | ximc.EnumerateFlags.ENUMERATE_PROBE)
if len(devices) == 0:
print("The real devices were not found. A virtual device will be used.")
else:
# Print real devices list
print("Found {} real device(s):".format(len(devices)))
for device in devices:
print(" {}".format(device))
The real devices were not found. A virtual device will be used.
Each motor controller in the system should have a unique URI. Use such URI to connect to the specific device.
There are several URI types:
xi-emu:///<abs_path_to_file>
- for virtual devices.xi-com:\\.\COM<N>
- for local devices connected via USB.xi-tcp://<IP>:<Port>
- for network devices with Ethernet support.xi-net://<IP>/serial
- for network devices connected through Standa 8Eth1 converter.To use real device, change URI in the cell below. In case you don’t have any real devices, execute the cell without URI modification.
device_uri = "xi-emu:///{}".format(virtual_device_file_path)
# device_uri = r"xi-com:\\.\COM29"
# device_uri = "xi-tcp://172.16.131.140:1820"
# device_uri = "xi-net://192.168.1.120/abcd"
In the beginning you should create Axis
instance using desired URI. As a result you will get an object (in our example it's called axis
) through which you will be able to interact with the device.
The connection is closed automatically when the garbage collector destructs Axis
object. But of course you can close the connection manually via axis.close_device()
.
axis = ximc.Axis(device_uri)
# To open the connection, you must manually call `open_device()` method
axis.open_device()
In case you received an error message at this step, check that the device:
Try reconnecting the device physically or try making a hardware reboot.
Now let’s perform a simple shift.
# get_position method returns position_t object
position = axis.get_position()
print("Initial position:", position.Position)
print("Start moving")
axis.command_right()
for i in range(3):
time.sleep(1)
print("Moving...")
print("Stop moving")
axis.command_stop()
position = axis.get_position()
print("Final position:", position.Position)
Initial position: 3024 Start moving Moving... Moving... Moving... Stop moving Final position: 6043
Don’t forget you may manually disconnect the device.
axis.close_device()
print("Device disconnected")
Device disconnected
You can use command_move()
and command_movr()
methods to move axis to a specific absolute or relative position respectively.
The following sequence of absolute and relative movements will be performed in the cell below.
axis.open_device()
# ==== Set current position as zero ====
axis.command_zero()
# Object instances should be passed by reference
position = axis.get_position()
print("Initial position:", position.Position)
# ==== Move to the first absolute position (X = 100) ====
next_position = 100
print("Move to position:", next_position)
axis.command_move(next_position, 0)
print("Moving...")
axis.command_wait_for_stop(100)
position = axis.get_position()
print("Current position:", position.Position)
# ==== Move to the second absolute position (X = 50) ====
next_position = 50
print("Move to position:", next_position)
axis.command_move(next_position, 0)
print("Moving...")
axis.command_wait_for_stop(100)
position = axis.get_position()
print("Current position:", position.Position)
# ==== Perform a relative shift by 100 ====
relative_shift = 100
print("Perform a relative shift by", relative_shift)
print("So we are going to", position.Position, "+", relative_shift, " =", position.Position + relative_shift)
axis.command_movr(relative_shift, 0)
print("Moving...")
axis.command_wait_for_stop(100)
position = axis.get_position()
print("Current position:", position.Position)
# ==== Perform a relative shift by -150 ====
relative_shift = -150
print("Perform a relative shift by", relative_shift)
print("So we are going to", position.Position, "+ (", relative_shift, ") =", position.Position + relative_shift)
axis.command_movr(relative_shift, 0)
print("Moving...")
axis.command_wait_for_stop(100)
position = axis.get_position()
print("Current position:", position.Position)
axis.close_device()
print("Done")
Initial position: 0 Move to position: 100 Moving... Current position: 100 Move to position: 50 Moving... Current position: 50 Perform a relative shift by 100 So we are going to 50 + 100 = 150 Moving... Current position: 150 Perform a relative shift by -150 So we are going to 150 + ( -150 ) = 0 Moving... Current position: 0 Done
By default all values (position, speed, acceleration…) are represented in motor steps and microsteps. You can use more convenient custom units such as millimeters, inches, degrees, radians by calling _calb
postfixed methods.
To use user units you should know conversion coefficient in units per step. In most cases you can find such coefficient in specification for your stage.
For example 8MT193-100 Motorized Linear Stage has 2.5 µm / step resolution. In case you would like to use millimeters as user units, the conversion coefficient will be 0.0025 mm / step.
axis.open_device()
# ==== User unit setup ====
# We will use mm as user units
# In our example conversion coefficient will be 0.0025 mm / step.
# Set conversion coefficient for your stage here if needed
step_to_mm_conversion_coeff = 0.0025 # mm / step
# Get information about microstep mode
engine_settings = axis.get_engine_settings()
# Now we can set calibration settings for our axis
axis.set_calb(step_to_mm_conversion_coeff, engine_settings.MicrostepMode)
# ==== Perform a shift by using user units (mm in our case) ====
position_calb = axis.get_position_calb()
print("Current position:", position_calb.Position, "mm")
next_position_in_mm = 5.21
print("Move to position:", next_position_in_mm, "mm")
axis.command_move_calb(next_position_in_mm)
print("Moving...")
axis.command_wait_for_stop(100)
position_calb = axis.get_position_calb()
print("Current position:", position_calb.Position, "mm")
next_position_in_mm = 0
print("Move to position:", next_position_in_mm, "mm")
axis.command_move_calb(next_position_in_mm)
print("Moving...")
axis.command_wait_for_stop(100)
position_calb = axis.get_position_calb()
print("Current position:", position_calb.Position, "mm")
axis.close_device()
print("Done")
Current position: 0.0 mm Move to position: 5.21 mm Moving... Current position: 5.210000038146973 mm Move to position: 0 mm Moving... Current position: 0.0 mm Done
Physical axis motion takes some time. In previous examples we have used command_wait_for_stop()
to suspend the program execution until the end of the movement.
You can manually check current device status and perform some other operations in parallel with the axis motion in your program.
Now let’s record and plot the motion profile.
# Install matplotlib library(https://matplotlib.org/) to create plots
!pip install matplotlib
import matplotlib.pyplot as plt
axis.open_device()
# Arrays for logging positions and timestamps during motion
positions = []
timestamps_s = []
axis.command_zero()
position = axis.get_position()
print("Current position:", position.Position)
# ==== Shift the stage ====
next_position = 4000
print("Move to position:", next_position)
axis.command_move(next_position, 0)
print("Moving...")
t_start_s = time.time()
# Check axis motion status in a loop
status = axis.get_status()
while status.MvCmdSts & ximc.MvcmdStatus.MVCMD_RUNNING:
position = axis.get_position()
# Do some job during the motion
positions.append(position.Position)
timestamps_s.append(time.time() - t_start_s)
time.sleep(0.1)
# Update status for the next check of the loop condition
status = axis.get_status()
position = axis.get_position()
print("Current position:", position.Position)
# ==== Shift the stage back ====
next_position = 0
print("Move to position:", next_position)
axis.command_move(next_position, 0)
print("Moving...")
# Check axis motion status in a loop
status = axis.get_status()
while status.MvCmdSts & ximc.MvcmdStatus.MVCMD_RUNNING:
position = axis.get_position()
# Do some job during the motion
positions.append(position.Position)
timestamps_s.append(time.time() - t_start_s)
time.sleep(0.1)
# Update status for the next check of the loop condition
status = axis.get_status()
print("Movement finished")
position = axis.get_position()
print("Current position:", position.Position)
axis.close_device()
# ==== Plot motion profile ====
plt.plot(timestamps_s, positions)
plt.xlabel("Time, s")
plt.ylabel("Position, steps")
plt.show()
Current position: 0 Move to position: 4000 Moving... Current position: 4000 Move to position: 0 Moving... Movement finished Current position: 0
You can change motion settings (for example speed) at any time. Even while the axis is moving.
axis.open_device()
positions = []
timestamps_s = []
# ==== Shift the stage ====
next_position = 4000
print("Move to position:", next_position)
axis.command_move(next_position, 0)
print("Moving...")
t_start_s = time.time()
# We will move for 2 s with current speed. Then we will increase the speed.
while (time.time() < t_start_s + 2):
position = axis.get_position()
# Do some job during the motion
positions.append(position.Position)
timestamps_s.append(time.time() - t_start_s)
time.sleep(0.1)
# Update status for the next check of the loop condition
statux = axis.get_status()
# Increase the speed on the fly
move_settings = axis.get_move_settings()
move_settings.Speed *= 4
move_settings.Accel *= 4
move_settings.Decel *= 4
axis.set_move_settings(move_settings)
print("Speed increased 4 times")
# Wait until the end of the motion
status = axis.get_status()
while status.MvCmdSts & ximc.MvcmdStatus.MVCMD_RUNNING:
position = axis.get_position()
# Do some job during the motion
positions.append(position.Position)
timestamps_s.append(time.time() - t_start_s)
time.sleep(0.1)
# Update status for the next check of the loop condition
status = axis.get_status()
# ==== Shift the stage back ====
# Decrease the speed
move_settings = axis.get_move_settings()
move_settings.Speed //= 8
axis.set_move_settings(move_settings)
print("Speed decreased 8 times")
next_position = 0
print("Move to position:", next_position)
axis.command_move(next_position, 0)
print("Moving...")
# Check axis motion status in a loop
status = axis.get_status()
while status.MvCmdSts & ximc.MvcmdStatus.MVCMD_RUNNING:
position = axis.get_position()
# Do some job during the motion
positions.append(position.Position)
timestamps_s.append(time.time() - t_start_s)
time.sleep(0.1)
# Update status for the next check of the loop condition
status = axis.get_status()
print("Movement finished")
print("Set initial speed")
move_settings = axis.get_move_settings()
move_settings.Speed *= 2
move_settings.Accel //= 4
move_settings.Decel //= 4
axis.set_move_settings(move_settings)
axis.close_device()
# ==== Plot motion profile ====
plt.plot(timestamps_s, positions)
plt.axvline(2, linestyle="dotted")
plt.xlabel("Time, s")
plt.ylabel("Position, steps")
plt.show()
Move to position: 4000 Moving... Speed increased 4 times Speed decreased 8 times Move to position: 0 Moving... Movement finished Set initial speed
More information about the libximc library you can find on this page: https://libximc.xisupport.com/doc-en/index.html
Standa 8SMC5-USB motor controller user manual: https://doc.xisupport.com/en/8smc5-usb/
Standa website: https://www.standa.lt/