better live tuning implementation with opEdit

This commit is contained in:
Shane Smiskol
2020-03-10 04:24:16 -05:00
parent adffbc9fd4
commit e3015eccd4
2 changed files with 55 additions and 51 deletions
+32 -19
View File
@@ -4,6 +4,7 @@ import json
import time
import string
import random
from selfdrive.swaglog import cloudlog
from common.travis_checker import travis
@@ -30,6 +31,7 @@ class KeyInfo:
live = False
has_default = False
has_description = False
hidden = False
class opParams:
@@ -49,7 +51,8 @@ class opParams:
self.default_params = {'dynamic_follow': {'default': 'relaxed', 'allowed_types': [str], 'description': "Can be: ('traffic', 'relaxed', 'roadtrip'): Left to right increases in following distance.\n"
"All profiles support dynamic follow so you'll get your preferred distance while\n"
"retaining the smoothness and safety of dynamic follow!", 'live': True},}
"retaining the smoothness and safety of dynamic follow!", 'live': True},
'op_edit_live_mode': {'default': False, 'description': 'This parameter controls which mode opEdit starts in. It should be hidden from the user with the hide key', 'hide': True}}
self.params = {}
self.params_file = "/data/op_params.json"
@@ -126,39 +129,49 @@ class opParams:
def get(self, key=None, default=None, force_update=False): # can specify a default value if key doesn't exist
self.update_params(key, force_update)
if key is None:
return self.params
return self.get_all()
if key in self.params:
key_info = self.get_key_info(key)
key_info = self.key_info(key)
if key_info.has_allowed_types:
value = self.params[key]
allowed_types = self.default_params[key]['allowed_types']
valid_type = type(value) in allowed_types
if not valid_type:
if key_info.has_default: # if value in op_params.json is not correct type, use default
value = self.default_params[key]['default']
else: # else use a standard value based on type (last resort to keep openpilot running)
value = self.value_from_types(allowed_types)
if type(value) not in allowed_types:
cloudlog.warning('op_params: User\'s value is not valid!')
if key_info.has_default: # invalid value type, try to use default value
default_value = self.default_params[key]['default']
if type(default_value) in allowed_types: # actually check if the default is valid
# return default value because user's value of key is not in the allowed_types to avoid crashing openpilot
return default_value
else: # else use a standard value based on type (last resort to keep openpilot running if user's value is of invalid type)
return self.value_from_types(allowed_types)
else:
return value # all good, returning user's value
else:
value = self.params[key]
else:
value = default
return self.params[key] # no defined allowed types, returning user's value
return value
return default # not in params
def get_key_info(self, key):
def get_all(self): # returns all non-hidden params
return {k: v for k, v in self.params.items() if not self.key_info(k).hidden}
def key_info(self, key):
key_info = KeyInfo()
if key is None:
return key_info
if key in self.default_params:
if 'allowed_types' in self.default_params[key]:
allowed_types = self.default_params[key]['allowed_types']
if isinstance(allowed_types, list) and len(allowed_types) > 0:
key_info.has_allowed_types = True
if 'live' in self.default_params[key] and self.default_params[key]['live'] is True:
key_info.live = True
if 'live' in self.default_params[key]:
key_info.live = self.default_params[key]['live']
if 'default' in self.default_params[key]:
key_info.has_default = True
if 'description' in self.default_params[key]:
key_info.has_description = True
if 'hide' in self.default_params[key]:
key_info.hidden = self.default_params[key]['hide']
return key_info
def value_from_types(self, allowed_types):
@@ -173,8 +186,8 @@ class opParams:
return None # unknown type
def update_params(self, key, force_update):
if force_update or self.get_key_info(key).live: # if is a live param, we want to get updates while openpilot is running
if not travis and time.time() - self.last_read_time >= self.read_frequency: # make sure we aren't reading file too often
if force_update or self.key_info(key).live: # if is a live param, we want to get updates while openpilot is running
if not travis and (time.time() - self.last_read_time >= self.read_frequency or force_update): # make sure we aren't reading file too often
self.params, read_status = read_params(self.params_file, self.format_default_params())
if not read_status:
time.sleep(1/100.)
@@ -184,4 +197,4 @@ class opParams:
def delete(self, key):
if key in self.params:
del self.params[key]
write_params(self.params, self.params_file)
write_params(self.params, self.params_file)
+23 -32
View File
@@ -9,46 +9,33 @@ class opEdit: # use by running `python /data/openpilot/op_edit.py`
self.op_params = opParams()
self.params = None
self.sleep_time = 1.0
self.live_tuning = False
self.welcome()
def welcome(self):
print('Welcome to the opParams command line editor!')
while True:
print('Would you like to enter opEdit\'s live-tuning mode?')
choice = input('[Y/n]: ').lower().strip()
if choice in ['y', 'ye', 'yes']:
self.live_tuning = True
self.run_loop()
break
elif choice in ['n', 'no', '']:
self.run_loop()
break
elif choice in ['e', 'exit']:
break
self.live_tuning = self.op_params.get('op_edit_live_mode', False)
self.run_loop()
def run_loop(self):
if not self.live_tuning:
print('Here are your parameters:\n')
else:
print('Here are your live parameters:\n')
print('Welcome to the opParams command line editor!')
while True:
if not self.live_tuning:
print('Here are your parameters:\n')
else:
print('Here are your live parameters:\n')
self.params = self.op_params.get(force_update=True)
if self.live_tuning: # only display live tunable params
self.params = {k: v for k, v in self.params.items() if self.op_params.get_key_info(k).live}
self.params = {k: v for k, v in self.params.items() if self.op_params.key_info(k).live}
values_list = [self.params[i] if len(str(self.params[i])) < 20 else '{} ... {}'.format(str(self.params[i])[:30], str(self.params[i])[-15:]) for i in self.params]
live = ['(live!)' if self.op_params.get_key_info(i).live else '' for i in self.params]
live = ['(live!)' if self.op_params.key_info(i).live else '' for i in self.params]
to_print = ['{}. {}: {} {}'.format(idx + 1, i, values_list[idx], live[idx]) for idx, i in enumerate(self.params)]
to_print.append('\n{}. Add new parameter!'.format(len(self.params) + 1))
to_print.append('{}. Delete parameter!'.format(len(self.params) + 2))
to_print.append('---\n{}. Add new parameter'.format(len(to_print) + 1))
to_print.append('{}. Delete parameter'.format(len(to_print) + 1))
to_print.append('{}. Toggle live tuning'.format(len(to_print) + 1))
print('\n'.join(to_print))
print('\nChoose a parameter to explore (by integer index): ')
choice = input('>> ').strip()
parsed, choice = self.parse_choice(choice)
parsed, choice = self.parse_choice(choice, len(to_print))
if parsed == 'continue':
continue
elif parsed == 'add':
@@ -57,10 +44,13 @@ class opEdit: # use by running `python /data/openpilot/op_edit.py`
self.change_parameter(choice)
elif parsed == 'delete':
self.delete_parameter()
elif parsed == 'live':
self.live_tuning = not self.live_tuning
self.op_params.put('op_edit_live_mode', self.live_tuning) # for next opEdit startup
elif parsed == 'error':
return
def parse_choice(self, choice):
def parse_choice(self, choice, opt_len):
if choice.isdigit():
choice = int(choice)
choice -= 1
@@ -70,22 +60,23 @@ class opEdit: # use by running `python /data/openpilot/op_edit.py`
else:
self.message('Not an integer!')
return 'retry', choice
if choice not in range(0, len(self.params) + 2): # three for add/delete parameter
if choice not in range(opt_len): # number of options to choose from
self.message('Not in range!')
return 'continue', choice
if choice == len(self.params): # add new parameter
if choice == opt_len - 3: # add new parameter
return 'add', choice
if choice == len(self.params) + 1: # delete parameter
elif choice == opt_len - 2: # delete parameter
return 'delete', choice
elif choice == opt_len - 1: # live tuning mode
return 'live', choice
return 'change', choice
def change_parameter(self, choice):
while True:
chosen_key = list(self.params)[choice]
key_info = self.op_params.get_key_info(chosen_key)
key_info = self.op_params.key_info(chosen_key)
old_value = self.params[chosen_key]
print('Chosen parameter: {}'.format(chosen_key))