add ROS related code (ongoing) + add speaker actuator/interface

This commit is contained in:
Brian Delhaisse
2019-06-24 03:16:51 +02:00
parent 80830791d5
commit 62b7603929
56 changed files with 1079 additions and 109 deletions
+2 -1
View File
@@ -17,7 +17,8 @@ __email__ = "briandelhaisse@gmail.com"
__status__ = "Development"
# Butterworth Filter
# Butterworth Filter: "The Butterworth filter is a type of signal processing filter designed to have a frequency
# response as flat as possible in the passband." (Wikipedia: https://en.wikipedia.org/wiki/Butterworth_filter)
# Example from http://scipy-cookbook.readthedocs.io/items/ButterworthBandpass.html
def butter_bandpass(lowcut, highcut, fs, order=5):
nyq = 0.5 * fs
+2 -2
View File
@@ -5,7 +5,7 @@ This folder contains the various robots that can be used in the PRL framework. C
```python
import pyrobolearn as prl
sim = prl.simulators.BulletSim()
sim = prl.simulators.Bullet()
robot = prl.robots.<RobotClass>(sim)
```
@@ -14,7 +14,7 @@ or
```python
import pyrobolearn as prl
sim = prl.simulators.BulletSim()
sim = prl.simulators.Bullet()
world = prl.worlds.BasicWorld(sim)
robot = world.loadRobot(<Robot_name_or_robot_class>)
```
+4 -2
View File
@@ -6,8 +6,10 @@ import inspect
# General robot class
from .base import Body, MovableBody, ControllableBody
from .actuators import *
from .sensors import *
from . import actuators
from . import sensors
# from .actuators import *
# from .sensors import *
from .robot import Robot
# Categories/types of robots
+3
View File
@@ -4,3 +4,6 @@ from .actuator import Actuator
# import joint actuators
from .joints import *
# import speaker
from .speaker import Speaker
+84
View File
@@ -0,0 +1,84 @@
#!/usr/bin/env python
"""Define the various actuators used in robotics.
This is decoupled from the robots such that actuators can be defined outside the robot class and can be selected at
run-time. This is useful for instance when a version of the robot has specific joint motors while another version has
other joint actuators. Additionally, this is important as more realistic motors can result in a better transfer from
simulation to reality.
"""
from pyrobolearn.robots.actuators import Actuator
from pyrobolearn.utils.data_structures.queues import FIFOQueue
import pyrobolearn as prl
__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 Speaker(Actuator):
r"""Speaker class
"""
def __init__(self, capacity=1, check_same_text=False, verbose=False):
"""
Initialize the speaker actuator.
Args:
capacity (int): maximum capacity of the queue. Every given text is appended to that queue. If set to 0,
it will an infinite capacity.
check_same_text (bool): if True, it will check that the same text is not being said twice.
verbose (bool): if True, it will print the messages returned by the speaker interface.
"""
super(Speaker, self).__init__()
self.queue = FIFOQueue(maxsize=int(capacity))
self.check_same_text = bool(check_same_text)
self.last_text = None
self.interface = prl.tools.interfaces.audio.SpeakerInterface(use_thread=True, verbose=verbose)
###########
# Methods #
###########
def say(self, text=None):
# if we have something to say, add it to the queue
if text is not None:
if not isinstance(text, str):
raise TypeError("Expecting the 'given' text to be an instance of ")
if not self.check_same_text or self.last_text != text:
self.queue.append(text)
# if the interface is ready and we have something to say
if not self.interface.updated and not self.queue.empty():
data = self.queue.get()
self.interface.data = data
def compute(self, text=None):
self.say(text)
def __del__(self):
self.interface.close()
# Test the actuator
if __name__ == '__main__':
import time
speaker = Speaker(capacity=1)
speaker.say('Hello world!')
speaker.say('My name is Boxy!')
for t in range(700):
if t == 300:
speaker.say('How are you today?')
time.sleep(0.01)
# TODO: that should be automatic
del speaker
+2 -2
View File
@@ -5,7 +5,7 @@
import os
from pyrobolearn.robots.legged_robot import BipedRobot
from pyrobolearn.robots.manipulator import BiManipulatorRobot
from pyrobolearn.robots.manipulator import BiManipulator
__author__ = "Brian Delhaisse"
__copyright__ = "Copyright 2018, PyRoboLearn"
@@ -16,7 +16,7 @@ __email__ = "briandelhaisse@gmail.com"
__status__ = "Development"
class Atlas(BipedRobot, BiManipulatorRobot):
class Atlas(BipedRobot, BiManipulator):
r"""Atlas robot
Atlas robot developed by Boston Dynamics.
+2 -2
View File
@@ -4,7 +4,7 @@
import os
from pyrobolearn.robots.manipulator import BiManipulatorRobot
from pyrobolearn.robots.manipulator import BiManipulator
__author__ = "Brian Delhaisse"
__copyright__ = "Copyright 2018, PyRoboLearn"
@@ -15,7 +15,7 @@ __email__ = "briandelhaisse@gmail.com"
__status__ = "Development"
class Baxter(BiManipulatorRobot):
class Baxter(BiManipulator):
r"""Baxter robot
Baxter robot built by Rethink Robotics.
+1
View File
@@ -24,6 +24,7 @@ class Blackbird(BipedRobot):
References:
[1] https://hackaday.io/project/160882-blackbird-bipedal-robot
[2] https://github.com/G-Levine
"""
def __init__(self,
+2 -2
View File
@@ -7,7 +7,7 @@ import numpy as np
from pyrobolearn.robots.legged_robot import QuadrupedRobot
from pyrobolearn.robots.wheeled_robot import WheeledRobot
from pyrobolearn.robots.manipulator import BiManipulatorRobot
from pyrobolearn.robots.manipulator import BiManipulator
__author__ = "Brian Delhaisse"
__copyright__ = "Copyright 2018, PyRoboLearn"
@@ -18,7 +18,7 @@ __email__ = "briandelhaisse@gmail.com"
__status__ = "Development"
class Centauro(WheeledRobot, QuadrupedRobot, BiManipulatorRobot):
class Centauro(WheeledRobot, QuadrupedRobot, BiManipulator):
r"""Centauro robot
References:
+2 -2
View File
@@ -5,7 +5,7 @@
import os
from pyrobolearn.robots.legged_robot import BipedRobot
from pyrobolearn.robots.manipulator import BiManipulatorRobot
from pyrobolearn.robots.manipulator import BiManipulator
__author__ = "Brian Delhaisse"
__copyright__ = "Copyright 2018, PyRoboLearn"
@@ -16,7 +16,7 @@ __email__ = "briandelhaisse@gmail.com"
__status__ = "Development"
class Cogimon(BipedRobot, BiManipulatorRobot):
class Cogimon(BipedRobot, BiManipulator):
r"""Cogimon humanoid robot.
References:
+2 -2
View File
@@ -6,7 +6,7 @@ import os
import numpy as np
from pyrobolearn.robots.legged_robot import BipedRobot
from pyrobolearn.robots.manipulator import BiManipulatorRobot
from pyrobolearn.robots.manipulator import BiManipulator
__author__ = "Brian Delhaisse"
__copyright__ = "Copyright 2018, PyRoboLearn"
@@ -17,7 +17,7 @@ __email__ = "briandelhaisse@gmail.com"
__status__ = "Development"
class Coman(BipedRobot, BiManipulatorRobot):
class Coman(BipedRobot, BiManipulator):
r"""Coman robot
References:
+2 -2
View File
@@ -6,7 +6,7 @@ import os
import numpy as np
from pyrobolearn.robots.legged_robot import BipedRobot
from pyrobolearn.robots.manipulator import BiManipulatorRobot
from pyrobolearn.robots.manipulator import BiManipulator
__author__ = "Brian Delhaisse"
__copyright__ = "Copyright 2018, PyRoboLearn"
@@ -17,7 +17,7 @@ __email__ = "briandelhaisse@gmail.com"
__status__ = "Development"
class Darwin(BipedRobot, BiManipulatorRobot):
class Darwin(BipedRobot, BiManipulator):
r"""Darwin robot
References:
+2 -2
View File
@@ -4,7 +4,7 @@
import os
from pyrobolearn.robots.manipulator import ManipulatorRobot
from pyrobolearn.robots.manipulator import Manipulator
__author__ = "Brian Delhaisse"
__copyright__ = "Copyright 2018, PyRoboLearn"
@@ -15,7 +15,7 @@ __email__ = "briandelhaisse@gmail.com"
__status__ = "Development"
class Edo(ManipulatorRobot):
class Edo(Manipulator):
r"""Edo robot
E.Do robot developed by Comau.
+2 -2
View File
@@ -5,7 +5,7 @@
import os
from pyrobolearn.robots.wheeled_robot import WheeledRobot
from pyrobolearn.robots.manipulator import ManipulatorRobot
from pyrobolearn.robots.manipulator import Manipulator
__author__ = "Brian Delhaisse"
__copyright__ = "Copyright 2018, PyRoboLearn"
@@ -16,7 +16,7 @@ __email__ = "briandelhaisse@gmail.com"
__status__ = "Development"
class Fetch(WheeledRobot, ManipulatorRobot):
class Fetch(WheeledRobot, Manipulator):
r"""Fetch robot
References:
+2 -2
View File
@@ -4,7 +4,7 @@
import os
from pyrobolearn.robots.manipulator import ManipulatorRobot
from pyrobolearn.robots.manipulator import Manipulator
__author__ = "Brian Delhaisse"
__copyright__ = "Copyright 2018, PyRoboLearn"
@@ -15,7 +15,7 @@ __email__ = "briandelhaisse@gmail.com"
__status__ = "Development"
class Franka(ManipulatorRobot):
class Franka(Manipulator):
r"""Franka Emika robot
WARNING: CURRENTLY, THE INERTIAL TAGS ARE NOT SET IN THE URDF!!
View File
+2 -2
View File
@@ -5,7 +5,7 @@
import os
from pyrobolearn.robots.legged_robot import BipedRobot
from pyrobolearn.robots.manipulator import BiManipulatorRobot
from pyrobolearn.robots.manipulator import BiManipulator
from pyrobolearn.robots.hand import TwoHand
__author__ = "Brian Delhaisse"
@@ -17,7 +17,7 @@ __email__ = "briandelhaisse@gmail.com"
__status__ = "Development"
class Hubo(BipedRobot, BiManipulatorRobot, TwoHand):
class Hubo(BipedRobot, BiManipulator, TwoHand):
r"""Hubo robot
"The HUBO series of biped robots was developed by the Humanoid Robot Research Center at KAIST (Korea Advanced
+2 -2
View File
@@ -5,7 +5,7 @@
import os
from pyrobolearn.robots.legged_robot import BipedRobot
from pyrobolearn.robots.manipulator import BiManipulatorRobot
from pyrobolearn.robots.manipulator import BiManipulator
__author__ = "Brian Delhaisse"
__copyright__ = "Copyright 2018, PyRoboLearn"
@@ -16,7 +16,7 @@ __email__ = "briandelhaisse@gmail.com"
__status__ = "Development"
class Humanoid(BipedRobot, BiManipulatorRobot):
class Humanoid(BipedRobot, BiManipulator):
r"""Humanoid Mujoco Model
References:
+2 -2
View File
@@ -5,7 +5,7 @@
import os
from pyrobolearn.robots.legged_robot import BipedRobot
from pyrobolearn.robots.manipulator import BiManipulatorRobot
from pyrobolearn.robots.manipulator import BiManipulator
__author__ = "Brian Delhaisse"
__copyright__ = "Copyright 2018, PyRoboLearn"
@@ -16,7 +16,7 @@ __email__ = "briandelhaisse@gmail.com"
__status__ = "Development"
class ICub(BipedRobot, BiManipulatorRobot):
class ICub(BipedRobot, BiManipulator):
r"""ICub robot
References:
+2 -2
View File
@@ -4,7 +4,7 @@
import os
from pyrobolearn.robots.manipulator import ManipulatorRobot
from pyrobolearn.robots.manipulator import Manipulator
__author__ = "Brian Delhaisse"
__copyright__ = "Copyright 2018, PyRoboLearn"
@@ -15,7 +15,7 @@ __email__ = "briandelhaisse@gmail.com"
__status__ = "Development"
class Jaco(ManipulatorRobot):
class Jaco(Manipulator):
r"""Jaco (manipulator) robot
References:
+2 -2
View File
@@ -4,7 +4,7 @@
import os
from pyrobolearn.robots.manipulator import ManipulatorRobot
from pyrobolearn.robots.manipulator import Manipulator
__author__ = "Brian Delhaisse"
__copyright__ = "Copyright 2018, PyRoboLearn"
@@ -15,7 +15,7 @@ __email__ = "briandelhaisse@gmail.com"
__status__ = "Development"
class KR5(ManipulatorRobot):
class KR5(Manipulator):
r"""Kuka KR5 sixx R650 robot
Payload of 5.00kg and a reach of 650mm or 850mm.
+2 -2
View File
@@ -4,7 +4,7 @@
import os
from pyrobolearn.robots.manipulator import ManipulatorRobot
from pyrobolearn.robots.manipulator import Manipulator
__author__ = "Brian Delhaisse"
__copyright__ = "Copyright 2018, PyRoboLearn"
@@ -15,7 +15,7 @@ __email__ = "briandelhaisse@gmail.com"
__status__ = "Development"
class KukaIIWA(ManipulatorRobot):
class KukaIIWA(Manipulator):
r"""Kuka IIWA robot
IIWA stands for 'Intelligent Industrial Work Assistant'. This robot has 7 DoFs, and an ATI F/T sensor at the
+2 -2
View File
@@ -4,7 +4,7 @@
import os
from pyrobolearn.robots.manipulator import ManipulatorRobot
from pyrobolearn.robots.manipulator import Manipulator
__author__ = "Brian Delhaisse"
__copyright__ = "Copyright 2018, PyRoboLearn"
@@ -15,7 +15,7 @@ __email__ = "briandelhaisse@gmail.com"
__status__ = "Development"
class KukaLWR(ManipulatorRobot):
class KukaLWR(Manipulator):
r"""Kuka LWR robot
LWR stands for 'Light Weight Robot'. This robot has 7 DoFs, and an ATI F/T sensor at the end-effector.
+4 -4
View File
@@ -14,7 +14,7 @@ __email__ = "briandelhaisse@gmail.com"
__status__ = "Development"
class ManipulatorRobot(Robot):
class Manipulator(Robot):
r"""Manipulator robot
Manipulator robots are robots that use some of its end-effectors to manipulate objects in its environment.
@@ -27,7 +27,7 @@ class ManipulatorRobot(Robot):
orientation=(0, 0, 0, 1),
fixed_base=False,
scale=1.):
super(ManipulatorRobot, self).__init__(simulator, urdf, position, orientation, fixed_base, scale)
super(Manipulator, self).__init__(simulator, urdf, position, orientation, fixed_base, scale)
self.arms = [] # list of arms where an arm is a list of links
self.hands = [] # list of end-effectors/hands
@@ -117,7 +117,7 @@ class ManipulatorRobot(Robot):
return self.hands
class BiManipulatorRobot(ManipulatorRobot):
class BiManipulator(Manipulator):
r"""Bi-manipulator Robot
Bi-manipulators are robots that have two manipulators to manipulate objects in the environment.
@@ -125,7 +125,7 @@ class BiManipulatorRobot(ManipulatorRobot):
def __init__(self, simulator, urdf, position=(0, 0, 1.5), orientation=(0, 0, 0, 1), fixed_base=False,
scale=1.):
super(BiManipulatorRobot, self).__init__(simulator, urdf, position, orientation, fixed_base, scale)
super(BiManipulator, self).__init__(simulator, urdf, position, orientation, fixed_base, scale)
self.left_arm_id = 0
self.left_hand_id = 0
+2 -2
View File
@@ -5,7 +5,7 @@
import os
import numpy as np
from pyrobolearn.robots.manipulator import ManipulatorRobot
from pyrobolearn.robots.manipulator import Manipulator
__author__ = "Brian Delhaisse"
__copyright__ = "Copyright 2018, PyRoboLearn"
@@ -16,7 +16,7 @@ __email__ = "briandelhaisse@gmail.com"
__status__ = "Development"
class Manipulator2D(ManipulatorRobot):
class Manipulator2D(Manipulator):
r"""2D manipulator robot
References:
+2 -2
View File
@@ -5,7 +5,7 @@
import os
from pyrobolearn.robots.legged_robot import BipedRobot
from pyrobolearn.robots.manipulator import BiManipulatorRobot
from pyrobolearn.robots.manipulator import BiManipulator
from pyrobolearn.robots.hand import TwoHand
__author__ = "Brian Delhaisse"
@@ -17,7 +17,7 @@ __email__ = "briandelhaisse@gmail.com"
__status__ = "Development"
class Nao(BipedRobot, BiManipulatorRobot, TwoHand):
class Nao(BipedRobot, BiManipulator, TwoHand):
r"""Nao robot
"""
+2 -2
View File
@@ -5,7 +5,7 @@
import os
from pyrobolearn.robots.wheeled_robot import WheeledRobot
from pyrobolearn.robots.manipulator import BiManipulatorRobot
from pyrobolearn.robots.manipulator import BiManipulator
from pyrobolearn.robots.sensors.camera import CameraSensor
__author__ = "Brian Delhaisse"
@@ -17,7 +17,7 @@ __email__ = "briandelhaisse@gmail.com"
__status__ = "Development"
class Pepper(WheeledRobot, BiManipulatorRobot):
class Pepper(WheeledRobot, BiManipulator):
r"""Pepper robot.
The Pepper robot is a robot from the Aldebaran company.
+2 -1
View File
@@ -19,7 +19,8 @@ class PhantomX(HexapodRobot):
r"""Phantom X Hexapod robot
References:
[1] https://github.com/HumaRobotics/phantomx_description
[1] https://www.trossenrobotics.com/phantomx-ax-hexapod.aspx
[2] https://github.com/HumaRobotics/phantomx_description
"""
def __init__(self,
+2 -2
View File
@@ -5,7 +5,7 @@
import os
from pyrobolearn.robots.wheeled_robot import WheeledRobot
from pyrobolearn.robots.manipulator import BiManipulatorRobot
from pyrobolearn.robots.manipulator import BiManipulator
__author__ = "Brian Delhaisse"
__copyright__ = "Copyright 2018, PyRoboLearn"
@@ -16,7 +16,7 @@ __email__ = "briandelhaisse@gmail.com"
__status__ = "Development"
class PR2(WheeledRobot, BiManipulatorRobot):
class PR2(WheeledRobot, BiManipulator):
r"""PR2 robot
References:
+1 -1
View File
@@ -55,7 +55,7 @@ class Robot(ControllableBody):
"""
# check parameters
if position is None:
position = (0., 0., 1.5)
position = (0., 0., 0)
if orientation is None:
orientation = (0, 0, 0, 1)
if fixed_base is None:
@@ -1,5 +1,9 @@
#!/usr/bin/env python
"""Define the Coman subscriber.
"""
import rospy
#from std_msgs import msg as stdMsg
# from std_msgs import msg as stdMsg
from sensor_msgs import msg as senMsg
from geometry_msgs import msg as geoMsg
@@ -7,14 +11,17 @@ import sys
import numpy as np
__author__ = "Brian Delhaisse"
__email__ = "Brian.Delhaisse@iit.it"
__copyright__ = "Copyright 2018, PyRoboLearn"
__credits__ = ["Brian Delhaisse"]
__license__ = "GNU GPLv3"
__version__ = "1.0.0"
__date__ = "06/02/2017"
__maintainer__ = "Brian Delhaisse"
__email__ = "briandelhaisse@gmail.com"
__status__ = "Development"
# ROS - Gazebo
class ComanListener:
class ComanSubscriber(object):
"""
ROS node that subscribes to topics to get the state of the coman.
@@ -25,25 +32,28 @@ class ComanListener:
- cameras
"""
def __init__(self):
def __init__(self, robot_id=None):
rospy.init_node('ComanListener')
if robot_id is None:
rospy.init_node('ComanSubscriber', anonymous=True)
else:
rospy.init_node('ComanSubscriber' + str(robot_id))
### Joint States
# Joint States
self.sub_joints = rospy.Subscriber(
"/coman/joint_states", senMsg.JointState, self.joints_callback)
self.joints = None
### IMUs
self.sub_imu1 = rospy.Subscriber(
"/coman/sensor/IMU", senMsg.Imu, self.imu1_callback)
self.imu1 = None
# IMUs
# self.sub_imu1 = rospy.Subscriber(
# "/coman/sensor/IMU", senMsg.Imu, self.imu1_callback)
# self.imu1 = None
# self.sub_imu2 = rospy.Subscriber(
# "/coman/sensor/imu2", senMsg.Imu, self.imu2_callback)
# self.imu2 = None
### Force-Torque sensors
# Force-Torque sensors
self.sub_ft_LForearm = rospy.Subscriber(
"/coman/ft_sensor/LForearm", geoMsg.WrenchStamped, self.ft_LForearm_callback)
self.ft_LForearm = None
@@ -60,7 +70,7 @@ class ComanListener:
"/coman/ft_sensor/RAnkle", geoMsg.WrenchStamped, self.ft_RAnkle_callback)
self.ft_RAnkle = None
### Cameras
# Cameras
self.sub_camera_rgb = rospy.Subscriber(
"/camera/rgb/image_raw", senMsg.Image, self.camera_rgb_callback)
self.camera_rgb = None
@@ -89,4 +99,4 @@ class ComanListener:
# rospy.loginfo("Running coman listener...")
# rospy.spin()
# rospy.spin()
+147
View File
@@ -0,0 +1,147 @@
#!/usr/bin/env python
"""Define the abstract robot publisher.
"""
import rospy
# import the messages
from std_msgs import msg as std_msg
from sensor_msgs import msg as sensor_msg
from geometry_msgs import msg as geometry_msg
__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 PublisherData(object):
r"""Publisher data holder
"""
def __init__(self, topic, data_class, queue_size=10):
self.__dict__['publisher'] = rospy.Publisher(topic, data_class, queue_size=queue_size)
self.__dict__['attributes'] = [attr for attr in [attr for attr in dir(data_class) if not attr.startswith('_')]
if not callable(getattr(data_class, attr))]
self.__dict__['publisher_data'] = data_class()
def publish(self, data=None):
if data is None:
self.publisher.publish(self.publisher_data)
else:
self.publisher.publish(data)
def __setattr__(self, key, value):
if key in self.attributes:
setattr(self.publisher_data, key, value)
class Publisher(object):
r"""Publisher class
This Publisher abstract class is the class from which all the other publishers inherit from. It provides the
common functionalities between the various publishers.
"""
def __init__(self, publisher_id=None):
"""
Initialize the publisher.
Args:
publisher_id (int, None): publisher id which is used when initializing the node. If None, a name will be
auto-generated for the name using name as the base. See the documentation for the :attr:`anonymous`
parameter in `rospy.init_node`.
"""
# initialize the node
if publisher_id is None:
rospy.init_node(self.__class__.__name__, anonymous=True)
else:
rospy.init_node(self.__class__.__name__ + str(publisher_id))
# all publishers
self.publishers = dict()
def create_publisher(self, name, topic, data_class):
"""
Create a publisher to the specific topic.
Args:
name (str): unique name of the publisher. The name must be unique. You will be able to access to this
topic (str): name of the topic.
data_class (object): data type class to use for messages
Returns:
PublisherData: the publisher data holder.
"""
publisher = PublisherData(topic, data_class)
self.publishers[name] = publisher
setattr(self, name, publisher)
return publisher
def publish(self, name=None, data=None):
if name is None and data is None:
for publisher in self.publishers.values():
publisher.publish()
elif name is not None:
self.name.publish(data)
# def __getattr__(self, name):
# return self.publishers[name]
class RobotPublisher(Publisher):
r"""Robot Publisher class
This Robot Publisher class is the class from which all the robot publishers inherit from.
"""
def __init__(self, name, id_=None):
r"""
Initialize the robot publisher.
Args:
name (str): name of the robot. This will be used to create the topics.
id_ (int, None): robot id which is used when initializing the node. If None, a name will be
auto-generated for the name using name as the base. See the documentation for the :attr:`anonymous`
parameter in `rospy.init_node`.
"""
super(RobotPublisher, self).__init__(publisher_id=id_)
self.name = name.lower()
# create Joint states
# self.create_publisher('joint_states', self.name + '/joint_states', sensor_msg.JointState)
self.joint_states = PublisherData(self.name + '/joint_states', sensor_msg.JointState)
self.publishers['joint_states'] = self.joint_states
def set_joint_positions(self, joint_ids, positions):
self.joint_states.position[joint_ids] = positions
def set_joint_velocities(self, joint_ids, velocities):
self.joint_states.velocity[joint_ids] = velocities
def set_joint_torques(self, joint_ids, torques):
self.joint_states.effort[joint_ids] = torques
# Tests
if __name__ == '__main__':
# NOTE: run roscore before hand
import numpy as np
import time
publisher = RobotPublisher('walter')
print("Robot joint state attributes: {}".format(publisher.joint_states.attributes))
publisher.joint_states.position = np.array(range(3))
for t in range(20):
publisher.publish()
time.sleep(0.1)
print("Published topics: {}".format(rospy.get_published_topics()))
@@ -0,0 +1,29 @@
#!/usr/bin/env python
"""Define the RRBot subscriber.
"""
from pyrobolearn.robots.ros.subscriber import RobotSubscriber
__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 RRBotSubscriber(RobotSubscriber):
def __init__(self, id_=None):
"""
Initialize the RRBot Subscriber.
Args:
id_ (int, None): robot id which is used when initializing the node. If None, a name will be
auto-generated for the name using name as the base. See the documentation for the :attr:`anonymous`
parameter in `rospy.init_node`.
"""
super(RRBotSubscriber, self).__init__(name='rrbot', id_=id_)
+149
View File
@@ -0,0 +1,149 @@
#!/usr/bin/env python
"""Define the abstract robot subscriber.
"""
import numpy as np
import rospy
# import the messages
# from std_msgs import msg as std_msg
from sensor_msgs import msg as sensor_msg
# from geometry_msgs import msg as geometry_msg
__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 SubscriberData(object):
r"""Subscriber data holder
"""
def __init__(self, topic, data_class):
self.subscriber = rospy.Subscriber(topic, data_class, callback=self.callback)
self.attributes = set([attr for attr in [attr for attr in dir(data_class) if not attr.startswith('_')]
if not callable(getattr(data_class, attr))])
self.subscriber_data = None
def callback(self, data):
self.subscriber_data = data
def __getattr__(self, name):
if self.subscriber_data is not None:
return getattr(self.subscriber_data, name, None)
def unregister(self):
self.subscriber.unregister()
class Subscriber(object):
r"""Subscriber class
This Subscriber abstract class is the class from which all the other subscribers inherit from. It provides the
common functionalities between the various subscribers.
"""
def __init__(self, subscriber_id=None):
"""
Initialize the subscriber.
Args:
subscriber_id (int, None): subscriber id which is used when initializing the node. If None, a name will be
auto-generated for the name using name as the base. See the documentation for the :attr:`anonymous`
parameter in `rospy.init_node`.
"""
# initialize the node
if subscriber_id is None:
rospy.init_node(self.__class__.__name__, anonymous=True)
else:
rospy.init_node(self.__class__.__name__ + str(subscriber_id))
# all subscribers
self.subscribers = dict()
def create_subscriber(self, name, topic, data_class):
"""
Create a subscriber to the specific topic.
Args:
name (str): unique name of the subscriber. The name must be unique. You will be able to access to this
topic (str): name of the topic.
data_class (object): data type class to use for messages
Returns:
SubscriberData: the subscriber data holder.
"""
subscriber = SubscriberData(topic, data_class)
self.subscribers[name] = subscriber
return subscriber
def __getattr__(self, name):
return self.subscribers[name]
def unregister(self, name=None):
if name is None:
for subscriber in self.subscribers.values():
subscriber.unregister()
else:
self.subscribers[name].unregister()
def close(self):
self.unregister()
def __del__(self):
self.unregister()
class RobotSubscriber(Subscriber):
r"""Robot Subscriber class
This Robot Subscriber class is the class from which all the robot subscribers inherit from.
"""
def __init__(self, name, id_=None):
r"""
Initialize the robot subscriber.
Args:
name (str): name of the robot. This will be used to create the topics.
id_ (int, None): robot id which is used when initializing the node. If None, a name will be
auto-generated for the name using name as the base. See the documentation for the :attr:`anonymous`
parameter in `rospy.init_node`.
"""
super(RobotSubscriber, self).__init__(subscriber_id=id_)
self.name = name.lower()
# create Joint states
self.create_subscriber('joint_states', self.name + '/joint_states', sensor_msg.JointState)
def get_joint_positions(self, joint_ids):
return np.asarray(self.joint_states.position)[joint_ids]
def get_joint_velocities(self, joint_ids):
return np.asarray(self.joint_states.velocity)[joint_ids]
def get_joint_torques(self, joint_ids):
return np.asarray(self.joint_states.effort)[joint_ids]
# Tests
if __name__ == '__main__':
# NOTE: run roscore before hand and don't forget to run the publisher code
import time
from itertools import count
subscriber = RobotSubscriber('walter')
print("Published topics: {}".format(rospy.get_published_topics()))
print("Robot joint state attributes: {}".format(subscriber.joint_states.attributes))
for t in range(100):
print(t)
print("Joint position data: {}".format(subscriber.joint_states.position))
time.sleep(0.1)
+2 -2
View File
@@ -5,7 +5,7 @@
import os
import numpy as np
from pyrobolearn.robots.manipulator import ManipulatorRobot
from pyrobolearn.robots.manipulator import Manipulator
__author__ = "Brian Delhaisse"
__copyright__ = "Copyright 2018, PyRoboLearn"
@@ -16,7 +16,7 @@ __email__ = "briandelhaisse@gmail.com"
__status__ = "Development"
class RRBot(ManipulatorRobot):
class RRBot(Manipulator):
r"""RRBot
Note that in the URDF, the continuous joints were replace by revolute joints. Be careful, that the limit values
+2 -2
View File
@@ -5,7 +5,7 @@
import os
from pyrobolearn.robots.wheeled_robot import WheeledRobot
from pyrobolearn.robots.manipulator import ManipulatorRobot
from pyrobolearn.robots.manipulator import Manipulator
__author__ = "Brian Delhaisse"
__copyright__ = "Copyright 2018, PyRoboLearn"
@@ -16,7 +16,7 @@ __email__ = "briandelhaisse@gmail.com"
__status__ = "Development"
class Sawyer(ManipulatorRobot, WheeledRobot):
class Sawyer(Manipulator, WheeledRobot):
r"""Sawyer robot
Sawyer robot built by Rethink Robotics.
-3
View File
@@ -76,6 +76,3 @@ Here is the list of repos where you can find the original URDF/meshes of each ro
* Currently (Apr.25, 2018), pybullet sets a mass of 1 and an identity inertia to links which don't have an inertia tag defined in the urdf (see bullet3/examples/Importers/ImportURDFDemo/UrdfParser.cpp, line 991-997). This is not desirable, and as such, you should set a mass/inertia of zero to fake/dummy links which are connected by a **fixed** joint. For other links connected by other type of joints, sets a reasonable inertia matrix; an identity matrix is often unreasonable (see ["Adding Physical and Collision Properties to a URDF Model"](http://wiki.ros.org/urdf/Tutorials/Adding%20Physical%20and%20Collision%20Properties%20to%20a%20URDF%20Model)).
* The first link (often called base_link/world_link) shouldn't have a mass/inertia of zero, this causes the robot to float in the air. Remove the corresponding tag from the urdf.
* I do not own the rights to the URDFs (see the corresponding repo and their associated license). They are just regrouped here for convenience, and slightly updated to be used with pybullet. Each URDF has a pybullet robot class associated with it.
---> TODO: ADD IN EACH FOLDER THEIR CORRESPONDING LICENSE.
+2 -2
View File
@@ -5,7 +5,7 @@
import os
from pyrobolearn.robots.legged_robot import BipedRobot
from pyrobolearn.robots.manipulator import BiManipulatorRobot
from pyrobolearn.robots.manipulator import BiManipulator
from pyrobolearn.robots.sensors import CameraSensor
__author__ = "Brian Delhaisse"
@@ -17,7 +17,7 @@ __email__ = "briandelhaisse@gmail.com"
__status__ = "Development"
class Walkman(BipedRobot, BiManipulatorRobot):
class Walkman(BipedRobot, BiManipulator):
r"""Walk-man robot
The Walk-man robot is a humanoid robot developed at the Italian Institute of Technology (IIT) with ... degrees
+2 -2
View File
@@ -4,7 +4,7 @@
import os
from pyrobolearn.robots.manipulator import ManipulatorRobot
from pyrobolearn.robots.manipulator import Manipulator
__author__ = "Brian Delhaisse"
__copyright__ = "Copyright 2018, PyRoboLearn"
@@ -15,7 +15,7 @@ __email__ = "briandelhaisse@gmail.com"
__status__ = "Development"
class WAM(ManipulatorRobot):
class WAM(Manipulator):
r"""Wam robot
References:
+4 -4
View File
@@ -7,7 +7,7 @@ These include: YoubotBase, KukaYoubotArm, Youbot, YoubotDualArm
import os
import numpy as np
from pyrobolearn.robots.manipulator import ManipulatorRobot, BiManipulatorRobot
from pyrobolearn.robots.manipulator import Manipulator, BiManipulator
from pyrobolearn.robots.wheeled_robot import DifferentialWheeledRobot
__author__ = "Brian Delhaisse"
@@ -51,7 +51,7 @@ class YoubotBase(DifferentialWheeledRobot):
# self.wheel_directions = np.ones(len(self.wheels))
class KukaYoubotArm(ManipulatorRobot):
class KukaYoubotArm(Manipulator):
r"""Kuka Youbot arm robot
References:
@@ -79,7 +79,7 @@ class KukaYoubotArm(ManipulatorRobot):
self.name = 'kuka_youbot_arm'
class Youbot(ManipulatorRobot, DifferentialWheeledRobot):
class Youbot(Manipulator, DifferentialWheeledRobot):
r"""Youbot robot
References:
@@ -111,7 +111,7 @@ class Youbot(ManipulatorRobot, DifferentialWheeledRobot):
# self.wheel_directions = np.ones(len(self.wheels))
class YoubotDualArm(BiManipulatorRobot, DifferentialWheeledRobot):
class YoubotDualArm(BiManipulator, DifferentialWheeledRobot):
r"""Youbot dual arm robot
References:
+232 -11
View File
@@ -16,18 +16,21 @@ Dependencies in PRL:
* `pyrobolearn.simulators.ros.ROS`
References:
[1] PyBullet: https://pybullet.org
[2] PyBullet Quickstart Guide: https://docs.google.com/document/d/10sXEhzFRSnvFcl3XxNGhnD4N2SedqwdAvK3dsihxVUA
[3] ROS: http://www.ros.org/
[4] PEP8: https://www.python.org/dev/peps/pep-0008/
- [1] PyBullet: https://pybullet.org
- [2] PyBullet Quickstart Guide: https://docs.google.com/document/d/10sXEhzFRSnvFcl3XxNGhnD4N2SedqwdAvK3dsihxVUA
- [3] ROS: http://www.ros.org/
- [4] PEP8: https://www.python.org/dev/peps/pep-0008/
"""
# TODO
import os
import subprocess
import psutil
import signal
import rospy
from pyrobolearn.simulators.simulator import Simulator
# from pyrobolearn.simulators.bullet import Bullet
# from pyrobolearn.simulators.simulator import Simulator
from pyrobolearn.simulators.bullet import Bullet
# from pyrobolearn.simulators.ros import ROS
@@ -41,7 +44,7 @@ __email__ = "briandelhaisse@gmail.com"
__status__ = "Development"
class BulletROS(Simulator): # Bullet, ROS):
class BulletROS(Bullet): # , ROS):
r"""Bullet ROS
Update the Bullet simulator based on the real robot(s): it updates the robot kinematic and dynamic state based on
@@ -51,6 +54,224 @@ class BulletROS(Simulator): # Bullet, ROS):
sensors, actuators, and forces, to map the real world to the simulated one, etc.
"""
def __init__(self):
super(BulletROS, self).__init__()
raise NotImplementedError
def __init__(self, render=True, subscribe=False, publish=False, ros_master_uri=11311, **kwargs):
"""
Initialize the Bullet-ROS simulator.
Args:
render (bool): if True, it will open the GUI, otherwise, it will just run the server.
subscribe (bool): if True, it will subscribe to the topics associated to the loaded robots, and will read
the values published on these topics.
publish (bool): if True, it will publish the given values to the topics associated to the loaded robots.
**kwargs (dict): optional arguments (this is not used here).
"""
super(BulletROS, self).__init__(render=render, **kwargs)
# Environment variable
self.env = os.environ.copy()
self.env["ROS_MASTER_URI"] = "http://localhost:" + str(ros_master_uri)
# this is for the rospy methods such as: wait_for_service(), init_node(), ...
os.environ['ROS_MASTER_URI'] = self.env['ROS_MASTER_URI']
# run ROS core if not already running
self.roscore = None
if "roscore" not in [p.name() for p in psutil.process_iter()]:
# subprocess.Popen("roscore", env=self.env)
self.roscore = subprocess.Popen(["roscore", "-p", str(ros_master_uri)], env=self.env,
preexec_fn=os.setsid) # , shell=True)
# set variables
self.subscribe = subscribe
self.publish = publish
# remember each publisher/subscriber
self.subscribers = {}
self.publishers = {}
def close(self):
"""
Close everything
"""
# delete each subscribers
# delete each publishers
# delete ROS
os.killpg(os.getpgid(self.roscore.pid), signal.SIGTERM)
@property
def is_subscribing(self):
"""Return True if we are subscribing to topics."""
return self.subscribe
@property
def is_publishing(self):
"""Return True if we are publishing to topics."""
return self.publish
def load_urdf(self, filename, position=None, orientation=None, use_maximal_coordinates=None,
use_fixed_base=None, flags=None, scale=None):
"""Load the given URDF file.
The load_urdf will send a command to the physics server to load a physics model from a Universal Robot
Description File (URDF). The URDF file is used by the ROS project (Robot Operating System) to describe robots
and other objects, it was created by the WillowGarage and the Open Source Robotics Foundation (OSRF).
Many robots have public URDF files, you can find a description and tutorial here:
http://wiki.ros.org/urdf/Tutorials
Important note:
most joints (slider, revolute, continuous) have motors enabled by default that prevent free
motion. This is similar to a robot joint with a very high-friction harmonic drive. You should set the joint
motor control mode and target settings using `pybullet.setJointMotorControl2`. See the
`setJointMotorControl2` API for more information.
Warning:
by default, PyBullet will cache some files to speed up loading. You can disable file caching using
`setPhysicsEngineParameter(enableFileCaching=0)`.
Args:
filename (str): a relative or absolute path to the URDF file on the file system of the physics server.
position (vec3): create the base of the object at the specified position in world space coordinates [x,y,z]
orientation (quat): create the base of the object at the specified orientation as world space quaternion
[x,y,z,w]
use_maximal_coordinates (int): Experimental. By default, the joints in the URDF file are created using the
reduced coordinate method: the joints are simulated using the Featherstone Articulated Body algorithm
(btMultiBody in Bullet 2.x). The useMaximalCoordinates option will create a 6 degree of freedom rigid
body for each link, and constraints between those rigid bodies are used to model joints.
use_fixed_base (bool): force the base of the loaded object to be static
flags (int): URDF_USE_INERTIA_FROM_FILE (val=2): by default, Bullet recomputed the inertia tensor based on
mass and volume of the collision shape. If you can provide more accurate inertia tensor, use this flag.
URDF_USE_SELF_COLLISION (val=8): by default, Bullet disables self-collision. This flag let's you
enable it.
You can customize the self-collision behavior using the following flags:
* URDF_USE_SELF_COLLISION_EXCLUDE_PARENT (val=16) will discard self-collision between links that
are directly connected (parent and child).
* URDF_USE_SELF_COLLISION_EXCLUDE_ALL_PARENTS (val=32) will discard self-collisions between a
child link and any of its ancestors (parents, parents of parents, up to the base).
* URDF_USE_IMPLICIT_CYLINDER (val=128), will use a smooth implicit cylinder. By default, Bullet
will tessellate the cylinder into a convex hull.
scale (float): scale factor to the URDF model.
Returns:
int (non-negative): unique id associated to the load model.
"""
id_ = super(BulletROS, self).load_urdf(filename=filename, position=position, orientation=orientation,
use_maximal_coordinates=use_maximal_coordinates,
use_fixed_base=use_fixed_base, flags=flags, scale=scale)
# get path to directory of urdf
path = os.path.dirname(filename)
robot_directory_name = path.split('/')[-1]
path = path + '/../../ros/' + robot_directory_name + '/'
# check if valid robot directory
# load subscriber in simulator
if self.subscribe:
pass
# load publisher in simulator
if self.publish:
pass
return id_
def get_joint_positions(self, body_id, joint_ids):
"""
Get the position of the given joint(s).
Args:
body_id (int): unique body id.
joint_ids (int, list of int): joint id, or list of joint ids.
Returns:
if 1 joint:
float: joint position [rad]
if multiple joints:
np.float[N]: joint positions [rad]
"""
if body_id in self.subscribers:
q = self.subscribers[body_id].get_joint_positions[joint_ids]
super(BulletROS, self).set_joint_positions(body_id=body_id, joint_ids=joint_ids, positions=q) # or reset?
else:
q = super(BulletROS, self).get_joint_positions(body_id=body_id, joint_ids=joint_ids)
return q
def set_joint_positions(self, body_id, joint_ids, positions, velocities=None, kps=None, kds=None, forces=None):
"""
Set the position of the given joint(s) (using position control).
Args:
body_id (int): unique body id.
joint_ids (int, list of int): joint id, or list of joint ids.
positions (float, np.float[N]): desired position, or list of desired positions [rad]
velocities (None, float, np.float[N]): desired velocity, or list of desired velocities [rad/s]
kps (None, float, np.float[N]): position gain(s)
kds (None, float, np.float[N]): velocity gain(s)
forces (None, float, np.float[N]): maximum motor force(s)/torque(s) used to reach the target values.
"""
super(BulletROS, self).set_joint_positions(body_id, joint_ids, positions, velocities, kps, kds, forces)
if body_id in self.publishers:
self.publishers[body_id].set_joint_positions(joint_ids, positions)
def get_joint_velocities(self, body_id, joint_ids):
"""
Get the velocity of the given joint(s).
Args:
body_id (int): unique body id.
joint_ids (int, list of int): joint id, or list of joint ids.
Returns:
if 1 joint:
float: joint velocity [rad/s]
if multiple joints:
np.float[N]: joint velocities [rad/s]
"""
pass
def set_joint_velocities(self, body_id, joint_ids, velocities, max_force=None):
"""
Set the velocity of the given joint(s) (using velocity control).
Args:
body_id (int): unique body id.
joint_ids (int, list of int): joint id, or list of joint ids.
velocities (float, np.float[N]): desired velocity, or list of desired velocities [rad/s]
max_force (None, float, np.float[N]): maximum motor forces/torques
"""
pass
def get_joint_torques(self, body_id, joint_ids):
"""
Get the applied torque(s) on the given joint(s). "This is the motor torque applied during the last `step`.
Note that this only applies in VELOCITY_CONTROL and POSITION_CONTROL. If you use TORQUE_CONTROL then the
applied joint motor torque is exactly what you provide, so there is no need to report it separately." [1]
Args:
body_id (int): unique body id.
joint_ids (int, list of int): a joint id, or list of joint ids.
Returns:
if 1 joint:
float: torque [Nm]
if multiple joints:
np.float[N]: torques associated to the given joints [Nm]
"""
pass
def set_joint_torques(self, body_id, joint_ids, torques):
"""
Set the torque/force to the given joint(s) (using force/torque control).
Args:
body_id (int): unique body id.
joint_ids (int, list of int): joint id, or list of joint ids.
torques (float, list of float): desired torque(s) to apply to the joint(s) [N].
"""
pass
# Test
if __name__ == '__main__':
pass
+1
View File
@@ -17,6 +17,7 @@ References:
"""
# TODO
# import mujoco_py as mujoco
from pyrobolearn.simulators.simulator import Simulator
+180 -12
View File
@@ -50,17 +50,185 @@ class ROS(Simulator):
r"""ROS Interface
"""
def __init__(self):
super(ROS, self).__init__()
def __init__(self, subscribe=False, publish=False, master_uri=11311, **kwargs):
super(ROS, self).__init__(render=False)
# Environment variable
self.env = os.environ.copy()
self.env["ROS_MASTER_URI"] = "http://localhost:" + str(ros_master_uri)
# this is for the rospy methods such as: wait_for_service(), init_node(), ...
os.environ['ROS_MASTER_URI'] = self.env['ROS_MASTER_URI']
# run ROS core if not already running
self.roscore = None
if "roscore" not in [p.name() for p in psutil.process_iter()]:
# subprocess.Popen("roscore", env=self.env)
self.roscore = subprocess.Popen(["roscore", "-p", str(ros_master_uri)], env=self.env,
preexec_fn=os.setsid) # , shell=True)
# set variables
self.subscribe = subscribe
self.publish = publish
# remember each publisher/subscriber
self.subscribers = {}
self.publishers = {}
self.models = []
# def load_urdf(self, filename, position=None, orientation=None):
# # load URDF: get ros services and ros topics
# model = ROSModel(filename)
#
# # create id and add model to the list of models
# idx = len(self.models)
# self.models.append(model)
#
# # return id
# return idx
self.count_id = -1
def load_urdf(self, filename, position=None, orientation=None, use_maximal_coordinates=None,
use_fixed_base=None, flags=None, scale=None):
"""Load the given URDF file.
The load_urdf will send a command to the physics server to load a physics model from a Universal Robot
Description File (URDF). The URDF file is used by the ROS project (Robot Operating System) to describe robots
and other objects, it was created by the WillowGarage and the Open Source Robotics Foundation (OSRF).
Many robots have public URDF files, you can find a description and tutorial here:
http://wiki.ros.org/urdf/Tutorials
Important note:
most joints (slider, revolute, continuous) have motors enabled by default that prevent free
motion. This is similar to a robot joint with a very high-friction harmonic drive. You should set the joint
motor control mode and target settings using `pybullet.setJointMotorControl2`. See the
`setJointMotorControl2` API for more information.
Warning:
by default, PyBullet will cache some files to speed up loading. You can disable file caching using
`setPhysicsEngineParameter(enableFileCaching=0)`.
Args:
filename (str): a relative or absolute path to the URDF file on the file system of the physics server.
position (vec3): create the base of the object at the specified position in world space coordinates [x,y,z]
orientation (quat): create the base of the object at the specified orientation as world space quaternion
[x,y,z,w]
use_maximal_coordinates (int): Experimental. By default, the joints in the URDF file are created using the
reduced coordinate method: the joints are simulated using the Featherstone Articulated Body algorithm
(btMultiBody in Bullet 2.x). The useMaximalCoordinates option will create a 6 degree of freedom rigid
body for each link, and constraints between those rigid bodies are used to model joints.
use_fixed_base (bool): force the base of the loaded object to be static
flags (int): URDF_USE_INERTIA_FROM_FILE (val=2): by default, Bullet recomputed the inertia tensor based on
mass and volume of the collision shape. If you can provide more accurate inertia tensor, use this flag.
URDF_USE_SELF_COLLISION (val=8): by default, Bullet disables self-collision. This flag let's you
enable it.
You can customize the self-collision behavior using the following flags:
* URDF_USE_SELF_COLLISION_EXCLUDE_PARENT (val=16) will discard self-collision between links that
are directly connected (parent and child).
* URDF_USE_SELF_COLLISION_EXCLUDE_ALL_PARENTS (val=32) will discard self-collisions between a
child link and any of its ancestors (parents, parents of parents, up to the base).
* URDF_USE_IMPLICIT_CYLINDER (val=128), will use a smooth implicit cylinder. By default, Bullet
will tessellate the cylinder into a convex hull.
scale (float): scale factor to the URDF model.
Returns:
int (non-negative): unique id associated to the load model.
"""
# get path to directory of urdf
path = os.path.dirname(filename)
robot_directory_name = path.split('/')[-1]
path = path + '/../../ros/' + robot_directory_name + '/'
# check if valid robot directory
# load subscriber in simulator
if self.subscribe:
pass
# load publisher in simulator
if self.publish:
pass
self.count_id += 1
return self.count_id
def get_joint_positions(self, body_id, joint_ids):
"""
Get the position of the given joint(s).
Args:
body_id (int): unique body id.
joint_ids (int, list of int): joint id, or list of joint ids.
Returns:
if 1 joint:
float: joint position [rad]
if multiple joints:
np.float[N]: joint positions [rad]
"""
if body_id in self.subscribers:
return self.subscribers[body_id].get_joint_positions[joint_ids]
def set_joint_positions(self, body_id, joint_ids, positions, velocities=None, kps=None, kds=None, forces=None):
"""
Set the position of the given joint(s) (using position control).
Args:
body_id (int): unique body id.
joint_ids (int, list of int): joint id, or list of joint ids.
positions (float, np.float[N]): desired position, or list of desired positions [rad]
velocities (None, float, np.float[N]): desired velocity, or list of desired velocities [rad/s]
kps (None, float, np.float[N]): position gain(s)
kds (None, float, np.float[N]): velocity gain(s)
forces (None, float, np.float[N]): maximum motor force(s)/torque(s) used to reach the target values.
"""
if body_id in self.publishers:
self.publishers[body_id].set_joint_positions(joint_ids, positions)
def get_joint_velocities(self, body_id, joint_ids):
"""
Get the velocity of the given joint(s).
Args:
body_id (int): unique body id.
joint_ids (int, list of int): joint id, or list of joint ids.
Returns:
if 1 joint:
float: joint velocity [rad/s]
if multiple joints:
np.float[N]: joint velocities [rad/s]
"""
pass
def set_joint_velocities(self, body_id, joint_ids, velocities, max_force=None):
"""
Set the velocity of the given joint(s) (using velocity control).
Args:
body_id (int): unique body id.
joint_ids (int, list of int): joint id, or list of joint ids.
velocities (float, np.float[N]): desired velocity, or list of desired velocities [rad/s]
max_force (None, float, np.float[N]): maximum motor forces/torques
"""
pass
def get_joint_torques(self, body_id, joint_ids):
"""
Get the applied torque(s) on the given joint(s). "This is the motor torque applied during the last `step`.
Note that this only applies in VELOCITY_CONTROL and POSITION_CONTROL. If you use TORQUE_CONTROL then the
applied joint motor torque is exactly what you provide, so there is no need to report it separately." [1]
Args:
body_id (int): unique body id.
joint_ids (int, list of int): a joint id, or list of joint ids.
Returns:
if 1 joint:
float: torque [Nm]
if multiple joints:
np.float[N]: torques associated to the given joints [Nm]
"""
pass
def set_joint_torques(self, body_id, joint_ids, torques):
"""
Set the torque/force to the given joint(s) (using force/torque control).
Args:
body_id (int): unique body id.
joint_ids (int, list of int): joint id, or list of joint ids.
torques (float, list of float): desired torque(s) to apply to the joint(s) [N].
"""
pass
@@ -1,4 +1,6 @@
# import audio interfaces
# from audio import *
# from .audio import *
# from . import audio
from .speaker import SpeakerInterface
+89 -4
View File
@@ -1,8 +1,93 @@
#!/usr/bin/env python
"""Define the speaker audio interface.
"""
from pyrobolearn.tools.interfaces.audio import OutputAudioInterface
import os
# Speech synthesis
# Good tutorial: https://pythonprogramminglanguage.com/text-to-speech/
# If Python3.3 or higher: https://pypi.org/project/google_speech/
try:
from gtts import gTTS
except ImportError as e:
string = "\nHint: try to install gTTS by typing: `pip install gTTS`." \
"Also install `mpg321` using `sudo apt-get install mpg321`."
raise ImportError(e.__str__() + string)
from pyrobolearn.tools.interfaces import OutputInterface
class SpeakerInterface(OutputAudioInterface):
r"""Speaker Interface
__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 SpeakerInterface(OutputInterface):
r"""Speech Synthesizer Interface
References:
[1] tutorial: https://pythonprogramminglanguage.com/text-to-speech/
[2] If Python3.3 or higher: https://pypi.org/project/google_speech/
"""
pass
available_languages = {'french', 'english', 'american english', 'british english', 'indian english', 'italian',
'japanese', 'korean', 'german', 'dutch', 'spanish', 'spanish (peru)', 'chinese',
'mandarin', 'polish', 'portuguese', 'russian', 'greek'}
def __init__(self, use_thread=False, lang='english', verbose=False):
"""
Initialize the speech synthesizer output 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.
lang (str): language to recognize
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.
"""
languages = {'french': 'fr', 'english': 'en', 'american english': 'en-US', 'british english': 'en-GB',
'indian english': 'en-IN', 'italian': 'it', 'japanese': 'ja', 'korean': 'ko', 'german': 'de',
'dutch': 'nl', 'spanish': 'es', 'spanish (peru)': 'es-PE', 'chinese': 'zh-CN',
'mandarin': 'zh-CN', 'polish': 'pl', 'portuguese': 'pt', 'russian': 'ru', 'greek': 'el-GR'}
# Check < https://gist.github.com/traysr/2001377 > for more
self.lang = languages[lang]
self.updated = False
self._data = ''
super(SpeakerInterface, self).__init__(use_thread=use_thread, verbose=verbose)
@property
def data(self):
return self._data
@data.setter
def data(self, data):
if isinstance(data, (str, unicode)):
self._data = data
self.updated = True
def run(self):
"""Run the interface."""
if self.updated:
if self.verbose:
print("Generating speech...")
# tts = text-to-speech
tts = gTTS(text=self.data, lang=self.lang)
tts.save('tmp.mp3')
os.system('mpg321 tmp.mp3 > /dev/null 2>&1') # TODO: use subprocess instead
os.system('rm tmp.mp3')
# subprocess.call(['mpg321 tmp.mp3'])
# subprocess.call(['rm tmp.mp3'])
self.updated = False
print("Speech generated!")
return self.data
def update(self, data):
"""Update the data."""
self.data = data
@@ -0,0 +1,49 @@
# To run the code `H3DLoad
import H3DInterface as h3d
# print(dir(h3d))
# ['AutoUpdate', 'Console', 'Field', 'H3DConsole', 'INITIALIZE_ONLY', 'INPUT_ONLY', 'INPUT_OUTPUT', 'LogLevel',
# 'MFBOOL', 'MFBool', 'MFCOLOR', 'MFCOLORRGBA', 'MFColor', 'MFColorRGBA', 'MFDOUBLE', 'MFDouble', 'MFFLOAT',
# 'MFFloat', 'MFINT32', 'MFInt32', 'MFMATRIX3D', 'MFMATRIX3F', 'MFMATRIX4D', 'MFMATRIX4F', 'MFMatrix3d',
# 'MFMatrix3f', 'MFMatrix4d', 'MFMatrix4f', 'MFNODE', 'MFNode', 'MFQUATERNION', 'MFQuaternion', 'MFROTATION',
# 'MFRotation', 'MFSTRING', 'MFString', 'MFTIME', 'MFTime', 'MFVEC2D', 'MFVEC2F', 'MFVEC3D', 'MFVEC3F', 'MFVEC4D',
# 'MFVEC4F', 'MFVec2d', 'MFVec2f', 'MFVec3d', 'MFVec3f', 'MFVec4d', 'MFVec4f', 'MField', 'MFieldBack', 'MFieldClear',
# 'MFieldEmpty', 'MFieldErase', 'MFieldFront', 'MFieldPopBack', 'MFieldPushBack', 'MFieldSize', 'Matrix3d',
# 'Matrix3f', 'Matrix4d', 'Matrix4f', 'Node', 'OUTPUT_ONLY', 'PeriodicUpdate', 'Quaternion', 'RGB', 'RGBA',
# 'Rotation', 'SFBOOL', 'SFBool', 'SFCOLOR', 'SFCOLORRGBA', 'SFColor', 'SFColorRGBA', 'SFDOUBLE', 'SFDouble',
# 'SFFLOAT', 'SFFloat', 'SFINT32', 'SFInt32', 'SFMATRIX3D', 'SFMATRIX3F', 'SFMATRIX4D', 'SFMATRIX4F', 'SFMatrix3d',
# 'SFMatrix3f', 'SFMatrix4d', 'SFMatrix4f', 'SFNODE', 'SFNode', 'SFQUATERNION', 'SFQuaternion', 'SFROTATION',
# 'SFRotation', 'SFSTRING', 'SFString', 'SFStringGetValidValues', 'SFStringIsValidValue', 'SFTIME', 'SFTime',
# 'SFVEC2D', 'SFVEC2F', 'SFVEC3D', 'SFVEC3F', 'SFVEC4D', 'SFVEC4F', 'SFVec2d', 'SFVec2f', 'SFVec3d', 'SFVec3f',
# 'SFVec4d', 'SFVec4f', 'SField', 'TypedField', 'UNKNOWN_X3D_TYPE', 'Vec2d', 'Vec2f', 'Vec3d', 'Vec3f', 'Vec4d',
# 'Vec4f', '_ConsoleStderr', '_ConsoleStdout', '__builtins__', '__doc__', '__name__', '__package__',
# 'addProgramSetting', 'addURNResolveRule', 'auto_update_classes', 'createField', 'createNode',
# 'createVRMLFromString', 'createVRMLFromURL', 'createVRMLNodeFromString', 'createVRMLNodeFromURL',
# 'createX3DFromString', 'createX3DFromURL', 'createX3DNodeFromString', 'createX3DNodeFromURL', 'eventSink',
# 'exportGeometryAsSTL', 'fieldGetAccessType', 'fieldGetFullName', 'fieldGetName', 'fieldGetOwner',
# 'fieldGetRoutesIn', 'fieldGetRoutesOut', 'fieldGetTypeName', 'fieldGetValue', 'fieldGetValueAsString',
# 'fieldHasRouteFrom', 'fieldIsAccessCheckOn', 'fieldIsUpToDate', 'fieldReplaceRoute', 'fieldReplaceRouteNoEvent',
# 'fieldRoute', 'fieldRouteNoEvent', 'fieldRoutesTo', 'fieldSetAccessCheck', 'fieldSetAccessType', 'fieldSetName',
# 'fieldSetOwner', 'fieldSetValue', 'fieldSetValueFromString', 'fieldTouch', 'fieldUnroute', 'fieldUnrouteAll',
# 'fieldUpToDate', 'findNodes', 'getActiveBackground', 'getActiveBindableNode', 'getActiveDeviceInfo', 'getActiveFog',
# 'getActiveGlobalSettings', 'getActiveNavigationInfo', 'getActiveStereoInfo', 'getActiveViewpoint', 'getCPtr',
# 'getCurrentScenes', 'getHapticsDevice', 'getNamedNode', 'getNrHapticsDevices', 'log', 'mfield_types',
# 'periodic_update_classes', 'resolveURLAsFile', 'resolveURLAsFolder', 'sfield_types', 'sys', 't',
# 'takeScreenshot', 'throwQuitAPIException', 'time', 'typed_field_classes', 'writeNodeAsX3D']
num_devices = h3d.getNrHapticsDevices()
print("Number of devices: {}".format(num_devices))
device = None
info = h3d.getActiveDeviceInfo()
if info:
device = info.device.getValue()[0]
if device is not None:
print(device.trackerPosition)
print(device.trackerOrientation)
print(device.mainButton)
print(device.secondaryButton)
+14 -1
View File
@@ -3,6 +3,13 @@
Prerequisites:
- follow instructions in [4] on how to install h3dapi.
- To use the Python library, you have to run `H3DLoad <file>.x3d` where in the `<file>.x3d` you make a reference to
the Python script you want to run. Inside this Python script, you will be able to access to the Python library
`H3DInterface`... This is bad... Need to find a better alternative.
Troubleshooting:
- Check: https://www.h3dapi.org/modules/mediawiki/index.php/H3DAPI_FAQ#Python
-
Dependencies:
- `pyrobolearn.tools.interfaces`
@@ -18,7 +25,13 @@ References:
# TODO
# import H3DInterface as h3d
try:
import H3DInterface as h3d
except ImportError as e:
string = "\nHint: try to install h3dapi by following the instructions on: " \
"https://www.h3dapi.org/modules/mediawiki/index.php/H3DAPI_Installation" \
"\nNote that you will have to install the API from source to have the Python library."
raise ImportError(e.__str__() + string)
from pyrobolearn.tools.interfaces import InputOutputInterface
@@ -0,0 +1,3 @@
<Scene>
<PythonScript url="h3d_haptic.py" />
</Scene>
+4
View File
@@ -0,0 +1,4 @@
## Tests
This folder contains the unit and integration tests.
View File
View File