rlmf 0.1.0__py3-none-any.whl
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.
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: rlmf
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Robot Locomotion & Morphology Framework — universal legged-robot API
|
|
5
|
+
Project-URL: Homepage, https://github.com/Wafula_Ian01/rlmf
|
|
6
|
+
Project-URL: Documentation, https://github.com/Wafula_Ian01/rlmf#readme
|
|
7
|
+
Project-URL: Repository, https://github.com/Wafula_Ian01/rlmf.git
|
|
8
|
+
Project-URL: Bug Tracker, https://github.com/Wafula_Ian01/rlmf/issues
|
|
9
|
+
Author-email: Wafula Ian Elmer <ianwelmer01@gmail.com>
|
|
10
|
+
License: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: gait,hexapod,inverse kinematics,locomotion,quadruped,robotics,servo
|
|
13
|
+
Classifier: Development Status :: 3 - Alpha
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Intended Audience :: Education
|
|
16
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
+
Classifier: Topic :: Scientific/Engineering
|
|
23
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
24
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
25
|
+
Requires-Python: >=3.10
|
|
26
|
+
Requires-Dist: pyyaml>=6.0
|
|
27
|
+
Provides-Extra: dev
|
|
28
|
+
Requires-Dist: build>=1.2; extra == 'dev'
|
|
29
|
+
Requires-Dist: hatchling>=1.24; extra == 'dev'
|
|
30
|
+
Requires-Dist: mypy>=1.10; extra == 'dev'
|
|
31
|
+
Requires-Dist: pytest-cov>=5.0; extra == 'dev'
|
|
32
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
33
|
+
Requires-Dist: ruff>=0.4; extra == 'dev'
|
|
34
|
+
Requires-Dist: twine>=5.1; extra == 'dev'
|
|
35
|
+
Provides-Extra: pi
|
|
36
|
+
Requires-Dist: adafruit-circuitpython-servokit>=1.3; extra == 'pi'
|
|
37
|
+
Requires-Dist: gpiozero>=2.0; extra == 'pi'
|
|
38
|
+
Requires-Dist: pigpio>=1.78; extra == 'pi'
|
|
39
|
+
Description-Content-Type: text/markdown
|
|
40
|
+
|
|
41
|
+
# rlmf — Robot Locomotion & Morphology Framework
|
|
42
|
+
|
|
43
|
+
[](https://pypi.org/project/rlmf/)
|
|
44
|
+
[](https://pypi.org/project/rlmf/)
|
|
45
|
+
[](LICENSE)
|
|
46
|
+
[]()
|
|
47
|
+
|
|
48
|
+
A universal robot body model and locomotion API.
|
|
49
|
+
Define any legged robot in YAML, then call `robot.walk()`.
|
|
50
|
+
|
|
51
|
+
```python
|
|
52
|
+
from rlmf import Robot
|
|
53
|
+
|
|
54
|
+
robot = Robot.load("hexapod.yaml")
|
|
55
|
+
robot.walk()
|
|
56
|
+
robot.turn_left(45)
|
|
57
|
+
robot.climb()
|
|
58
|
+
robot.follow_path([(0.5, 0), (0.5, 0.5), (0, 0.5)])
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
The API is **identical** regardless of morphology — 4-leg, 6-leg, 8-leg, 12-leg.
|
|
62
|
+
No gait code. No IK math. No synchronization logic. No motor coordination code.
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## Installation
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
pip install rlmf
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
With Raspberry Pi hardware support (PCA9685 + GPIO servos):
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
pip install "rlmf[pi]"
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## Quickstart
|
|
81
|
+
|
|
82
|
+
```python
|
|
83
|
+
from rlmf import Robot
|
|
84
|
+
import rlmf.robots as robots
|
|
85
|
+
|
|
86
|
+
# Use a bundled robot definition
|
|
87
|
+
robot = Robot.load(robots.get("hexapod"))
|
|
88
|
+
|
|
89
|
+
# Or load your own YAML
|
|
90
|
+
robot = Robot.load("my_robot.yaml")
|
|
91
|
+
|
|
92
|
+
# Behaviours — same API for any morphology
|
|
93
|
+
robot.walk()
|
|
94
|
+
robot.turn_left(45)
|
|
95
|
+
robot.turn_right(30)
|
|
96
|
+
robot.climb()
|
|
97
|
+
robot.stop()
|
|
98
|
+
robot.follow_path([(0.5, 0.0), (0.5, 0.5)])
|
|
99
|
+
|
|
100
|
+
# Low-level access
|
|
101
|
+
robot.reach("leg_0", (0.20, 0.05, -0.10)) # IK to world target
|
|
102
|
+
robot.move_joint("leg_0.hip", 30.0) # direct joint control
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## Morphology Description Language
|
|
108
|
+
|
|
109
|
+
Define any robot in YAML — the framework handles the rest:
|
|
110
|
+
|
|
111
|
+
```yaml
|
|
112
|
+
name: HexaBot
|
|
113
|
+
body:
|
|
114
|
+
segments: [thorax]
|
|
115
|
+
length: 0.30
|
|
116
|
+
width: 0.18
|
|
117
|
+
height: 0.09
|
|
118
|
+
limbs:
|
|
119
|
+
- type: leg
|
|
120
|
+
count: 6
|
|
121
|
+
joints:
|
|
122
|
+
hip: {min: -90, max: 90}
|
|
123
|
+
knee: {min: 0, max: 120}
|
|
124
|
+
ankle: {min: -45, max: 45}
|
|
125
|
+
segment_lengths: [0.07, 0.12, 0.10]
|
|
126
|
+
physics:
|
|
127
|
+
mass: 1.2kg
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
After loading, the full model is immediately introspectable:
|
|
131
|
+
|
|
132
|
+
```python
|
|
133
|
+
robot.topology.family # "hexapod"
|
|
134
|
+
robot.topology.limb_count # 6
|
|
135
|
+
robot.topology.total_joints # 18
|
|
136
|
+
robot.mass # 1.2
|
|
137
|
+
robot.limbs # List[Limb]
|
|
138
|
+
robot.joints # Dict[str, JointDef]
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## Bundled robots
|
|
144
|
+
|
|
145
|
+
```python
|
|
146
|
+
import rlmf.robots as robots
|
|
147
|
+
|
|
148
|
+
robots.list_robots() # ['centipede', 'hexapod', 'quadruped', 'spider']
|
|
149
|
+
path = robots.get("hexapod") # Path to bundled hexapod.yaml
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## Architecture
|
|
155
|
+
|
|
156
|
+
```
|
|
157
|
+
User API robot.walk() / robot.turn_left() / robot.climb()
|
|
158
|
+
↓
|
|
159
|
+
Gait Engine TripodGait / WaveGait / RippleGait / TrotGait
|
|
160
|
+
↓
|
|
161
|
+
Kinematics solve_ik() / solve_fk() per limb
|
|
162
|
+
↓
|
|
163
|
+
Balance Engine support polygon · stability margin · tip risk
|
|
164
|
+
↓
|
|
165
|
+
Motor Layer SimulatedDriver / PCA9685 / your hardware
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### Gait engine
|
|
169
|
+
|
|
170
|
+
| Gait | Class | Best for | Duty factor |
|
|
171
|
+
|------|-------|----------|-------------|
|
|
172
|
+
| tripod | `TripodGait` | hexapods, fast walking | 0.50 |
|
|
173
|
+
| wave | `WaveGait` | any, climbing | 0.83 |
|
|
174
|
+
| ripple | `RippleGait` | octopods, medium speed | 0.67 |
|
|
175
|
+
| trot | `TrotGait` | quadrupeds | 0.50 |
|
|
176
|
+
|
|
177
|
+
Gait selection is automatic — `robot.walk()` picks the right pattern for your morphology.
|
|
178
|
+
Override it when needed:
|
|
179
|
+
|
|
180
|
+
```python
|
|
181
|
+
from rlmf import select_gait, TrotGait
|
|
182
|
+
|
|
183
|
+
gait = select_gait("hexapod", "climb") # → WaveGait
|
|
184
|
+
frames = robot.get_gait_frames("ripple", num_frames=60)
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### Balance engine
|
|
188
|
+
|
|
189
|
+
```python
|
|
190
|
+
state = robot.balance_state()
|
|
191
|
+
|
|
192
|
+
state.center_of_mass # (x, y, z) world space
|
|
193
|
+
state.support_polygon # convex hull of stance feet
|
|
194
|
+
state.stability_margin # metres to nearest polygon edge (>0 = stable)
|
|
195
|
+
state.is_stable # bool
|
|
196
|
+
state.tip_risk # 0.0 (safe) … 1.0 (falling)
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### Motor abstraction
|
|
200
|
+
|
|
201
|
+
Swap the hardware driver without touching any robot code:
|
|
202
|
+
|
|
203
|
+
```python
|
|
204
|
+
from rlmf import Robot, MotorAbstractionLayer
|
|
205
|
+
from my_hardware import MyServoDriver # your implementation
|
|
206
|
+
|
|
207
|
+
robot = Robot(
|
|
208
|
+
Robot.load("hexapod.yaml")._model,
|
|
209
|
+
motor_layer=MotorAbstractionLayer(driver=MyServoDriver()),
|
|
210
|
+
)
|
|
211
|
+
robot.walk() # drives your hardware
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
Implement `MotorDriver` for any servo bus:
|
|
215
|
+
|
|
216
|
+
```python
|
|
217
|
+
from rlmf import MotorDriver
|
|
218
|
+
|
|
219
|
+
class MyDriver(MotorDriver):
|
|
220
|
+
def set_angle(self, joint_id, angle_deg, speed=1.0): ...
|
|
221
|
+
def get_angle(self, joint_id) -> float: ...
|
|
222
|
+
def enable(self, joint_id): ...
|
|
223
|
+
def disable(self, joint_id): ...
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
---
|
|
227
|
+
|
|
228
|
+
## CLI
|
|
229
|
+
|
|
230
|
+
```bash
|
|
231
|
+
rlmf robots # list bundled robots
|
|
232
|
+
rlmf describe hexapod # print topology
|
|
233
|
+
rlmf describe path/to/mybot.yaml # your own robot
|
|
234
|
+
rlmf walk hexapod --gait wave # simulate gait, print stats
|
|
235
|
+
rlmf balance quadruped # print balance state
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
---
|
|
239
|
+
|
|
240
|
+
## Raspberry Pi deployment
|
|
241
|
+
|
|
242
|
+
Install with hardware extras:
|
|
243
|
+
|
|
244
|
+
```bash
|
|
245
|
+
pip install "rlmf[pi]"
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
Wire a PCA9685 to the Pi over I2C, then:
|
|
249
|
+
|
|
250
|
+
```python
|
|
251
|
+
from adafruit_servokit import ServoKit
|
|
252
|
+
from rlmf import Robot, MotorAbstractionLayer
|
|
253
|
+
from rlmf.motors import MotorDriver
|
|
254
|
+
|
|
255
|
+
class PCA9685Driver(MotorDriver):
|
|
256
|
+
def __init__(self):
|
|
257
|
+
self._kit = ServoKit(channels=16)
|
|
258
|
+
self._map = {}
|
|
259
|
+
|
|
260
|
+
def assign(self, joint_id, channel):
|
|
261
|
+
self._map[joint_id] = channel
|
|
262
|
+
|
|
263
|
+
def set_angle(self, joint_id, angle_deg, speed=1.0):
|
|
264
|
+
ch = self._map.get(joint_id)
|
|
265
|
+
if ch is not None:
|
|
266
|
+
self._kit.servo[ch].angle = max(0, min(180, angle_deg + 90))
|
|
267
|
+
|
|
268
|
+
def get_angle(self, joint_id): return 0.0
|
|
269
|
+
def enable(self, joint_id): pass
|
|
270
|
+
def disable(self, joint_id): pass
|
|
271
|
+
|
|
272
|
+
driver = PCA9685Driver()
|
|
273
|
+
driver.assign("leg_0.hip", 0)
|
|
274
|
+
# … assign all 18 joints …
|
|
275
|
+
|
|
276
|
+
import rlmf.robots as robots
|
|
277
|
+
robot = Robot(
|
|
278
|
+
Robot.load(robots.get("hexapod"))._model,
|
|
279
|
+
motor_layer=MotorAbstractionLayer(driver=driver),
|
|
280
|
+
)
|
|
281
|
+
robot.walk() # moves physical servos
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
---
|
|
285
|
+
|
|
286
|
+
## Roadmap
|
|
287
|
+
|
|
288
|
+
- **v0.1** — Phase 1: 3–12 leg robots, four gait patterns, analytical IK, balance engine, motor abstraction
|
|
289
|
+
- **v0.2** — Phase 2: biped support, dynamic balance, weight shifting, fall recovery
|
|
290
|
+
- **v0.3** — Phase 3: arbitrary morphologies, terrain adaptation, path planning
|
|
291
|
+
|
|
292
|
+
---
|
|
293
|
+
|
|
294
|
+
## Development
|
|
295
|
+
|
|
296
|
+
```bash
|
|
297
|
+
git clone https://github.com/Wafula_Ian01/rlmf
|
|
298
|
+
cd rlmf
|
|
299
|
+
pip install -e ".[dev]"
|
|
300
|
+
pytest
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
---
|
|
304
|
+
|
|
305
|
+
## License
|
|
306
|
+
|
|
307
|
+
MIT — see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
rlmf-0.1.0.dist-info/METADATA,sha256=9Yqhr_baLPBHZL-JtQ0ILZl1L1NvgwPBwx5QwCJKspg,8053
|
|
2
|
+
rlmf-0.1.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
|
|
3
|
+
rlmf-0.1.0.dist-info/entry_points.txt,sha256=4E05XVA_bXoF70yq8Q913pbn8woVYrJg6ElIFoVxCTY,40
|
|
4
|
+
rlmf-0.1.0.dist-info/licenses/LICENSE,sha256=AHDmKS0ZzvxZ7hzCAM-W-uds-RQhJfyKEBx2F-ba0OQ,1073
|
|
5
|
+
rlmf-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Wafula Ian Elmer
|
|
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.
|