diff --git a/examples/interfaces/README.md b/examples/interfaces/README.md index 4bf31cc..7b07e1c 100644 --- a/examples/interfaces/README.md +++ b/examples/interfaces/README.md @@ -12,5 +12,6 @@ Here are few examples that depict the various interfaces available to the user: 5. `asus_xtion.py`: use the Asus Xtion interface 6. `openpose.py`: use the Openpose interface 7. `speech.py`: use the speech (recognizer/translator) interface +8. `spacemouse.py`: use the (3Dconnexion) SpaceMouse interface **Note**: some interfaces require to install different libraries, and possibly to configure them. Check the raised errors. diff --git a/examples/interfaces/spacemouse.py b/examples/interfaces/spacemouse.py new file mode 100644 index 0000000..10243fa --- /dev/null +++ b/examples/interfaces/spacemouse.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python +"""Run the Space mouse interface. + +Make sure that the `spnav` library is installed, and the space mouse (by 3Dconnexion) is connected to the computer +before running this code. +""" + +import argparse +import time + +from pyrobolearn.tools.interfaces.mouse_keyboard.spacemouse import SpaceMouseInterface + + +# create parser +parser = argparse.ArgumentParser() +parser.add_argument('-t', '--use_thread', help='If we should run the webcam interface in a thread.', type=bool, + default=True) +args = parser.parse_args() + + +# create webcam interface +if args.use_thread: + # create and run interface in a thread + interface = SpaceMouseInterface(use_thread=True, sleep_dt=0.001, verbose=True) + raw_input('Press key to stop the space mouse interface') +else: + # create interface + interface = SpaceMouseInterface(use_thread=False, verbose=True) + + while True: + # perform a `step` with the interface + interface.step() + time.sleep(0.001) diff --git a/pyrobolearn/__init__.py b/pyrobolearn/__init__.py index e9e9824..c97fcd2 100644 --- a/pyrobolearn/__init__.py +++ b/pyrobolearn/__init__.py @@ -1,3 +1,6 @@ +"""PyRoboLearn: a Python framework in Robot Learning for Education and Research. +""" + name = "pyrobolearn" import sys diff --git a/pyrobolearn/tools/interfaces/haptic/__init__.py b/pyrobolearn/tools/interfaces/haptic/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pyrobolearn/tools/interfaces/haptic/haptic.py b/pyrobolearn/tools/interfaces/haptic/haptic.py new file mode 100644 index 0000000..98580b4 --- /dev/null +++ b/pyrobolearn/tools/interfaces/haptic/haptic.py @@ -0,0 +1,88 @@ +#!/usr/bin/env python +"""Define the haptic interface. + +Prerequisites: +- follow instructions in [4] on how to install h3dapi. + +Dependencies: +- `pyrobolearn.tools.interfaces` + +References: + - [1] Haptic devices from Force Dimension: http://www.forcedimension.com/ + - [2] Haptic devices from 3D Systems: https://www.3dsystems.com/haptics-devices/openhaptics + - [3] H3D API: https://h3dapi.org/ + - [4] H3D API - Documentation: https://www.h3dapi.org/modules/mediawiki/index.php/Main_Page + - [6] H3D API - Github repo: https://github.com/SenseGraphics/h3dapi + - [7] Haptic devices: https://www.h3dapi.org/modules/mediawiki/index.php/Haptics_devices +""" + +# TODO + +# import H3DInterface as h3d + +from pyrobolearn.tools.interfaces import InputOutputInterface + + +__author__ = "Brian Delhaisse" +__copyright__ = "Copyright 2018, PyRoboLearn" +__credits__ = ["Brian Delhaisse"] +__license__ = "GNU GPLv3" +__version__ = "1.0.0" +__maintainer__ = "Brian Delhaisse" +__email__ = "briandelhaisse@gmail.com" +__status__ = "Development" + + +class HapticInterface(InputOutputInterface): + r"""Haptic Interface + + Provide the haptic interface which, for instance, allows the user to interact with the world and the robots. + + References: + - [1] Haptic devices from Force Dimension: http://www.forcedimension.com/ + - [2] Haptic devices from 3D Systems: https://www.3dsystems.com/haptics-devices/openhaptics + - [3] H3D API: https://h3dapi.org/ + - [4] H3D API - Documentation: https://www.h3dapi.org/modules/mediawiki/index.php/Main_Page + - [6] H3D API - Github repo: https://github.com/SenseGraphics/h3dapi + """ + + def __init__(self, use_thread=False, sleep_dt=0., verbose=False): + """ + Initialize the SpaceMouse interface. + + Args: + use_thread (bool): If True, it will run the interface in a separate thread than the main one. + The interface will update its data automatically. + sleep_dt (float): If :attr:`use_thread` is True, it will sleep the specified amount before acquiring or + setting the next sample. + verbose (bool): If True, it will print information about the state of the interface. This is let to the + programmer what he / she wishes to print. + """ + + # From [7] + # root, = references.getValue() + # + # di = getActiveDeviceInfo() + # if di: + # hd = di.device.getValue()[0] + + # available fields + # hd.trackerPosition + # hd.trackerOrientation + # hd.mainButton + # hd.secondaryButton + + super(HapticInterface, self).__init__(use_thread=use_thread, sleep_dt=sleep_dt, verbose=verbose) + + ########### + # Methods # + ########### + + def run(self): + """Run the interface.""" + pass + + +# Test the interface +if __name__ == '__main__': + pass diff --git a/pyrobolearn/tools/interfaces/mouse_keyboard/spacemouse.py b/pyrobolearn/tools/interfaces/mouse_keyboard/spacemouse.py new file mode 100644 index 0000000..9fcd312 --- /dev/null +++ b/pyrobolearn/tools/interfaces/mouse_keyboard/spacemouse.py @@ -0,0 +1,187 @@ +#!/usr/bin/env python +"""Define the space mouse (from 3D connexion) interface. + +Prerequisites: +1. install spacenavd (http://spacenav.sourceforge.net/): `sudo apt-get install spacenavd` +2. install spnav (https://spnav.readthedocs.io/en/latest/): `pip install spnav` + +Troubleshooting: +- if you get 'OSError: libspnav.so: cannot open shared object file: No such file or directory', you have to copy-paste +the file '/usr/lib/libspnav.so.0' to '/usr/lib/libspnav.so' + +Dependencies: +- `pyrobolearn.tools.interfaces` + +References: + - [1] SpaceMouse from 3D connexion: https://www.3dconnexion.com/products/spacemouse.html + - [2] SpaceNav: http://spacenav.sourceforge.net/ + - [3] spnav: Space Navigator support in Python: + - documentation: https://spnav.readthedocs.io/en/latest/ + - repository: https://bitbucket.org/seibert/spnav/src/default/ +""" + +import numpy as np + +try: + import spnav +except ImportError as e: + string = "\nHint: try to install `spnav` by typing: " \ + "\n`sudo apt-get install spacenavd`" \ + "\n`pip install spnav`" + raise ImportError(e.__str__() + string) + + +from pyrobolearn.tools.interfaces import InputInterface + + +__author__ = "Brian Delhaisse" +__copyright__ = "Copyright 2018, PyRoboLearn" +__credits__ = ["Brian Delhaisse"] +__license__ = "GNU GPLv3" +__version__ = "1.0.0" +__maintainer__ = "Brian Delhaisse" +__email__ = "briandelhaisse@gmail.com" +__status__ = "Development" + + +class SpaceMouseInterface(InputInterface): + r"""Space Mouse Interface + + Provide the space mouse interface which, for instance, allows the user to interact with the world and the robots + using the space mouse (from 3D connexion). + + Space mouse: + * 2 buttons + * 3 degrees of translation and 3 degrees of rotation + + References: + - [1] SpaceMouse from 3D connexion: https://www.3dconnexion.com/products/spacemouse.html + - [2] SpaceNav: http://spacenav.sourceforge.net/ + - [3] spnav: Space Navigator support in Python: + - documentation: https://spnav.readthedocs.io/en/latest/ + - repository: https://bitbucket.org/seibert/spnav/src/default/ + """ + + def __init__(self, use_thread=False, sleep_dt=0., verbose=False, translation_range=(-1, 1), + rotation_range=(-1, 1)): + """ + Initialize the SpaceMouse interface. + + Args: + use_thread (bool): If True, it will run the interface in a separate thread than the main one. + The interface will update its data automatically. + sleep_dt (float): If :attr:`use_thread` is True, it will sleep the specified amount before acquiring or + setting the next sample. + verbose (bool): If True, it will print information about the state of the interface. This is let to the + programmer what he / she wishes to print. + translation_range (np.array[2], np.array[2,3], tuple of 2 float, tuple of 2 np.array[3]): the lower and + higher bounds for the (x, y, z). This is used to normalize the translation range to be between + [-1, 1]. The frame (x,y,z) is defined with x pointing forward, y to the left, and z up. + rotation_range (np.array[2], np.array[2,3], tuple of 2 float, tuple of 2 np.array[3]): the lower and higher + bounds for the roll-pitch-yaw angles. This is used to normalize the rotation range to be between + [-1, 1]. The frame (x,y,z) is defined with x pointing forward, y to the left, and z up. + """ + + # Open connection to the daemon via AF_UNIX socket. + spnav.spnav_open() + + # check the ranges and their shapes + translation_range = np.abs(np.asarray(translation_range, dtype=np.float)) + rotation_range = np.abs(np.asarray(rotation_range, dtype=np.float)) + + if translation_range.shape != (2,) and translation_range.shape != (2, 3): + raise ValueError("Expecting the shape of 'translation_range' to be (2,) or (2,3), instead got: " + "{}".format(translation_range.shape)) + if rotation_range.shape != (2,) and rotation_range.shape != (2, 3): + raise ValueError("Expecting the shape of 'rotation_range' to be (2,) or (2,3), instead got: " + "{}".format(rotation_range.shape)) + + if translation_range.shape == (2,): + translation_range = np.vstack([translation_range]*3).T + if rotation_range.shape == (2,): + rotation_range = np.vstack([rotation_range]*3).T + + self.translation_range = translation_range + self.rotation_range = rotation_range + + # define space mouse events + self.left_button_pressed = False + self.right_button_pressed = False + self.translation = np.zeros(3) + self.rotation = np.zeros(3) + self.updated = False + + super(SpaceMouseInterface, self).__init__(use_thread=use_thread, sleep_dt=sleep_dt, verbose=verbose) + + ########### + # Methods # + ########### + + def run(self): + """Run the interface.""" + + # # Remove pending space navigator events from the queue + # spnav.spnav_remove_events(spnav.SPNAV_EVENT_ANY) + + # get space mouse event (without waiting) + # event = spnav_wait_event() # blocking wait for the next event + # try: + event = spnav.spnav_poll_event() + # except KeyboardInterrupt: + # print '\nQuitting...' + # finally: + # self.close() + + # check the event + self.updated = True + if isinstance(event, spnav.SpnavButtonEvent): # button event + if event.bnum == 0: # left + self.left_button_pressed = event.press + elif event.bnum == 1: # right + self.right_button_pressed = event.press + elif isinstance(event, spnav.SpnavMotionEvent): # mouse motion event + # translation + x, y, z = event.translation # x, y, z in spnav + self.translation = np.array([z, -x, y], dtype=np.float) # x (front), y (left), z (up) + condition = self.translation < 0 + self.translation[condition] /= self.translation_range[0][condition] + self.translation[~condition] /= self.translation_range[1][~condition] + + # rotation + r, p, y = event.rotation # r, p, y in spnav + self.rotation = np.array([y, -r, p], dtype=np.float) # x (front), y (left), z (up) + condition = self.rotation < 0 + self.rotation[condition] /= self.rotation_range[0][condition] + self.rotation[~condition] /= self.rotation_range[1][~condition] + else: + self.updated = False + + # if we should print the events + if self.verbose and self.updated: + print("Left and Right buttons: {} and {}".format(self.left_button_pressed, self.right_button_pressed)) + print("Space mouse translation and orientation: {} and {}".format(self.translation, self.rotation)) + + def close(self): + """Close the interface.""" + # spnav.spnav_close() + if self.use_thread: + self.stop_thread = True + + +# Test the interface +if __name__ == '__main__': + import time + from itertools import count + + # create interface + # interface = SpaceMouseInterface(verbose=True) + interface = SpaceMouseInterface(verbose=True, translation_range=[[-410, -410, -430], [460, 400, 420]], + rotation_range=[[-410., -420, -360], [410, 400, 410]]) + + for i in count(): + interface.step() + # if i > 1e6: + # print('closing the program') + # interface.close() + # break + time.sleep(0.001) diff --git a/pyrobolearn/tools/interfaces/vr/oculus.py b/pyrobolearn/tools/interfaces/vr/oculus.py index 99c60e3..5d5dc1c 100644 --- a/pyrobolearn/tools/interfaces/vr/oculus.py +++ b/pyrobolearn/tools/interfaces/vr/oculus.py @@ -25,7 +25,7 @@ from pyrobolearn.tools.interfaces.vr import VRInterface __author__ = "Brian Delhaisse" __copyright__ = "Copyright 2018, PyRoboLearn" __credits__ = ["Brian Delhaisse"] -__license__ = "MIT" +__license__ = "GNU GPLv3" __version__ = "1.0.0" __maintainer__ = "Brian Delhaisse" __email__ = "briandelhaisse@gmail.com" diff --git a/pyrobolearn/tools/interfaces/vr/vr.py b/pyrobolearn/tools/interfaces/vr/vr.py index 3349730..460495c 100644 --- a/pyrobolearn/tools/interfaces/vr/vr.py +++ b/pyrobolearn/tools/interfaces/vr/vr.py @@ -1,10 +1,53 @@ +#!/usr/bin/env python +"""Define the main Virtual Reality (VR) interface class. + +References: + - steamvr: https://www.steamvr.com/en/ + - SteamVR on Linux: + - https://github.com/ValveSoftware/SteamVR-for-Linux + - https://www.cgl.ucsf.edu/chimera/data/linux-vr-oct2018/ubuntuvr.html + - OpenVR (not open source): + - Github repository: https://github.com/ValveSoftware/openvr + - Documentation: https://github.com/ValveSoftware/openvr/wiki/API-Documentation + - PyOpenVR: + - Github repository: https://github.com/cmbruns/pyopenvr + - OSVR + - http://www.osvr.org/ + - https://osvr.github.io/doc/installing/linux/ + - Misc: + - How to use the HTC Vive Trackers in Ubuntu using Python 3.6: https://gist.github.com/DanielArnett/c9a56c9c7cc0def20648480bca1f6772 + - https://github.com/osudrl/CassieVrControls/wiki/Tracking:-OpenVR-SDK + +Log: +$ sudo add-apt-repository multiverse +$ sudo apt install steam +$ sudo apt install steam-devices + +* installing OSVR +""" + +# TODO # import openvr from pyrobolearn.tools.interfaces.interface import InputOutputInterface +__author__ = "Brian Delhaisse" +__copyright__ = "Copyright 2018, PyRoboLearn" +__credits__ = ["Brian Delhaisse"] +__license__ = "GNU GPLv3" +__version__ = "1.0.0" +__maintainer__ = "Brian Delhaisse" +__email__ = "briandelhaisse@gmail.com" +__status__ = "Development" + + class VRInterface(InputOutputInterface): r"""Virtual Reality Interface + + Virtual reality interface from which other VR interfaces (such as HTC, Oculus) inherit from. """ - pass + + def __init__(self, use_thread=False, sleep_dt=0., verbose=False): + super(VRInterface, self).__init__(use_thread=use_thread, sleep_dt=sleep_dt, verbose=verbose)