A package to control the Mecademic robots through python
Project description
Mecademic Python API
A python module designed for robot products from Mecademic. The module offers tools that give access to the features of the Mecademic Robots such as MoveLin and MoveJoints available through the TCP/IP text interface. The module can be started from a terminal or a python application and controls the Mecademic products.
Supported Robots
- Meca500, Mcs500
Supported Firmware Versions
- 8.3 and up
Supported Python versions
Mecademicpy 2.2 targets Python version 3.7 and above. It has been tested with the following Python versions:
- Python 3.7.17
- Python 3.8.18
- Python 3.9.18
- Python 3.10.13
- Python 3.11.8
- Python 3.12.2
Prerequisites
Please read the user programming manual to understand concepts necessary for proper usage of the API. This API implements a subset of the commands in the Communicating over TCP/IP
section. For the exact list of available commands, use the help()
command as explained in API Reference.
To be able to use the module without unexpected errors, the user must have a copy of python installed on their machine and it is required to use python version 3.7 or higher. We recommend using Python 3.9 since this is the version on which this module is actively tested. Python can be installed from its main website (a reboot will be require after the installation to complete the setup).
The user can validate their python installation by running python --version
in a terminal.
This library is compatible with Windows, Linux, and Mac.
Downloading the package
To download and install the package, the user can easily do so through pip. Pip will download and install the package on your machine and place it in the python local packages directory. This is done by running the following command:
pip install mecademicpy
Quick Start
Ensure the robot is properly connected to the computer, powered on, and in a nominal state.
In a python shell or script, import the library. Then initialize an instance of the Robot
class. Finally, use the Connect
function by passing the IP Address of the robot as an argument to establish a connection:
import mecademicpy.robot as mdr
robot = mdr.Robot()
robot.Connect(address='192.168.0.100')
The Connect
function will raise if connection with robot fails.
This function is synchronous (awaits for success or timeout) even when using the Robot
class in asynchronous mode.
Before using the robot, it must be activated and homed. To do so, run the following functions:
robot.ActivateRobot()
robot.Home()
The robot should move slightly to perform its homing routine. We can also use robot.WaitHomed()
or synchronous mode to block execution until homing is done.
Once homing is complete, the robot is now ready to perform operations. The user programming manual or the documentation in the module is sufficient to be able to make the Robot perform actions and control the robot.
Here is an example of a simple motion to perform:
robot.MoveJoints(0, 0, 0, 0, 0, 0)
robot.MoveJoints(0, -60, 60, 0, 0, 0)
When done with the robot, the user should always deactivate and disconnect. Note that deactivating before the motion is complete will cause the motion to immediately stop. The user can wait for motions to complete using WaitIdle()
.
Deactivating and disconnecting can be done with the following commands:
robot.WaitIdle()
robot.DeactivateRobot()
robot.Disconnect()
It is recommended to use GetStatusRobot()
to learn about the current robot status before resuming operation.
For complete and working examples, please refer to the examples
folder.
Features and Additional Information
Synchronous vs. Asynchronous Mode
By default the API operates in 'asynchronous mode', which means sending a command to the robot does not block program execution. To illustrate, the following code will be able to successfully print out the changing joint values resulting from the MoveJoints
command:
import mecademicpy.robot as mdr
import time
robot = mdr.Robot()
robot.Connect(address='192.168.0.100', enable_synchronous_mode=False)
robot.ActivateAndHome()
robot.WaitHomed()
robot.MoveJoints(0, 0, 0, 0, 0, 0)
robot.MoveJoints(0, -60, 60, 0, 0, 0)
# Print robot position while it's moving
for _ in range(100):
print(robot.GetJoints())
time.sleep(0.05)
robot.WaitIdle()
robot.DeactivateRobot()
robot.WaitDeactivated()
robot.Disconnect()
However, sometimes it is desired for programs to wait until the previous command is completed before sending the next command. It is generally encouraged to use the checkpoints system or the various Wait()
functions, but for smaller or simpler programs, the user can set enable_synchronous_mode=True
to have each command block until the robot has completed the command.
The code block below will only print out the final joint position, since robot.GetJoints()
doesn't execute until the motion is complete.
import mecademicpy.robot as mdr
robot = mdr.Robot()
robot.Connect(address='192.168.0.100', enable_synchronous_mode=True)
robot.ActivateAndHome()
robot.MoveJoints(0, 0, 0, 0, 0, 0)
robot.MoveJoints(0, -60, 60, 0, 0, 0)
# The returned robot position will be (0, -60, 60, 0, 0, 0), because this line will only be executed once MoveJoints(0, -60, 60, 0, 0, 0) has completed.
print(robot.GetJoints())
robot.DeactivateRobot()
robot.Disconnect()
One disadvantage of using synchronous mode is that blending between motions is not possible, since the next motion is not sent to the robot until the previous motion is complete.
Disconnect on Exception
By default, if any unrecoverable error occurs during usage of the Robot class, the class will automatically disconnect from the robot to avoid possible issues. Disconnection also causes the robot to pause its motion.
However, disconnecting on exceptions may be undesired when using an interactive terminal or Jupyter notebook, as an accidental mal-formed function call may cause disconnection. As such, this feature can be disabled by setting disconnect_on_exception=False
when attempting the connection:
robot.Connect(address='192.168.0.100', disconnect_on_exception=False)
Checkpoints
The checkpoint system allows for creating event objects which will be triggered once the robot reaches a specified point in its execution. The SetCheckpoint(n)
call registers a checkpoint with the robot (with n
as the ID), and returns an event-type object that can be used to wait for the checkpoint. This is true for both robot connection type (asynchronous and synchronous mode). For example, the following code will wait until both MoveJoints()
motions have completed, and then print "The MoveJoints() motions are complete.
":
robot.MoveJoints(0, -60, 60, 0, 0, 0)
robot.MoveJoints(0, 0, 0, 0, 0, 0)
checkpoint_1 = robot.SetCheckpoint(1)
checkpoint_1.wait(timeout=10) # A timeout of 10s is set to avoid infinite wait in case of error.
print('The MoveJoints() motions are complete.')
Note that creating multiple checkpoints with the same ID is possible but not recommended. The checkpoints will be triggered in the order they are set.
Checkpoints may also be set in an offline program, saved to robot memory. Use ExpectExternalCheckpoint(n)
to receive these checkpoints while the robot is running the offline program. The call to ExpectExternalCheckpoint(n)
should be made before the offline program is started, or otherwise must be guaranteed to occur before the robot can possibly send the checkpoint. For example, the following code will start an offline program and expect to receive a checkpoint from the program, and then print "Received expected external checkpoint
":
checkpoint_event = robot.ExpectExternalCheckpoint(5)
robot.StartOfflineProgram(1)
checkpoint_event.wait(timeout=30)
print("Received expected checkpoint from running program")
where offline program 1 is
MoveJoints(100,0,0,0,0,0)
MoveJoints(-100,0,0,0,0,0)
SetCheckpoint(5)
MoveJoints(0,-60,60,0,0,0)
MoveJoints(0,0,0,0,0,0)
SetOfflineProgramLoop(1)
If the robot motion command queue is cleared (using ClearMotion()
for example), or the robot is disconnected, all pending checkpoints will be aborted, and all active wait()
calls will raise an InterruptException
.
Callbacks
The Robot
class supports user-provided callback functions on a variety of events. These callbacks are entirely optional and are not required, but useful to implement asynchronous applications. The available events are listed in the RobotCallbacks
class. Here are some of these callbacks:
on_connected
on_disconnected
on_activated
on_deactivated
on_homed
on_error
on_checkpoint_reached
- etc... (refer to class
RobotCallbacks
for exhaustive list of callbacks)
Note that some callbacks pass arguments. For example on_checkpoint_reached
passes the ID of the checkpoint, on_command_message
and on_monitor_message
passes a mecademicpy.robot.Message
object. Refer to class documentation for details.
A simple usage example:
import mecademicpy.robot as mdr
robot = mdr.Robot()
def print_connected():
print('Connected!')
callbacks = mdr.RobotCallbacks()
callbacks.on_connected = print_connected
robot.RegisterCallbacks(callbacks=callbacks, run_callbacks_in_separate_thread=True)
robot.Connect(address='192.168.0.100') # Will print 'Connected!' if successful.
If the user does not want to automatically run callbacks in a separate thread, set run_callbacks_in_separate_thread=False
and call RunCallbacks()
when ready to run all triggered callbacks.
Running any callback in a separate thread (either through the Robot
class or otherwise) requires that the callback function is thread-safe and uses the proper locks when accessing shared state. Calling any public method of the Robot
class is thread-safe.
Note that, due to a Python limitation, all Python threads share the same CPU core and will not take advantage of parallelism and multiple CPU cores of a PC. Unfortunately, this means that an application performing heavy computations (in callback thread or in any other thread) may impact the performance of the Robot
class (especially when processing many monitoring messages at high frequency).
If non-trivial computation and high-frequency monitoring are both necessary, the user may offload computation into a separate python process using the built-in multiprocessing library.
Handling Robot Errors
If the robot encounters an error during use, the robot will go into error mode. In this mode, the module will refuse any command to the robot unless the error is reset. If the robot is in an error state, GetStatusRobot().error_status
will return True
. To properly reset errors on the robot, the following function must be run:
robot.ResetError()
It is recommended to use GetStatusRobot()
to learn about the current robot status and reset the relevant flags to an appropriate state before resuming operation. For example, an error may require to call ResumeMotion()
. In this case, verify that GetStatusRobot().pause_motion_status
is set to True
before calling ResumeMotion()
.
The on_error
callback can also be used to manage robot errors.
Improper use of the class can also cause exceptions to be raised. For example, calling MoveJoints()
without any arguments will raise an exception.
If the user is waiting on an event or checkpoint that the Robot
class later determines will never occur, the event will unblock and raise an exception. For example, if the user is waiting on a checkpoint (WaitCheckpoint
), but calls Disconnect()
or ClearMotion()
before the checkpoint is received, the checkpoint will unblock and raise an exception. Events and checkpoints will also unblock with exception on robot error state.
The user should use python's built-in try...except
blocks to handle appropriate exceptions.
Handling Robot safety stop conditions
(Note: This section applies to robots running firmware 10.1 and above)
The robot may encounter safety stop conditions based on external safety signals (EStop, PStop1, PStop2, operation mode changed, enabling device released) or other conditions (connection timeout).
It is recommended to use GetSafetyStatus
to learn about the current safety stop signals status.
The on_safety_stop
, on_safety_stop_reset
, on_safety_stop_resettable
and on_safety_stop_state_change
callbacks can also be used to manage the safety stop signals status.
Some safety stop signals cause motor voltage to be removed (EStop, PStop1, operation mode change, etc.). When these safety signals are present, the robot cannot be activated until the safety signals are reset. For safety reasons, resetting these signals cannot be done programmatically from the Mecademicpy API. They require to press the Reset button on the power supply (or trigger the reset function from dedicated power supply input pins).
Other safety stop signals will cause the robot to pause motion (PStop2, enabling device released, connection dropped).
Once these safety conditions are resolved, the robot may be activated or, if already activated, motion can be resumed using ResumeMotion()
.
For more information about safety signals, refer to the robot's programming manual.
Preserved State on Disconnection
Once the robot is disconnected, not all states are immediately cleared. Therefore, it is possible to still get the last-known state of the robot.
Restore a well-known robot configuration
File robot_initializer.py contains utilities that can be used to configure the robot to a well-known state. It contains useful methods that can be called when reconnecting to a robot (which state is unknown) or activating a robot in order to restore a well-known state. See documentation in file robot_initializer.py for more information.
RobotWithTools
: This is a specialization of the Robot
class that adds utilities required for using methods in robot_initializer.py.
An application that wants to use the methods from robot_initializer.py shall instantiate RobotWithTools
instead of Robot
.
reset_robot_configuration
: This method restores default robot's static (permanent) configuration parameters, including joint limits, work zone limits, PStop2 configuration etc.
reset_motion_queue
: This method configures the robot's motion queue with default values (or user-defined values).
It receives, as argument, an instance of class MotionQueueParams
which lists all available motion queue parameters with their default values.
This method is is useful every time the robot is reactivated (remember that the robot resets its motion queue to default parameters every time it's activated).
reset_vacuum_module
: This method is used to reset the vacuum module states (digital output states, vacuum parameters and states) to default values.
It can be used at any time since the vacuum module is functional even when the robot is not activated. Vacuum parameters are thus managed separately from other motion queue parameters.
Example
# (use "with" block to ensure proper disconnection at end of block)
with initializer.RobotWithTools() as robot:
robot.Connect(address='192.168.0.100')
# Reset the robot's static (permanent) configuration (joint limits, work zone, etc.)
initializer.reset_robot_configuration(robot)
# Reset the vacuum module states (clear digital outputs, vacuum off)
initializer.reset_vacuum_module(robot)
# Activate robot and initialize its motion queue with desired parameters
mq_params = initializer.MotionQueueParams()
# Customize some motion queue parameters
mq_params.joint_vel = 20
mq_params.trf = [10,20,0,0,45,0]
initializer.reset_motion_queue(robot, params=mq_params, activate_home=True)
# Wait until robot is activated and homed
robot.WaitHomed()
Logging Data to File
It is possible to continuously log the robot state to a file using the API either using the StartLogging
and EndLogging
functions or using the FileLogger
context.
An example usage of StartLogging
and EngLogging
:
robot.WaitIdle()
robot.StartLogging(0.001)
try:
robot.MoveJoints(0, -60, 60, 0, 0, 0)
robot.MoveJoints(0, 0, 0, 0, 0, 0)
robot.WaitIdle()
except BaseException as e:
print(f'Logging unsuccessful, exception encountered: {e}')
finally:
robot.EndLogging()
Note that the user should wait for the robot to be idle (WaitIdle) before starting to log, and also wait for idle before ending the log. This is to ensure the log correctly captures the movements of interest.
It should also be noted that it is mandatory to give a monitoring interval, in seconds, to StartLogging
, to specify at which rate data should be logged. In the example above, the monitoring interval is set at 0.001 seconds, or 1 ms. It is the minimum monitoring interval that can be set using SetMonitoringInterval
, which is the robot command used by StartLogging
to choose the monitoring interval.
The user can also use the FileLogger
context:
robot.WaitIdle()
with robot.FileLogger(0.001):
robot.MoveJoints(0, -60, 60, 0, 0, 0)
robot.MoveJoints(0, 0, 0, 0, 0, 0)
robot.WaitIdle()
The FileLogger
context will automatically end logging after either completing the with
block or encountering an exception.
The user can select which fields to log using the fields
parameter in StartLogging
or FileLogger
. By default, all available fields are logged. The available fields are currently:
-
"TargetJointPos"
-
"TargetCartPos"
-
"TargetJointVel"
-
"TargetCartVel"
-
"TargetConf"
-
"TargetConfTurn"
-
"JointPos"
-
"CartPos"
-
"JointVel"
-
"JointTorq"
-
"CartVel"
-
"Conf"
-
"ConfTurn"
-
"Accel"
-
"Wrf"
-
"Trf"
-
"Checkpoint"
-
"ExtToolStatus"
-
"ValveState"
-
"GripperState"
-
"GripperForce"
-
"GripperPos"
-
"IoModuleStatus"
-
"IoModuleOutputState"
-
"IoModuleInputState"
-
"SigGenStatus"
-
"SigGenOutputState"
-
"SigGenInputState"
-
"VacuumState"
-
"VacuumPressure"
These strings should be placed into the list given to the fields
parameter.
The following example only logs the "TargetJointPos"
and "JointPos"
.
robot.WaitIdle()
with robot.FileLogger(0.001, fields=['TargetJointPos', 'JointPos']):
robot.MoveJoints(0, -60, 60, 0, 0, 0)
robot.MoveJoints(0, 0, 0, 0, 0, 0)
robot.WaitIdle()
Note that the SetRealTimeMonitoring
command is used by in StartLogging
or FileLogger
to enable all the real-time monitoring events which are logged.
Sending Custom Commands
It is possible to send an arbitrary command to the robot using the SendCustomCommand()
call. The user can optionally provide expected response codes, which will cause SendCustomCommand()
to return an event which can be used to wait for the response.
Example usage:
import mecademicpy.robot as mdr
import mecademicpy.mx_robot_def as mdr_def
# Connect, activate, and home robot...
response_codes = [mdr_def.MX_ST_ERROR_RESET, mdr_def.MX_ST_NO_ERROR_RESET]
response = robot.SendCustomCommand('ResetError', expected_responses=response_codes, timeout=10)
Although raw numerical response codes can also be used, it is recommended to use the named aliases provided in mx_robot_def.py
for clarity.
Waiting for specific robot message
The robot API method GetInterruptableEvent()
creates a "waitable event". This object contains a "wait" function that will block the Python application execution until the robot sends the expected message (or until timeout).
This event object can optionally get interrupted (throw InterruptException) in case the robot encounters an error or in case motion gets cleared.
Note that the robot API also contains other "wait" methods for various common conditions, like WaitActivated, WaitMotionPaused, WaitIdle, WaitHoldingPart, etc.
Method GetInterruptableEvent()
is generally used for specific cases that are not covered by other "Wait" methods.
Example 1:
# Create interruptable event that will wait until robot sends code MX_ST_RT_INPUT_STATE (which will happen if a digital input state changes)
input_state_changed_event = robot.GetInterruptableEvent([mdr.MxRobotStatusCode.MX_ST_RT_INPUT_STATE])
# (there can be code here that move the robot or whatever is expected to trigger digital input change)
# (...)
# Wait (block) until the event is received or until timeout (TimeoutException).
input_state_changed_event.wait(10)
Example 2:
# Create interruptable event that will trigger when torque limit is exceeded, i.e. event id MX_ST_TORQUE_LIMIT_STATUS with data 1.
# Here we want this event to be interrupted (throw InterruptException) if the robot is in error or motion cleared.
torque_exceeded_event = robot.GetInterruptableEvent(
codes=[mdr.Message(mdr.MxRobotStatusCode.MX_ST_TORQUE_LIMIT_STATUS, '1')],
abort_on_error=True,
abort_on_clear_motion=True)
# (there can be code here that move the robot or do other things, for example)
# (...)
# Check if the torque limit exceeded status event was received
if torque_exceeded_event.is_set():
# Torque limit was exceeded between call to GetInterruptableEvent above and now
pass
# Wait (block) until the torque limit exceeded event is received or until timeout (TimeoutException)
torque_exceeded_event.wait(10)
API Reference
For a complete list of available methods and further documentation, use the help() function on any class in a python terminal (such as ipython
).
>>> import mecademicpy.robot as mdr
>>> help(mdr.Robot)
>>> help(mdr.Message)
>>> help(mdr.RobotCallbacks)
Getting Help
To get support, you can start an issue on the Mecademic github page, issues section or send an email to support@mecademic.com.
License
All packages in this repository are licensed under the MIT license.
Authors
- Mecademic - Continuous work
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
File details
Details for the file mecademicpy-2.2.0.tar.gz
.
File metadata
- Download URL: mecademicpy-2.2.0.tar.gz
- Upload date:
- Size: 157.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/5.1.0 CPython/3.12.2
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 5418681a4ea2c5dd42ad0cfdb6ccd3b3bdfdd85e6a05b23aa0b2083dd2c38fc8 |
|
MD5 | a207a01046cc5945d024245eeaf00b12 |
|
BLAKE2b-256 | a3feebabf29bb0a4dc6adf87bcd3cae75e3b5eee3beac8cae198088573677d6d |
File details
Details for the file mecademicpy-2.2.0-py3-none-any.whl
.
File metadata
- Download URL: mecademicpy-2.2.0-py3-none-any.whl
- Upload date:
- Size: 158.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/5.1.0 CPython/3.12.2
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 3c6f123567cadc7f9130b71adda66eff4caaf47b3da7e83a4516abddf22b5f84 |
|
MD5 | 88e4111404044c9d0b002b3fb9a8da53 |
|
BLAKE2b-256 | 378757225c1ed21a5632b4ef2b2bfac4ce4edbce40f876ab1f53e4829168c963 |