Git Product home page Git Product logo

pyspacemouse's Introduction

PySpaceMouse

A Python library for 3Dconnexion SpaceMouse devices

3Dconnexion Space Mouse in Python using raw HID. Note: you don't need to install or use any of the drivers or 3Dconnexion software to use this package. It interfaces with the controller directly with hidapi and python wrapper library easyhid.

PySpaceMouse is forked from: johnhw/pyspacenavigator

Connected project PySpaceApp is a simple example of how controll your PC with SpaceMouse.

Implements a simple interface to the 6 DoF 3Dconnexion Space Mouse device as well as similar devices.

Control a robot with a Space Mouse

Supported 3Dconnexion devices

  • SpaceNavigator
  • SpaceMouse Pro
  • SpaceMouse Pro Wireless
  • SpaceMouse Wireless
  • 3Dconnexion Universal Receiver
  • SpaceMouse Compact
  • SpacePilot
  • SpacePilot Pro
  • SpaceMouse Enterprise
  • Add more devices

Installation

Use the package manager pip to install pyspacemouse. If you are using a Mac with an ARM processor, you'll need a patched version of easyhid.

# Only needed for ARM Macs
pip install git+https://github.com/bglopez/python-easyhid.git

# Install package
pip install pyspacemouse

Dependencies

  • hidapi is C library for direct communication with HID devices

    • Linux

      • libhidapi-dev to access HID data

      • sudo apt-get install libhidapi-dev (Debian/Ubuntu)

      • Compile and install hidapi. (other Linux distributions)

      • add rules for permissions

        sudo echo 'KERNEL=="hidraw*", SUBSYSTEM=="hidraw", MODE="0664", GROUP="plugdev"' > /etc/udev/rules.d/99-hidraw-permissions.rules
        sudo usermod -aG plugdev $USER
        newgrp plugdev
        Aleternative option - with tee (RPi)
          echo 'KERNEL=="hidraw*", SUBSYSTEM=="hidraw", MODE="0664", GROUP="plugdev"' | sudo tee /etc/udev/rules.d/99-hidraw-permissions.rules
          sudo usermod -aG plugdev $USER
          newgrp plugdev
          
    • Windows

      • Install the latest release of hidapi.dll and hidapi.lib from the hidapi releases page.
      • Set system environment: add absolute path for x64 or x86 folder in Path.
    • Mac OS X (M1)

      • Install from Homebrew
      • brew install hidapi
      • Add hidapi to your DYLD_LIBRARY_PATH directory.
        export DYLD_LIBRARY_PATH=/opt/homebrew/Cellar/hidapi/0.14.0/lib:$DYLD_LIBRARY_PATH
      • On MacOS M1 you will need patched version of easyhid. If easyhid is already installed, please uninstall it first.
        pip install git+https://github.com/bglopez/python-easyhid.git
      • In case of problem with M1 chip, try to run you code with Rosseta 2
      • Tested and developed by consi - thanks!
  • easyhid is hidapi interface for Python - required on all platforms

    • pip install git+https://github.com/bglopez/python-easyhid.git
    • this fork fix problems with hidapi on MacOS.
    • on other platforms it possible works with original package pip install easyhid

Basic Usage:

If the 3Dconnexion driver is installed, please ensure to stop 3DconnexionHelper before running your python scripts.

basicExample.py

import pyspacemouse
import time

success = pyspacemouse.open()
if success:
    while 1:
        state = pyspacemouse.read()
        print(state.x, state.y, state.z)
        time.sleep(0.01)

State objects

State objects returned from read() have 7 attributes: [t,x,y,z,roll,pitch,yaw,button].

  • t: timestamp in seconds since the script started.
  • x,y,z: translations in the range [-1.0, 1.0]
  • roll, pitch, yaw: rotations in the range [-1.0, 1.0].
  • button: list of button states (0 or 1), in order specified in the device specifier

Usage with callback

callbackExample.py

import pyspacemouse
import time


def button_0(state, buttons, pressed_buttons):
    print("Button:", pressed_buttons)


def button_0_1(state, buttons, pressed_buttons):
    print("Buttons:", pressed_buttons)


def someButton(state, buttons):
    print("Some button")


def callback():
    button_arr = [pyspacemouse.ButtonCallback(0, button_0),
                  pyspacemouse.ButtonCallback([1], lambda state, buttons, pressed_buttons: print("Button: 1")),
                  pyspacemouse.ButtonCallback([0, 1], button_0_1), ]

    success = pyspacemouse.open(dof_callback=pyspacemouse.print_state, button_callback=someButton,
                                button_callback_arr=button_arr)
    if success:
        while True:
            pyspacemouse.read()
            time.sleep(0.01)


if __name__ == '__main__':
    callback()

API

open(callback=None, button_callback=None, button_callback_arr=None, set_nonblocking_loop=True, device=None)
    Open a 3D space navigator device. Makes this device the current active device, which enables the module-level read() and close()
    calls. For multiple devices, use the read() and close() calls on the returned object instead, and don't use the module-level calls.

    Parameters:
        callback: If callback is provided, it is called on each HID update with a copy of the current state namedtuple
        dof_callback: If dof_callback is provided, it is called only on DOF state changes with the argument (state).
        button_callback: If button_callback is provided, it is called on each button push, with the arguments (state_tuple, button_state)
        device: name of device to open, as a string like "SpaceNavigator". Must be one of the values in `supported_devices`.
                If `None`, chooses the first supported device found.
    Returns:
        Device object if the device was opened successfully
        None if the device could not be opened

read()              Return a namedtuple giving the current device state (t,x,y,z,roll,pitch,yaw,button)
close()             Close the connection to the current device, if it is open
list_devices()      Return a list of supported devices found, or an empty list if none found

open() returns a DeviceSpec object. If you have multiple 3Dconnexion devices, you can use the object-oriented API to access them individually. Each object has the following API, which functions exactly as the above API, but on a per-device basis:

dev.open()          Opens the connection (this is always called by the module-level open command,
                    so you should not need to use it unless you have called close())
dev.read()          Return the state of the device as namedtuple [t,x,y,z,roll,pitch,yaw,button]
dev.close()         Close this device

There are also attributes:

dev.connected       True if the device is connected, False otherwise
dev.state           Convenience property which returns the same value as read()

Predefined callbacks

import pyspacemouse
import time

success = pyspacemouse.open(dof_callback=pyspacemouse.print_state, button_callback=pyspacemouse.print_buttons)
if success:
    while 1:
        state = pyspacemouse.read()
        time.sleep(0.01)

Callback: print_state

Print all axis states

x +0.00    y +0.00    z +0.00 roll +0.00 pitch +0.00  yaw +0.00    t +0.0

Callback: print_buttons

Print all buttons states

[ 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, ]

Troubleshooting

ModuleNotFoundError: No module named 'easyhid'

  • Install easyhid by pip install easyhid.

AttributeError: function/symbol 'hid_enumerate' not found in library '': python3: undefined symbol: hid_enumerate

  • HID library for your computer is not installed.
  • Follow the instructions in requirements.

pyspacemouse's People

Contributors

bburky avatar consi avatar deivard avatar emil302s avatar hlprev avatar jakubandrysek avatar skekfab avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

pyspacemouse's Issues

roll/pitch/yaw Spacemouse Wireless

Dear Jakub,
Thank you for your great contribution!

I had some issues when I changed from USB to wireless Spacemouse, but I found the solution here (the byte-array issue that was solved here) I dont think it is implemented in the current version that is downloaded from PyPi however.

My next issue is the fact that rotation doesnt show any reaction ( roll, pitch, yaw). I'm testing on a beaglebone blue running some new debian release. I tried to change channels in the pyspacemouse.py similar to the wired versions, but no change there...

When I connect the device, i get 5 HID devices popping up..

Best regards
Viktor

Error while running example.py

Hi, I am receiving the following error on a raspi installation, any idea where it come from ?

Traceback (most recent call last):
  File "/home/user/git/example.py", line 8, in <module>
    state=pyspacemouse.read()
  File "/home/user/.local/lib/python3.9/site-packages/pyspacemouse/pyspacemouse.py", line 586, in read
    return _active_device.read()
  File "/home/user/.local/lib/python3.9/site-packages/pyspacemouse/pyspacemouse.py", line 209, in read
    self.process(ret)
  File "/home/user/.local/lib/python3.9/site-packages/pyspacemouse/pyspacemouse.py", line 241, in process
    flip * to_int16(data[b1], data[b2]) / float(self.axis_scale)
IndexError: bytearray index out of range

Explore HIDAPI callback function

Try to implement a callback read using a low-level function from the HIDAPI library - read_callback

This function has to be additionally implemented to a python HID wrapper like easyhid

The parent project pyspacenavigator, which supports only the Windows platform uses a callback function to the smooth read

Latency of Data Reading

In a Python application with a SpaceMouse Wireless and SpaceMouse Compact we have very high delays when reading data.
We get the first values after initiating a movement after 100ms, but after reaching the zero position again it takes about 1-2 seconds until the values return to zero.
Is this problem known or are certain filters applied?
We are using the pyspacemouse package, and the 3 methods open(), read(), close().
Here is my code:
Bildschirm­foto 2023-03-15 um 12 36 17

Thank you very much for your support.

PySpaceMouse on MacOS ARM M1

I have tried to install PySpaceMouse on MacOS M1, but it doesn`t work.
Is there anybody who fixed this issue?

I have installed hidapi trough brew install hidapi.
My result from basicExample.py is there:

 ✘ kuba@KA-M1  ~/Documents/git/kuba/pyspacemouse   master ±  python3.11 ./examples/basicExample.py
Traceback (most recent call last):
  File "/Users/kuba/Documents/git/kuba/pyspacemouse/./examples/basicExample.py", line 4, in <module>
    success = pyspacemouse.open()
              ^^^^^^^^^^^^^^^^^^^
  File "/opt/homebrew/lib/python3.11/site-packages/pyspacemouse/pyspacemouse.py", line 703, in open
    all_devices = list_devices()
                  ^^^^^^^^^^^^^^
  File "/opt/homebrew/lib/python3.11/site-packages/pyspacemouse/pyspacemouse.py", line 643, in list_devices
    hid = Enumeration()
          ^^^^^^^^^^^^^
  File "/opt/homebrew/lib/python3.11/site-packages/easyhid/easyhid.py", line 366, in __init__
    self.device_list = _hid_enumerate(vid, pid)
                       ^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/homebrew/lib/python3.11/site-packages/easyhid/easyhid.py", line 428, in _hid_enumerate
    start = hidapi.hid_enumerate(vendor_id, product_id)
            ^^^^^^^^^^^^^^^^^^^^
  File "/opt/homebrew/lib/python3.11/site-packages/cffi/api.py", line 912, in __getattr__
    make_accessor(name)
  File "/opt/homebrew/lib/python3.11/site-packages/cffi/api.py", line 908, in make_accessor
    accessors[name](name)
  File "/opt/homebrew/lib/python3.11/site-packages/cffi/api.py", line 838, in accessor_function
    value = backendlib.load_function(BType, name)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: function/symbol 'hid_enumerate' not found in library '<None>': dlsym(RTLD_DEFAULT, hid_enumerate): symbol not found

Related links:

[docs] Setting permission rules on Raspberry Pi fails [fixed]

Following the documentation for installing the dependencies, I ran into a Permission denied error with:

sudo echo 'KERNEL=="hidraw*", SUBSYSTEM=="hidraw", MODE="0664", GROUP="plugdev"' > /etc/udev/rules.d/99-hidraw-permissions.rules

The solution was to use sudo tee:

echo 'KERNEL=="hidraw*", SUBSYSTEM=="hidraw", MODE="0664", GROUP="plugdev"' | sudo tee /etc/udev/rules.d/99-hidraw-permissions.rules

Windows 10 error ; and raspi error

C:\Users\61240\Downloads\weimo_ws\src>python mouse.py
Traceback (most recent call last):
  File "C:\opt\ros\noetic\x64\lib\site-packages\easyhid\easyhid.py", line 53, in <module>
    hidapi = ffi.dlopen('hidapi.dll')
  File "C:\opt\ros\noetic\x64\lib\site-packages\cffi\api.py", line 150, in dlopen
    lib, function_cache = _make_ffi_library(self, name, flags)
  File "C:\opt\ros\noetic\x64\lib\site-packages\cffi\api.py", line 832, in _make_ffi_library
    backendlib = _load_backend_lib(backend, libname, flags)
  File "C:\opt\ros\noetic\x64\lib\site-packages\cffi\api.py", line 827, in _load_backend_lib
    raise OSError(msg)
OSError: cannot load library 'hidapi.dll': error 0x7e.  Additionally, ctypes.util.find_library() did not manage to locate a library called 'hidapi.dll'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "mouse.py", line 1, in <module>
    import pyspacemouse
  File "C:\opt\ros\noetic\x64\lib\site-packages\pyspacemouse\__init__.py", line 1, in <module>
    from .pyspacemouse import *
  File "C:\opt\ros\noetic\x64\lib\site-packages\pyspacemouse\pyspacemouse.py", line 1, in <module>
    from easyhid import Enumeration
  File "C:\opt\ros\noetic\x64\lib\site-packages\easyhid\__init__.py", line 8, in <module>
    from easyhid.easyhid import *
  File "C:\opt\ros\noetic\x64\lib\site-packages\easyhid\easyhid.py", line 55, in <module>
    hidapi = ffi.dlopen(ctypes.util.find_library('hidapi.dll'))
  File "C:\opt\ros\noetic\x64\lib\site-packages\cffi\api.py", line 150, in dlopen
    lib, function_cache = _make_ffi_library(self, name, flags)
  File "C:\opt\ros\noetic\x64\lib\site-packages\cffi\api.py", line 832, in _make_ffi_library
    backendlib = _load_backend_lib(backend, libname, flags)
  File "C:\opt\ros\noetic\x64\lib\site-packages\cffi\api.py", line 821, in _load_backend_lib
    raise OSError("dlopen(None) cannot work on Windows for Python 3 "
OSError: dlopen(None) cannot work on Windows for Python 3 (see http://bugs.python.org/issue23606)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.