Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multiple Myos #28

Open
aditya17a opened this issue Sep 5, 2015 · 20 comments
Open

Multiple Myos #28

aditya17a opened this issue Sep 5, 2015 · 20 comments

Comments

@aditya17a
Copy link

Hi, is it possible to use multiple Myos with these bindings? If so, how?

@NiklasRosenstein
Copy link
Owner

Hi Aditya, I don't have multiple Myos so I can't test it, but it should be possible without further ado. If you chose to implement a DeviceListener, you will receive an on_pair() and on_unpair() call for every Myo connected. Using the Feed class, you can get a list of the paired devices with Feed.get_devices().

@shibshib
Copy link

Hi Niklas,

I haven't gone through the code yet but I'm wondering whether it's possible to make it work with two USB dongles?

Thanks!

Ala

@NiklasRosenstein
Copy link
Owner

I have no idea, I only have one Myo. :)

@sharkwheels
Copy link

Hi Niklas,

Related to this: Is there a way to get the MAC Address of each Myo? Also could you please toss up an example on using Feed.get_devices() ?

Thanks!

@NiklasRosenstein
Copy link
Owner

@sharkwheels

Is there a way to get the MAC Address of each Myo?

The libmyo.h header from the 0.9.0 SDK declares a uint64_t libmyo_get_mac_address(libmyo_myo_t) function, but it doesn't seem to be actually exported to the myo dynamic libraries or was forgotten to be removed from the headers. Ergo, I can't add a function to read the MAC address of a Myo.

Also could you please toss up an example on using Feed.get_devices() ?

Could you tell me what is unclear about this function? You can add this line in the feed_myo.py example

print("Connected devices:", feed.get_devices())

and get something like

<MyoProxy (connected) at 0xc1a180>

@sharkwheels
Copy link

Could you tell me what is unclear about this function?

I was wondering how/where it might be used in the hello_myo example set up. I should have been clearer about that.

Ah, bummer, re: MAC Address. Was hoping that was possible.

@NiklasRosenstein
Copy link
Owner

@sharkwheels The "hello_myo" example implements a DeviceListener and does not use the Feed class. No way to use the Feed.get_devices() method then.

@NiklasRosenstein
Copy link
Owner

@sharkwheels As an alternative, you could use the memory address of the Myo C-object to identify a Myo. For a MyoProxy, you can use its hash() (added 9e6cfdb). For a C-types lowlevel wrapper Myo object, you can use the .value property.

@GanKunlu
Copy link

GanKunlu commented May 23, 2016

Hi Niklas,

I want to get the sEMG data from two Myos, but some troubles make me at a loss. When I use the feed.get_connected_devices() to get every MyoProxy Objects and .emg to get the EMG data from each one, I got None EMG data. The code is:

from myo import init, Hub, Feed
import time
init()
feed = Feed()
hub = Hub()
hub.run(1000, feed)
try:
    myo = feed.get_connected_devices()
    print(myo)
    while hub.running:
        if myo:
            print(myo[0].emg)
            time.sleep(0.1)
            print(myo[1].emg)
finally:
    hub.shutdown()

@NiklasRosenstein
Copy link
Owner

Hi @GanKunlu,

you have to enable EMG streaming first. See MyoProxy.set_stream_emg() You have to pass it an enumeration value of StreamEmg.

myo.set_stream_emg(StreamEmg.enabled)

Note that myo.emg will be None until the first EMG data is received.

@NiklasRosenstein
Copy link
Owner

I've added an entry in the FAQ section of the docs. http://myo-python.readthedocs.io/en/latest/faq.html

@mark-toma
Copy link

Beware of attempting to stream EMG from two Myos using a single dongle. There isn't enough bandwidth in the hardware to support the required throughput.

https://developer.thalmic.com/forums/topic/2958/

@umutdemirel
Copy link

@NiklasRosenstein

If you chose to implement a DeviceListener, you will receive an on_pair() and on_unpair() call for every Myo connected.

By using this method, for 2 Myos, 2 on_pair() calls are received however myo values of the calls are unfortunately the same.

Hello, Myo! <Myo object at 0x1093f28c0> 390540611026 (1L, 5L, 1970L)
Hello, Myo! <Myo object at 0x1093f28c0> 389946548255 (1L, 5L, 1970L)

Using the Feed class, you can get a list of the paired devices with Feed.get_devices().

By using this method, 2 different Myos can be used. However 200Hz for EMG and 50Hz for IMU cannot be reached.

[<MyoProxy (connected) at 0x7fc511a07af0>, <MyoProxy (connected) at 0x7fc511a0a890>]
<MyoProxy (connected) at 0x7fc511a07af0> (0, 0, -2, -2, -75, -4, 0, -5) time between 2 captures : 0.0121109485626
<MyoProxy (connected) at 0x7fc511a0a890> (3, -3, 2, 4, 0, 0, -2, -8) time between 2 captures : 0.0121109485626
<MyoProxy (connected) at 0x7fc511a07af0> (-7, 0, 1, 0, -81, 0, 2, -6) time between 2 captures : 0.0154161453247
<MyoProxy (connected) at 0x7fc511a0a890> (0, 2, 5, 3, -2, -2, -5, -7) time between 2 captures : 0.0154161453247
<MyoProxy (connected) at 0x7fc511a07af0> (3, -6, -2, 0, 61, 1, -1, 4) time between 2 captures : 0.00678586959839
<MyoProxy (connected) at 0x7fc511a0a890> (0, 2, 5, 3, -2, -2, -5, -7) time between 2 captures : 0.00678586959839
<MyoProxy (connected) at 0x7fc511a07af0> (3, -6, -2, 0, 61, 1, -1, 4) time between 2 captures : 0.00690793991089
<MyoProxy (connected) at 0x7fc511a0a890> (-6, -2, -1, -6, -1, -3, -2, -5) time between 2 captures : 0.00690793991089
<MyoProxy (connected) at 0x7fc511a07af0> (-2, 2, 0, -2, -72, 2, -4, -3) time between 2 captures : 0.00649404525757
<MyoProxy (connected) at 0x7fc511a0a890> (3, 3, 1, 3, -2, -1, 1, -8) time between 2 captures : 0.00649404525757
<MyoProxy (connected) at 0x7fc511a07af0> (-2, 2, 0, -2, -72, 2, -4, -3) time between 2 captures : 0.00651812553406
<MyoProxy (connected) at 0x7fc511a0a890> (3, 3, 1, 3, -2, -1, 1, -8) time between 2 captures : 0.00651812553406
<MyoProxy (connected) at 0x7fc511a07af0> (1, -1, 0, -2, 70, 0, 2, 2) time between 2 captures : 0.00809502601624
<MyoProxy (connected) at 0x7fc511a0a890> (-5, 0, 3, -1, -2, 0, -3, -5) time between 2 captures : 0.00809502601624
<MyoProxy (connected) at 0x7fc511a07af0> (-7, -2, -3, 0, -60, -4, 0, -3) time between 2 captures : 0.00592494010925
<MyoProxy (connected) at 0x7fc511a0a890> (-5, 0, -2, -5, -4, -4, -3, -5) time between 2 captures : 0.00592494010925
<MyoProxy (connected) at 0x7fc511a07af0> (-7, -2, -3, 0, -60, -4, 0, -3) time between 2 captures : 0.00881695747375
<MyoProxy (connected) at 0x7fc511a0a890> (1, -2, 4, 0, -1, -2, -3, 2) time between 2 captures : 0.00881695747375
<MyoProxy (connected) at 0x7fc511a07af0> (-1, 3, -3, -6, 31, 0, -2, 2) time between 2 captures : 0.00545907020569

@mark-toma
Copy link

@umutdemirel

Just to clarify data streaming limitations with multiple Myos:

When using two Myos with one dongle (as must be the case when relying upon Myo Connect), EMG data must not be streaming. Another way to say this is that when EMG data is streaming, only one Myo can be used without having arbitrary data lost by a single dongle.

Since IMU data is always transmitted by Myo firmware, you can plan on having all of this data available. When using two Myos, I have found that all IMU data is received from both devices.

@NiklasRosenstein
Copy link
Owner

NiklasRosenstein commented May 9, 2017

@umutdemirel

By using this method, for 2 Myos, 2 on_pair() calls are received however myo values of the calls are unfortunately the same.

Hello, Myo! <Myo object at 0x1093f28c0> 390540611026 (1L, 5L, 1970L)
Hello, Myo! <Myo object at 0x1093f28c0> 389946548255 (1L, 5L, 1970L)

That is strange, because the Feed class also just listens on the pair event.

fmw_version = event.firmware_version
self._myos[myo.value] = self.MyoProxy(myo, timestamp, fmw_version)
self.synchronized.notify_all()

Unfortunately, I won't be able to reproduce the issue. As mentioned above, I have only one Myo device. I'd be happy to review patches, though.

@mark-toma

When using two Myos with one dongle (as must be the case when relying upon Myo Connect).

Is there an alternative for Myo Connect?

@mark-toma
Copy link

mark-toma commented May 9, 2017

@NiklasRosenstein

I can think of two such alternatives to Myo Connect. They both travel down the stack closer to Myo.

  1. Talk to the dongle's virtual serial port as is done by /dzhu/myo-raw. This is communicating over the dongle's virtual serial port to interact with its BLE stack. Developers can sniff Myo Connect transactions with the dongle and refer to the dongle datasheet (BlueGiga BLED112) for protocol "reversing" insight. This essentially replaces Myo Connect.
  2. Talk to the BLE protocol documented by Thalmic Labs here. Use your favorite BLE stack. This replaces Myo Connect and the BLE dongle.

Both of the above solutions afford the developer the freedom to avert the hardware bandwidth limitation affecting streaming EMG from multiple Myos.

@umutdemirel
Copy link

@NiklasRosenstein

More interesting results occured when I implemented on_event call of DeviceListener class.

(from on_event)event's myo : <Myo object at 0x10c012950>
(from on_event)event's type : <EventType: paired>
(from on_pair)Hello, Myo! <Myo object at 0x10c0128c0> 437608531172 (1L, 5L, 1970L)

(from on_event)event's myo : <Myo object at 0x10c012950>
(from on_event)event's type : <EventType: connected>
(from on_connect)connected: <Myo object at 0x10c0128c0> 437608531172 (1L, 5L, 1970L)

(from on_event)event's myo : <Myo object at 0x10c012950>
(from on_event)event's type : <EventType: paired>
(from on_pair)Hello, Myo! <Myo object at 0x10c0128c0> 437610019592 (1L, 5L, 1970L)

(from on_event)event's myo : <Myo object at 0x10c012950>
(from on_event)event's type : <EventType: connected>
(from on_connect)connected: <Myo object at 0x10c0128c0> 437610019592 (1L, 5L, 1970L)

on_event call always catches one of the Myos. And all other events like on_pair or on_connect catches the other Myo.

This is really weird, I wish you had the 2nd Myo :)

@NiklasRosenstein
Copy link
Owner

NiklasRosenstein commented May 12, 2017

That's even more strange. on_event() and on_pair() are called for the "paired" event from the same function:

myo-python/myo/__init__.py

Lines 291 to 367 in d80f650

def _invoke_listener(listener, event):
"""
Invokes the :class:`DeviceListener` callback methods for
the specified :class:`event<myo.lowlevel.event_t>`. If any
of the callbacks return False, this function will return False
as well. It also issues a warning when a DeviceListener method
did not return None or a boolean value.
:meth:`DeviceListener.on_event_finished` is always called,
event when any of the calls in between returned False already.
"""
myo = event.myo
timestamp = event.timestamp
# Invokes a method on the listener. If defaults=True, will prepend
# the myo and timestamp argument to *args.
def _(name, *args, **kwargs):
defaults = kwargs.pop('defaults', True)
if kwargs:
raise TypeError('unexpected arguments')
if defaults:
args = (myo, timestamp) + tuple(args)
method = getattr(listener, name)
result = method(*args)
if result is None:
return True
elif not isinstance(result, bool):
sys.stderr.write('DeviceListener.%s() must return None or bool\n' % name)
result = False
return result
kind = event.type
result = _('on_event', kind, event, defaults=False)
if kind == EventType.paired:
result = result and _('on_pair', event.firmware_version)
elif kind == EventType.unpaired:
result = result and _('on_unpair')
elif kind == EventType.connected:
result = result and _('on_connect', event.firmware_version)
elif kind == EventType.disconnected:
result = result and _('on_disconnect')
elif kind == EventType.arm_synced:
result = result and _('on_arm_sync', event.arm, event.x_direction,
event.rotation, event.warmup_state)
elif kind == EventType.arm_unsynced:
result = result and _('on_arm_unsync')
elif kind == EventType.unlocked:
result = result and _('on_unlock')
elif kind == EventType.locked:
result = result and _('on_lock')
elif kind == EventType.pose:
result = result and _('on_pose', event.pose)
elif kind == EventType.orientation:
result = result and _('on_orientation_data', event.orientation)
result = result and _('on_accelerometor_data', event.acceleration)
result = result and _('on_gyroscope_data', event.gyroscope)
elif kind == EventType.rssi:
result = result and _('on_rssi', event.rssi)
elif kind == EventType.bettery_level:
result = result and _('on_battery_level_received', event.level)
elif kind == EventType.emg:
result = result and _('on_emg_data', event.emg)
elif kind == EventType.warmup_completed:
result = result and _('on_warmup_completed', event.warmup_result)
elif kind.name:
warnings.warn('unhandled myo.EventType: {0}'.format(kind.name), RuntimeWarning)
else:
warnings.warn('unknown myo.EventType: {0}'.format(kind.value), RuntimeWarning)
if not _('on_event_finished', kind, event, defaults=False):
result = False
return result

I recommend you use Myo.value though instead of the Python object ID to check if its actually the same Myo.

@NiklasRosenstein
Copy link
Owner

Ah yes, you actually have to use Myo.value to compare two Myos. Every event you will get a new instance of the Myo class, because it only wraps the underlying pointer to the Myo C structure. Please do your tests again and print myo.value instead of myo.

@BczImHappy
Copy link

BczImHappy commented Aug 27, 2018

I am trying to run my hub every 5 ms but I am getting the error AttributeError: 'Listener' object has no attribute 'emg' when I lower the duration time below 200 ms using hub.run(listener,100)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

8 participants