tau-ctrl 0.1.0__tar.gz → 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {tau_ctrl-0.1.0 → tau_ctrl-0.2.0}/PKG-INFO +7 -9
- {tau_ctrl-0.1.0 → tau_ctrl-0.2.0}/README.md +5 -7
- {tau_ctrl-0.1.0 → tau_ctrl-0.2.0}/pyproject.toml +2 -2
- {tau_ctrl-0.1.0 → tau_ctrl-0.2.0}/src/tau_ctrl/__init__.py +13 -5
- {tau_ctrl-0.1.0 → tau_ctrl-0.2.0}/src/tau_ctrl/algorithms/__init__.py +5 -4
- {tau_ctrl-0.1.0 → tau_ctrl-0.2.0}/src/tau_ctrl/algorithms/base.py +81 -11
- {tau_ctrl-0.1.0 → tau_ctrl-0.2.0}/src/tau_ctrl/algorithms/ilqr.py +17 -5
- {tau_ctrl-0.1.0 → tau_ctrl-0.2.0}/src/tau_ctrl/algorithms/mppi.py +7 -1
- tau_ctrl-0.2.0/src/tau_ctrl/algorithms/mujoco.py +72 -0
- {tau_ctrl-0.1.0 → tau_ctrl-0.2.0}/src/tau_ctrl/algorithms/ppo.py +12 -6
- {tau_ctrl-0.1.0 → tau_ctrl-0.2.0}/src/tau_ctrl/algorithms/sac.py +9 -3
- {tau_ctrl-0.1.0 → tau_ctrl-0.2.0}/src/tau_ctrl/algorithms/strategy.py +9 -9
- {tau_ctrl-0.1.0 → tau_ctrl-0.2.0}/src/tau_ctrl/algorithms/td3.py +9 -3
- {tau_ctrl-0.1.0 → tau_ctrl-0.2.0}/src/tau_ctrl/algorithms/vec_env.py +4 -4
- {tau_ctrl-0.1.0 → tau_ctrl-0.2.0}/src/tau_ctrl.egg-info/PKG-INFO +7 -9
- {tau_ctrl-0.1.0 → tau_ctrl-0.2.0}/src/tau_ctrl.egg-info/SOURCES.txt +1 -0
- {tau_ctrl-0.1.0 → tau_ctrl-0.2.0}/LICENSE +0 -0
- {tau_ctrl-0.1.0 → tau_ctrl-0.2.0}/setup.cfg +0 -0
- {tau_ctrl-0.1.0 → tau_ctrl-0.2.0}/src/tau_ctrl/algorithms/adapters.py +0 -0
- {tau_ctrl-0.1.0 → tau_ctrl-0.2.0}/src/tau_ctrl/algorithms/cbf.py +0 -0
- {tau_ctrl-0.1.0 → tau_ctrl-0.2.0}/src/tau_ctrl/algorithms/envs/__init__.py +0 -0
- {tau_ctrl-0.1.0 → tau_ctrl-0.2.0}/src/tau_ctrl/algorithms/envs/toy.py +0 -0
- {tau_ctrl-0.1.0 → tau_ctrl-0.2.0}/src/tau_ctrl/algorithms/off_policy.py +0 -0
- {tau_ctrl-0.1.0 → tau_ctrl-0.2.0}/src/tau_ctrl/algorithms/pid.py +0 -0
- {tau_ctrl-0.1.0 → tau_ctrl-0.2.0}/src/tau_ctrl/py.typed +0 -0
- {tau_ctrl-0.1.0 → tau_ctrl-0.2.0}/src/tau_ctrl/tuning/__init__.py +0 -0
- {tau_ctrl-0.1.0 → tau_ctrl-0.2.0}/src/tau_ctrl/tuning/auto_tuner.py +0 -0
- {tau_ctrl-0.1.0 → tau_ctrl-0.2.0}/src/tau_ctrl/tuning/bayesian_opt.py +0 -0
- {tau_ctrl-0.1.0 → tau_ctrl-0.2.0}/src/tau_ctrl/tuning/genetic_opt.py +0 -0
- {tau_ctrl-0.1.0 → tau_ctrl-0.2.0}/src/tau_ctrl.egg-info/dependency_links.txt +0 -0
- {tau_ctrl-0.1.0 → tau_ctrl-0.2.0}/src/tau_ctrl.egg-info/requires.txt +0 -0
- {tau_ctrl-0.1.0 → tau_ctrl-0.2.0}/src/tau_ctrl.egg-info/top_level.txt +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tau-ctrl
|
|
3
|
-
Version: 0.
|
|
4
|
-
Summary:
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Simulator-agnostic control algorithms over any gymnasium.Env — PID, MPPI/CEM/iCEM, iLQR, CBF, and GPU-native RL (PPO/SAC/TD3) with vectorized on-device training
|
|
5
5
|
Author: Tau Intelligence
|
|
6
6
|
License: Apache-2.0
|
|
7
7
|
Project-URL: Homepage, https://tau-intelligence.com/
|
|
@@ -40,9 +40,9 @@ Dynamic: license-file
|
|
|
40
40
|
|
|
41
41
|
# tau-ctrl
|
|
42
42
|
|
|
43
|
-
**
|
|
43
|
+
**Simulator-agnostic control algorithms — feedback, sampling-based MPC, safety filtering, and GPU-native RL behind one interface.**
|
|
44
44
|
|
|
45
|
-
|
|
45
|
+
A unified library for the whole controller spectrum, over any `gymnasium.Env`. No simulator dependency: works with whatever hands you an env (e.g. [tau-sim](https://tau-intelligence.com/tau_sim/)). The RL methods train **on-device** and scale to vectorized environments for real GPU speedup.
|
|
46
46
|
|
|
47
47
|
## Installation
|
|
48
48
|
|
|
@@ -57,7 +57,7 @@ pip install tau-ctrl[torch] # + RL: PPO, SAC, TD3, and vectorized on-device
|
|
|
57
57
|
from tau_ctrl import make
|
|
58
58
|
|
|
59
59
|
ctrl = make("mppi", env, horizon=25, n_samples=300) # or MPPI(env, ...), SAC(env), ...
|
|
60
|
-
action, _ = ctrl.predict(obs) #
|
|
60
|
+
action, _ = ctrl.predict(obs) # standard interface
|
|
61
61
|
ctrl.learn(total_timesteps=100_000) # trainable methods (ppo/sac/td3)
|
|
62
62
|
ctrl.save("ctrl.pkl")
|
|
63
63
|
```
|
|
@@ -84,7 +84,7 @@ seeding of torch's RNG).
|
|
|
84
84
|
|
|
85
85
|
## GPU-native RL: vectorized, on-device training
|
|
86
86
|
|
|
87
|
-
|
|
87
|
+
Standard pipelines step CPU environments sequentially and perform updates in slow Python loops.
|
|
88
88
|
tau-ctrl's SAC/TD3 instead run the replay buffer and the update **on the target
|
|
89
89
|
device**, and — given a batched env — step thousands of environments in parallel
|
|
90
90
|
with no numpy in the hot loop. `Trainer.auto` probes your env and hardware and
|
|
@@ -108,8 +108,7 @@ action, _ = model.predict(obs)
|
|
|
108
108
|
| a single, non-batchable env (PyBullet, classic MuJoCo) + a factory | `SyncTorchVecEnv` | batched update on device |
|
|
109
109
|
| one env you can't replicate (a real robot) | — (single-env path) | only the update |
|
|
110
110
|
|
|
111
|
-
See [`benchmarks/RESULTS.md`](benchmarks/RESULTS.md) for
|
|
112
|
-
Stable-Baselines3 and skrl, and [`examples/`](examples/) for runnable scripts
|
|
111
|
+
See [`benchmarks/RESULTS.md`](benchmarks/RESULTS.md) for performance comparison benchmarks, and [`examples/`](examples/) for runnable scripts
|
|
113
112
|
(`quickstart.py`, `vectorized_rl.py`, `adaptive_training.py`).
|
|
114
113
|
|
|
115
114
|
## Safety filtering
|
|
@@ -151,4 +150,3 @@ Apache 2.0 — see [LICENSE](LICENSE).
|
|
|
151
150
|
## Related
|
|
152
151
|
|
|
153
152
|
- [tau-sim](https://tau-intelligence.com/tau_sim/) — robotics environment builder
|
|
154
|
-
- [Stable-Baselines3](https://stable-baselines3.readthedocs.io/) — RL algorithms (API inspiration)
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# tau-ctrl
|
|
2
2
|
|
|
3
|
-
**
|
|
3
|
+
**Simulator-agnostic control algorithms — feedback, sampling-based MPC, safety filtering, and GPU-native RL behind one interface.**
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
A unified library for the whole controller spectrum, over any `gymnasium.Env`. No simulator dependency: works with whatever hands you an env (e.g. [tau-sim](https://tau-intelligence.com/tau_sim/)). The RL methods train **on-device** and scale to vectorized environments for real GPU speedup.
|
|
6
6
|
|
|
7
7
|
## Installation
|
|
8
8
|
|
|
@@ -17,7 +17,7 @@ pip install tau-ctrl[torch] # + RL: PPO, SAC, TD3, and vectorized on-device
|
|
|
17
17
|
from tau_ctrl import make
|
|
18
18
|
|
|
19
19
|
ctrl = make("mppi", env, horizon=25, n_samples=300) # or MPPI(env, ...), SAC(env), ...
|
|
20
|
-
action, _ = ctrl.predict(obs) #
|
|
20
|
+
action, _ = ctrl.predict(obs) # standard interface
|
|
21
21
|
ctrl.learn(total_timesteps=100_000) # trainable methods (ppo/sac/td3)
|
|
22
22
|
ctrl.save("ctrl.pkl")
|
|
23
23
|
```
|
|
@@ -44,7 +44,7 @@ seeding of torch's RNG).
|
|
|
44
44
|
|
|
45
45
|
## GPU-native RL: vectorized, on-device training
|
|
46
46
|
|
|
47
|
-
|
|
47
|
+
Standard pipelines step CPU environments sequentially and perform updates in slow Python loops.
|
|
48
48
|
tau-ctrl's SAC/TD3 instead run the replay buffer and the update **on the target
|
|
49
49
|
device**, and — given a batched env — step thousands of environments in parallel
|
|
50
50
|
with no numpy in the hot loop. `Trainer.auto` probes your env and hardware and
|
|
@@ -68,8 +68,7 @@ action, _ = model.predict(obs)
|
|
|
68
68
|
| a single, non-batchable env (PyBullet, classic MuJoCo) + a factory | `SyncTorchVecEnv` | batched update on device |
|
|
69
69
|
| one env you can't replicate (a real robot) | — (single-env path) | only the update |
|
|
70
70
|
|
|
71
|
-
See [`benchmarks/RESULTS.md`](benchmarks/RESULTS.md) for
|
|
72
|
-
Stable-Baselines3 and skrl, and [`examples/`](examples/) for runnable scripts
|
|
71
|
+
See [`benchmarks/RESULTS.md`](benchmarks/RESULTS.md) for performance comparison benchmarks, and [`examples/`](examples/) for runnable scripts
|
|
73
72
|
(`quickstart.py`, `vectorized_rl.py`, `adaptive_training.py`).
|
|
74
73
|
|
|
75
74
|
## Safety filtering
|
|
@@ -111,4 +110,3 @@ Apache 2.0 — see [LICENSE](LICENSE).
|
|
|
111
110
|
## Related
|
|
112
111
|
|
|
113
112
|
- [tau-sim](https://tau-intelligence.com/tau_sim/) — robotics environment builder
|
|
114
|
-
- [Stable-Baselines3](https://stable-baselines3.readthedocs.io/) — RL algorithms (API inspiration)
|
|
@@ -4,8 +4,8 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "tau-ctrl"
|
|
7
|
-
version = "0.
|
|
8
|
-
description = "
|
|
7
|
+
version = "0.2.0"
|
|
8
|
+
description = "Simulator-agnostic control algorithms over any gymnasium.Env — PID, MPPI/CEM/iCEM, iLQR, CBF, and GPU-native RL (PPO/SAC/TD3) with vectorized on-device training"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = {text = "Apache-2.0"}
|
|
11
11
|
requires-python = ">=3.9"
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
"""
|
|
2
|
-
tau-ctrl —
|
|
2
|
+
tau-ctrl — a simulator-agnostic controller/algorithm framework.
|
|
3
3
|
|
|
4
|
-
Call algorithms directly off the package
|
|
4
|
+
Call algorithms directly off the package:
|
|
5
5
|
|
|
6
6
|
from tau_ctrl import make, PID, MPPI, CEM, ICEM, ILQR, CBFFilter, PPO, SAC, TD3
|
|
7
7
|
|
|
8
8
|
ctrl = make("mppi", env, horizon=25, n_samples=200) # or PID(env), PPO(env), ...
|
|
9
|
-
action, _ = ctrl.predict(obs) #
|
|
9
|
+
action, _ = ctrl.predict(obs) # returns action, next_state
|
|
10
10
|
ctrl.learn(total_timesteps=100_000) # trainable methods (ppo/sac/td3)
|
|
11
11
|
|
|
12
12
|
Every algorithm shares one interface (``predict``/``learn``/``save``/``load``)
|
|
@@ -20,10 +20,15 @@ Model-based methods (MPPI/CEM/ICEM/ILQR/CBF) need the env to be *branchable*
|
|
|
20
20
|
env. :mod:`tau_ctrl.tuning` adds Bayesian/genetic auto-tuning of any gains.
|
|
21
21
|
"""
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
from importlib.metadata import PackageNotFoundError
|
|
24
|
+
from importlib.metadata import version as _pkg_version
|
|
25
|
+
|
|
26
|
+
try:
|
|
27
|
+
__version__ = _pkg_version("tau-ctrl")
|
|
28
|
+
except PackageNotFoundError: # not installed (e.g. running from a source checkout)
|
|
29
|
+
__version__ = "0.0.0+unknown"
|
|
24
30
|
|
|
25
31
|
from .algorithms import (
|
|
26
|
-
CBFFilter,
|
|
27
32
|
CEM,
|
|
28
33
|
ICEM,
|
|
29
34
|
ILQR,
|
|
@@ -33,7 +38,9 @@ from .algorithms import (
|
|
|
33
38
|
SAC,
|
|
34
39
|
TD3,
|
|
35
40
|
BaseController,
|
|
41
|
+
CBFFilter,
|
|
36
42
|
ControllerManifest,
|
|
43
|
+
MujocoBranchable,
|
|
37
44
|
Trainer,
|
|
38
45
|
available,
|
|
39
46
|
get_state,
|
|
@@ -63,6 +70,7 @@ __all__ = [
|
|
|
63
70
|
"ICEM",
|
|
64
71
|
"ILQR",
|
|
65
72
|
"CBFFilter",
|
|
73
|
+
"MujocoBranchable",
|
|
66
74
|
"PPO",
|
|
67
75
|
"SAC",
|
|
68
76
|
"TD3",
|
|
@@ -1,12 +1,11 @@
|
|
|
1
|
-
"""tau-ctrl's
|
|
1
|
+
"""tau-ctrl's simulator-agnostic controller/algorithm framework.
|
|
2
2
|
|
|
3
|
-
One interface for the whole controller spectrum over a Gymnasium env
|
|
4
|
-
the way you'd call SB3:
|
|
3
|
+
One interface for the whole controller spectrum over a Gymnasium env:
|
|
5
4
|
|
|
6
5
|
from tau_ctrl import make, PID, MPPI, CEM, ICEM, ILQR, CBFFilter, PPO, SAC, TD3
|
|
7
6
|
|
|
8
7
|
ctrl = make("mppi", env, horizon=25, n_samples=200) # or PID(env), PPO(env), ...
|
|
9
|
-
action, _ = ctrl.predict(obs) #
|
|
8
|
+
action, _ = ctrl.predict(obs) # returns action, next_state
|
|
10
9
|
ctrl.learn(total_timesteps=100_000) # for trainable ones (ppo/sac/td3)
|
|
11
10
|
|
|
12
11
|
Design goals:
|
|
@@ -38,6 +37,7 @@ from .base import (
|
|
|
38
37
|
from .cbf import CBFFilter
|
|
39
38
|
from .ilqr import ILQR
|
|
40
39
|
from .mppi import CEM, ICEM, MPPI
|
|
40
|
+
from .mujoco import MujocoBranchable
|
|
41
41
|
from .pid import PID
|
|
42
42
|
from .ppo import PPO
|
|
43
43
|
from .sac import SAC
|
|
@@ -72,6 +72,7 @@ __all__ = [
|
|
|
72
72
|
"ICEM",
|
|
73
73
|
"ILQR",
|
|
74
74
|
"CBFFilter",
|
|
75
|
+
"MujocoBranchable",
|
|
75
76
|
"PPO",
|
|
76
77
|
"SAC",
|
|
77
78
|
"TD3",
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
"""Core abstractions for tau-ctrl's algorithm framework —
|
|
2
|
-
|
|
1
|
+
"""Core abstractions for tau-ctrl's algorithm framework — a simulator-agnostic
|
|
2
|
+
controller framework over a Gymnasium environment.
|
|
3
3
|
|
|
4
4
|
Every method (PID, MPPI, CEM-MPC, CBF safety filter, PPO, ...) implements the
|
|
5
|
-
same :class:`BaseController` interface — ``predict(obs) -> action
|
|
6
|
-
|
|
5
|
+
same :class:`BaseController` interface — ``predict(obs) -> action``, plus
|
|
6
|
+
an optional ``learn()`` for the ones that train. Nothing here imports a
|
|
7
7
|
simulator: controllers talk only to the ``gymnasium.Env`` API. Methods that
|
|
8
8
|
need to roll the dynamics forward (MPPI, CEM, CBF) do so through the optional
|
|
9
9
|
*branching* capability (:func:`get_state` / :func:`set_state`), which any env
|
|
@@ -37,16 +37,78 @@ def register(name: str):
|
|
|
37
37
|
|
|
38
38
|
|
|
39
39
|
def make(name: str, env: Any, **kwargs: Any) -> "BaseController":
|
|
40
|
-
"""Construct a controller by name
|
|
40
|
+
"""Construct a controller by name: ``make("mppi", env, ...)``."""
|
|
41
41
|
if name not in _REGISTRY:
|
|
42
42
|
raise KeyError(f"unknown controller {name!r}; available: {sorted(_REGISTRY)}")
|
|
43
|
+
_reject_raw_vector_env(env)
|
|
43
44
|
return _REGISTRY[name](env, **kwargs)
|
|
44
45
|
|
|
45
46
|
|
|
47
|
+
def _reject_raw_vector_env(env: Any) -> None:
|
|
48
|
+
"""Guard against handing a raw ``gymnasium.vector.VectorEnv`` to a controller.
|
|
49
|
+
|
|
50
|
+
Its *batched* spaces would size the networks wrong (``obs_dim`` would come
|
|
51
|
+
out as ``num_envs * obs_dim``) and its ``reset``/``step`` signature doesn't
|
|
52
|
+
match the on-device vectorized trainer, so ``learn()`` would crash. Route
|
|
53
|
+
vectorized envs through :meth:`Trainer.auto`, which wraps them
|
|
54
|
+
(``GymVectorAdapter``) and picks a convergence-safe update ratio. A native
|
|
55
|
+
``tau_ctrl`` ``TorchVecEnv`` (incl. the adapters) is fine and passes through.
|
|
56
|
+
"""
|
|
57
|
+
try:
|
|
58
|
+
import gymnasium # noqa: PLC0415
|
|
59
|
+
|
|
60
|
+
is_gym_vec = isinstance(env, gymnasium.vector.VectorEnv)
|
|
61
|
+
except Exception:
|
|
62
|
+
is_gym_vec = False
|
|
63
|
+
if is_gym_vec:
|
|
64
|
+
raise TypeError(
|
|
65
|
+
"make() received a gymnasium.vector.VectorEnv, which cannot be used "
|
|
66
|
+
"directly (its batched spaces mis-size the networks). Train vectorized "
|
|
67
|
+
"envs via Trainer.auto, which adapts them to the on-device loop:\n"
|
|
68
|
+
" from tau_ctrl import Trainer\n"
|
|
69
|
+
" model = Trainer.auto('sac', env=vec_env, total_timesteps=...)"
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
|
|
46
73
|
def available() -> list[str]:
|
|
47
74
|
return sorted(_REGISTRY)
|
|
48
75
|
|
|
49
76
|
|
|
77
|
+
def require_torch():
|
|
78
|
+
"""Import and return torch, or raise a clear, actionable error.
|
|
79
|
+
|
|
80
|
+
The RL methods (PPO/SAC/TD3) and ``Trainer.auto`` need PyTorch, which ships
|
|
81
|
+
only via the optional extra. Without this, a user hits a bare
|
|
82
|
+
``ModuleNotFoundError: No module named 'torch'`` with no hint of the fix.
|
|
83
|
+
"""
|
|
84
|
+
try:
|
|
85
|
+
import torch # noqa: PLC0415
|
|
86
|
+
except ImportError as e:
|
|
87
|
+
raise ImportError(
|
|
88
|
+
"PyTorch is required for the RL methods (PPO/SAC/TD3) and Trainer.auto, "
|
|
89
|
+
"but it is not installed. Install it with:\n"
|
|
90
|
+
" pip install tau-ctrl[torch]"
|
|
91
|
+
) from e
|
|
92
|
+
return torch
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def require_box_action_space(action_space: Any, algo: str) -> None:
|
|
96
|
+
"""Reject non-continuous action spaces up front with a clear message.
|
|
97
|
+
|
|
98
|
+
PID and the RL/MPC controllers all assume a continuous ``Box`` action
|
|
99
|
+
space; handing them a ``Discrete`` env otherwise fails deep inside with an
|
|
100
|
+
opaque ``AttributeError: 'Discrete' object has no attribute 'low'``.
|
|
101
|
+
"""
|
|
102
|
+
from gymnasium import spaces # noqa: PLC0415
|
|
103
|
+
|
|
104
|
+
if not isinstance(action_space, spaces.Box):
|
|
105
|
+
raise TypeError(
|
|
106
|
+
f"{algo} requires a continuous Box action space, got "
|
|
107
|
+
f"{type(action_space).__name__}. These controllers are for continuous "
|
|
108
|
+
"control; discrete-action envs are not supported."
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
|
|
50
112
|
# ---------------------------------------------------------------------------
|
|
51
113
|
# Manifest
|
|
52
114
|
# ---------------------------------------------------------------------------
|
|
@@ -73,23 +135,31 @@ class BranchingNotSupported(RuntimeError):
|
|
|
73
135
|
def get_state(env: Any) -> Any:
|
|
74
136
|
"""Snapshot the env's dynamical state so a rollout can branch from it.
|
|
75
137
|
|
|
76
|
-
Resolution order (no simulator assumptions): an explicit ``get_state``
|
|
77
|
-
the
|
|
78
|
-
|
|
138
|
+
Resolution order (no simulator assumptions): an explicit ``get_state``
|
|
139
|
+
anywhere on the wrapper stack — the env itself or a branchable wrapper such
|
|
140
|
+
as :class:`~tau_ctrl.algorithms.mujoco.MujocoBranchable` layered over a sim
|
|
141
|
+
that doesn't expose state natively — else classic-control ``.state``.
|
|
142
|
+
Simulators that want model-based control just expose ``get_state``/``set_state``.
|
|
79
143
|
"""
|
|
80
144
|
u = getattr(env, "unwrapped", env)
|
|
145
|
+
if hasattr(env, "get_state"):
|
|
146
|
+
return env.get_state()
|
|
81
147
|
if hasattr(u, "get_state"):
|
|
82
148
|
return u.get_state()
|
|
83
149
|
if hasattr(u, "state") and u.state is not None:
|
|
84
150
|
return np.array(u.state, dtype=float)
|
|
85
151
|
raise BranchingNotSupported(
|
|
86
152
|
f"{type(u).__name__} exposes no get_state()/.state; model-based methods "
|
|
87
|
-
"(MPPI/CEM/CBF) need a branchable env."
|
|
153
|
+
"(MPPI/CEM/CBF) need a branchable env. For MuJoCo envs, wrap with "
|
|
154
|
+
"tau_ctrl.MujocoBranchable(env)."
|
|
88
155
|
)
|
|
89
156
|
|
|
90
157
|
|
|
91
158
|
def set_state(env: Any, state: Any) -> None:
|
|
92
159
|
u = getattr(env, "unwrapped", env)
|
|
160
|
+
if hasattr(env, "set_state"):
|
|
161
|
+
env.set_state(state)
|
|
162
|
+
return
|
|
93
163
|
if hasattr(u, "set_state"):
|
|
94
164
|
u.set_state(state)
|
|
95
165
|
return
|
|
@@ -131,7 +201,7 @@ def resolve_device(pref: str = "auto") -> str:
|
|
|
131
201
|
# ---------------------------------------------------------------------------
|
|
132
202
|
|
|
133
203
|
class BaseController(ABC):
|
|
134
|
-
"""
|
|
204
|
+
"""Base controller: ``predict`` always; ``learn`` when trainable."""
|
|
135
205
|
|
|
136
206
|
name: str = "base"
|
|
137
207
|
manifest: ControllerManifest
|
|
@@ -164,7 +234,7 @@ class BaseController(ABC):
|
|
|
164
234
|
def predict(
|
|
165
235
|
self, obs: Any, state: Any = None, deterministic: bool = True
|
|
166
236
|
) -> tuple[np.ndarray, Any]:
|
|
167
|
-
"""Return ``(action, next_internal_state)
|
|
237
|
+
"""Return ``(action, next_internal_state)``."""
|
|
168
238
|
|
|
169
239
|
def learn(self, total_timesteps: int = 0, **kwargs: Any) -> "BaseController":
|
|
170
240
|
"""Train the controller. Analytic controllers are no-ops."""
|
|
@@ -56,6 +56,11 @@ class ILQR(BaseController):
|
|
|
56
56
|
state0 = get_state(self.env)
|
|
57
57
|
self.nx = int(np.asarray(state0).size)
|
|
58
58
|
self._u_nom = np.zeros((self.horizon, self.nu))
|
|
59
|
+
# Probe/roll out against the *unwrapped* env (see MPPI): stepping the
|
|
60
|
+
# wrapped env would trip TimeLimit truncation mid-rollout (producing a
|
|
61
|
+
# short trajectory and an IndexError in the backward pass) and corrupt
|
|
62
|
+
# the live episode's wrapper state.
|
|
63
|
+
self._rollout_env = getattr(self.env, "unwrapped", self.env)
|
|
59
64
|
|
|
60
65
|
def reset(self) -> None:
|
|
61
66
|
self._u_nom[:] = 0.0
|
|
@@ -67,7 +72,7 @@ class ILQR(BaseController):
|
|
|
67
72
|
"""One step from (x, u): returns (next_state, reward)."""
|
|
68
73
|
set_state(self.env, x)
|
|
69
74
|
u = self._clip_action(u)
|
|
70
|
-
_, r, _, _, _ = self.
|
|
75
|
+
_, r, _, _, _ = self._rollout_env.step(u)
|
|
71
76
|
return np.asarray(get_state(self.env), dtype=float), float(r)
|
|
72
77
|
|
|
73
78
|
def _linearize(self, x: np.ndarray, u: np.ndarray):
|
|
@@ -112,7 +117,7 @@ class ILQR(BaseController):
|
|
|
112
117
|
for t in range(self.horizon):
|
|
113
118
|
set_state(self.env, x)
|
|
114
119
|
u = self._clip_action(u_seq[t])
|
|
115
|
-
_, r, term, trunc, _ = self.
|
|
120
|
+
_, r, term, trunc, _ = self._rollout_env.step(u)
|
|
116
121
|
x = np.asarray(get_state(self.env), dtype=float)
|
|
117
122
|
xs.append(x.copy())
|
|
118
123
|
total_cost += -float(r)
|
|
@@ -154,14 +159,21 @@ class ILQR(BaseController):
|
|
|
154
159
|
xs, best_cost = self._rollout(state0, u_seq)
|
|
155
160
|
|
|
156
161
|
for _ in range(self.iterations):
|
|
157
|
-
|
|
162
|
+
# A rollout can terminate before the full horizon (a genuinely
|
|
163
|
+
# terminating env, e.g. the ant falling), leaving fewer states than
|
|
164
|
+
# controls. Only optimize over the segment we actually have a
|
|
165
|
+
# trajectory for; controls past it stay at their nominal value.
|
|
166
|
+
H = min(len(u_seq), len(xs) - 1)
|
|
167
|
+
if H <= 0:
|
|
168
|
+
break
|
|
169
|
+
K, k = self._backward_pass(xs[:H], u_seq[:H])
|
|
158
170
|
improved = False
|
|
159
171
|
for alpha in self.line_search_steps:
|
|
160
172
|
if alpha == 0.0:
|
|
161
173
|
break # no-op fallback: keep current nominal
|
|
162
|
-
u_try =
|
|
174
|
+
u_try = u_seq.copy()
|
|
163
175
|
x = state0.copy()
|
|
164
|
-
for t in range(
|
|
176
|
+
for t in range(H):
|
|
165
177
|
u_try[t] = self._clip_action(
|
|
166
178
|
u_seq[t] + alpha * k[t] + K[t] @ (x - xs[t])
|
|
167
179
|
)
|
|
@@ -56,6 +56,12 @@ class _SamplingMPC(BaseController):
|
|
|
56
56
|
def _setup(self) -> None:
|
|
57
57
|
self.nu = self.action_dim
|
|
58
58
|
self._nominal = np.zeros((self.horizon, self.nu))
|
|
59
|
+
# Roll candidate sequences out against the *unwrapped* env. get_state /
|
|
60
|
+
# set_state already operate on env.unwrapped, so stepping the wrapped env
|
|
61
|
+
# here would leak into stateful wrappers (TimeLimit._elapsed_steps,
|
|
62
|
+
# OrderEnforcing) and corrupt/terminate the live episode — while the
|
|
63
|
+
# dynamics and reward are identical either way.
|
|
64
|
+
self._rollout_env = getattr(self.env, "unwrapped", self.env)
|
|
59
65
|
self._venv = None
|
|
60
66
|
if self._vector_env_fn is not None:
|
|
61
67
|
self._venv = self._vector_env_fn(self.n_samples)
|
|
@@ -101,7 +107,7 @@ class _SamplingMPC(BaseController):
|
|
|
101
107
|
set_state(self.env, state0)
|
|
102
108
|
disc = 1.0
|
|
103
109
|
for h in range(self.horizon):
|
|
104
|
-
_, r, term, trunc, _ = self.
|
|
110
|
+
_, r, term, trunc, _ = self._rollout_env.step(seqs[k, h])
|
|
105
111
|
R[k] += disc * r
|
|
106
112
|
disc *= self.gamma
|
|
107
113
|
if term or trunc:
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"""Make a MuJoCo Gymnasium env *branchable* for the model-based controllers.
|
|
2
|
+
|
|
3
|
+
The sampling/gradient planners (MPPI/CEM/ICEM/ILQR) and the CBF safety filter
|
|
4
|
+
plan by rolling candidate action sequences forward from the current state, which
|
|
5
|
+
needs the env to expose ``get_state``/``set_state`` (see
|
|
6
|
+
:mod:`tau_ctrl.algorithms.base`). Classic-control envs provide this via
|
|
7
|
+
``.state``; MuJoCo envs don't. :class:`MujocoBranchable` adds it by
|
|
8
|
+
snapshotting/restoring MuJoCo's *full physics state* (time, ``qpos``, ``qvel``,
|
|
9
|
+
actuator activation, plugin state) through ``mj_getState``/``mj_setState`` — the
|
|
10
|
+
complete, deterministic state, not just ``qpos``/``qvel``::
|
|
11
|
+
|
|
12
|
+
import gymnasium as gym
|
|
13
|
+
from tau_ctrl import make, MujocoBranchable
|
|
14
|
+
|
|
15
|
+
env = MujocoBranchable(gym.make("HalfCheetah-v5"))
|
|
16
|
+
ctrl = make("mppi", env, horizon=20, n_samples=200) # now branchable
|
|
17
|
+
action, _ = ctrl.predict(obs)
|
|
18
|
+
|
|
19
|
+
Scope
|
|
20
|
+
-----
|
|
21
|
+
Works on **any** Gymnasium MuJoCo env — the built-in ones (HalfCheetah, Ant,
|
|
22
|
+
Hopper, …) and your own ``MujocoEnv`` subclasses — whose complete state is the
|
|
23
|
+
physics state. It does **not** magically make a *non*-MuJoCo env branchable:
|
|
24
|
+
branching needs the full state, which an arbitrary env (hidden RNG, external
|
|
25
|
+
process) can't expose. If a *custom* MuJoCo env also carries Python-side state
|
|
26
|
+
that affects dynamics or reward (a goal, a step counter, an action history),
|
|
27
|
+
subclass this and extend ``get_state``/``set_state`` to include it.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
from __future__ import annotations
|
|
31
|
+
|
|
32
|
+
from typing import Any
|
|
33
|
+
|
|
34
|
+
import gymnasium as gym
|
|
35
|
+
import numpy as np
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class MujocoBranchable(gym.Wrapper):
|
|
39
|
+
"""Wrap a MuJoCo Gymnasium env so model-based controllers can branch it.
|
|
40
|
+
|
|
41
|
+
Adds ``get_state``/``set_state`` backed by MuJoCo's full physics state.
|
|
42
|
+
Transparent otherwise — ``reset``/``step``/spaces pass straight through.
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
def __init__(self, env: gym.Env) -> None:
|
|
46
|
+
super().__init__(env)
|
|
47
|
+
raw = env.unwrapped
|
|
48
|
+
if not (hasattr(raw, "model") and hasattr(raw, "data")):
|
|
49
|
+
raise TypeError(
|
|
50
|
+
"MujocoBranchable expects a MuJoCo Gymnasium env (env.unwrapped "
|
|
51
|
+
f"with .model/.data), got {type(raw).__name__}. Non-MuJoCo envs "
|
|
52
|
+
"must provide get_state/set_state themselves to be branchable."
|
|
53
|
+
)
|
|
54
|
+
import mujoco # noqa: PLC0415
|
|
55
|
+
|
|
56
|
+
self._mujoco = mujoco
|
|
57
|
+
self._spec = mujoco.mjtState.mjSTATE_FULLPHYSICS
|
|
58
|
+
self._size = int(mujoco.mj_stateSize(raw.model, self._spec))
|
|
59
|
+
|
|
60
|
+
def get_state(self) -> np.ndarray:
|
|
61
|
+
raw = self.env.unwrapped
|
|
62
|
+
arr = np.zeros(self._size, dtype=np.float64)
|
|
63
|
+
self._mujoco.mj_getState(raw.model, raw.data, arr, self._spec)
|
|
64
|
+
return arr
|
|
65
|
+
|
|
66
|
+
def set_state(self, state: Any) -> None:
|
|
67
|
+
raw = self.env.unwrapped
|
|
68
|
+
arr = np.ascontiguousarray(state, dtype=np.float64)
|
|
69
|
+
self._mujoco.mj_setState(raw.model, raw.data, arr, self._spec)
|
|
70
|
+
# Recompute derived quantities (contacts, sensors) so the restored state
|
|
71
|
+
# is fully consistent before the next step is planned/taken.
|
|
72
|
+
self._mujoco.mj_forward(raw.model, raw.data)
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"""A compact, self-contained PPO (continuous actions) in PyTorch.
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
Standard surface (``learn`` / ``predict`` / ``save`` / ``load``) implementation.
|
|
4
|
+
Runs on GPU automatically when torch reports CUDA available (``device="auto"``),
|
|
5
|
+
else CPU.
|
|
6
6
|
|
|
7
7
|
Simulator-agnostic: talks only to the Gymnasium env's ``reset``/``step``.
|
|
8
8
|
Kept intentionally small (single-env on-policy rollouts, GAE, clipped
|
|
@@ -16,12 +16,17 @@ from typing import Any
|
|
|
16
16
|
|
|
17
17
|
import numpy as np
|
|
18
18
|
|
|
19
|
-
from .base import
|
|
19
|
+
from .base import (
|
|
20
|
+
BaseController,
|
|
21
|
+
ControllerManifest,
|
|
22
|
+
register,
|
|
23
|
+
require_box_action_space,
|
|
24
|
+
require_torch,
|
|
25
|
+
)
|
|
20
26
|
|
|
21
27
|
|
|
22
28
|
def _torch():
|
|
23
|
-
|
|
24
|
-
return torch
|
|
29
|
+
return require_torch()
|
|
25
30
|
|
|
26
31
|
|
|
27
32
|
@register("ppo")
|
|
@@ -52,6 +57,7 @@ class PPO(BaseController):
|
|
|
52
57
|
super().__init__(env, **kw)
|
|
53
58
|
|
|
54
59
|
def _setup(self) -> None:
|
|
60
|
+
require_box_action_space(self.action_space, "PPO")
|
|
55
61
|
torch = _torch()
|
|
56
62
|
import torch.nn as nn
|
|
57
63
|
|
|
@@ -17,7 +17,13 @@ from typing import Any, Optional
|
|
|
17
17
|
|
|
18
18
|
import numpy as np
|
|
19
19
|
|
|
20
|
-
from .base import
|
|
20
|
+
from .base import (
|
|
21
|
+
BaseController,
|
|
22
|
+
ControllerManifest,
|
|
23
|
+
register,
|
|
24
|
+
require_box_action_space,
|
|
25
|
+
require_torch,
|
|
26
|
+
)
|
|
21
27
|
from .off_policy import (
|
|
22
28
|
ReplayBuffer,
|
|
23
29
|
build_mlp,
|
|
@@ -29,8 +35,7 @@ from .off_policy import (
|
|
|
29
35
|
|
|
30
36
|
|
|
31
37
|
def _torch():
|
|
32
|
-
|
|
33
|
-
return torch
|
|
38
|
+
return require_torch()
|
|
34
39
|
|
|
35
40
|
|
|
36
41
|
@register("sac")
|
|
@@ -57,6 +62,7 @@ class SAC(BaseController):
|
|
|
57
62
|
super().__init__(env, **kw)
|
|
58
63
|
|
|
59
64
|
def _setup(self) -> None:
|
|
65
|
+
require_box_action_space(self.action_space, "SAC")
|
|
60
66
|
torch = _torch()
|
|
61
67
|
import torch.nn as nn
|
|
62
68
|
|
|
@@ -2,15 +2,15 @@
|
|
|
2
2
|
execution strategy, dispatch. One ``Trainer.auto(...)`` call adapts to whatever
|
|
3
3
|
you hand it, so tau-ctrl generalizes past any single env.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
regardless of
|
|
7
|
-
that wall-clock-to-reward is bounded by the *slower* of
|
|
8
|
-
and which one dominates depends on the env — so a general
|
|
9
|
-
the regime and route to the matching engine:
|
|
5
|
+
Standard frameworks typically support a single execution model (e.g., CPU vector envs,
|
|
6
|
+
synchronous collect-then-update) regardless of the target environment. The whole
|
|
7
|
+
speed argument for tau-ctrl is that wall-clock-to-reward is bounded by the *slower* of
|
|
8
|
+
collection and update, and which one dominates depends on the env — so a general
|
|
9
|
+
framework must detect the regime and route to the matching engine:
|
|
10
10
|
|
|
11
11
|
ON_DEVICE_VECTORIZED env already batched + on GPU (MJX/Brax via dlpack,
|
|
12
|
-
Isaac Gym, or a native TorchVecEnv) → 10-100x
|
|
13
|
-
|
|
12
|
+
Isaac Gym, or a native TorchVecEnv) → 10-100x speedup
|
|
13
|
+
by running everything on-device.
|
|
14
14
|
GYM_VECTOR_ADAPTED a gymnasium.vector.VectorEnv (numpy/CPU) → batched
|
|
15
15
|
update on device, collection stays CPU.
|
|
16
16
|
SYNC_VEC a single non-batchable env replicated N times, stepped
|
|
@@ -192,7 +192,7 @@ def select_strategy(
|
|
|
192
192
|
why = (
|
|
193
193
|
f"Env is a native on-device TorchVecEnv ({ec.backend}, on_device={ec.on_device}). "
|
|
194
194
|
"Best case: env stepping AND the gradient update run batched on the target "
|
|
195
|
-
"device — the regime
|
|
195
|
+
"device — the regime that maximizes GPU throughput by avoiding CPU-GPU synchronization. "
|
|
196
196
|
f"Scaling updates_per_step→{ups} so {n} fresh transitions/step actually train."
|
|
197
197
|
)
|
|
198
198
|
return Plan(Strategy.ON_DEVICE_VECTORIZED, n, dev, ups, why)
|
|
@@ -225,7 +225,7 @@ def select_strategy(
|
|
|
225
225
|
|
|
226
226
|
why = (
|
|
227
227
|
"Single env, not replicable (e.g. a real robot). Physics can't be parallelised — "
|
|
228
|
-
"only the gradient update is GPU-accelerated. Honest regime:
|
|
228
|
+
"only the gradient update is GPU-accelerated. Honest regime: focus on "
|
|
229
229
|
"sample efficiency (fewer env steps to reward), not raw throughput. Use SAC/TD3."
|
|
230
230
|
)
|
|
231
231
|
return Plan(Strategy.SINGLE_ENV, 1, dev, 1, why)
|
|
@@ -13,7 +13,13 @@ from typing import Any
|
|
|
13
13
|
|
|
14
14
|
import numpy as np
|
|
15
15
|
|
|
16
|
-
from .base import
|
|
16
|
+
from .base import (
|
|
17
|
+
BaseController,
|
|
18
|
+
ControllerManifest,
|
|
19
|
+
register,
|
|
20
|
+
require_box_action_space,
|
|
21
|
+
require_torch,
|
|
22
|
+
)
|
|
17
23
|
from .off_policy import (
|
|
18
24
|
ReplayBuffer,
|
|
19
25
|
build_mlp,
|
|
@@ -25,8 +31,7 @@ from .off_policy import (
|
|
|
25
31
|
|
|
26
32
|
|
|
27
33
|
def _torch():
|
|
28
|
-
|
|
29
|
-
return torch
|
|
34
|
+
return require_torch()
|
|
30
35
|
|
|
31
36
|
|
|
32
37
|
@register("td3")
|
|
@@ -57,6 +62,7 @@ class TD3(BaseController):
|
|
|
57
62
|
super().__init__(env, **kw)
|
|
58
63
|
|
|
59
64
|
def _setup(self) -> None:
|
|
65
|
+
require_box_action_space(self.action_space, "TD3")
|
|
60
66
|
torch = _torch()
|
|
61
67
|
import torch.nn as nn
|
|
62
68
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
"""GPU-vectorized environments — the piece that unlocks real GPU speedup.
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
time: one transition, one tiny update, ~100 sequential
|
|
5
|
-
per step. At that granularity kernel-launch latency dwarfs
|
|
6
|
-
so "putting the network on the GPU" caps out at ~1.3x.
|
|
3
|
+
Single-environment training loops (and tau-ctrl's single-env ``learn()``) step
|
|
4
|
+
one CPU environment at a time: one transition, one tiny update, ~100 sequential
|
|
5
|
+
CUDA kernel launches per step. At that granularity kernel-launch latency dwarfs
|
|
6
|
+
the actual math, so "putting the network on the GPU" caps out at ~1.3x.
|
|
7
7
|
|
|
8
8
|
A :class:`TorchVecEnv` instead advances ``num_envs`` environments *in
|
|
9
9
|
parallel*, entirely as batched tensor ops on the target device. Nothing
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tau-ctrl
|
|
3
|
-
Version: 0.
|
|
4
|
-
Summary:
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Simulator-agnostic control algorithms over any gymnasium.Env — PID, MPPI/CEM/iCEM, iLQR, CBF, and GPU-native RL (PPO/SAC/TD3) with vectorized on-device training
|
|
5
5
|
Author: Tau Intelligence
|
|
6
6
|
License: Apache-2.0
|
|
7
7
|
Project-URL: Homepage, https://tau-intelligence.com/
|
|
@@ -40,9 +40,9 @@ Dynamic: license-file
|
|
|
40
40
|
|
|
41
41
|
# tau-ctrl
|
|
42
42
|
|
|
43
|
-
**
|
|
43
|
+
**Simulator-agnostic control algorithms — feedback, sampling-based MPC, safety filtering, and GPU-native RL behind one interface.**
|
|
44
44
|
|
|
45
|
-
|
|
45
|
+
A unified library for the whole controller spectrum, over any `gymnasium.Env`. No simulator dependency: works with whatever hands you an env (e.g. [tau-sim](https://tau-intelligence.com/tau_sim/)). The RL methods train **on-device** and scale to vectorized environments for real GPU speedup.
|
|
46
46
|
|
|
47
47
|
## Installation
|
|
48
48
|
|
|
@@ -57,7 +57,7 @@ pip install tau-ctrl[torch] # + RL: PPO, SAC, TD3, and vectorized on-device
|
|
|
57
57
|
from tau_ctrl import make
|
|
58
58
|
|
|
59
59
|
ctrl = make("mppi", env, horizon=25, n_samples=300) # or MPPI(env, ...), SAC(env), ...
|
|
60
|
-
action, _ = ctrl.predict(obs) #
|
|
60
|
+
action, _ = ctrl.predict(obs) # standard interface
|
|
61
61
|
ctrl.learn(total_timesteps=100_000) # trainable methods (ppo/sac/td3)
|
|
62
62
|
ctrl.save("ctrl.pkl")
|
|
63
63
|
```
|
|
@@ -84,7 +84,7 @@ seeding of torch's RNG).
|
|
|
84
84
|
|
|
85
85
|
## GPU-native RL: vectorized, on-device training
|
|
86
86
|
|
|
87
|
-
|
|
87
|
+
Standard pipelines step CPU environments sequentially and perform updates in slow Python loops.
|
|
88
88
|
tau-ctrl's SAC/TD3 instead run the replay buffer and the update **on the target
|
|
89
89
|
device**, and — given a batched env — step thousands of environments in parallel
|
|
90
90
|
with no numpy in the hot loop. `Trainer.auto` probes your env and hardware and
|
|
@@ -108,8 +108,7 @@ action, _ = model.predict(obs)
|
|
|
108
108
|
| a single, non-batchable env (PyBullet, classic MuJoCo) + a factory | `SyncTorchVecEnv` | batched update on device |
|
|
109
109
|
| one env you can't replicate (a real robot) | — (single-env path) | only the update |
|
|
110
110
|
|
|
111
|
-
See [`benchmarks/RESULTS.md`](benchmarks/RESULTS.md) for
|
|
112
|
-
Stable-Baselines3 and skrl, and [`examples/`](examples/) for runnable scripts
|
|
111
|
+
See [`benchmarks/RESULTS.md`](benchmarks/RESULTS.md) for performance comparison benchmarks, and [`examples/`](examples/) for runnable scripts
|
|
113
112
|
(`quickstart.py`, `vectorized_rl.py`, `adaptive_training.py`).
|
|
114
113
|
|
|
115
114
|
## Safety filtering
|
|
@@ -151,4 +150,3 @@ Apache 2.0 — see [LICENSE](LICENSE).
|
|
|
151
150
|
## Related
|
|
152
151
|
|
|
153
152
|
- [tau-sim](https://tau-intelligence.com/tau_sim/) — robotics environment builder
|
|
154
|
-
- [Stable-Baselines3](https://stable-baselines3.readthedocs.io/) — RL algorithms (API inspiration)
|
|
@@ -14,6 +14,7 @@ src/tau_ctrl/algorithms/base.py
|
|
|
14
14
|
src/tau_ctrl/algorithms/cbf.py
|
|
15
15
|
src/tau_ctrl/algorithms/ilqr.py
|
|
16
16
|
src/tau_ctrl/algorithms/mppi.py
|
|
17
|
+
src/tau_ctrl/algorithms/mujoco.py
|
|
17
18
|
src/tau_ctrl/algorithms/off_policy.py
|
|
18
19
|
src/tau_ctrl/algorithms/pid.py
|
|
19
20
|
src/tau_ctrl/algorithms/ppo.py
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|