-
Notifications
You must be signed in to change notification settings - Fork 7
/
config_util.py
169 lines (143 loc) · 5.5 KB
/
config_util.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
from typing import Type, List
from dataclasses import dataclass, field
import time
import csv
import sys
import importlib
from enum import Enum
import argparse
import constants
class Task(Enum):
'''Used to determine gait_event_detector used and state machines used.'''
WALKING = 0
STANDINGPERTURBATION = 1
BILATERALSTANDINGPERTURBATION = 2
SLIPDETECTFROMSYNC = 3
WALKINGMLGAITPHASE = 4
class StanceCtrlStyle(Enum):
'''Used to determine behavior during stance.'''
FOURPOINTSPLINE = 0
GENERICSPLINE = 1
SAWICKIWICKI = 2
GENERICIMPEDANCE = 3
FIVEPOINTSPLINE = 4
@dataclass
class ConfigurableConstants():
'''Class that stores configuration-related constants.
These variables serve to allow 1) loadable configurations from files in /custom_constants/,
2) online updating of device behavior via parameter_passers.py, and 3) to store calibration
details. Below are the default config constants. DO NOT MODIFY DEFAULTS. Write your own short
script in /custom_constants/ (see default_config.py for example).
(see ) '''
# Set by functions... no need to change in config file
loop_time: float = 0
actual_time: float = time.time()
LEFT_STANDING_ANGLE: float = None # Deg
RIGHT_STANDING_ANGLE: float = None # Deg
TARGET_FREQ: float = 175 # Hz
ACTPACK_FREQ: float = 200 # Hz
DO_DEPHY_LOG: bool = False
DEPHY_LOG_LEVEL: int = 4
ONLY_LOG_IF_NEW: bool = True
TASK: Type[Task] = Task.WALKING
STANCE_CONTROL_STYLE: Type[StanceCtrlStyle] = StanceCtrlStyle.FOURPOINTSPLINE
MAX_ALLOWABLE_CURRENT = 20000 # mA
# Gait State details
HS_GYRO_THRESHOLD: float = 100
HS_GYRO_FILTER_N: int = 2
HS_GYRO_FILTER_WN: float = 3
HS_GYRO_DELAY: float = 0.05
SWING_SLACK: int = 10000
TOE_OFF_FRACTION: float = 0.60
REEL_IN_MV: int = 1200
REEL_IN_SLACK_CUTOFF: int = 1200
REEL_IN_TIMEOUT: float = 0.2
NUM_STRIDES_REQUIRED: int = 2
SWING_ONLY: bool = False
# 4 point Spline
RISE_FRACTION: float = 0.2
PEAK_FRACTION: float = 0.53
FALL_FRACTION: float = 0.60
PEAK_TORQUE: float = 5
SPLINE_BIAS: float = 3 # Nm
# Impedance
K_VAL: int = 500
B_VAL: int = 0
B_RATIO: float = 0.5 # when B_VAL is a function of B_RATIO. 2.5 is approx. crit. damped
SET_POINT: float = 0 # Deg
READ_ONLY: bool = False # Does not require Lipos
DO_READ_FSRS: bool = False
DO_READ_SYNC: bool = False
PRINT_HS: bool = True # Print heel strikes
VARS_TO_PLOT: List = field(default_factory=lambda: [])
DO_DETECT_SLIP: bool = False
SLIP_DETECT_ACTIVE: bool = False
DO_INCLUDE_GEN_VARS: bool = False
SLIP_DETECT_DELAY: int = 0
DO_FILTER_GAIT_PHASE: bool = False
EXPERIMENTER_NOTES: str = 'Experimenter notes go here'
class ConfigSaver():
def __init__(self, file_ID: str, config: Type[ConfigurableConstants]):
'''file_ID is used as a custom file identifier after date.'''
self.file_ID = file_ID
self.config = config
subfolder_name = 'exo_data/'
filename = subfolder_name + \
time.strftime("%Y%m%d_%H%M_") + file_ID + \
'_CONFIG' + '.csv'
self.my_file = open(filename, 'w', newline='')
self.writer = csv.DictWriter(
self.my_file, fieldnames=self.config.__dict__.keys())
self.writer.writeheader()
def write_data(self, loop_time):
'''Writes new row of Config data to Config file.'''
self.config.loop_time = loop_time
self.config.actual_time = time.time()
self.writer.writerow(self.config.__dict__)
def close_file(self):
if self.file_ID is not None:
self.my_file.close()
def load_config(config_filename) -> Type[ConfigurableConstants]:
try:
# strip extra parts off
config_filename = config_filename.lower()
if config_filename.endswith('_config'):
config_filename = config_filename[:-7]
elif config_filename.endswith('_config.py'):
config_filename = config_filename[:-11]
elif config_filename.endswith('.py'):
config_filename = config_filename[:-4]
config_filename = config_filename + '_config'
module = importlib.import_module('.' + config_filename,
package='custom_configs')
except:
error_str = 'Unable to find config file: ' + \
config_filename + ' in custom_constants'
raise ValueError(error_str)
config = module.config
print('Using ConfigurableConstants from: ', config_filename)
return config
def parse_args():
# Create the parser
my_parser = argparse.ArgumentParser(prog='Exoboot Code',
description='Run Exoboot Controllers',
epilog='Enjoy the program! :)')
# Add the arguments
my_parser.add_argument('-c', '--config', action='store',
type=str, required=False, default='default_config')
# Execute the parse_args() method
args = my_parser.parse_args()
return args
def load_config_from_args():
args = parse_args()
config = load_config(config_filename=args.config)
return config
def get_sync_detector(config: Type[ConfigurableConstants]):
if config.DO_READ_SYNC:
print('Creating sync detector')
import gpiozero # pylint: disable=import-error
sync_detector = gpiozero.InputDevice(
pin=constants.SYNC_PIN, pull_up=False)
return sync_detector
else:
return None