Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft: Tobi Dance Branch #12

Draft
wants to merge 43 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
68bf537
Starting template for integrating vector of target positions
marcinpaluch1994 Nov 24, 2022
6a6d218
to fix tensorflow JIT compile case insentivity problem, renamed cart …
tobidelbruck Nov 27, 2022
d1d9203
docstring for update_parameters
tobidelbruck Nov 28, 2022
cc2b58c
controller_mpc.py now copies all modified controller, cost and optimi…
tobidelbruck Nov 28, 2022
06c7981
rename config_cost_function.yml to config_cost_functions.yml for cons…
tobidelbruck Nov 28, 2022
068a0ac
finally the dynamically modifiable control cost parameters are workin…
tobidelbruck Dec 11, 2022
64148a3
now spin and balance both work! and so does changing the policy and …
tobidelbruck Dec 12, 2022
fef38b0
got basic shimmy movement to work now. added helper vars to access co…
tobidelbruck Dec 12, 2022
d2e9942
added cartonly trajectory and fixed bug that erased the target positi…
tobidelbruck Dec 12, 2022
03fa3af
passing current state to cartpole_trajectory_generator.py so it can e…
tobidelbruck Dec 12, 2022
ab0f34e
added MPPI papers to docstring
tobidelbruck Dec 13, 2022
7ad7be1
added MPPI papers to docstring
tobidelbruck Dec 13, 2022
86c8d68
Rename num_rollouts -> batch_size in config templates
frehe Dec 14, 2022
3ade5d0
add comment
tobidelbruck Dec 18, 2022
56d3e51
local changes, all minor except for trajectory cost that is in flux
tobidelbruck Dec 18, 2022
9dad3a7
Merge remote-tracking branch 'origin/Tobi_Dance' into Tobi_Dance
tobidelbruck Dec 18, 2022
67ba599
renamed s to state for clariy in many of the classes.
Dec 24, 2022
1f324f4
added dancer that reads CSV file to specify sequence of 'steps' (beha…
Dec 26, 2022
0b543a0
fixed import of CompileTF to point to SI_Toolkit
tobidelbruck Jan 28, 2023
8fe2a96
Merge remote-tracking branch 'origin/main' into Tobi_Dance
tobidelbruck Jan 31, 2023
c0ee620
merged from Tobi_Dance and added some loggers
tobidelbruck Feb 3, 2023
ee9ca38
moved get_logger to own file in SI_Toolkit
tobidelbruck Feb 6, 2023
ecf2cc8
Merge remote-tracking branch 'origin/Tobi_Dance' into Tobi_Dance
tobidelbruck Feb 7, 2023
69b886c
added search path for running from physical-cartpole.
tobidelbruck Feb 7, 2023
4ce131e
update path to config_cost_functions.yml
tobidelbruck Feb 7, 2023
32c0a50
move get_logger.py to Control_Toolkit so that it can be used by physi…
tobidelbruck Feb 8, 2023
45f1d56
Merge remote-tracking branch 'origin/Tobi_Dance' into Tobi_Dance
tobidelbruck Feb 8, 2023
f0ec1d1
cartpole_dancer.py starts to work. Music starts and stops, some steps…
tobidelbruck Feb 10, 2023
5d13e63
improved control slightly by adding back more cost terms to provide s…
tobidelbruck Feb 11, 2023
e2162fd
added some docstrings, but they are not very informative
tobidelbruck Feb 12, 2023
518a00f
added prediction and target trajectory to logging to allow model mism…
tobidelbruck Feb 13, 2023
9190c2a
add computation of pole natural frequency to p_globals.py.
tobidelbruck Feb 14, 2023
6b8a676
added 'cartwheel' step to cartpole_trajectory_generator.py.
tobidelbruck Feb 16, 2023
039d385
added warning for cart calibration.
tobidelbruck Feb 19, 2023
ed2c118
fixed some logic and reduced some loggers to debug level
tobidelbruck Feb 19, 2023
a16b454
fixed shimmy math.
tobidelbruck Feb 20, 2023
000ff67
Merge remote-tracking branch 'origin/Tobi_Dance' into Tobi_Dance
tobidelbruck Feb 20, 2023
ea33a00
improved console reporting of current objective and logging output so…
tobidelbruck Feb 21, 2023
6e8ef51
improved logging output to make debug logger light gray, include file…
tobidelbruck Feb 21, 2023
e894dfc
fixed get_logger.py that now uses a single logger name to only add th…
tobidelbruck Feb 22, 2023
3489a44
reduced chatter in logging
tobidelbruck Feb 23, 2023
a1a36db
major changes to cartpole_dancer_cost and cartpole_trajectory_generat…
tobidelbruck Feb 28, 2023
807c46d
initial commit of Shreyan's code for energy-based controller for cart…
tobidelbruck May 13, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
import os

from Control_Toolkit.Controllers import template_controller
from others.globals_and_utils import create_rng
from others.globals_and_utils import create_rng, update_attributes


# TODO: You can load and access config files here, like this:
# config = yaml.load(open("config.yml", "r"), Loader=yaml.FullLoader)
Expand All @@ -32,9 +33,10 @@ def configure(self):
pass

def step(self, s: np.ndarray, time=None, updated_attributes: "dict[str, TensorType]" = {}):

# The controller has to adapt when environment-related attributes such as target positions change
# Updated targets etc. are passed as a dictionary updated_attributes
self.update_attributes(updated_attributes) # After this call, updated attributes are available as self.<<attribute_name>>
update_attributes(updated_attributes,self) # After this call, updated attributes are available as self.<<attribute_name>>

# TODO: Implement your controller here
# Examples:
Expand Down
4 changes: 2 additions & 2 deletions Control_Toolkit_ASF_Template/Controllers/controller_do_mpc.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from CartPole.cartpole_model import Q2u, cartpole_ode_namespace
from CartPole.state_utilities import cartpole_state_vector_to_namespace
from Control_Toolkit.Controllers import template_controller
from others.globals_and_utils import create_rng
from others.globals_and_utils import create_rng, update_attributes
from SI_Toolkit.computation_library import NumpyLibrary, TensorType


Expand Down Expand Up @@ -137,7 +137,7 @@ def tvp_fun(self, t_ind):


def step(self, s: np.ndarray, time=None, updated_attributes: "dict[str, TensorType]" = {}):
self.update_attributes(updated_attributes)
update_attributes(updated_attributes,self)

s = cartpole_state_vector_to_namespace(s)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from CartPole.state_utilities import cartpole_state_vector_to_namespace
from Control_Toolkit.Controllers import template_controller
from SI_Toolkit.computation_library import NumpyLibrary, TensorType
from others.globals_and_utils import update_attributes


def mpc_next_state(s, u, dt):
Expand Down Expand Up @@ -148,7 +149,7 @@ def tvp_fun(self, t_ind):
return self.tvp_template

def step(self, s: np.ndarray, time=None, updated_attributes: "dict[str, TensorType]" = {}):
self.update_attributes(updated_attributes)
update_attributes(updated_attributes,self)

s = cartpole_state_vector_to_namespace(s)

Expand Down
4 changes: 2 additions & 2 deletions Control_Toolkit_ASF_Template/Controllers/controller_lqr.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from CartPole.state_utilities import (ANGLE_IDX, ANGLED_IDX, POSITION_IDX,
POSITIOND_IDX)
from Control_Toolkit.Controllers import template_controller
from others.globals_and_utils import create_rng
from others.globals_and_utils import create_rng, update_attributes

config = yaml.load(open("config.yml", "r"), Loader=yaml.FullLoader)
actuator_noise = config["cartpole"]["actuator_noise"]
Expand Down Expand Up @@ -81,7 +81,7 @@ def configure(self):
self.eigVals = eigVals

def step(self, s: np.ndarray, time=None, updated_attributes: "dict[str, TensorType]" = {}):
self.update_attributes(updated_attributes)
update_attributes(updated_attributes,self)

state = np.array(
[[s[POSITION_IDX] - self.target_position], [s[POSITIOND_IDX]], [s[ANGLE_IDX]], [s[ANGLED_IDX]]])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@


# TODO: Load constants from the cost config file, like this:
config = yaml.load(open(os.path.join("Control_Toolkit_ASF", "config_cost_function.yml"), "r"), Loader=yaml.FullLoader)
config = yaml.load(open(os.path.join("Control_Toolkit_ASF", "config_cost_functions.yml"), "r"), Loader=yaml.FullLoader)

# TODO: Rename parent folder from EnvironmentName to the actual name of you environment
# TODO: Load constants like this:
Expand Down Expand Up @@ -37,7 +37,7 @@ def get_terminal_cost(self, terminal_states: TensorType):
# return terminal_cost
pass

# all stage costs together
# all stage costs together. A 'stage' is one timestep of a rollout.
def get_stage_cost(self, states: TensorType, inputs: TensorType, previous_input: TensorType):
# Shape of states: [batch_size, mpc_horizon, num_states]
# TODO: Compute stage cost
Expand Down
56 changes: 39 additions & 17 deletions Controllers/__init__.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
import os
from abc import ABC, abstractmethod
from typing import Tuple
from typing import Tuple, Union

import numpy as np
import yaml
from Control_Toolkit.others.globals_and_utils import get_logger
from SI_Toolkit.computation_library import (ComputationLibrary, NumpyLibrary,
PyTorchLibrary, TensorFlowLibrary,
TensorType)
from others.globals_and_utils import load_or_reload_config_if_modified

config_cost_function = yaml.load(open(os.path.join("Control_Toolkit_ASF", "config_cost_function.yml")), Loader=yaml.FullLoader)
config_cost_function = yaml.load(open(os.path.join("Control_Toolkit_ASF", "config_cost_functions.yml")), Loader=yaml.FullLoader)
logger = get_logger(__name__)

"""
For a controller to be found and imported by CartPoleGUI/DataGenerator it must:
1. Be in Controller folder
1. Be in Controllers folder
2. Have a name starting with "controller_"
3. The name of the controller class must be the same as the name of the file.
4. It must have __init__ and step methods
Expand All @@ -26,7 +27,7 @@
class template_controller(ABC):
_has_optimizer = False
# Define the computation library in your controller class or in the controller's configuration:
_computation_library: "type[ComputationLibrary]" = None
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Technically, we always work with a static reference to the computation library. That means we don't create instances of it, but always call the methods on the class itself. Therefore, the type of this attribute is type. This is really only for documentation. The code will work no matter what we put here.

Btw, the subscript type notation "type[ComputationLibrary]" in quotes is necessary for python<=3.8. In later versions one can use type[ComputationLibrary], but we maintain backward compatibility for now

_computation_library: ComputationLibrary = None

def __init__(
self,
Expand All @@ -38,14 +39,13 @@ def __init__(
initial_environment_attributes: "dict[str, TensorType]",
):
# Load controller config and select the entry for the current controller
config_controllers = yaml.load(
open(os.path.join("Control_Toolkit_ASF", "config_controllers.yml")),
Loader=yaml.FullLoader
)
(config_controllers,_) = load_or_reload_config_if_modified(os.path.join("Control_Toolkit_ASF", "config_controllers.yml")) # ignore the _ changes return since this is initial call
# self.controller_name is inferred from the class name, which is the class being instantiated
# Example: If you create a controller_mpc, this controller_template.__init__ will be called
# but the class name will be controller_mpc, not template_controller.
self.config_controller = dict(config_controllers[self.controller_name])
config_key=self.controller_name
self.config_controller = config_controllers[config_key]
# add timestep .dt to all controllers here
self.config_controller["dt"] = dt

# Set computation library
Expand Down Expand Up @@ -78,11 +78,20 @@ def __init__(
self.num_control_inputs = num_control_inputs
self.control_limits = control_limits
self.action_low, self.action_high = self.control_limits

# todo these are special for cartpole but we would need a base cartpole controller class to put them there
# self.target_position=None
# self.target_equilibrium=None

# Set properties like target positions on this controller
for property, new_value in initial_environment_attributes.items():
setattr(self, property, self.computation_library.to_variable(new_value, self.computation_library.float32))


# set all controller config numerical values as float variables in the computation space, e.g. tensorflow, so they can be updqted during runtime
for property, value in self.config_controller.items():
if value is float or value is int:
setattr(self, property, self.computation_library.to_variable(value, self.computation_library.float32))

# Initialize control variable
self.u = 0.0

Expand All @@ -103,12 +112,18 @@ def configure(self, **kwargs):
# In your controller, implement any additional initialization steps here
pass

def update_attributes(self, updated_attributes: "dict[str, TensorType]"):
for property, new_value in updated_attributes.items():
self.computation_library.assign(getattr(self, property), self.lib.to_tensor(new_value, self.lib.float32))

@abstractmethod
def step(self, s: np.ndarray, time=None, updated_attributes: "dict[str, TensorType]" = {}):
def step(self, s: np.ndarray, time=None, updated_attributes: "dict[str, Union[TensorType,float]]" = dict()):
"""
Execute one timestep of control.

:param s: the state array, dimensions are TODO add dimension to help users
:param time: the time in seconds
:param updated_attributes: dict of updated attributes

:returns: the next control action u e.g. a normed control input in the range [-1,1] TODO is this correct?

"""
### Any computations in order to retrieve the current control. Such as:
## If the environment's target positions etc. change, copy the new attributes over to this controller so the cost function knows about it:
# self.update_attributes(updated_attributes)
Expand All @@ -134,10 +149,17 @@ def controller_report(self):
def controller_reset(self):
raise NotImplementedError

@property
@property # decorates the controller so it has the field .controller_name that gets its short name which is the key in the .yml config file
def controller_name(self):
""" Generates standard name for this controller, but use this method like it were a field.

:returns: the short name which is the key to the controller in the config_controller.yml file, e.g. 'cartpole_mppi'

"""
name = self.__class__.__name__
if name != "template_controller":
if 'controller_' not in name:
raise AttributeError(f'this controller named "{name}" does not contain "controller_". Controllers should start or contain "controller_" and the key in the config_controllers.yml file should follow the underscore')
return name.replace("controller_", "").replace("_", "-").lower()
else:
raise AttributeError()
Expand All @@ -147,7 +169,7 @@ def controller_data_for_csv(self):
return {}

@property
def computation_library(self) -> "type[ComputationLibrary]":
def computation_library(self) -> ComputationLibrary:
if self._computation_library == None:
raise NotImplementedError("Controller class needs to specify its computation library")
return self._computation_library
Expand Down
82 changes: 82 additions & 0 deletions Controllers/cartpole_trajectory_generator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# stub for genrating desired future trajectory of cartpole
import numpy
import numpy as np
from torch import TensorType

from Control_Toolkit.Controllers import template_controller
from Control_Toolkit.others.globals_and_utils import get_logger
from SI_Toolkit.computation_library import ComputationLibrary
from CartPole import state_utilities

period=1

log=get_logger(__name__)

class cartpole_trajectory_generator:
""" Generates target state trajectory for the cartpole """
def __init__(self, lib:ComputationLibrary, controller:template_controller=None):
""" Construct the trajectory generator.

:param lib: the computation library, e.g. tensorflow
:param horizon: the MPC horizon in timesteps
"""
self.lib = lib
self.controller:template_controller=controller

def step(self, time: float, horizon: int, dt:float) -> TensorType:
""" Computes the desired future state trajectory at this time.

:param time: the scalar time in seconds
:param horizon: the number of horizon steps
:param dt: the timestep in seconds

:returns: the target state trajectory of cartpole.
It should be a Tensor with NaN as at least first entries for don't care states, and otherwise the desired state values.

"""

traj=np.zeros((state_utilities.NUM_STATES, horizon)) # must be numpy here because tensor is immutable
traj[:]=self.lib.nan # set all states undetermined

cost_function=self.controller.cost_function_wrapper.cost_function # use cost_function to access attributes (fields) set in config_cost_functions.yml
controller=self.controller # use controller to access attributes set in config_optimizers

policy=cost_function.policy
if policy is None:
raise RuntimeError(f'set policy in config_cost_functions.yml')

if policy == 'spin': # spin pole CW or CCW depending on target_equilibrium up or down
traj[state_utilities.POSITION_IDX] = controller.target_position
# traj[state_utilities.ANGLE_COS_IDX, :] = controller.target_equilibrium
# traj[state_utilities.ANGLE_SIN_IDX, :] = 0
# traj[state_utilities.ANGLE_IDX, :] = self.lib.pi * controller.target_equilibrium
traj[state_utilities.ANGLED_IDX, :] = 1000*controller.target_equilibrium # 1000 rad/s is arbitrary, not sure if this is best target
# traj[state_utilities.POSITIOND_IDX, :] = 0
elif policy == 'balance': # balance upright or down at desired cart position
traj[state_utilities.POSITION_IDX] = controller.target_position
target_angle=self.lib.pi * (1-controller.target_equilibrium)/2 # either 0 for up and pi for down
traj[state_utilities.ANGLE_COS_IDX, :] = np.cos(target_angle)
traj[state_utilities.ANGLE_SIN_IDX, :] = np.sin(target_angle)
traj[state_utilities.ANGLE_IDX, :] = target_angle
traj[state_utilities.ANGLED_IDX, :] = 0
traj[state_utilities.POSITIOND_IDX, :] = 0
elif policy == 'shimmy': # cart follows a desired cart position shimmy while keeping pole up or down
shimmy_period=cost_function.shimmy_period # seconds
shimmy_amp=cost_function.shimmy_amp # meters
endtime=time+horizon*dt
times=np.linspace(time,endtime,num=horizon)
shimmy=shimmy_amp*np.sin((2*np.pi/shimmy_period)*times)
shimmyd=np.gradient(shimmy,dt)
traj[state_utilities.POSITION_IDX] = controller.target_position+shimmy
target_angle=self.lib.pi * (1-controller.target_equilibrium)/2 # either 0 for up and pi for down
traj[state_utilities.ANGLE_COS_IDX, :] = np.cos(target_angle)
traj[state_utilities.ANGLE_SIN_IDX, :] = np.sin(target_angle)
traj[state_utilities.ANGLE_IDX, :] = target_angle
# traj[state_utilities.ANGLED_IDX, :] = 0
traj[state_utilities.POSITIOND_IDX, :] = shimmyd
else:
log.error(f'cost policy "{policy}" is unknown')

# traj=self.lib.to_variable(traj, self.lib.float32)

return traj
Loading