update game controllers and keyboard interfaces + example with quadcopter and wheeled robot

This commit is contained in:
Brian Delhaisse
2019-06-21 03:26:36 +02:00
parent f512d42d31
commit c356c062bb
18 changed files with 1475 additions and 164 deletions
+5 -1
View File
@@ -12,6 +12,8 @@ from pyrobolearn.tools.interfaces.mouse_keyboard import MouseKeyboardInterface
sim = prl.simulators.Bullet()
# create mouse keyboard interface
# Note that to give the simulator `sim` to the interface can be optional especially if there is only one simulator.
# The interface will look in the memory to check the instantiated simulators and take the first one if it exists.
interface = MouseKeyboardInterface(sim)
# run interface
@@ -20,8 +22,10 @@ for _ in count():
interface.step()
# print pressed keys
if len(interface.key_pressed) > 0:
print("Keys that are pressed: {}".format(interface.key_pressed))
if len(interface.key_down) > 0:
print("Keys that are pressed: {}".format(interface.key_down))
print("Keys that are down: {}".format(interface.key_down))
# perform a step with the simulator
sim.step(sleep_time=sim.dt)
+14 -4
View File
@@ -1,23 +1,33 @@
#!/usr/bin/env python
"""Load the Playstation game controller interface
How to run:
```
$ python playstation.py --help # for help
$ python playstation.py --controller ps # to use any PS game controller (by default)
$ python playstation.py --controller ps3 # to use PS3 game controller
$ python playstation.py --controller ps4 # to use PS4 game controller
```
"""
import time
from itertools import count
import argparse
from pyrobolearn.tools.interfaces.controllers.playstation import PS3ControllerInterface, PS4ControllerInterface
from pyrobolearn.tools.interfaces.controllers.playstation import *
# create parser to select the game controller
parser = argparse.ArgumentParser()
parser.add_argument('-c', '--controller', help='The Playstation game controller to use (ps3 or ps4)', type=str,
choices=['ps3', 'ps4'], default='ps4')
parser.add_argument('-c', '--controller', help='The Playstation game controller to use (ps, ps3, or ps4)', type=str,
choices=['ps', 'ps3', 'ps4'], default='ps')
args = parser.parse_args()
# load corresponding Playstation controller interface
if args.controller == 'ps':
controller = PSControllerInterface(verbose=False)
if args.controller == 'ps3':
controller = PS3ControllerInterface(verbose=True)
controller = PS3ControllerInterface(verbose=False)
elif args.controller == 'ps4':
controller = PS4ControllerInterface(verbose=False)
else:
+40 -20
View File
@@ -3,13 +3,25 @@
how to run:
```
$ python quadcopter_controller.py --help # for help
$ python quadcopter_controller.py --controller xbox # to use Xbox game controller
$ python quadcopter_controller.py --controller ps3 # to use PS3 game controller
$ python quadcopter_controller.py --help # for help
$ python quadcopter_controller.py --controller keyboard # to use the keyboard
$ python quadcopter_controller.py --controller xbox # to use Xbox game controller
$ python quadcopter_controller.py --controller ps # to use PS game controller
```
Mapping of the keyboard interface:
- `top arrow`: move forward
- `bottom arrow`: move backward
- `left arrow`: move sideways to the left
- `right arrow`: move sideways to the right
- `ctrl + top arrow`: ascend
- `ctrl + bottom arrow`: descend
- `ctrl + left arrow`: turn to the right
- `ctrl + right arrow`: turn to the left
- `space`: switch between first-person and third-person view
"""
import numpy as np
# import numpy as np
from itertools import count
import argparse
@@ -18,18 +30,26 @@ import pyrobolearn as prl
# create parser to select the game controller
parser = argparse.ArgumentParser()
parser.add_argument('-c', '--controller', help='the game controller to use', type=str,
choices=['xbox', 'ps3'], default='ps3')
parser.add_argument('-c', '--controller', help='the controller to use', type=str,
choices=['keyboard', 'xbox', 'ps'], default='keyboard')
args = parser.parse_args()
# load corresponding interface
# if args.controller == 'xbox':
# from pyrobolearn.tools.interfaces.controllers.xbox import Xbox360ControllerInterface as Controller
# elif args.controller == 'ps3':
# from pyrobolearn.tools.interfaces.controllers.playstation import PS3ControllerInterface as Controller
# else:
# raise NotImplementedError("Unknown game controller")
if args.controller == 'keyboard': # keyboard interface
from pyrobolearn.tools.interfaces.mouse_keyboard.mousekeyboard import MouseKeyboardInterface as Controller
from pyrobolearn.tools.bridges.mouse_keyboard.bridge_mousekeyboard_quadcopter \
import BridgeMouseKeyboardQuadcopter as Bridge
elif args.controller == 'xbox': # Xbox interface
from pyrobolearn.tools.interfaces.controllers.xbox import XboxControllerInterface as Controller
from pyrobolearn.tools.bridges.controllers.robots.bridge_controller_quadcopter import BridgeControllerQuadcopter \
as Bridge
elif args.controller == 'ps': # PS interface
from pyrobolearn.tools.interfaces.controllers.playstation import PSControllerInterface as Controller
from pyrobolearn.tools.bridges.controllers.robots.bridge_controller_quadcopter import BridgeControllerQuadcopter \
as Bridge
else:
raise NotImplementedError("Unknown game controller")
# create simulator
@@ -42,17 +62,17 @@ world = prl.worlds.BasicWorld(sim)
robot = prl.robots.Quadcopter(sim, position=[0., 0., 2.])
world.load_robot(robot)
# load interface
# controller = Controller()
# load interface that accepts input events
controller = Controller()
# load bridge that connects the interface/controller with the quadcopter
# The bridge is the one that maps the input events from the interface to commands sent to the quadcopter
bridge = Bridge(quadcopter=robot, interface=controller)
# run simulator
for t in count():
# robot.hover()
# robot.set_propeller_velocities(velocity)
robot.move([1., 1., 1.])
# follow quadcopter (seen from behind)
world.follow(robot, distance=2, yaw=-np.pi / 2)
# perform a step with the bridge and interface
bridge.step(update_interface=True)
# perform one step in the world
world.step(sleep_dt=1. / 240)
+23 -3
View File
@@ -49,15 +49,33 @@ class Epuck(DifferentialWheeledRobot):
if link in self.link_names]
self.wheel_directions = np.ones(len(self.wheels))
def turn(self, speed):
"""Turn the robot. If the speed is positive, turn to the left, otherwise turn to the right (using the
right-hand rule).
Args:
speed (float): speed to turn to the left (if speed is positive) or to the right (if speed is negative).
"""
self.set_joint_velocities(speed * np.array([-1, 1]), self.wheels)
def move(self, velocity):
"""Move the robot at the specified 2D velocity vector.
Args:
velocity (np.array[2]): 2D velocity vector defined in the xy plane. The magnitude represents the speed.
"""
velocities = np.array([velocity[0] + velocity[1], velocity[0] - velocity[1]])
self.set_joint_velocities(velocities=velocities)
# Test
if __name__ == "__main__":
from itertools import count
from pyrobolearn.simulators import BulletSim
from pyrobolearn.simulators import Bullet
from pyrobolearn.worlds import BasicWorld
# Create simulator
sim = BulletSim()
sim = Bullet()
# create world
world = BasicWorld(sim)
@@ -79,5 +97,7 @@ if __name__ == "__main__":
for _ in count():
# robots[0].update_joint_slider()
for robot in robots:
robot.drive(5)
# robot.drive(5)
# robot.turn(5)
robot.move([0., 1.])
world.step(sleep_dt=1./240)
+1 -1
View File
@@ -51,7 +51,7 @@ class F10Racecar(AckermannWheeledRobot):
self.steering = [self.get_link_ids(link) for link in ['left_steering_hinge', 'right_steering_hinge']
if link in self.link_names]
def set_steering(self, angle):
def steer(self, angle):
"""Set steering angle"""
angle = angle * np.ones(len(self.steering))
self.set_joint_positions(angle, joint_ids=self.steering)
+1 -1
View File
@@ -54,7 +54,7 @@ class MKZ(AckermannWheeledRobot):
self.steering = [self.get_link_ids(link) for link in ['steer_fl', 'steer_fr']
if link in self.link_names]
def set_steering(self, angle):
def steer(self, angle):
"""Set steering angle"""
angle = angle * np.ones(len(self.steering))
self.set_joint_positions(angle, joint_ids=self.steering)
+3 -2
View File
@@ -9,6 +9,7 @@ from pyrobolearn.robots.uav import RotaryWingUAV
from pyrobolearn.utils.transformation import get_matrix_from_quaternion
from pyrobolearn.utils.units import inches_to_meters, rpm_to_rad_per_second, rad_per_second_to_rpm
__author__ = "Brian Delhaisse"
__copyright__ = "Copyright 2018, PyRoboLearn"
__license__ = "GNU GPLv3"
@@ -278,7 +279,7 @@ class Quadcopter(RotaryWingUAV):
"""Turn the quadcopter to the left.
Args:
speed (float): speed to turn to the left.
speed (float): positive speed to turn to the left.
"""
velocities = self.stationary_velocities + speed * np.array([0, 1., 0., 1.]) * self.propeller_directions
self.set_propeller_velocities(velocities)
@@ -287,7 +288,7 @@ class Quadcopter(RotaryWingUAV):
"""Turn the quadcopter to the right.
Args:
speed (float): speed to turn to the right.
speed (float): positive speed to turn to the right.
"""
velocities = self.stationary_velocities + speed * np.array([1., 0., 1., 0.]) * self.propeller_directions
self.set_propeller_velocities(velocities)
+95 -8
View File
@@ -6,6 +6,7 @@ import numpy as np
from pyrobolearn.robots.robot import Robot
__author__ = "Brian Delhaisse"
__copyright__ = "Copyright 2018, PyRoboLearn"
__credits__ = ["Brian Delhaisse"]
@@ -68,6 +69,14 @@ class WheeledRobot(Robot):
return wheel_ids
return self.wheels
def move(self, velocity):
"""Move the robot at the specified 2D velocity vector.
Args:
velocity (np.array[2]): 2D velocity vector defined in the xy plane. The magnitude represents the speed.
"""
pass
def drive(self, speed):
if isinstance(speed, (int, float)):
speed = speed * np.ones(self.num_wheels)
@@ -83,12 +92,6 @@ class WheeledRobot(Robot):
def drive_backward(self, speed):
self.drive(-speed)
def turn_right(self):
pass
def turn_left(self):
pass
class DifferentialWheeledRobot(WheeledRobot):
r"""Differential Wheeled Robot
@@ -100,18 +103,62 @@ class DifferentialWheeledRobot(WheeledRobot):
Check also [2] for the different types of drive.
The kinematics of these kind of platforms (with two wheels) can be described mathematically by [4]:
.. math::
v &= \frac{r (\omega_R + \omega_L)}{2} \\
\omega &= \frac{r (\omega_R - \omega_L)}{d}
where :math:`\omega_R` (resp. :math:`\omega_L`) is the angular velocity of the right (resp. left) wheel,
:math:`v` is the driving velocity of the platform, :math:`\omega` is its steering velocity, :math:`r` is the
radius of the wheels and :math:`d` is the distance between their centers.
This formulation is equivalent to:
.. math::
\omega_R &= v + \frac{d}{2r} \omega \\
\omega_L &= v - \frac{d}{2r} \omega
References:
[1] Wikipedia: https://en.wikipedia.org/wiki/Differential_wheeled_robot
[2] "Pros and cons for different types of drive selection":
https://robohub.org/pros-and-cons-for-different-types-of-drive-selection/
[3] Wheel Control Theory:
http://www.robotplatform.com/knowledge/Classification_of_Robots/wheel_control_theory.html
[4] "Robotics: Modelling, Planning and Control" (section 11.2), Siciliano et al., 2010
"""
def __init__(self, simulator, urdf, position=None, orientation=None, fixed_base=False, scale=1.):
super(DifferentialWheeledRobot, self).__init__(simulator, urdf, position, orientation, fixed_base,
scale)
def turn(self, speed):
"""Turn the robot. If the speed is positive, turn to the left, otherwise turn to the right (using the
right-hand rule).
Args:
speed (float): speed to turn to the left (if speed is positive) or to the right (if speed is negative).
"""
pass
def turn_right(self, speed):
"""Turn the quadcopter to the right.
Args:
speed (float): positive speed to turn to the right.
"""
self.turn(speed)
def turn_left(self, speed):
"""Turn the robot to the left.
Args:
speed (float): positive speed to turn to the left.
"""
self.turn(-speed)
class AckermannWheeledRobot(WheeledRobot):
r"""Ackermann steering Wheeled Robot
@@ -136,6 +183,46 @@ class AckermannWheeledRobot(WheeledRobot):
self.steering = 0 # id of steering joint
def set_steering(self, angle):
"""Set steering angle"""
def move(self, velocity):
"""Move the robot at the specified 2D velocity vector.
Args:
velocity (np.array[2]): 2D velocity vector defined in the xy plane. The magnitude represents the speed.
"""
if velocity[0] > 0: # forward
angle = np.arctan2(velocity[1], velocity[0])
magnitude = np.linalg.norm(velocity)
self.steer(angle)
self.drive_forward(magnitude)
else: # backward
angle = np.arctan2(velocity[1], -velocity[0])
magnitude = np.linalg.norm(velocity)
self.steer(angle)
self.drive_backward(magnitude)
def steer(self, angle):
"""Set steering angle. If the angle is positive, turn to the left, otherwise turn to the right (using the
right-hand rule).
Args:
angle (float): steering angle. If the angle is positive, steer to the left, otherwise, steer to the right.
"""
pass
def steer_left(self, angle):
"""
Steer to the left at the specified angle.
Args:
angle (float): positive steering angle.
"""
self.steer(angle)
def steer_right(self, angle):
"""
Steer to the right at the specified angle.
Args:
angle (float): positive steering angle.
"""
self.steer(-angle)
+7
View File
@@ -17,6 +17,7 @@ References:
[3] PEP8: https://www.python.org/dev/peps/pep-0008/
"""
from pyrobolearn.utils.data_structures.orderedset import OrderedSet
__author__ = "Brian Delhaisse"
__copyright__ = "Copyright 2018, PyRoboLearn"
@@ -45,6 +46,9 @@ class Simulator(object):
[2] PEP8: https://www.python.org/dev/peps/pep-0008/
"""
# keep track of the instantiated simulators
instances = OrderedSet()
# TODO: this is really bad to have attributes like that... It doesn't generalize well to other simulators...
B3G_ALT = 65308
@@ -188,6 +192,9 @@ class Simulator(object):
self.default_timestep = 1. / 240
self.dt = self.default_timestep
# add instance to the set of all instantiated simulators
self.__class__.instances.add(self)
# TODO: this is really bad to have attributes like that... It doesn't generalize well to other simulators...
# import pybullet
# for attribute in dir(pybullet):
@@ -0,0 +1,202 @@
#!/usr/bin/env python
"""Define the Bridge between the game controller interface and the world.
Dependencies:
- `pyrobolearn.tools.interfaces.controllers.controller`
- `pyrobolearn.tools.bridges.Bridge`
"""
import numpy as np
from pyrobolearn.tools.interfaces.controllers.controller import GameControllerInterface
from pyrobolearn.tools.bridges import Bridge
from pyrobolearn.robots.quadcopter import Quadcopter
from pyrobolearn.worlds.world_camera import WorldCamera
from pyrobolearn.utils.transformation import get_rpy_from_quaternion
__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 BridgeControllerQuadcopter(Bridge):
r"""Bridge between game Controller and Quadcopter
Bridge between the game controller and a quadcopter robot.
Here is the mapping between the controller and the quadcopter:
- left joystick: use to move the quadcopter
- right joystick: use to ascend/descend and turn
- south button (X on PlayStation and A on Xbox): change between the first-person and third-person view.
- east button (circle on PlayStation and B on Xbox): increase the speed
- west button (square on PlayStation and X on Xbox): decrease the speed
"""
def __init__(self, quadcopter, interface, camera=None, first_person_view=False, speed=10,
priority=None, verbose=False):
"""
Initialize the Bridge between a game controller interface and a quadcopter.
Args:
quadcopter (Quadcopter): quadcopter robot instance.
interface (None, GameControllerInterface): game controller interface.
camera (WorldCamera): world camera instance. This will allow the user to switch between first-person and
third-person view. If None, it will create an instance of it.
first_person_view (bool): if True, it will set the world camera to the first person view. If False, it
will be the third-person view.
speed (float): speed of the propeller
priority (int): priority of the bridge.
verbose (bool): If True, print information on the standard output.
"""
# set quadcopter
self.quadcopter = quadcopter
if speed == 0:
speed = 1
self.speed = speed if speed > 0 else -speed
# check interface
if not isinstance(interface, GameControllerInterface):
raise TypeError("Expecting the given 'interface' to be an instance of `GameControllerInterface`, instead "
"got: {}".format(type(interface)))
# call superclass
super(BridgeControllerQuadcopter, self).__init__(interface, priority)
# camera
self.camera = camera
self.verbose = verbose
self.fpv = first_person_view
self.camera_pitch = self.camera.pitch
# joystick threshold (to remove noise)
self.threshold = 0.05
##############
# Properties #
##############
@property
def quadcopter(self):
"""Return the quadcopter instance."""
return self._quadcopter
@quadcopter.setter
def quadcopter(self, quadcopter):
"""Set the quadcopter instance."""
if not isinstance(quadcopter, Quadcopter):
raise TypeError("Expecting the given 'quadcopter' to be an instance of `Quadcopter`, instead got: "
"{}".format(type(quadcopter)))
self._quadcopter = quadcopter
@property
def simulator(self):
"""Return the simulator instance."""
return self._quadcopter.simulator
@property
def camera(self):
"""Return the world camera instance."""
return self._camera
@camera.setter
def camera(self, camera):
"""Set the world camera instance."""
if camera is None:
camera = WorldCamera(self.simulator)
elif not isinstance(camera, WorldCamera):
raise TypeError("Expecting the given 'camera' to be an instance of `WorldCamera`, instead got: "
"{}".format(type(camera)))
self._camera = camera
###########
# Methods #
###########
def step(self, update_interface=False):
"""Perform a step: map the mouse-keyboard interface to the world"""
# update interface
if update_interface:
self.interface()
# check interface events
self.check_events()
# set camera view
pitch, yaw = get_rpy_from_quaternion(self.quadcopter.orientation)[1:]
if self.fpv: # first-person view
target_pos = self.quadcopter.position + 2 * np.array([np.cos(yaw) * np.cos(pitch),
np.sin(yaw) * np.cos(pitch),
np.sin(pitch)])
self.camera.reset(distance=2, pitch=-pitch, yaw=yaw - np.pi / 2, target_position=target_pos)
else: # third-person view
self.camera.follow(body_id=self.quadcopter.id, distance=2, yaw=yaw - np.pi / 2, pitch=self.camera_pitch)
def change_camera_view(self):
"""Change camera view between first-person view and third-person view."""
self.fpv = not self.fpv
def check_events(self):
# move the quadcopter
left_joystick = self.interface.LJ # (x,y)
right_joystick = self.interface.RJ # (x,y)
south_button = self.interface.BTN_SOUTH
east_button = self.interface.BTN_EAST
west_button = self.interface.BTN_WEST
left_norm, right_norm = np.linalg.norm(left_joystick), np.linalg.norm(right_joystick)
# change camera view
if south_button:
self.change_camera_view()
# change speed
if east_button:
self.speed += 1
if west_button:
self.speed -= 1
# move the quadcopter
if left_norm > self.threshold: # left joystick
if right_norm > self.threshold: # with right joystick
velocity = self.speed * np.array([left_joystick[1], left_joystick[0], right_joystick[1]])
else:
velocity = self.speed * np.array([left_joystick[1], left_joystick[0], 0.])
self.quadcopter.move(velocity=velocity)
elif right_norm > self.threshold: # right joystick
if np.abs(right_joystick[1]) > np.abs(right_joystick[0]):
self.quadcopter.move(velocity=self.speed * np.array([0, 0, right_joystick[1]]))
else:
self.quadcopter.turn(speed=-1 * right_joystick[0])
else:
self.quadcopter.hover()
# Tests
if __name__ == '__main__':
from itertools import count
import pyrobolearn as prl
from pyrobolearn.tools.interfaces.controllers.playstation import PSControllerInterface
# create simulator
sim = prl.simulators.Bullet()
# create World
world = prl.worlds.BasicWorld(sim)
# load robot
# robot = world.load_robot('quadcopter')
robot = Quadcopter(sim, position=[0, 0, 2.])
# create bridge/interface
controller = PSControllerInterface(use_thread=True, sleep_dt=0.01)
bridge = BridgeControllerQuadcopter(robot, interface=controller, verbose=True)
for _ in count():
bridge.step(update_interface=False) # when using thread for the interface, it updates itself automatically
world.step(sleep_dt=sim.dt)
@@ -2,9 +2,15 @@
"""Bridges between controller interface and wheeled robots
"""
from pyrobolearn.robots import WheeledRobot, AckermannWheeledRobot
from pyrobolearn.tools.interfaces.controllers.xbox import XboxControllerInterface, XboxOneControllerInterface
from abc import ABCMeta
import numpy as np
from pyrobolearn.tools.interfaces.controllers.controller import GameControllerInterface
from pyrobolearn.tools.bridges.bridge import Bridge
from pyrobolearn.robots.wheeled_robot import WheeledRobot, DifferentialWheeledRobot, AckermannWheeledRobot
from pyrobolearn.worlds.world_camera import WorldCamera
from pyrobolearn.utils.transformation import get_rpy_from_quaternion
__author__ = "Brian Delhaisse"
@@ -17,74 +23,246 @@ __email__ = "briandelhaisse@gmail.com"
__status__ = "Development"
class BridgeXboxWheeledRobot(Bridge):
r"""Bridge Xbox Wheeled Robot
class BridgeControllerWheeledRobot(Bridge):
r"""Bridge between GameController and a wheeled robot
Bridge between the Xbox controller interface and a wheeled robot. You can move the robot using the joystick.
Bridge between the game controller and a wheeled robot.
Here is the mapping between the controller and the robot:
- left joystick: velocity of the wheeled robot
- south button (X on PlayStation and A on Xbox): change between the first-person and third-person view # TODO
- east button (circle on PlayStation and B on Xbox): increase the speed # TODO
- west button (square on PlayStation and X on Xbox): decrease the speed # TODO
"""
__metaclass__ = ABCMeta
def __init__(self, robot, interface=None, camera=None, first_person_view=False, speed=10,
priority=None, verbose=False):
"""
Initialize the Bridge between a game controller interface and a wheeled robot instance.
Args:
robot (WheeledRobot): wheeled robot instance.
interface (GameControllerInterface): game controller interface.
camera (WorldCamera): world camera instance. This will allow the user to switch between first-person and
third-person view. If None, it will create an instance of it.
first_person_view (bool): if True, it will set the world camera to the first person view. If False, it
will be the third-person view.
speed (float): speed of the propeller
priority (int): priority of the bridge.
verbose (bool): If True, print information on the standard output.
"""
# set robot
self.robot = robot
if speed == 0:
speed = 1
self.speed = speed if speed > 0 else -speed
# if interface not defined, create one.
if not isinstance(interface, GameControllerInterface):
raise TypeError
# call superclass
super(BridgeControllerWheeledRobot, self).__init__(interface, priority)
# camera
self.camera = camera
self.verbose = verbose
self.fpv = first_person_view
self.camera_pitch = self.camera.pitch
# joystick threshold (to remove noise)
self.threshold = 0.05
##############
# Properties #
##############
@property
def robot(self):
"""Return the wheeled robot instance."""
return self._robot
@robot.setter
def robot(self, robot):
"""Set the wheeled robot instance."""
if not isinstance(robot, WheeledRobot):
raise TypeError("Expecting the given 'robot' to be an instance of `WheeledRobot`, instead got: "
"{}".format(type(robot)))
self._robot = robot
@property
def simulator(self):
"""Return the simulator instance."""
return self._robot.simulator
@property
def camera(self):
"""Return the world camera instance."""
return self._camera
@camera.setter
def camera(self, camera):
"""Set the world camera instance."""
if camera is None:
camera = WorldCamera(self.simulator)
elif not isinstance(camera, WorldCamera):
raise TypeError("Expecting the given 'camera' to be an instance of `WorldCamera`, instead got: "
"{}".format(type(camera)))
self._camera = camera
###########
# Methods #
###########
def step(self, update_interface=False):
"""Perform a step: map the mouse-keyboard interface to the world"""
# update interface
if update_interface:
self.interface()
# check keyboard events
self.check_key_events()
# set camera view
pitch, yaw = get_rpy_from_quaternion(self.robot.orientation)[1:]
if self.fpv: # first-person view
target_pos = self.robot.position + 2 * np.array([np.cos(yaw) * np.cos(pitch),
np.sin(yaw) * np.cos(pitch),
np.sin(pitch)])
self.camera.reset(distance=2, pitch=-pitch, yaw=yaw - np.pi / 2, target_position=target_pos)
else: # third-person view
self.camera.follow(body_id=self.robot.id, distance=2, yaw=yaw - np.pi / 2, pitch=self.camera_pitch)
def change_camera_view(self):
"""Change camera view between first-person view and third-person view."""
self.fpv = not self.fpv
def check_key_events(self):
left_joystick = self.interface.LJ # (x,y)
# south_button = self.interface.BTN_SOUTH
# east_button = self.interface.BTN_EAST
# west_button = self.interface.BTN_WEST
# change camera view
# if south_button:
# self.change_camera_view()
# change speed
# if east_button:
# self.speed += 1
# if west_button:
# self.speed -= 1
# move robot
# if np.linalg.norm(left_joystick) > self.threshold:
# # print(left_joystick)
# self.robot.move(velocity=self.speed * left_joystick)
# else:
# self.robot.move(velocity=[0., 0.])
print(left_joystick[0])
class BridgeControllerDifferentialWheeledRobot(BridgeControllerWheeledRobot):
r"""Bridge between the mouse-keyboard and a differential wheeled robot.
Here is the mapping between the controller and the robot:
- left joystick: velocity of the wheeled robot
- south button (X on PlayStation and A on Xbox): change between the first-person and third-person view.
- east button (circle on PlayStation and B on Xbox): increase the speed
- west button (square on PlayStation and X on Xbox): decrease the speed
"""
def __init__(self, interface, wheeled_robot):
# quick checks
if not isinstance(interface, XboxControllerInterface):
raise TypeError("Expecting a speech recognizer interface")
if not isinstance(wheeled_robot, WheeledRobot):
raise TypeError("Expecting a wheeled robot")
def __init__(self, robot, interface=None, camera=None, first_person_view=False, speed=10, priority=None,
verbose=False):
"""
Initialize the Bridge between a game controller interface and a differential wheeled robot instance.
# call super class
super(BridgeXboxWheeledRobot, self).__init__(interface)
self.robot = wheeled_robot
self.speed = 1.
Args:
robot (AckermannWheeledRobot): wheeled robot instance.
interface (GameControllerInterface): game controller interface.
camera (WorldCamera): world camera instance. This will allow the user to switch between first-person and
third-person view. If None, it will create an instance of it.
first_person_view (bool): if True, it will set the world camera to the first person view. If False, it
will be the third-person view.
speed (float): speed of the propeller
priority (int): priority of the bridge.
verbose (bool): If True, print information on the standard output.
"""
if not isinstance(robot, DifferentialWheeledRobot):
raise TypeError("Expecting the given 'robot' to be an instance of `DifferentialWheeledRobot`, instead "
"got: {}".format(type(robot)))
super(BridgeControllerDifferentialWheeledRobot, self).__init__(robot, interface=interface, camera=camera,
first_person_view=first_person_view,
speed=speed, priority=priority,
verbose=verbose)
def step(self):
x, y = self.interface.LJ
def check_key_events(self):
super(BridgeControllerDifferentialWheeledRobot, self).check_key_events()
directional_pad = self.interface.Dpad # (x,y)
# move robot
if directional_pad[0] != 0:
self.robot.turn(directional_pad[0])
if directional_pad[1] != 0:
self.robot.drive_forward(directional_pad[1] * self.speed)
class BridgeXboxOneWheeledRobot(Bridge):
r"""Bridge Xbox Wheeled Robot
class BridgeControllerAckermannWheeledRobot(BridgeControllerWheeledRobot):
r"""Bridge between the mouse-keyboard and a Ackermann wheeled robot.
Bridge between the Xbox One controller interface and a wheeled robot. You can move the robot using the joystick.
Here is the mapping between the controller and the robot:
- left joystick: velocity of the wheeled robot
- south button (X on PlayStation and A on Xbox): change between the first-person and third-person view.
- east button (circle on PlayStation and B on Xbox): increase the speed
- west button (square on PlayStation and X on Xbox): decrease the speed
"""
def __init__(self, interface, wheeled_robot):
# quick check
if not isinstance(interface, XboxOneControllerInterface):
raise TypeError("Expecting a speech recognizer interface")
if not isinstance(wheeled_robot, WheeledRobot):
raise TypeError("Expecting a wheeled robot")
def __init__(self, robot, interface, camera=None, first_person_view=False, speed=10, priority=None,
verbose=False):
"""
Initialize the Bridge between a game controller interface and a wheeled robot instance.
super(BridgeXboxOneWheeledRobot, self).__init__(interface)
self.robot = wheeled_robot
self.speed = 1.
def step(self):
x, y = self.interface.LJ
Args:
robot (DifferentialWheeledRobot): wheeled robot instance.
interface (GameControllerInterface): game controller interface.
camera (WorldCamera): world camera instance. This will allow the user to switch between first-person and
third-person view. If None, it will create an instance of it.
first_person_view (bool): if True, it will set the world camera to the first person view. If False, it
will be the third-person view.
speed (float): speed of the propeller
priority (int): priority of the bridge.
verbose (bool): If True, print information on the standard output.
"""
if not isinstance(robot, AckermannWheeledRobot):
raise TypeError("Expecting the given 'robot' to be an instance of `AckermannWheeledRobot`, instead got: "
"{}".format(type(robot)))
super(BridgeControllerAckermannWheeledRobot, self).__init__(robot, interface=interface, camera=camera,
first_person_view=first_person_view,
speed=speed, priority=priority, verbose=verbose)
class BridgeXboxOneAckermannWheeledRobot(Bridge):
r"""Bridge Xbox One Ackermann Wheeled Robot
# Tests
if __name__ == '__main__':
from itertools import count
import pyrobolearn as prl
from pyrobolearn.tools.interfaces.controllers.playstation import PSControllerInterface
Bridge between the Xbox One controller interface and a wheeled robot. You can move the robot using the joystick.
"""
# create simulator
sim = prl.simulators.Bullet()
def __init__(self, interface, wheeled_robot):
if not isinstance(interface, XboxOneControllerInterface):
raise TypeError("Expecting a speech recognizer interface")
if not isinstance(wheeled_robot, AckermannWheeledRobot):
raise TypeError("Expecting a wheeled robot")
super(BridgeXboxOneAckermannWheeledRobot, self).__init__(interface)
self.robot = wheeled_robot
self.speed = 1.
# create World
world = prl.worlds.BasicWorld(sim)
def step(self):
x, y = self.interface.LJ
self.robot.set_steering(-x / 2.)
self.robot.drive_forward(y * self.speed)
# load robot
# robot = world.load_robot('epuck')
robot = prl.robots.Epuck(sim)
if self.interface.A:
print('increasing speed +1')
self.speed += 1.
if self.interface.B:
print('decreasing speed -1')
self.speed -= 1.
if self.speed < 1.:
self.speed = 1.
# create bridge/interface
interface = PSControllerInterface(use_thread=True, sleep_dt=0.01)
bridge = BridgeControllerWheeledRobot(robot, interface=interface, verbose=True)
# run simulator
for _ in count():
bridge.step(update_interface=False) # when using thread for the interface, it updates itself automatically
world.step(sleep_dt=sim.dt)
@@ -0,0 +1,210 @@
#!/usr/bin/env python
"""Define the Bridge between the mouse-keyboard interface and the world.
Dependencies:
- `pyrobolearn.tools.interfaces.MouseKeyboardInterface`
- `pyrobolearn.tools.bridges.Bridge`
"""
import numpy as np
from pyrobolearn.tools.interfaces import MouseKeyboardInterface
from pyrobolearn.tools.bridges import Bridge
from pyrobolearn.robots.quadcopter import Quadcopter
from pyrobolearn.worlds.world_camera import WorldCamera
from pyrobolearn.utils.transformation import get_rpy_from_quaternion
__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 BridgeMouseKeyboardQuadcopter(Bridge):
r"""Bridge between MouseKeyboard and Quadcopter
Bridge between the mouse-keyboard and a quadcopter robot.
Mouse:
* predefined in simulator:
* `scroll wheel`: zoom
* `ctrl`/`alt` + `scroll button`: move the camera using the mouse
* `ctrl`/`alt` + `left-click`: rotate the camera using the mouse
* `left-click` and drag: transport the object
Keyboard:
* `top arrow`: move forward
* `bottom arrow`: move backward
* `left arrow`: move sideways to the left
* `right arrow`: move sideways to the right
* `ctrl + top arrow`: ascend
* `ctrl + bottom arrow`: descend
* `ctrl + left arrow`: turn to the right
* `ctrl + right arrow`: turn to the left
* `space`: switch between first-person and third-person view.
* predefined in simulator:
* `w`: show the wireframe (collision shapes)
* `s`: show the reference system
* `v`: show bounding boxes
* `g`: show/hide parts of the GUI the side columns
* `esc`: quit the simulator
"""
def __init__(self, quadcopter, interface=None, camera=None, first_person_view=False, speed=10,
priority=None, verbose=False):
"""
Initialize the Bridge between a Mouse-Keyboard interface and a quadcopter.
Args:
quadcopter (Quadcopter): quadcopter robot instance.
interface (None, MouseKeyboardInterface): mouse keyboard interface. If None, it will create one.
camera (WorldCamera): world camera instance. This will allow the user to switch between first-person and
third-person view. If None, it will create an instance of it.
first_person_view (bool): if True, it will set the world camera to the first person view. If False, it
will be the third-person view.
speed (float): speed of the propeller
priority (int): priority of the bridge.
verbose (bool): If True, print information on the standard output.
"""
# set quadcopter
self.quadcopter = quadcopter
if speed == 0:
speed = 1
self.speed = speed if speed > 0 else -speed
# if interface not defined, create one.
if not isinstance(interface, MouseKeyboardInterface):
interface = MouseKeyboardInterface(self.simulator)
# call superclass
super(BridgeMouseKeyboardQuadcopter, self).__init__(interface, priority)
# camera
self.camera = camera
self.verbose = verbose
self.fpv = first_person_view
self.camera_pitch = self.camera.pitch
##############
# Properties #
##############
@property
def quadcopter(self):
"""Return the quadcopter instance."""
return self._quadcopter
@quadcopter.setter
def quadcopter(self, quadcopter):
"""Set the quadcopter instance."""
if not isinstance(quadcopter, Quadcopter):
raise TypeError("Expecting the given 'quadcopter' to be an instance of `Quadcopter`, instead got: "
"{}".format(type(quadcopter)))
self._quadcopter = quadcopter
@property
def simulator(self):
"""Return the simulator instance."""
return self._quadcopter.simulator
@property
def camera(self):
"""Return the world camera instance."""
return self._camera
@camera.setter
def camera(self, camera):
"""Set the world camera instance."""
if camera is None:
camera = WorldCamera(self.simulator)
elif not isinstance(camera, WorldCamera):
raise TypeError("Expecting the given 'camera' to be an instance of `WorldCamera`, instead got: "
"{}".format(type(camera)))
self._camera = camera
###########
# Methods #
###########
def step(self, update_interface=False):
"""Perform a step: map the mouse-keyboard interface to the world"""
# update interface
if update_interface:
self.interface()
# check keyboard events
self.check_key_events()
# set camera view
pitch, yaw = get_rpy_from_quaternion(self.quadcopter.orientation)[1:]
if self.fpv: # first-person view
target_pos = self.quadcopter.position + 2 * np.array([np.cos(yaw) * np.cos(pitch),
np.sin(yaw) * np.cos(pitch),
np.sin(pitch)])
self.camera.reset(distance=2, pitch=-pitch, yaw=yaw - np.pi / 2, target_position=target_pos)
else: # third-person view
self.camera.follow(body_id=self.quadcopter.id, distance=2, yaw=yaw - np.pi / 2, pitch=self.camera_pitch)
def change_camera_view(self):
"""Change camera view between first-person view and third-person view."""
self.fpv = not self.fpv
def check_key_events(self):
key, pressed, down = self.interface.key, self.interface.key_pressed, self.interface.key_down
# change camera view
if key.space in pressed:
self.change_camera_view()
# move the quadcopter
if key.ctrl in down:
if key.top_arrow in down:
self.quadcopter.ascend(speed=10 * self.speed)
elif key.bottom_arrow in down:
self.quadcopter.descend(speed=10 * self.speed)
elif key.left_arrow in down:
self.quadcopter.turn_left(speed=self.speed)
elif key.right_arrow in down:
self.quadcopter.turn_right(speed=self.speed)
else:
self.quadcopter.hover()
else:
if key.top_arrow in down:
self.quadcopter.move_forward(speed=self.speed)
elif key.bottom_arrow in down:
self.quadcopter.move_backward(speed=self.speed)
elif key.left_arrow in down:
self.quadcopter.move_left(speed=self.speed)
elif key.right_arrow in down:
self.quadcopter.move_right(speed=self.speed)
else:
self.quadcopter.hover()
# Tests
if __name__ == '__main__':
from itertools import count
import pyrobolearn as prl
# create simulator
sim = prl.simulators.Bullet()
# create World
world = prl.worlds.BasicWorld(sim)
# load robot
# robot = world.load_robot('quadcopter')
robot = Quadcopter(sim, position=[0, 0, 1.])
# create bridge/interface
bridge = BridgeMouseKeyboardQuadcopter(robot, verbose=True)
for _ in count():
bridge.step(update_interface=True)
world.step(sleep_dt=sim.dt)
@@ -0,0 +1,301 @@
#!/usr/bin/env python
"""Define the Bridge between the mouse-keyboard interface and the world.
Dependencies:
- `pyrobolearn.tools.interfaces.MouseKeyboardInterface`
- `pyrobolearn.tools.bridges.Bridge`
"""
from abc import ABCMeta
import numpy as np
from pyrobolearn.tools.interfaces import MouseKeyboardInterface
from pyrobolearn.tools.bridges import Bridge
from pyrobolearn.robots.wheeled_robot import WheeledRobot, DifferentialWheeledRobot, AckermannWheeledRobot
from pyrobolearn.worlds.world_camera import WorldCamera
from pyrobolearn.utils.transformation import get_rpy_from_quaternion
__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 BridgeMouseKeyboardWheeledRobot(Bridge):
r"""Bridge between MouseKeyboard and a wheeled robot
Bridge between the mouse-keyboard and a wheeled robot.
Mouse:
* predefined in simulator:
* `scroll wheel`: zoom
* `ctrl`/`alt` + `scroll button`: move the camera using the mouse
* `ctrl`/`alt` + `left-click`: rotate the camera using the mouse
* `left-click` and drag: transport the object
Keyboard:
* `top arrow`: move forward
* `bottom arrow`: move backward
* `left arrow`: turn/steer to the left
* `right arrow`: turn/steer to the right
* `space`: switch between first-person and third-person view. # TODO
* predefined in simulator:
* `w`: show the wireframe (collision shapes)
* `s`: show the reference system
* `v`: show bounding boxes
* `g`: show/hide parts of the GUI the side columns
* `esc`: quit the simulator
"""
__metaclass__ = ABCMeta
def __init__(self, robot, interface=None, camera=None, first_person_view=False, speed=10,
priority=None, verbose=False):
"""
Initialize the Bridge between a Mouse-Keyboard interface and a wheeled robot instance.
Args:
robot (WheeledRobot): wheeled robot instance.
interface (None, MouseKeyboardInterface): mouse keyboard interface. If None, it will create one.
camera (WorldCamera): world camera instance. This will allow the user to switch between first-person and
third-person view. If None, it will create an instance of it.
first_person_view (bool): if True, it will set the world camera to the first person view. If False, it
will be the third-person view.
speed (float): speed of the propeller
priority (int): priority of the bridge.
verbose (bool): If True, print information on the standard output.
"""
# set robot
self.robot = robot
if speed == 0:
speed = 1
self.speed = speed if speed > 0 else -speed
# if interface not defined, create one.
if not isinstance(interface, MouseKeyboardInterface):
interface = MouseKeyboardInterface(self.simulator)
# call superclass
super(BridgeMouseKeyboardWheeledRobot, self).__init__(interface, priority)
# camera
self.camera = camera
self.verbose = verbose
self.fpv = first_person_view
self.camera_pitch = self.camera.pitch
##############
# Properties #
##############
@property
def robot(self):
"""Return the wheeled robot instance."""
return self._robot
@robot.setter
def robot(self, robot):
"""Set the wheeled robot instance."""
if not isinstance(robot, WheeledRobot):
raise TypeError("Expecting the given 'robot' to be an instance of `WheeledRobot`, instead got: "
"{}".format(type(robot)))
self._robot = robot
@property
def simulator(self):
"""Return the simulator instance."""
return self._robot.simulator
@property
def camera(self):
"""Return the world camera instance."""
return self._camera
@camera.setter
def camera(self, camera):
"""Set the world camera instance."""
if camera is None:
camera = WorldCamera(self.simulator)
elif not isinstance(camera, WorldCamera):
raise TypeError("Expecting the given 'camera' to be an instance of `WorldCamera`, instead got: "
"{}".format(type(camera)))
self._camera = camera
###########
# Methods #
###########
def step(self, update_interface=False):
"""Perform a step: map the mouse-keyboard interface to the world"""
# update interface
if update_interface:
self.interface()
# check keyboard events
self.check_key_events()
# set camera view
pitch, yaw = get_rpy_from_quaternion(self.robot.orientation)[1:]
if self.fpv: # first-person view
target_pos = self.robot.position + 2 * np.array([np.cos(yaw) * np.cos(pitch),
np.sin(yaw) * np.cos(pitch),
np.sin(pitch)])
self.camera.reset(distance=2, pitch=-pitch, yaw=yaw - np.pi / 2, target_position=target_pos)
else: # third-person view
self.camera.follow(body_id=self.robot.id, distance=2, yaw=yaw - np.pi / 2, pitch=self.camera_pitch)
def change_camera_view(self):
"""Change camera view between first-person view and third-person view."""
self.fpv = not self.fpv
def check_key_events(self):
pass
class BridgeMouseKeyboardDifferentialWheeledRobot(BridgeMouseKeyboardWheeledRobot):
r"""Bridge between the mouse-keyboard and a differential wheeled robot.
Keyboard:
* `top arrow`: move forward
* `bottom arrow`: move backward
* `left arrow`: turn to the left
* `right arrow`: turn to the right
* `space`: switch between first-person and third-person view.
* predefined in simulator:
* `w`: show the wireframe (collision shapes)
* `s`: show the reference system
* `v`: show bounding boxes
* `g`: show/hide parts of the GUI the side columns
* `esc`: quit the simulator
"""
def __init__(self, robot, interface=None, camera=None, first_person_view=False, speed=10, priority=None,
verbose=False):
"""
Initialize the Bridge between a Mouse-Keyboard interface and a differential wheeled robot instance.
Args:
robot (AckermannWheeledRobot): wheeled robot instance.
interface (None, MouseKeyboardInterface): mouse keyboard interface. If None, it will create one.
camera (WorldCamera): world camera instance. This will allow the user to switch between first-person and
third-person view. If None, it will create an instance of it.
first_person_view (bool): if True, it will set the world camera to the first person view. If False, it
will be the third-person view.
speed (float): speed of the propeller
priority (int): priority of the bridge.
verbose (bool): If True, print information on the standard output.
"""
if not isinstance(robot, DifferentialWheeledRobot):
raise TypeError("Expecting the given 'robot' to be an instance of `DifferentialWheeledRobot`, instead "
"got: {}".format(type(robot)))
super(BridgeMouseKeyboardDifferentialWheeledRobot, self).__init__(robot, interface=interface, camera=camera,
first_person_view=first_person_view,
speed=speed, priority=priority,
verbose=verbose)
def check_key_events(self):
key, pressed, down = self.interface.key, self.interface.key_pressed, self.interface.key_down
# change camera view
# if key.space in pressed:
# self.change_camera_view()
# move the robot
if key.top_arrow in down:
self.robot.drive(speed=self.speed)
elif key.bottom_arrow in down:
self.robot.drive(speed=-self.speed)
elif key.left_arrow in down:
self.robot.turn(speed=self.speed)
elif key.right_arrow in down:
self.robot.turn(speed=-self.speed)
else:
self.robot.drive(speed=0)
class BridgeMouseKeyboardAckermannWheeledRobot(BridgeMouseKeyboardWheeledRobot):
r"""Bridge between the mouse-keyboard and a Ackermann wheeled robot.
Keyboard:
* `top arrow`: move forward
* `bottom arrow`: move backward
* `left arrow`: steer to the left
* `right arrow`: steer to the right
* `space`: switch between first-person and third-person view.
* predefined in simulator:
* `w`: show the wireframe (collision shapes)
* `s`: show the reference system
* `v`: show bounding boxes
* `g`: show/hide parts of the GUI the side columns
* `esc`: quit the simulator
"""
def __init__(self, robot, interface=None, camera=None, first_person_view=False, speed=10, priority=None,
verbose=False):
"""
Initialize the Bridge between a Mouse-Keyboard interface and a wheeled robot instance.
Args:
robot (DifferentialWheeledRobot): wheeled robot instance.
interface (None, MouseKeyboardInterface): mouse keyboard interface. If None, it will create one.
camera (WorldCamera): world camera instance. This will allow the user to switch between first-person and
third-person view. If None, it will create an instance of it.
first_person_view (bool): if True, it will set the world camera to the first person view. If False, it
will be the third-person view.
speed (float): speed of the propeller
priority (int): priority of the bridge.
verbose (bool): If True, print information on the standard output.
"""
if not isinstance(robot, AckermannWheeledRobot):
raise TypeError("Expecting the given 'robot' to be an instance of `AckermannWheeledRobot`, instead got: "
"{}".format(type(robot)))
super(BridgeMouseKeyboardAckermannWheeledRobot, self).__init__(robot, interface=interface, camera=camera,
first_person_view=first_person_view,
speed=speed, priority=priority, verbose=verbose)
def check_key_events(self):
key, pressed, down = self.interface.key, self.interface.key_pressed, self.interface.key_down
# change camera view
# if key.space in pressed:
# self.change_camera_view()
# move the robot
if key.top_arrow in down:
self.robot.drive(speed=self.speed)
elif key.bottom_arrow in down:
self.robot.drive(speed=self.speed)
elif key.left_arrow in down:
self.robot.steer(speed=self.speed)
elif key.right_arrow in down:
self.robot.steer(speed=-self.speed)
else:
self.robot.drive(speed=0)
# Tests
if __name__ == '__main__':
from itertools import count
import pyrobolearn as prl
# create simulator
sim = prl.simulators.Bullet()
# create World
world = prl.worlds.BasicWorld(sim)
# load robot
# robot = world.load_robot('epuck')
robot = prl.robots.Epuck(sim, position=[0, 0, 1.])
# create bridge/interface
bridge = BridgeMouseKeyboardDifferentialWheeledRobot(robot, verbose=True)
for _ in count():
bridge.step(update_interface=True)
world.step(sleep_dt=sim.dt)
@@ -4,6 +4,8 @@
This provides the interfaces for the PlayStation controllers (PS3 and PS4) using the `inputs` library.
"""
import numpy as np
try:
from inputs import devices, get_gamepad
except ImportError as e:
@@ -53,21 +55,23 @@ class PSControllerInterface(GameControllerInterface):
raise ValueError("The specified gamepad/controller was not detected.")
# translation
buttons = ['BTN_SOUTH', 'BTN_EAST', 'BTN_WEST', 'BTN_NORTH', 'BTN_THUMBL', 'BTN_THUMBR', 'BTN_TL', 'BTN_TL2',
'BTN_TR', 'BTN_TR2', 'BTN_START', 'BTN_SELECT', 'BTN_MODE', 'ABS_HAT0X', 'ABS_HAT0Y', 'ABS_Z',
'ABS_RZ', 'ABS_X', 'ABS_Y', 'ABS_RX', 'ABS_RY']
ps4_buttons = ['X', 'O', 'S', 'T', 'LJB', 'RJB', 'L1', 'L2', 'R1', 'R2', 'options', 'share', 'PS', 'L', 'R',
'RT', 'LJX', 'LJY', 'RJX', 'RJY']
buttons = ['BTN_EAST', 'BTN_C', 'BTN_SOUTH', 'BTN_NORTH', 'BTN_SELECT', 'BTN_START', 'BTN_WEST', 'BTN_TL',
'BTN_Z', 'BTN_TR', 'BTN_TR2', 'BTN_TL2', 'BTN_MODE', 'BTN_THUMBL', 'ABS_HAT0X', 'ABS_HAT0Y',
'ABS_X', 'ABS_Y', 'ABS_Z', 'ABS_RZ'] # , 'ABS_RX', 'ABS_RY', 'BTN_THUMBR']
ps4_buttons = ['X', 'O', 'S', 'T', 'LJB', 'RJB', 'L1', 'L2', 'R1', 'R2', 'options', 'share', 'PS', 'pad',
'DpadX', 'DpadY', 'LJX', 'LJY', 'RJX', 'RJY']
self.map = dict(zip(buttons, ps4_buttons))
self.inv_map = dict(zip(ps4_buttons, buttons))
# buttons and their values
self.buttons = dict(zip(ps4_buttons[:12], [0] * 12))
self.buttons.update(dict(zip(['Dpad', 'LJ', 'RJ'], [[0, 0]] * 3)))
self.buttons = dict(zip(ps4_buttons[:14], [0] * 14))
self.buttons.update(dict(zip(['Dpad', 'LJ', 'RJ'], [np.array([0., 0.])] * 3)))
# last updated button
self.last_updated_button = None
# pushed buttons
super(PSControllerInterface, self).__init__(use_thread=use_thread, sleep_dt=sleep_dt, verbose=verbose)
##############
@@ -79,24 +83,33 @@ class PSControllerInterface(GameControllerInterface):
"""Button X"""
return self.buttons['X']
# alias
cross = X
@property
def O(self):
"""Button O (circle)"""
return self.buttons['O']
# alias (C = circle)
C = O
# alias
circle = O
@property
def S(self):
"""Button Square"""
return self.buttons['S']
# alias
square = S
@property
def T(self):
"""Button Triangle"""
return self.buttons['T']
# alias
triangle = T
@property
def LJB(self):
"""Left Joystick Button"""
@@ -108,34 +121,44 @@ class PSControllerInterface(GameControllerInterface):
return self.buttons['RJB']
@property
def LB(self):
def L1(self):
"""left bumper; button for left index finger"""
return self.buttons['LB']
return self.buttons['L1']
@property
def RB(self):
def R1(self):
"""right bumper; button for right index finger"""
return self.buttons['RB']
return self.buttons['R1']
@property
def menu(self):
"""menu button"""
return self.buttons['menu']
@property
def view(self):
"""view button"""
return self.buttons['view']
@property
def LT(self):
def L2(self):
"""Left trigger; button for left middle finger"""
return self.buttons['LT']
return self.buttons['L2']
@property
def RT(self):
def R2(self):
"""Right trigger; button for right middle finger"""
return self.buttons['RT']
return self.buttons['R2']
@property
def options(self):
"""menu button"""
return self.buttons['options']
@property
def share(self):
"""share button"""
return self.buttons['share']
@property
def PS(self):
"""PS button"""
return self.buttons['PS']
@property
def pad(self):
"""pad button"""
return self.buttons['pad']
@property
def Dpad(self):
@@ -156,6 +179,117 @@ class PSControllerInterface(GameControllerInterface):
left_joystick = LJ
right_joystick = RJ
# NOTE: the following buttons have been manually remapped to better correspond to what their name suggests
@property
def BTN_SOUTH(self):
"""South button"""
return self.buttons['X']
@property
def BTN_EAST(self):
"""East button"""
return self.buttons['O']
@property
def BTN_WEST(self):
"""West button"""
return self.buttons['S']
@property
def BTN_NORTH(self):
"""North button"""
return self.buttons['T']
@property
def BTN_C(self):
"""Circle button"""
return self.buttons['O']
@property
def BTN_THUMBL(self):
"""Left thumb button"""
return self.buttons['LJB']
@property
def BTN_THUMBR(self):
"""Right thumb button"""
return self.buttons['RJB']
@property
def BTN_TL(self):
"""Left bumper; button for left index finger"""
return self.buttons['L1']
@property
def BTN_TL2(self):
"""Left bumper 2; button for left middle finger"""
return self.buttons['L2']
@property
def BTN_TR(self):
"""Right bumper; button for right index finger"""
return self.buttons['R1']
@property
def BTN_TR2(self):
"""Right bumper 2; button for right middle finger"""
return self.buttons['R2']
@property
def BTN_START(self):
"""Start button"""
return self.buttons['share']
@property
def BTN_SELECT(self):
"""Select button"""
return self.buttons['options']
@property
def BTN_MODE(self):
"""Mode button"""
return self.buttons['PS']
@property
def ABS_Z(self):
"""Left trigger; non-existent for PS controller; return the same as L2."""
return self.buttons['L2']
@property
def ABS_RZ(self):
"""Right trigger; non-existent for PS controller; return the same as R2."""
return self.buttons['R2']
@property
def ABS_HAT0X(self):
"""Directional pad X position"""
return self.buttons['Dpad'][0]
@property
def ABS_HAT0Y(self):
"""Directional pad Y position"""
return self.buttons['Dpad'][1]
@property
def ABS_X(self):
"""Left joystick X position"""
return self.buttons['LJ'][0]
@property
def ABS_Y(self):
"""Left joystick Y position"""
return self.buttons['LJ'][1]
@property
def ABS_RX(self):
"""Right joystick X position"""
return self.buttons['RJ'][0]
@property
def ABS_RY(self):
"""Right joystick Y position"""
return self.buttons['RJ'][1]
###########
# Methods #
###########
@@ -202,16 +336,16 @@ class PSControllerInterface(GameControllerInterface):
if event_type == 'Absolute':
if key == 'LJX':
self.buttons['LJ'][0] = value / 32768. # values between [-32768, 32767]
self.buttons['LJ'][0] = (value - 127.5) / 127.5 # values between [0, 255]
self.last_updated_button = 'LJ'
elif key == 'LJY':
self.buttons['LJ'][1] = -1. * value / 32768. # values between [-32767, 32768]
self.buttons['LJ'][1] = -1. * (value - 127.5) / 127.5 # values between [0, 255]
self.last_updated_button = 'LJ'
elif key == 'RJX':
self.buttons['RJ'][0] = value / 32768. # values between [-32768, 32767]
self.buttons['RJ'][0] = (value - 127.5) / 127.5 # values between [0, 255]
self.last_updated_button = 'RJ'
elif key == 'RJY':
self.buttons['RJ'][1] = -1. * value / 32768. # values between [-32767, 32768]
self.buttons['RJ'][1] = -1. * (value - 127.5) / 127.5 # values between [0, 255]
self.last_updated_button = 'RJ'
elif key == 'DpadX':
self.buttons['Dpad'][0] = value # left (-1) and right (1)
@@ -219,11 +353,11 @@ class PSControllerInterface(GameControllerInterface):
elif key == 'DpadY':
self.buttons['Dpad'][1] = -1 * value # down (-1) and high (1)
self.last_updated_button = 'Dpad'
elif key == 'LT' or key == 'RT': # max 1023
self.buttons[key] = value / 1023.
# self.last_updated_button = key
# elif key == 'LT' or key == 'RT': # max 1023
# self.buttons[key] = value / 1023.
# # self.last_updated_button = key
elif event_type == 'Key':
print(event_type, key, value)
# print(event_type, key, value)
self.buttons[key] = value
self.last_updated_button = key
@@ -267,12 +401,41 @@ class PS4ControllerInterface(PSControllerInterface):
# Tests
if __name__ == '__main__':
device = devices.gamepads[1]
print(device.name)
# create controller
controller = PSControllerInterface(use_thread=True, sleep_dt=0.01)
print(controller.map)
print(controller.buttons)
# check buttons
while True:
events = device.read() # blocking=False) # get_gamepad()
for event in events:
event_type, code, state = event.ev_type, event.code, event.state
if event_type != 'Absolute':
if code != 'SYN_REPORT':
print(code, state)
# controller.step()
if controller.cross:
print("X button has been pushed.")
if controller.square:
print("Square button has been pushed.")
if controller.circle:
print("Circle button has been pushed.")
if controller.triangle:
print("Triangle button has been pushed.")
if controller.L1:
print("L1 has been pushed.")
if controller.L2:
print("L2 has been pushed.")
if controller.R1:
print("R1 has been pushed.")
if controller.R2:
print("R2 has been pushed.")
if controller.LJB:
print("LJB has been pushed.")
if controller.RJB:
print("RJB has been pushed.")
if controller.options:
print("Options has been pushed.")
if controller.share:
print("Share has been pushed.")
if controller.PS:
print("PS has been pushed.")
if controller.pad:
print("Pad has been pushed.")
print("Left joystick: {}".format(controller.LJ[0]))
@@ -18,6 +18,8 @@ References:
[1] https://askubuntu.com/questions/783587/how-do-i-get-an-xbox-one-controller-to-work-with-16-04-not-steam
"""
import numpy as np
try:
from inputs import devices
# TODO: update the library inputs to make it non-blocking
@@ -71,7 +73,7 @@ class XboxControllerInterface(GameControllerInterface):
[3] https://askubuntu.com/questions/783587/how-do-i-get-an-xbox-one-controller-to-work-with-16-04-not-steam
"""
def __init__(self, use_thread=False, sleep_dt=0, verbose=False, controller_name='X-Box One'):
def __init__(self, use_thread=False, sleep_dt=0, verbose=False, controller_name='X-Box'):
# Check if some gamepads are connected to the computer
gamepads = devices.gamepads
if len(gamepads) == 0:
@@ -100,7 +102,7 @@ class XboxControllerInterface(GameControllerInterface):
# buttons and their values
self.buttons = dict(zip(xbox_buttons[:12], [0]*12))
self.buttons.update(dict(zip(['Dpad', 'LJ', 'RJ'], [[0,0]]*3)))
self.buttons.update(dict(zip(['Dpad', 'LJ', 'RJ'], [np.array([0., 0.])]*3)))
# last updated button
self.last_updated_button = None
@@ -143,12 +145,12 @@ class XboxControllerInterface(GameControllerInterface):
@property
def LB(self):
"""left bumper; button for left index finger"""
"""Left bumper; button for left index finger"""
return self.buttons['LB']
@property
def RB(self):
"""right bumper; button for right index finger"""
"""Right bumper; button for right index finger"""
return self.buttons['RB']
@property
@@ -190,6 +192,116 @@ class XboxControllerInterface(GameControllerInterface):
left_joystick = LJ
right_joystick = RJ
@property
def BTN_SOUTH(self):
"""South button"""
return self.buttons[self.map['BTN_SOUTH']]
@property
def BTN_EAST(self):
"""East button"""
return self.buttons[self.map['BTN_EAST']]
@property
def BTN_WEST(self):
"""West button"""
return self.buttons[self.map['BTN_WEST']]
@property
def BTN_NORTH(self):
"""North button"""
return self.buttons[self.map['BTN_NORTH']]
@property
def BTN_C(self):
"""Circle button; non-existent for Xbox controller; return the same as the B button."""
return self.buttons['B']
@property
def BTN_THUMBL(self):
"""Left thumb button"""
return self.buttons[self.map['BTN_THUMBL']]
@property
def BTN_THUMBR(self):
"""Right thumb button"""
return self.buttons[self.map['BTN_THUMBR']]
@property
def BTN_TL(self):
"""Left bumper; button for left index finger"""
return self.buttons[self.map['BTN_TL']]
@property
def BTN_TL2(self):
"""Left bumper 2; non-existent for Xbox controller; return the same as ABS_Z."""
return self.ABS_Z
@property
def BTN_TR(self):
"""Right bumper; button for right index finger"""
return self.buttons[self.map['BTN_TR']]
@property
def BTN_TR2(self):
"""Right bumper 2; non-existent for Xbox controller; return the same as ABS_RZ."""
return self.ABS_RZ
@property
def BTN_START(self):
"""Start button"""
return self.buttons[self.map['BTN_START']]
@property
def BTN_SELECT(self):
"""Select button"""
return self.buttons[self.map['BTN_SELECT']]
@property
def BTN_MODE(self):
"""Mode button; non-existent for Xbox controller; return the same as BTN_SELECT."""
return self.BTN_SELECT
@property
def ABS_Z(self):
"""Left trigger; button for left middle finger"""
return self.buttons['LT']
@property
def ABS_RZ(self):
"""Right trigger; button for right middle finger"""
return self.buttons['RT']
@property
def ABS_HAT0X(self):
"""Directional pad X position"""
return self.buttons['Dpad'][0]
@property
def ABS_HAT0Y(self):
"""Directional pad Y position"""
return self.buttons['Dpad'][1]
@property
def ABS_X(self):
"""Left joystick X position"""
return self.buttons['LJ'][0]
@property
def ABS_Y(self):
"""Left joystick Y position"""
return self.buttons['LJ'][1]
@property
def ABS_RX(self):
"""Right joystick X position"""
return self.buttons['RJ'][0]
@property
def ABS_RY(self):
"""Right joystick Y position"""
return self.buttons['RJ'][1]
###########
# Methods #
###########
@@ -71,13 +71,14 @@ class MouseKeyboardInterface(InputInterface):
triggered, 4 if it has been released.
"""
def __init__(self, simulator, verbose=False):
def __init__(self, simulator=None, verbose=False):
"""
Initialize the Mouse-Keyboard Interface. This interface is a little bit special in the sense that we use
the simulator to provide the mouse and keyboard events instead of using an external library.
Args:
simulator (Simulator): simulator instance from which we capture mouse and keyboard events.
simulator (Simulator, None): simulator instance from which we capture mouse and keyboard events. If None,
it will look for the first instantiated simulator.
verbose (bool): If True, print information on the standard output.
"""
super(MouseKeyboardInterface, self).__init__(use_thread=False, sleep_dt=0, verbose=verbose)
@@ -113,18 +114,14 @@ class MouseKeyboardInterface(InputInterface):
@simulator.setter
def simulator(self, simulator):
"""Set the simulator instance."""
# if isinstance(simulator, Simulator):
# pass
# elif isinstance(simulator, World):
# simulator = simulator.simulator
# elif isinstance(simulator, Env):
# simulator = simulator.world.simulator
# else:
# if not isinstance(simulator, Simulator):
# raise TypeError("Expecting the simulator to be an instance of Simulator, "
# "got instead {}".format(type(simulator)))
# if isinstance(simulator, World):
# simulator = simulator.simulator
if simulator is None:
if len(Simulator.instances) == 0:
raise RuntimeError("No simulator was given to the `MouseKeyboardInterface`, we thus tried to look "
"for an instantiated simulator but none was found...")
simulator = Simulator.instances[0] # by default, we take the first instantiated simulator
if not isinstance(simulator, Simulator):
raise TypeError("Expecting the given 'simulator' to be an instance of `Simulator`, but got instead: "
"{}".format(type(simulator)))
self._simulator = simulator
@property
+3 -3
View File
@@ -24,7 +24,7 @@ def gpr_heightmap(init_values, x, y, kernel=None, alpha=1e-10, min_height=0, max
Generate a heightmap using gaussian process regression. The advantages of using this method over others to
generate terrains lies in the capacity of adding prior knowledge through the kernel and the given initial values.
For instance, using a RBF kernel means that we want a smooth terrain instead of a bumpy one.
Furthermore, it allows to generate heightmaps which are not necessary square; i.e. they can be rectangular.
Furthermore, it allows to generate heightmaps which are not necessary square; i.e. they can be rectangular.
Warnings: this is pretty difficult to exploit if the given data is not consistent. See `heigthmap_rbf` for
a better way to generate heightmap.
@@ -65,8 +65,8 @@ def gpr_heightmap(init_values, x, y, kernel=None, alpha=1e-10, min_height=0, max
>>> heightmap = gpr_heightmap(init_values, x, y)
References:
[1] "Gaussian Processes for Machine Learning", Rasmussen and Williams, 2006
[2] Sklearn: https://scikit-learn.org/stable/modules/gaussian_process.html
- [1] "Gaussian Processes for Machine Learning", Rasmussen and Williams, 2006
- [2] Sklearn: https://scikit-learn.org/stable/modules/gaussian_process.html
"""
# check given x and y
if len(x.shape) == 1 and len(y.shape) == 1:
+3 -4
View File
@@ -1,6 +1,5 @@
#!/usr/bin/env python
"""Define the the `World` class which allows to specify what constitutes the world (i.e. what elements are in
the world).
"""Define the `World` class which allows to specify what constitutes the world (i.e. what elements are in the world).
Dependencies:
- `pyrobolearn.simulators`
@@ -58,8 +57,8 @@ class World(object):
For an excellent overview of available 3D models/scenes, check references [1, 2].
References:
[1] "3D Machine Learning": https://github.com/timzhang642/3D-Machine-Learning
[2] Open3D: http://www.open3d.org/
- [1] "3D Machine Learning": https://github.com/timzhang642/3D-Machine-Learning
- [2] Open3D: http://www.open3d.org/
"""
def __init__(self, simulator, gravity=(0., 0., -9.81)):