mujoco-toolbox 0.1.8__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.
- mujoco_toolbox-0.1.8/LICENSE +21 -0
- mujoco_toolbox-0.1.8/PKG-INFO +186 -0
- mujoco_toolbox-0.1.8/README.md +150 -0
- mujoco_toolbox-0.1.8/mujoco_toolbox/Controller.py +131 -0
- mujoco_toolbox-0.1.8/mujoco_toolbox/Utils.py +53 -0
- mujoco_toolbox-0.1.8/mujoco_toolbox/Wrapper.py +741 -0
- mujoco_toolbox-0.1.8/mujoco_toolbox/__init__.py +67 -0
- mujoco_toolbox-0.1.8/pyproject.toml +105 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Michael Gross
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: mujoco-toolbox
|
|
3
|
+
Version: 0.1.8
|
|
4
|
+
Summary: A modern package to handle MuJoCo environments
|
|
5
|
+
License: MIT
|
|
6
|
+
Keywords: MuJoCo,robotics,simulation,toolbox,wrapper
|
|
7
|
+
Author: MGross21
|
|
8
|
+
Author-email: MGross21@TODO.com
|
|
9
|
+
Requires-Python: >=3.10
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
15
|
+
Provides-Extra: dev
|
|
16
|
+
Requires-Dist: ffmpeg
|
|
17
|
+
Requires-Dist: ipywidgets
|
|
18
|
+
Requires-Dist: matplotlib
|
|
19
|
+
Requires-Dist: mediapy
|
|
20
|
+
Requires-Dist: mujoco
|
|
21
|
+
Requires-Dist: mypy ; extra == "dev"
|
|
22
|
+
Requires-Dist: numpy
|
|
23
|
+
Requires-Dist: pytest ; extra == "dev"
|
|
24
|
+
Requires-Dist: pyyaml
|
|
25
|
+
Requires-Dist: ruff ; extra == "dev"
|
|
26
|
+
Requires-Dist: scipy
|
|
27
|
+
Requires-Dist: screeninfo
|
|
28
|
+
Requires-Dist: tqdm
|
|
29
|
+
Requires-Dist: trimesh
|
|
30
|
+
Project-URL: Documentation, https://github.com/MGross21/mujoco-toolbox
|
|
31
|
+
Project-URL: Homepage, https://github.com/MGross21/mujoco-toolbox
|
|
32
|
+
Project-URL: Repository, https://github.com/MGross21/mujoco-toolbox
|
|
33
|
+
Project-URL: bug-tracker, https://github.com/MGross21/mujoco-toolbox/issues
|
|
34
|
+
Description-Content-Type: text/markdown
|
|
35
|
+
|
|
36
|
+

|
|
37
|
+

|
|
38
|
+

|
|
39
|
+
[](https://github.com/MGross21/mujoco-toolbox/actions/workflows/publish.yml)
|
|
40
|
+
|
|
41
|
+
# Mujoco Toolbox
|
|
42
|
+
|
|
43
|
+
Streamlines the MuJoCo Physics Simulator
|
|
44
|
+
|
|
45
|
+
## PyPI Package
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
pip install -U mujoco-toolbox
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## For Local Install
|
|
52
|
+
|
|
53
|
+
*Navigate to the Directory that you wish to place the package*
|
|
54
|
+
|
|
55
|
+
<details>
|
|
56
|
+
<summary><b>JUPYTER NOTEBOOK</b></summary>
|
|
57
|
+
</br>
|
|
58
|
+
*Place Commands Inside Cell*
|
|
59
|
+
|
|
60
|
+
One-Time Install:
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
!git clone https://github.com/MGross21/mujoco-toolbox
|
|
64
|
+
!pip install -U ./mujoco-toolbox
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Check for Updates:
|
|
68
|
+
|
|
69
|
+
```python
|
|
70
|
+
!git pull origin main
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Install/Check for Updates:
|
|
74
|
+
|
|
75
|
+
```python
|
|
76
|
+
folder = "mujoco-toolbox"
|
|
77
|
+
repo_url = f"https://github.com/MGross21/{folder}"
|
|
78
|
+
|
|
79
|
+
!if [ -d "{folder}" ]; then cd {folder} && git pull && cd ..; else git clone {repo_url}; fi
|
|
80
|
+
!pip install -U ./{folder}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
</details>
|
|
84
|
+
</br>
|
|
85
|
+
|
|
86
|
+
<details>
|
|
87
|
+
<summary><b>BASH WINDOW</b></summary>
|
|
88
|
+
</br>
|
|
89
|
+
One-Time Install:
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
git clone https://github.com/MGross21/mujoco-toolbox
|
|
93
|
+
pip install -U ./mujoco-toolbox
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Check for Updates:
|
|
97
|
+
```bash
|
|
98
|
+
git pull origin main
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Install/Check for Updates:
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
REPO_DIR="mujoco-toolbox"
|
|
105
|
+
REPO_URL="https://github.com/MGross21/mujoco-toolbox"
|
|
106
|
+
|
|
107
|
+
if [ -d "$REPO_DIR" ]; then
|
|
108
|
+
cd "$REPO_DIR" && git fetch origin && git pull origin main
|
|
109
|
+
else
|
|
110
|
+
git clone $REPO_URL
|
|
111
|
+
cd "$REPO_DIR"
|
|
112
|
+
fi
|
|
113
|
+
|
|
114
|
+
pip install -U ./mujoco-toolbox
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
</details>
|
|
118
|
+
|
|
119
|
+
## Extra Packages
|
|
120
|
+
|
|
121
|
+
<details>
|
|
122
|
+
<summary><b>FFMPEG</b></summary>
|
|
123
|
+
|
|
124
|
+
</br>
|
|
125
|
+
|
|
126
|
+
*Required for [mediapy](https://google.github.io/mediapy/mediapy.html) dependency*
|
|
127
|
+
|
|
128
|
+
**Windows**
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
winget install ffmpeg
|
|
132
|
+
ffmpeg -version
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
**Linux**
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
sudo apt update && sudo apt install ffmpeg
|
|
139
|
+
ffmpeg -version
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
**MacOS**
|
|
143
|
+
|
|
144
|
+
*Using Homebrew*
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
brew install ffmpeg
|
|
148
|
+
ffmpeg -version
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
*Using MacPorts*
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
sudo port install ffmpeg
|
|
155
|
+
ffmpeg -version
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
</details>
|
|
159
|
+
|
|
160
|
+
## To Run in Scripts
|
|
161
|
+
|
|
162
|
+
```python
|
|
163
|
+
import mujoco_toolbox as mjtb
|
|
164
|
+
from mujoco_toolbox import Wrapper
|
|
165
|
+
|
|
166
|
+
# Optional Global Variables
|
|
167
|
+
mjtb.VERBOSITY = True # Additional Performance and Debugging Print Statements (Default=False)
|
|
168
|
+
mjtb.CAPTURE_PARAMETERS = "all" # MjData Simulation Parameters (Default = ['time', 'qpos', 'qvel', 'act', 'qacc', 'xpos', 'xquat', 'xmat', 'ctrl', 'sensordata'])
|
|
169
|
+
|
|
170
|
+
# Example Code
|
|
171
|
+
Wrapper("path/to/your/xml").runSim(render=True).renderMedia()
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## Pre-Made Controllers
|
|
175
|
+
|
|
176
|
+
```python
|
|
177
|
+
import mujoco_toolbox as mjtb
|
|
178
|
+
|
|
179
|
+
mjtb.sineController(m,d,**kwargs)
|
|
180
|
+
mjtb.cosineController(m,d,**kwargs)
|
|
181
|
+
mjtb.stepController(m,d,**kwargs)
|
|
182
|
+
mjtb.randomController(m,d,**kwargs)
|
|
183
|
+
|
|
184
|
+
# Wrapper can use custom controllers as well!
|
|
185
|
+
```
|
|
186
|
+
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+

|
|
2
|
+

|
|
3
|
+

|
|
4
|
+
[](https://github.com/MGross21/mujoco-toolbox/actions/workflows/publish.yml)
|
|
5
|
+
|
|
6
|
+
# Mujoco Toolbox
|
|
7
|
+
|
|
8
|
+
Streamlines the MuJoCo Physics Simulator
|
|
9
|
+
|
|
10
|
+
## PyPI Package
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
pip install -U mujoco-toolbox
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## For Local Install
|
|
17
|
+
|
|
18
|
+
*Navigate to the Directory that you wish to place the package*
|
|
19
|
+
|
|
20
|
+
<details>
|
|
21
|
+
<summary><b>JUPYTER NOTEBOOK</b></summary>
|
|
22
|
+
</br>
|
|
23
|
+
*Place Commands Inside Cell*
|
|
24
|
+
|
|
25
|
+
One-Time Install:
|
|
26
|
+
|
|
27
|
+
```python
|
|
28
|
+
!git clone https://github.com/MGross21/mujoco-toolbox
|
|
29
|
+
!pip install -U ./mujoco-toolbox
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Check for Updates:
|
|
33
|
+
|
|
34
|
+
```python
|
|
35
|
+
!git pull origin main
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Install/Check for Updates:
|
|
39
|
+
|
|
40
|
+
```python
|
|
41
|
+
folder = "mujoco-toolbox"
|
|
42
|
+
repo_url = f"https://github.com/MGross21/{folder}"
|
|
43
|
+
|
|
44
|
+
!if [ -d "{folder}" ]; then cd {folder} && git pull && cd ..; else git clone {repo_url}; fi
|
|
45
|
+
!pip install -U ./{folder}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
</details>
|
|
49
|
+
</br>
|
|
50
|
+
|
|
51
|
+
<details>
|
|
52
|
+
<summary><b>BASH WINDOW</b></summary>
|
|
53
|
+
</br>
|
|
54
|
+
One-Time Install:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
git clone https://github.com/MGross21/mujoco-toolbox
|
|
58
|
+
pip install -U ./mujoco-toolbox
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Check for Updates:
|
|
62
|
+
```bash
|
|
63
|
+
git pull origin main
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Install/Check for Updates:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
REPO_DIR="mujoco-toolbox"
|
|
70
|
+
REPO_URL="https://github.com/MGross21/mujoco-toolbox"
|
|
71
|
+
|
|
72
|
+
if [ -d "$REPO_DIR" ]; then
|
|
73
|
+
cd "$REPO_DIR" && git fetch origin && git pull origin main
|
|
74
|
+
else
|
|
75
|
+
git clone $REPO_URL
|
|
76
|
+
cd "$REPO_DIR"
|
|
77
|
+
fi
|
|
78
|
+
|
|
79
|
+
pip install -U ./mujoco-toolbox
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
</details>
|
|
83
|
+
|
|
84
|
+
## Extra Packages
|
|
85
|
+
|
|
86
|
+
<details>
|
|
87
|
+
<summary><b>FFMPEG</b></summary>
|
|
88
|
+
|
|
89
|
+
</br>
|
|
90
|
+
|
|
91
|
+
*Required for [mediapy](https://google.github.io/mediapy/mediapy.html) dependency*
|
|
92
|
+
|
|
93
|
+
**Windows**
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
winget install ffmpeg
|
|
97
|
+
ffmpeg -version
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
**Linux**
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
sudo apt update && sudo apt install ffmpeg
|
|
104
|
+
ffmpeg -version
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
**MacOS**
|
|
108
|
+
|
|
109
|
+
*Using Homebrew*
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
brew install ffmpeg
|
|
113
|
+
ffmpeg -version
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
*Using MacPorts*
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
sudo port install ffmpeg
|
|
120
|
+
ffmpeg -version
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
</details>
|
|
124
|
+
|
|
125
|
+
## To Run in Scripts
|
|
126
|
+
|
|
127
|
+
```python
|
|
128
|
+
import mujoco_toolbox as mjtb
|
|
129
|
+
from mujoco_toolbox import Wrapper
|
|
130
|
+
|
|
131
|
+
# Optional Global Variables
|
|
132
|
+
mjtb.VERBOSITY = True # Additional Performance and Debugging Print Statements (Default=False)
|
|
133
|
+
mjtb.CAPTURE_PARAMETERS = "all" # MjData Simulation Parameters (Default = ['time', 'qpos', 'qvel', 'act', 'qacc', 'xpos', 'xquat', 'xmat', 'ctrl', 'sensordata'])
|
|
134
|
+
|
|
135
|
+
# Example Code
|
|
136
|
+
Wrapper("path/to/your/xml").runSim(render=True).renderMedia()
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## Pre-Made Controllers
|
|
140
|
+
|
|
141
|
+
```python
|
|
142
|
+
import mujoco_toolbox as mjtb
|
|
143
|
+
|
|
144
|
+
mjtb.sineController(m,d,**kwargs)
|
|
145
|
+
mjtb.cosineController(m,d,**kwargs)
|
|
146
|
+
mjtb.stepController(m,d,**kwargs)
|
|
147
|
+
mjtb.randomController(m,d,**kwargs)
|
|
148
|
+
|
|
149
|
+
# Wrapper can use custom controllers as well!
|
|
150
|
+
```
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
from numpy import cos, pi, random, sin
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def _apply_control(model, data, value, joint=None, axis=None, delay=0):
|
|
5
|
+
"""Common helper function for controller logic to reduce redundancy.
|
|
6
|
+
|
|
7
|
+
Args:
|
|
8
|
+
model: MuJoCo model.
|
|
9
|
+
data: MuJoCo data.
|
|
10
|
+
value: Control value to apply.
|
|
11
|
+
joint (list[int], optional): Joints to apply control to.
|
|
12
|
+
axis (int, optional): Axis to apply control to.
|
|
13
|
+
delay (float, optional): Delay before applying control.
|
|
14
|
+
"""
|
|
15
|
+
# Don't apply control until after delay
|
|
16
|
+
if data.time < delay:
|
|
17
|
+
return
|
|
18
|
+
|
|
19
|
+
# Determine targets if not explicitly provided
|
|
20
|
+
if joint is None and axis is None:
|
|
21
|
+
joint = range(model.nu)
|
|
22
|
+
|
|
23
|
+
# Apply control to joints or specific axis
|
|
24
|
+
if joint is not None and model.nu > 0:
|
|
25
|
+
for j in joint:
|
|
26
|
+
data.ctrl[j] = value
|
|
27
|
+
elif axis is not None:
|
|
28
|
+
data.qpos[axis] = value
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def stepController(model, data, **kwargs):
|
|
32
|
+
"""A step controller for the simulation.
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
amplitude (float): The amplitude of the step signal (default=1).
|
|
36
|
+
joint (list[int]): The joints to apply the step signal to (default=all).
|
|
37
|
+
axis (int): The axis to apply the step signal to (default=None).
|
|
38
|
+
delay (float): The delay before applying the step signal (default=0).
|
|
39
|
+
|
|
40
|
+
Returns:
|
|
41
|
+
None
|
|
42
|
+
"""
|
|
43
|
+
amplitude = kwargs.get("amplitude", 1)
|
|
44
|
+
joint = kwargs.get("joint", None)
|
|
45
|
+
axis = kwargs.get("axis", None)
|
|
46
|
+
delay = kwargs.get("delay", 0)
|
|
47
|
+
|
|
48
|
+
if delay < 0:
|
|
49
|
+
raise ValueError("Delay must be non-negative.")
|
|
50
|
+
if joint is not None and axis is not None:
|
|
51
|
+
raise ValueError("Cannot specify both 'joint' and 'axis'.")
|
|
52
|
+
|
|
53
|
+
_apply_control(model, data, amplitude, joint=joint, axis=axis, delay=delay)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def sineController(model, data, **kwargs):
|
|
57
|
+
"""A simple sine wave controller for the simulation.
|
|
58
|
+
|
|
59
|
+
Args:
|
|
60
|
+
amplitude (float): The amplitude of the sine wave (default=1).
|
|
61
|
+
frequency (float): The frequency of the sine wave (default=1).
|
|
62
|
+
phase (float): The phase shift of the sine wave (default=0).
|
|
63
|
+
joint (list[int]): The joint to apply the sine wave to (default=all).
|
|
64
|
+
delay (float): The delay before applying the sine wave (default=0).
|
|
65
|
+
|
|
66
|
+
Returns:
|
|
67
|
+
None
|
|
68
|
+
"""
|
|
69
|
+
amplitude = kwargs.get("amplitude", 1)
|
|
70
|
+
frequency = kwargs.get("frequency", 1)
|
|
71
|
+
phase = kwargs.get("phase", 0)
|
|
72
|
+
joint = kwargs.get("joint", None)
|
|
73
|
+
delay = kwargs.get("delay", 0)
|
|
74
|
+
|
|
75
|
+
if delay < 0:
|
|
76
|
+
raise ValueError("Delay must be non-negative.")
|
|
77
|
+
|
|
78
|
+
value = amplitude * sin(2 * pi * frequency * data.time + phase)
|
|
79
|
+
_apply_control(model, data, value, joint=joint, delay=delay)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def cosineController(model, data, **kwargs):
|
|
83
|
+
"""A simple cosine wave controller for the simulation.
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
amplitude (float): The amplitude of the cosine wave (default=1).
|
|
87
|
+
frequency (float): The frequency of the cosine wave (default=1).
|
|
88
|
+
phase (float): The phase shift of the cosine wave (default=0).
|
|
89
|
+
joint (list[int]): The joint to apply the cosine wave to (default=all).
|
|
90
|
+
delay (float): The delay before applying the cosine wave (default=0).
|
|
91
|
+
|
|
92
|
+
Returns:
|
|
93
|
+
None
|
|
94
|
+
"""
|
|
95
|
+
amplitude = kwargs.get("amplitude", 1)
|
|
96
|
+
frequency = kwargs.get("frequency", 1)
|
|
97
|
+
phase = kwargs.get("phase", 0)
|
|
98
|
+
joint = kwargs.get("joint", None)
|
|
99
|
+
delay = kwargs.get("delay", 0)
|
|
100
|
+
|
|
101
|
+
if delay < 0:
|
|
102
|
+
raise ValueError("Delay must be non-negative.")
|
|
103
|
+
|
|
104
|
+
value = amplitude * cos(2 * pi * frequency * data.time + phase)
|
|
105
|
+
_apply_control(model, data, value, joint=joint, delay=delay)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def randomController(model, data, **kwargs):
|
|
109
|
+
"""A random controller for the simulation.
|
|
110
|
+
|
|
111
|
+
Args:
|
|
112
|
+
amplitude (float): The maximum amplitude of the random signal (default=1).
|
|
113
|
+
joint (list[int]): The joints to apply the random signal to (default=all).
|
|
114
|
+
axis (int): The axis to apply the random signal to (default=None).
|
|
115
|
+
delay (float): The delay before applying the random signal (default=0).
|
|
116
|
+
|
|
117
|
+
Returns:
|
|
118
|
+
None
|
|
119
|
+
"""
|
|
120
|
+
amplitude = kwargs.get("amplitude", 1)
|
|
121
|
+
joint = kwargs.get("joint", None)
|
|
122
|
+
axis = kwargs.get("axis", None)
|
|
123
|
+
delay = kwargs.get("delay", 0)
|
|
124
|
+
|
|
125
|
+
if delay < 0:
|
|
126
|
+
raise ValueError("Delay must be non-negative.")
|
|
127
|
+
if joint is not None and axis is not None:
|
|
128
|
+
raise ValueError("Cannot specify both 'joint' and 'axis'.")
|
|
129
|
+
|
|
130
|
+
value = amplitude * random.rand()
|
|
131
|
+
_apply_control(model, data, value, joint=joint, axis=axis, delay=delay)
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
from functools import wraps
|
|
2
|
+
from sys import platform
|
|
3
|
+
from time import time
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def timer(func):
|
|
7
|
+
@wraps(func)
|
|
8
|
+
def time_wrapper(*args, **kwargs):
|
|
9
|
+
start_time = time()
|
|
10
|
+
result = func(*args, **kwargs)
|
|
11
|
+
end_time = time()
|
|
12
|
+
total_time = end_time - start_time
|
|
13
|
+
|
|
14
|
+
from . import VERBOSITY
|
|
15
|
+
|
|
16
|
+
if VERBOSITY:
|
|
17
|
+
print(f"Function '{func.__name__}' took {total_time:.4f} seconds")
|
|
18
|
+
return result
|
|
19
|
+
|
|
20
|
+
return time_wrapper
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def print_warning(*args) -> None:
|
|
24
|
+
"""
|
|
25
|
+
Prints the first argument as an orange warning message and prints the other arguments in regular text, all on the same line.
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
*args: First argument is printed as an orange warning, other arguments are printed normally.
|
|
29
|
+
"""
|
|
30
|
+
if args:
|
|
31
|
+
# Make sure the first argument is printed with the 'WARNING:' prefix and in orange
|
|
32
|
+
message = f"WARNING: {args[0]}"
|
|
33
|
+
|
|
34
|
+
# Check if we're on Windows or a Unix-like system for color support
|
|
35
|
+
if platform.startswith("win"):
|
|
36
|
+
try:
|
|
37
|
+
from colorama import Fore, Style, init
|
|
38
|
+
|
|
39
|
+
init() # Initialize colorama
|
|
40
|
+
print(
|
|
41
|
+
f"{Fore.YELLOW}{message}{Style.RESET_ALL}", end=" "
|
|
42
|
+
) # Yellow color for warning
|
|
43
|
+
print(
|
|
44
|
+
*args[1:], end=""
|
|
45
|
+
) # No newline, print all args separated by spaces
|
|
46
|
+
except ImportError:
|
|
47
|
+
# If colorama is not available, print without color
|
|
48
|
+
print(message, end=" ")
|
|
49
|
+
print(*args[1:], end="")
|
|
50
|
+
else:
|
|
51
|
+
# ANSI escape code for orange (for Unix-like systems)
|
|
52
|
+
print(f"\033[38;5;214m{message}\033[0m", end=" ") # Orange ANSI escape code
|
|
53
|
+
print(*args[1:], end="") # No newline, print all args separated by spaces
|