You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: _posts/2024-10-24-dynamic-module-loading-python.md
+48-18Lines changed: 48 additions & 18 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -66,18 +66,18 @@ Each module has a corresponding YAML configuration file that defines whether the
66
66
```yaml
67
67
servos:
68
68
enabled: true
69
-
path: "modules/actuators/servo"
69
+
path: "modules.actuators.servo.Servo"# Include class name here
70
70
instances:
71
71
- name: "leg_l_hip"
72
72
id: 0
73
73
pin: 9
74
74
range: [0, 180]
75
-
start_pos: 40
75
+
start: 40
76
76
- name: "leg_l_knee"
77
77
id: 1
78
78
pin: 10
79
79
range: [0, 180]
80
-
start_pos: 10
80
+
start: 10
81
81
```
82
82
83
83
In this example, the `servos` module is enabled, and two instances (`leg_l_hip` and `leg_l_knee`) are defined with their respective configurations. The `path` points to where the module is located within the project folder.
@@ -92,9 +92,31 @@ Here’s an implementation of `ModuleLoader`:
92
92
import os
93
93
import yaml
94
94
import importlib.util
95
+
from pubsub import pub
95
96
96
97
class ModuleLoader:
97
98
def __init__(self, config_folder='config'):
99
+
"""
100
+
ModuleLoader class
101
+
:param config_folder: folder containing the module configuration files
102
+
103
+
Example config file:
104
+
config/modules.yml
105
+
---
106
+
buzzer:
107
+
enabled: true # Required
108
+
path: "modules.audio.buzzer.Buzzer" # Required
109
+
config: # Passed as **kwargs to the module's __init__ method
110
+
pin: 27
111
+
name: 'buzzer'
112
+
113
+
Example:
114
+
loader = ModuleLoader()
115
+
modules = loader.load_modules()
116
+
117
+
Reference module once loaded:
118
+
translator_inst = modules['Translator']
119
+
"""
98
120
self.config_folder = config_folder
99
121
self.modules = self.load_yaml_files()
100
122
@@ -117,26 +139,31 @@ class ModuleLoader:
117
139
"""Dynamically load and instantiate the modules based on the config."""
118
140
instances = {} # Use a dictionary to store instances for easy access
instance_name = module_name + '_' + instance_config.get('name') if instance_config.get('name') is not None else module_name # Use the module name and instance name as the key or module_name if single instance
my_module.start() # Assuming there's a start method
207
+
vision = module_instances['vision'] # Access the instance by name
208
+
leg_servo = module_instances['Servo_leg_l_hip'] # Access one of multiple instances (the module name is prepended to the instance name in this case) )
182
209
183
210
if __name__ == '__main__':
184
211
main()
@@ -192,6 +219,7 @@ from pubsub import pub
192
219
pub.sendMessage('mytopic', data='somedata') # Publish to a topic
193
220
pub.subscribe(self.handler_method, 'anothertopic') # subscribe to another topic
194
221
```
222
+
This is the primary communication method between modules in the Modular Biped Project. A timing loop fires events at intervals, triggering functionality in the modules.
195
223
196
224
## Writing a New Module for This Architecture
197
225
@@ -208,8 +236,10 @@ class Buzzer:
208
236
def __init__(self, **kwargs):
209
237
self.pin = kwargs.get('pin') # Required, no default
210
238
print(f"Initializing Buzzer on pin {self.pin}")
239
+
pub.subscribe(self.buzz, 'buzz') # Subscribe to the buzz topic
211
240
212
241
def buzz(self):
242
+
# Buzzer functionality goes here
213
243
print(f"Buzzer on pin {self.pin} is buzzing!")
214
244
```
215
245
@@ -220,9 +250,9 @@ Create a corresponding YAML configuration file in the `config` folder that speci
0 commit comments