Welcome to another Myo Unleashed, the series where we get you set up with one of the cool tools or bindings made by our awesome developer community. This week we have Niklas, who wrote the Myo Python bindings. Take it away, Niklas!
Hey! I'm Niklas Rosenstein, a student at the Technical University of Munich and I make Cinema 4D plugins for a living. I want to show you how you can use the Myo Python bindings! But first, make sure you have the latest Myo SDK downloaded and unarchived.
The bindings are based on ctypes which is a built-in Python module to interface with shared libraries and the Myo SDK provides such a library. They're compatible with Python 2 and 3 and if you have downloaded the Myo SDK and the Myo Python source, you're good to go!
First of all, you have to make sure that Myo Python can find the Myo SDK shared library. You have two options to make it possible: Either 1) allow the OS to find it automatically or 2) keep it in a directory of your project/application and tell Myo Python the path to this directory when you initialize it.
# 1) import myo as libmyo libmyo.init() # 2) import myo as libmyo libmyo.init('path/to/myo/library/dir')
Note that I prefer to import the Myo library as
libmyo. The reason for this is that it is very common to have a
myo variable in your code that identifies a Myo armband.
If you choose path 1), it depends on your OS on how to let it find the Myo shared library. Let's say that
<MYO_SDK_DIR> is the path to your copy of the Myo SDK. On Windows, you need to add to your
PATH environment variable the path
<MYO_SDK_DIR>\bin. On Mac OS on the other hand, you must add to the
DYLD_LIBRARY_PATH variable the path
<MYO_SDK_DIR>/myo.framework. Note that on Mac OS and on Cygwin, you can use the
~/.bashrc file to export the environment variables:
# Cygwin export PATH=$PATH:$(cygpath C:\\myo-sdk-win-0.8.1\\bin) # Mac OS export DYLD_LIBRARY_PATH=$DYLD_LIBRARY_PATH:/Users/niklas/myo-sdk-mac-0.8.1/myo.framework
Now that we've got everything set up, let's get to the code!
Using Myo Python
There are two ways you can use Myo Python: You can either read the data of an armband from a Myo object when you need access to it or you can implement a listener that receives all the data. If possible, you should prefer the listener approach as you are free to ignore data when you don't need it, but the Myo object must be synchronized and keeps track of all data that comes from the Myo armband.
In either case, you need a
Hub. And when you run the hub, you can either pass your own
DeviceListener implementation or you pass a
myo.device_listener.Feed which is also just a subclass but allows you to read the data from a proxy object for each Myo armband.
If you're using your own
DeviceListener implementation, that is all you need to do. You should make sure to shut down the hub manually at some point in time. The Hub runs as a non-daemon thread so your program will not exit unless you stop it.
hub = libmyo.Hub() hub.run(1000, listener) try: while hub.running: time.sleep(0.5) except KeyboardInterrupt: print("Quitting...") finally: hub.shutdown()
You might notice that
hub.running part in the while-loop. You can shut down the hub from your
DeviceListener but not by calling
hub.shutdown but instead returning
False from any callback method. Apart from the DeviceListener thread, you can shut down the hub from any thread. More on that later.
If you prefer to read the data instead of receiving it or if you're just lazy, you can do it like this. The
Feed class provides a convenient method to wait until a single device is connected!
feed = libmyo.device_listener.Feed() hub = libmyo.Hub() hub.run(1000, feed) try: myo = feed.wait_for_single_device(timeout=10.0) # seconds if not myo: print("No Myo connected after 10 seconds.") sys.exit() while hub.running and myo.connected: quat = myo.orientation print("Orientation:", quat.x, quat.y, quat.z, quat.w) except KeyboardInterrupt: print("Quitting...") finally: hub.shutdown()
wait_for_single_device() method does not return until the timeout is exceeded or a Myo armband is connected. There's a difference between a paired and a connected Myo.
myo object you find in the code above is a
myo.device_listener.Feed.MyoProxy object which is thread-safe and can be accessed from anywhere. You can use the
Feed.get_connected_devices() to get a list of all connected devices.
Implementing a DeviceListener
DeviceListener interface provides callbacks for all Myo event types, you only have to override method that you need to receive events. Note that the
DeviceListener methods are called from their own thread so you must ensure appropriate synchronization if you do save the received data somewhere.
class Listener(libmyo.DeviceListener): def on_connect(self, myo, timestamp): print("Hello, Myo!") def on_disconnect(self, myo, timestamp): print("Goodbye, Myo!") def on_orientation_data(self, myo, timestamp, quat): print("Orientation:", quat.x, quat.y, quat.z, quat.w) def on_pose(self, myo, timestamp, pose): if pose == libmyo.Pose.fist: print("Don't show me 'ya fist!") return False # Stops the Hub
Note that Quaternion and Vector data comes in as
myo.vector.Vector objects respectively which you can use directly for computations!