add kilobot (unfinished) + few minor corrections

This commit is contained in:
Brian Delhaisse
2019-06-13 17:13:46 +02:00
parent 5ed2ddd3c3
commit 54a9a80e83
12 changed files with 2775 additions and 43 deletions
+7 -7
View File
@@ -29,7 +29,7 @@ class MLPActorCritic(ActorCritic):
r"""Multi-Layer Perceptron Actor Critic
"""
def __init__(self, states, actions, hidden_units=(), activation_fct='linear', last_activation_fct=None,
def __init__(self, states, actions, hidden_units=(), activation='linear', last_activation=None,
dropout_prob=None, rate=1, preprocessors=None, postprocessors=None):
"""Initialize MLP policy.
@@ -39,9 +39,9 @@ class MLPActorCritic(ActorCritic):
actions (Action): 1D-actions outputted by the policy and will be applied in the simulator (the output
dimensions will be inferred from the actions)
hidden_units (list/tuple of int): number of hidden units in the corresponding layer
activation_fct (None, str, or list/tuple of str/None): activation function to be applied after each layer.
activation (None, str, or list/tuple of str/None): activation function to be applied after each layer.
If list/tuple, then it has to match the
last_activation_fct (None or str): last activation function to be applied. If not specified, it will check
last_activation (None or str): last activation function to be applied. If not specified, it will check
if it is in the list/tuple of activation functions provided for the
previous argument.
dropout_prob (None, float, or list/tuple of float/None): dropout probability.
@@ -51,11 +51,11 @@ class MLPActorCritic(ActorCritic):
preprocessors (Processor, list of Processor, None): pre-processors to be applied to the given input
postprocessors (Processor, list of Processor, None): post-processors to be applied to the policy's output
"""
policy = MLPPolicy(states, actions, hidden_units=hidden_units, activation=activation_fct,
last_activation=last_activation_fct, dropout=dropout_prob, rate=rate,
policy = MLPPolicy(states, actions, hidden_units=hidden_units, activation=activation,
last_activation=last_activation, dropout=dropout_prob, rate=rate,
preprocessors=preprocessors, postprocessors=postprocessors)
value = MLPValue(states, hidden_units=hidden_units, activation_fct=activation_fct,
last_activation_fct=last_activation_fct, dropout_prob=dropout_prob,
value = MLPValue(states, hidden_units=hidden_units, activation=activation,
last_activation=last_activation, dropout=dropout_prob,
preprocessors=preprocessors)
super(MLPActorCritic, self).__init__(policy, value)
+7 -8
View File
@@ -59,9 +59,8 @@ class MLPDynamicModel(NNDynamicModel):
"""
def __init__(self, state, action, next_state=None, hidden_units=(), activation_fct='Linear',
last_activation_fct=None, dropout_prob=None, distributions=None, preprocessors=None,
postprocessors=None):
def __init__(self, state, action, next_state=None, hidden_units=(), activation='linear', last_activation=None,
dropout=None, distributions=None, preprocessors=None, postprocessors=None):
"""
Initialize the multi-layer perceptron model.
@@ -70,9 +69,9 @@ class MLPDynamicModel(NNDynamicModel):
action (Action): action inputs.
next_state (State, None): state outputs. If None, it will take the state inputs as the outputs.
hidden_units (tuple, list of int): number of hidden units in each layer
activation_fct (str): activation function to apply on each layer
last_activation_fct (str, None): activation function to apply on the last layer
dropout_prob (None, float): dropout probability
activation (str): activation function to apply on each layer
last_activation (str, None): activation function to apply on the last layer
dropout (None, float): dropout probability
distributions (torch.distributions.Distribution): distribution to use to sample the next state. If None,
it will be deterministic.
preprocessors (Processor, list of Processor, None): pre-processors to be applied to the given input
@@ -81,8 +80,8 @@ class MLPDynamicModel(NNDynamicModel):
if next_state is None:
next_state = state
model = MLPApproximator(inputs=[state, action], outputs=next_state, hidden_units=hidden_units,
activation=activation_fct, last_activation=last_activation_fct,
dropout=dropout_prob)
activation=activation, last_activation=last_activation,
dropout=dropout)
super(MLPDynamicModel, self).__init__(state, action, model=model, next_state=next_state,
distributions=distributions, preprocessors=preprocessors,
postprocessors=postprocessors)
+2 -2
View File
@@ -70,7 +70,7 @@ class Adam(Optimizer):
# optimize
self.optimizer.zero_grad()
loss.backward()
loss.backward(retain_graph=True)
if self.max_grad_norm is not None:
nn.utils.clip_grad_norm_(params, self.max_grad_norm)
self.optimizer.step()
@@ -99,7 +99,7 @@ class Adadelta(Optimizer):
# optimize
self.optimizer.zero_grad()
loss.backward()
loss.backward(retain_graph=True)
if self.max_grad_norm is not None:
nn.utils.clip_grad_norm_(params, self.max_grad_norm)
self.optimizer.step()
+1
View File
@@ -97,6 +97,7 @@ from .centauro import Centauro
# UAV
from .quadcopter import Quadcopter
# from .techpod import Techpod
from .flappy import Flappy
# UUV
# from .ecaa9 import ECAA9
+1 -1
View File
@@ -306,7 +306,7 @@ class Flappy(FlappingWingUAV):
described in the paper and code [2,3]. The gravity is carried out by pybullet.
References:
[1] "Design Optimization and System Integration of Robotic Hummingbird", 2017, Zhang et al.
[1] "Design Optimization and System Integration of Robotic Hummingbird", Zhang et al., 2017
[2] "Flappy Hummingbird: An Open Source Dynamic Simulation of Flapping Wing Robots and Animals", Fei et al.,
2019
[3] https://github.com/purdue-biorobotics/flappy
+108
View File
@@ -0,0 +1,108 @@
#!/usr/bin/env python
"""Provide the Kilobot robotic platform.
"""
# TODO: finish URDF: fix mass, inertia, dimensions, linear joint (spring mass)
# TODO: implement LRA vibration motor
import os
import numpy as np
from pyrobolearn.robots.robot import Robot
__author__ = "Brian Delhaisse"
__copyright__ = "Copyright 2018, PyRoboLearn"
__license__ = "MIT"
__version__ = "1.0.0"
__maintainer__ = "Brian Delhaisse"
__email__ = "briandelhaisse@gmail.com"
__status__ = "Development"
class Kilobot(Robot):
r"""Kilobot robot
The Kilobot robot [1,2,3,4] is a small robot (diameter=33mm, height=34mm) mostly used in swarm robotics.
It notably uses 2 coin shaped vibration motors [5] allowing the robot to move in a differential drive manner using
the slip-stick principle.
There are two types of vibration motors:
- eccentric rotating mass vibration motor (ERM) [5.1]
- linear resonant actuator (LRA) [5.2]
References:
[1] "Kilobot: a Low Cost Scalable Robot System for Collective Behaviors", Rubenstein et al., 2012
[2] "Programmable self-assembly in a thousand-robot swarm", Rubenstein et al., 2014
[3] Harvard's Self-Organizing Systems Research Group: https://ssr.seas.harvard.edu/kilobots
[4] K-Team Corporation: https://www.k-team.com/mobile-robotics-products/kilobot
[5] Precision Micro drives: https://www.precisionmicrodrives.com/
- ERM: https://www.precisionmicrodrives.com/vibration-motors/
- LRA: https://www.precisionmicrodrives.com/vibration-motors/linear-resonant-actuators-lras/
"""
def __init__(self,
simulator,
position=(0, 0, 0),
orientation=(0, 0, 0, 1),
fixed_base=False,
scale=1.,
urdf=os.path.dirname(__file__) + '/urdfs/kilobot/kilobot.urdf'): # TODO: finish URDF
# check parameters
if position is None:
position = (0., 0., 0)
if len(position) == 2: # assume x, y are given
position = tuple(position) + (0.0,)
if orientation is None:
orientation = (0, 0, 0, 1)
if fixed_base is None:
fixed_base = False
super(Kilobot, self).__init__(simulator, urdf, position, orientation, fixed_base, scale)
self.name = 'kilobot'
# 2 coin shaped vibration motors with 255 different power levels
self.motors = []
def drive(self, values):
"""
Drive the kilobot in a differential drive manner using the slip-stick principle.
Args:
values (float, int, np.array): 255 different power levels [0,255] for each motor.
"""
if isinstance(values, (int, float)):
values = np.ones(len(self.motors)) * values
pass
# Test
if __name__ == "__main__":
from itertools import count
from pyrobolearn.simulators import Bullet
from pyrobolearn.worlds import BasicWorld
# Create simulator
sim = Bullet()
# create world
world = BasicWorld(sim)
# create robot
robots = []
for _ in range(30):
x, y = np.random.uniform(low=-1, high=1, size=2)
robot = world.load_robot(Kilobot, position=(x, y, 0))
robots.append(robot)
# print information about the robot
robots[0].print_info()
# Position control using sliders
# robots[0].add_joint_slider()
# run simulator
for _ in count():
# robots[0].update_joint_slider()
for robot in robots:
robot.drive(5)
world.step(sleep_dt=1./240)
+8
View File
@@ -0,0 +1,8 @@
Copyright 2019, Brian Delhaisse
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,195 @@
<?xml version="1.0"?>
<!-- created by Brian: diameter=33mm, height=34mm, the dimensions of the different parts and the mass values are
random -->
<!-- solid cylinder: i_xx = i_yy = i_zz = 1/12 * m * (3*r^2 + h^2) -->
<robot name="kilobot">
<link name="base_link">
<inertial>
<origin xyz="0. 0. 0.02" />
<mass value="0.08"/>
<inertia ixx="3.220667e-05" ixy="0.0" ixz="0.0" iyy="3.220667e-05" iyz="0.0" izz="3.220667e-05" />
</inertial>
<visual>
<geometry>
<cylinder length="0.002" radius="0.0165"/>
</geometry>
<origin xyz="0.0 0.0 0.015"/>
<material name="green_pcb">
<color rgba="0.23 0.4 0.16 1.0"/>
</material>
</visual>
<collision>
<geometry>
<cylinder length="0.002" radius="0.0165"/>
</geometry>
<origin xyz="0.0 0.0 0.015"/>
</collision>
</link>
<link name="front_leg">
<inertial>
<mass value="0.001"/>
<inertia ixx="3.220667e-05" ixy="0.0" ixz="0.0" iyy="3.220667e-05" iyz="0.0" izz="3.220667e-05" />
</inertial>
<visual>
<geometry>
<cylinder length="0.02" radius="0.0005"/>
</geometry>
<material name="gold">
<color rgba="1.0 0.84 0.0 1.0"/>
</material>
</visual>
<collision>
<geometry>
<cylinder length="0.02" radius="0.0005"/>
</geometry>
</collision>
</link>
<joint name="base_to_front_leg" type="fixed">
<parent link="base_link"/>
<child link="front_leg"/>
<origin xyz="0.0165 0.0 0.0"/>
</joint>
<link name="left_leg">
<inertial>
<mass value="0.001"/>
<inertia ixx="3.220667e-05" ixy="0.0" ixz="0.0" iyy="3.220667e-05" iyz="0.0" izz="3.220667e-05" />
</inertial>
<visual>
<geometry>
<cylinder length="0.02" radius="0.0005"/>
</geometry>
<material name="gold">
<color rgba="1.0 0.84 0.0 1.0"/>
</material>
</visual>
<collision>
<geometry>
<cylinder length="0.02" radius="0.0005"/>
</geometry>
</collision>
</link>
<joint name="base_to_left_leg" type="fixed">
<parent link="base_link"/>
<child link="left_leg"/>
<origin xyz="-0.01166726 0.0116672 0.0"/>
</joint>
<link name="right_leg">
<inertial>
<mass value="0.001"/>
<inertia ixx="3.220667e-05" ixy="0.0" ixz="0.0" iyy="3.220667e-05" iyz="0.0" izz="3.220667e-05" />
</inertial>
<visual>
<geometry>
<cylinder length="0.02" radius="0.0005"/>
</geometry>
<material name="gold">
<color rgba="1.0 0.84 0.0 1.0"/>
</material>
</visual>
<collision>
<geometry>
<cylinder length="0.02" radius="0.0005"/>
</geometry>
</collision>
</link>
<joint name="base_to_right_leg" type="fixed">
<parent link="base_link"/>
<child link="right_leg"/>
<origin xyz="-0.01166726 -0.0116672 0.0"/>
</joint>
<link name="right_vibration_motor">
<inertial>
<origin rpy="0 1.57075 0"/>
<mass value="0.02"/>
<inertia ixx="2.006667e-06" ixy="0.0" ixz="0.0" iyy="2.006667e-06" iyz="0.0" izz="2.006667e-06" />
</inertial>
<visual>
<geometry>
<cylinder length="0.0015" radius="0.004"/>
</geometry>
<origin rpy="0 1.57075 0"/>
<material name="light_grey">
<color rgba="0.8 0.8 0.8 1"/>
</material>
</visual>
<collision>
<geometry>
<cylinder length="0.0015" radius="0.004"/>
</geometry>
<origin rpy="0 1.57075 0"/>
</collision>
</link>
<joint name="base_to_right_vibration_motor" type="continuous">
<parent link="base_link"/>
<child link="right_vibration_motor"/>
<origin rpy="0 0 -0.7853981633974483" xyz="0.0077782 -0.0077782 0.022"/>
<axis xyz="1 1 0"/>
</joint>
<link name="left_vibration_motor">
<inertial>
<origin rpy="0 1.57075 0"/>
<mass value="0.02"/>
<inertia ixx="2.006667e-06" ixy="0.0" ixz="0.0" iyy="2.006667e-06" iyz="0.0" izz="2.006667e-06" />
</inertial>
<visual>
<geometry>
<cylinder length="0.0015" radius="0.004"/>
</geometry>
<origin rpy="0 1.57075 0"/>
<material name="light_grey">
<color rgba="0.8 0.8 0.8 1"/>
</material>
</visual>
<collision>
<geometry>
<cylinder length="0.0015" radius="0.004"/>
</geometry>
<origin rpy="0 1.57075 0"/>
</collision>
</link>
<joint name="base_to_left_vibration_motor" type="continuous">
<parent link="base_link"/>
<child link="left_vibration_motor"/>
<origin rpy="0 0 0.7853981633974483" xyz="0.0077782 0.0077782 0.022"/>
<axis xyz="1 -1 0"/>
</joint>
<link name="body_top">
<inertial>
<mass value="0.03"/>
<inertia ixx="1.02775e-5" ixy="0.0" ixz="0.0" iyy="1.02775e-5" iyz="0.0" izz="1.02775e-5" />
</inertial>
<visual>
<geometry>
<cylinder length="0.008" radius="0.01"/>
</geometry>
<material name="black">
<color rgba="0.0 0.0 0.0 1"/>
</material>
</visual>
<collision>
<geometry>
<cylinder length="0.008" radius="0.01"/>
</geometry>
</collision>
</link>
<joint name="base_to_body_top" type="fixed">
<parent link="base_link"/>
<child link="body_top"/>
<origin xyz="0.0 0.0 0.022"/>
</joint>
</robot>
+3 -2
View File
@@ -22,10 +22,11 @@ Dependencies in PRL:
Dependencies in PRL: None
References:
[1] Dynamic Animation and Robotics Toolkit (DART):
[1] DART: Dynamic Animation and Robotics Toolkit
- paper: http://joss.theoj.org/papers/10.21105/joss.00500
- webpage: https://dartsim.github.io/
- github: https://github.com/dartsim/dart/
[2] PyDART:
[2] PyDART
- source code: https://pydart2.readthedocs.io/en/latest/
- documentation: https://pydart2.readthedocs.io/en/latest/
[3] PEP8: https://www.python.org/dev/peps/pep-0008/
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -7,7 +7,7 @@ in PyBullet [1,2], but in accordance with the PEP8 style guide [3].
Because the simulator is based on the PyBullet API and we want all the simulator APIs to be similar, all the other
simulators would have to be able to carry out operations such as querying the state of the robots, kinematics and
dynamics, .
dynamics, etc.
Dependencies in PRL: None
+20 -22
View File
@@ -110,7 +110,7 @@ class MLPValue(ValueNetwork):
This is defined by :math:`V_{\psi}(s_t)` where the function :math:`V` is approximated by a multilayer perceptron.
"""
def __init__(self, state, hidden_units=(), activation_fct='linear', last_activation_fct=None, dropout_prob=None,
def __init__(self, state, hidden_units=(), activation='linear', last_activation=None, dropout=None,
preprocessors=None):
"""Initialize the Value MLP approximator.
@@ -118,19 +118,18 @@ class MLPValue(ValueNetwork):
state (State): 1D-states that is feed to the policy (the input dimensions will be inferred from the
states)
hidden_units (list/tuple of int): number of hidden units in the corresponding layer
activation_fct (None, str, or list/tuple of str/None): activation function to be applied after each layer.
activation (None, str, or list/tuple of str/None): activation function to be applied after each layer.
If list/tuple, then it has to match the
last_activation_fct (None or str): last activation function to be applied. If not specified, it will check
last_activation (None or str): last activation function to be applied. If not specified, it will check
if it is in the list/tuple of activation functions provided for the
previous argument.
dropout_prob (None, float, or list/tuple of float/None): dropout probability.
dropout (None, float, or list/tuple of float/None): dropout probability.
preprocessors ((list of) Processor): pre-processors to be applied on the input state before being fed to
the inner model / function approximator.
"""
output = torch.Tensor([1.]) # torch.Tensor([[1.]])
model = MLPApproximator(state, output, hidden_units=hidden_units, activation=activation_fct,
last_activation=last_activation_fct, dropout=dropout_prob,
preprocessors=preprocessors)
model = MLPApproximator(state, output, hidden_units=hidden_units, activation=activation,
last_activation=last_activation, dropout=dropout, preprocessors=preprocessors)
super(MLPValue, self).__init__(state, model)
@@ -142,8 +141,8 @@ class MLPQValue(QValueNetwork):
and outputs the value :math:`Q(s,a)`. This can be used for continuous actions as well as discrete actions.
"""
def __init__(self, state, action, hidden_units=(), activation_fct='linear', last_activation_fct=None,
dropout_prob=None, preprocessors=None):
def __init__(self, state, action, hidden_units=(), activation='linear', last_activation=None, dropout=None,
preprocessors=None):
"""
Initialize the MLP state-action value function approximator.
@@ -151,18 +150,18 @@ class MLPQValue(QValueNetwork):
state (State): input state.
action (Action): input action.
hidden_units (list/tuple of int): number of hidden units in the corresponding layer
activation_fct (None, str, or list/tuple of str/None): activation function to be applied after each layer.
activation (None, str, or list/tuple of str/None): activation function to be applied after each layer.
If list/tuple, then it has to match the
last_activation_fct (None or str): last activation function to be applied. If not specified, it will check
last_activation (None or str): last activation function to be applied. If not specified, it will check
if it is in the list/tuple of activation functions provided for the
previous argument.
dropout_prob (None, float, or list/tuple of float/None): dropout probability.
dropout (None, float, or list/tuple of float/None): dropout probability.
preprocessors ((list of) Processor): pre-processors to be applied on the input state before being fed to
the inner model / function approximator.
"""
model = MLPApproximator(inputs=[state, action], outputs=torch.Tensor([1]), hidden_units=hidden_units,
activation=activation_fct, last_activation=last_activation_fct,
dropout=dropout_prob, preprocessors=preprocessors)
activation=activation, last_activation=last_activation, dropout=dropout,
preprocessors=preprocessors)
super(MLPQValue, self).__init__(state, action, model=model)
@@ -175,8 +174,8 @@ class MLPQValueOutput(ParametrizedQValueOutput):
:math:`Q(s,a)` for each discrete action. This can NOT be used with continuous actions.
"""
def __init__(self, state, action, hidden_units=(), activation_fct='linear', last_activation_fct=None,
dropout_prob=None, preprocessors=None):
def __init__(self, state, action, hidden_units=(), activation='linear', last_activation=None, dropout=None,
preprocessors=None):
"""
Initialize the MLP state-action value function approximator.
@@ -184,16 +183,15 @@ class MLPQValueOutput(ParametrizedQValueOutput):
state (State): input state.
action (Action): output action.
hidden_units (list/tuple of int): number of hidden units in the corresponding layer
activation_fct (None, str, or list/tuple of str/None): activation function to be applied after each layer.
activation (None, str, or list/tuple of str/None): activation function to be applied after each layer.
If list/tuple, then it has to match the
last_activation_fct (None or str): last activation function to be applied. If not specified, it will check
last_activation (None or str): last activation function to be applied. If not specified, it will check
if it is in the list/tuple of activation functions provided for the
previous argument.
dropout_prob (None, float, or list/tuple of float/None): dropout probability.
dropout (None, float, or list/tuple of float/None): dropout probability.
preprocessors ((list of) Processor): pre-processors to be applied on the input state before being fed to
the inner model / function approximator.
"""
model = MLPApproximator(inputs=state, outputs=action, hidden_units=hidden_units,
activation=activation_fct, last_activation=last_activation_fct,
dropout=dropout_prob, preprocessors=preprocessors)
model = MLPApproximator(inputs=state, outputs=action, hidden_units=hidden_units, activation=activation,
last_activation=last_activation, dropout=dropout, preprocessors=preprocessors)
super(MLPQValueOutput, self).__init__(state, action, model=model)