Skip to content

Commit

Permalink
opParams: More params are live (commaai#351)
Browse files Browse the repository at this point in the history
* debug

* run

* debug

* debug

* debug

* debug

* debug

* debug

* debug

* debug

* don't import if already imported

* fix

* debug

* debug

* debug

* update put

* update put

* update put

* update put

* update put

* update put

* update put

* update put

* update put

* debug

* debug

* debug

* debug

* clean up

* clean up

* safety for reading

* Rename live to not static

* Fixup opEdit for static params

* debug

* change to static

* Add msg

* Revert

* Debug

* Right in the middle

* Clean up

* Clean up

* Clean up

* debug

* clean up

* clean up

* debug

* debug

* debug

* clean up

* More params are live now

* Use travis again

* Quicker

* Make read frequency a Param class attr

* replace self._last_read_times with param class attr

* rm reference

* Add msg

* Add msg

* Don't show this

* Only show live params in live mode

* debug

* live defaults to false

* update msg

* Reenable default live params in menu

* remove redundant argument pass

* Simplify importing old params

* Revert rate changes

* Make static

* Update releases

* Update features

* Update README.md

* These params now update with ignition

* Use basedir

* Can get rid of travis

* Windows friendly, write file when imported

* Update documentation

* Can create its own instance without much issue

* Update msg

* Update msg

* don't lie lol

* Color

* Color
  • Loading branch information
sshane committed Mar 27, 2021
1 parent 438335d commit 848e250
Show file tree
Hide file tree
Showing 15 changed files with 172 additions and 155 deletions.
9 changes: 6 additions & 3 deletions OPEDIT_FEATURES.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
# opEdit Features:
- You can misspell parameter names and opEdit should be able to figure out which parameter you want. Ex. `cmra off` would be parsed as: `camera_offset`
- You can also still enter the corresponding parameter index while choosing parameters to edit
- Type `a` to add a parameter, `d` to delete a parameter, or `l` to toggle live tuning only mode
- Type `l` to toggle live tuning only mode, which only shows params that update within a second
- Shows a detailed description for each parameter once you choose it
- Parameter value type restriction. Ensures a user cannot save an unsupported value type for any parameters, breaking the fork
- Remembers which mode you were last in and initializes opEdit with that mode (live tuning or not)
- Case insensitive boolean and NoneType entrance. Type `faLsE` to save `False (bool)`, etc
- **Parameters marked with `(live!)` will have updates take affect within 3 seconds while driving! All other params will require a reboot of your EON/C2 to take effect.**
- Case-insensitive boolean and NoneType entrance. Type `faLsE` to save `False (bool)`, etc

## ❗NEW❗:
- **Now, any parameter that isn't marked with `(static)` can update while driving and onroad. Only the parameters in the "live" view update within a second, the rest update within 10 seconds. If you enter a parameter, it will tell you its update behavior**
- As before, static parameters will need a reboot of the device, or ignition in some cases.

## To run the opEdit parameter manager:
```python
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,8 @@ python op_edit.py # or ./op_edit.py

[**To see what features opEdit has, click me!**](/OPEDIT_FEATURES.md)

🆕 All params now update live while driving, and params that are marked with `static` need a reboot of the device, or ignition.

Here are the main parameters you can change with this fork:
- **Tuning params**:
- `camera_offset` **`(live!)`**: Your camera offset to use in lane_planner.py. Helps fix lane hugging
Expand Down
2 changes: 2 additions & 0 deletions SA_RELEASES.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ Stock Additions v0.6.6 - 2020-02-27 (0.8.2)
* Roadtrip profile is now stock longitudinal for smoother road trips
* No dynamic follow distance modiciations, all stock
* Add color corrections button, PR by ihsakashi
* More parameters now update while driving.
* If the param isn't marked with "static" you no longer have to reboot

Stock Additions v0.6.5 - 2020-12-07 (0.8)
===
Expand Down
229 changes: 118 additions & 111 deletions common/op_params.py

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion common/travis_checker.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
from common.basedir import BASEDIR
import os
BASEDIR = os.path.abspath(os.path.join(os.path.dirname(os.path.realpath(__file__)), "../"))
travis = BASEDIR.strip('/').split('/')[0] != 'data'
36 changes: 21 additions & 15 deletions op_edit.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,36 +24,38 @@ def __init__(self):

def run_init(self):
if self.username is None:
self.success('\nWelcome to the opParams command line editor!', sleep_time=0)
self.prompt('Parameter \'username\' is missing! Would you like to add your Discord username for easier crash debugging?')
self.success('\nWelcome to the {}opParams{} command line editor!'.format(COLORS.CYAN, COLORS.SUCCESS), sleep_time=0)
self.prompt('Would you like to add your Discord username for easier crash debugging for the fork owner?')
self.prompt('Your username is only used for reaching out if a crash occurs.')

username_choice = self.input_with_options(['Y', 'n', 'don\'t ask again'], default='n')[0]
if username_choice == 0:
self.prompt('Please enter your Discord username so the developers can reach out if a crash occurs:')
self.prompt('Enter a unique identifer/Discord username:')
username = ''
while username == '':
username = input('>> ').strip()
self.success('Thanks! Saved your Discord username\n'
'Edit the \'username\' parameter at any time to update', sleep_time=1.0)
self.op_params.put('username', username)
self.username = username
self.success('Thanks! Saved your username\n'
'Edit the \'username\' parameter at any time to update', sleep_time=1.5)
elif username_choice == 2:
self.op_params.put('username', False)
self.info('Got it, bringing you into opEdit\n'
'Edit the \'username\' parameter at any time to update', sleep_time=1.0)
else:
self.success('\nWelcome to the opParams command line editor, {}!'.format(self.username), sleep_time=0)
self.success('\nWelcome to the {}opParams{} command line editor, {}!'.format(COLORS.CYAN, COLORS.SUCCESS, self.username), sleep_time=0)

self.run_loop()

def run_loop(self):
while True:
if not self.live_tuning:
self.info('Here are your parameters:', end='\n', sleep_time=0)
self.info('Here are all your parameters:', sleep_time=0)
self.info('(non-static params update while driving)', end='\n', sleep_time=0)
else:
self.info('Here are your live parameters:', sleep_time=0)
self.info('(changes take effect within {} seconds)'.format(self.op_params.read_frequency), end='\n', sleep_time=0)
self.params = self.op_params.get(force_live=True)
self.info('(changes take effect within a second)', end='\n', sleep_time=0)
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.fork_params[k].live}

Expand All @@ -70,20 +72,20 @@ def run_loop(self):
v = '{} ... {}'.format(str(v)[:30], str(v)[-15:])
values_list.append(v)

live = [COLORS.INFO + '(live!)' + COLORS.ENDC if self.op_params.fork_params[k].live else '' for k in self.params]
static = [COLORS.INFO + '(static)' + COLORS.ENDC if self.op_params.fork_params[k].static else '' for k in self.params]

to_print = []
blue_gradient = [33, 39, 45, 51, 87]
for idx, param in enumerate(self.params):
line = '{}. {}: {} {}'.format(idx + 1, param, values_list[idx], live[idx])
line = '{}. {}: {} {}'.format(idx + 1, param, values_list[idx], static[idx])
if idx == self.last_choice and self.last_choice is not None:
line = COLORS.OKGREEN + line
else:
_color = blue_gradient[min(round(idx / len(self.params) * len(blue_gradient)), len(blue_gradient) - 1)]
line = COLORS.BASE(_color) + line
to_print.append(line)

extras = {'l': ('Toggle live tuning', COLORS.WARNING),
extras = {'l': ('Toggle live params', COLORS.WARNING),
'e': ('Exit opEdit', COLORS.PINK)}

to_print += ['---'] + ['{}. {}'.format(ext_col + e, ext_txt + COLORS.ENDC) for e, (ext_txt, ext_col) in extras.items()]
Expand Down Expand Up @@ -137,14 +139,18 @@ def change_parameter(self, choice):
param_info = self.op_params.fork_params[chosen_key]

old_value = self.params[chosen_key]
if param_info.live:
if not param_info.static:
self.info2('Chosen parameter: {}{} (live!)'.format(chosen_key, COLORS.BASE(207)), sleep_time=0)
else:
self.info2('Chosen parameter: {}'.format(chosen_key), sleep_time=0)
self.info2('Chosen parameter: {}{} (static)'.format(chosen_key, COLORS.BASE(207)), sleep_time=0)

to_print = []
if param_info.has_description:
to_print.append(COLORS.OKGREEN + '>> Description: {}'.format(param_info.description.replace('\n', '\n > ')) + COLORS.ENDC)
if param_info.static:
to_print.append(COLORS.WARNING + '>> A reboot is required for changes to this parameter!' + COLORS.ENDC)
if not param_info.static and not param_info.live:
to_print.append(COLORS.WARNING + '>> Changes take effect within 10 seconds for this parameter!' + COLORS.ENDC)
if param_info.has_allowed_types:
to_print.append(COLORS.RED + '>> Allowed types: {}'.format(', '.join([at.__name__ for at in param_info.allowed_types])) + COLORS.ENDC)

Expand Down Expand Up @@ -175,7 +181,7 @@ def change_parameter(self, choice):
self.error('The type of data you entered ({}) is not allowed with this parameter!'.format(type(new_value).__name__))
continue

if param_info.live: # stay in live tuning interface
if not param_info.static: # stay in live tuning interface
self.op_params.put(chosen_key, new_value)
self.success('Saved {} with value: {}! (type: {})'.format(chosen_key, new_value, type(new_value).__name__))
else: # else ask to save and break
Expand Down
6 changes: 3 additions & 3 deletions selfdrive/car/interfaces.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class CarInterfaceBase():
def __init__(self, CP, CarController, CarState):
self.CP = CP
self.VM = VehicleModel(CP)
self.disengage_on_gas = opParams().get('disengage_on_gas')
self.op_params = opParams()

self.frame = 0
self.low_speed_alert = False
Expand Down Expand Up @@ -108,7 +108,7 @@ def create_common_events(self, cs_out, extra_gears=[], gas_resume_speed=-1, pcm_
events.add(EventName.wrongCarMode)
if cs_out.espDisabled:
events.add(EventName.espDisabled)
if cs_out.gasPressed and self.disengage_on_gas:
if cs_out.gasPressed and self.op_params.get('disengage_on_gas'):
events.add(EventName.gasPressed)
if cs_out.stockFcw:
events.add(EventName.stockFcw)
Expand All @@ -127,7 +127,7 @@ def create_common_events(self, cs_out, extra_gears=[], gas_resume_speed=-1, pcm_
# Disable on rising edge of gas or brake. Also disable on brake when speed > 0.
# Optionally allow to press gas at zero speed to resume.
# e.g. Chrysler does not spam the resume button yet, so resuming with gas is handy. FIXME!
if (self.disengage_on_gas and cs_out.gasPressed and (not self.CS.out.gasPressed) and cs_out.vEgo > gas_resume_speed) or \
if (self.op_params.get('disengage_on_gas') and cs_out.gasPressed and (not self.CS.out.gasPressed) and cs_out.vEgo > gas_resume_speed) or \
(cs_out.brakePressed and (not self.CS.out.brakePressed or not cs_out.standstill)):
events.add(EventName.pedalPressed)

Expand Down
11 changes: 6 additions & 5 deletions selfdrive/car/toyota/interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,6 @@
from selfdrive.car.interfaces import CarInterfaceBase
from common.op_params import opParams

op_params = opParams()
use_lqr = op_params.get('use_lqr')
prius_use_pid = False # op_params.get('prius_use_pid')
corollaTSS2_use_indi = False # op_params.get('corollaTSS2_use_indi')
rav4TSS2_use_indi = op_params.get('rav4TSS2_use_indi')
EventName = car.CarEvent.EventName


Expand All @@ -22,6 +17,12 @@ def compute_gb(accel, speed):

@staticmethod
def get_params(candidate, fingerprint=gen_empty_fingerprint(), has_relay=False, car_fw=[]): # pylint: disable=dangerous-default-value
op_params = opParams()
use_lqr = op_params.get('use_lqr')
prius_use_pid = False # op_params.get('prius_use_pid')
corollaTSS2_use_indi = False # op_params.get('corollaTSS2_use_indi')
rav4TSS2_use_indi = op_params.get('rav4TSS2_use_indi')

ret = CarInterfaceBase.get_std_params(candidate, fingerprint, has_relay)

ret.carName = "toyota"
Expand Down
5 changes: 2 additions & 3 deletions selfdrive/controls/controlsd.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,7 @@ def __init__(self, sm=None, pm=None, can_sock=None):
'laneSpeed', 'dynamicCameraOffset', 'modelLongButton'])

self.op_params = opParams()
self.df_manager = dfManager(self.op_params)
self.hide_auto_df_alerts = self.op_params.get('hide_auto_df_alerts')
self.df_manager = dfManager()
self.support_white_panda = self.op_params.get('support_white_panda')
self.last_model_long = False

Expand Down Expand Up @@ -318,7 +317,7 @@ def add_stock_additions_alerts(self, CS):
df_alert = 'dfButtonAlert'
if df_out.is_auto and df_out.last_is_auto:
# only show auto alert if engaged, not hiding auto, and time since lane speed alert not showing
if CS.cruiseState.enabled and not self.hide_auto_df_alerts:
if CS.cruiseState.enabled and not self.op_params.get('hide_auto_df_alerts'):
df_alert += 'Silent'
self.AM.SA_add(df_alert, extra_text_1=df_out.model_profile_text + ' (auto)')
return
Expand Down
2 changes: 1 addition & 1 deletion selfdrive/controls/lib/dynamic_follow/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ def __init__(self, mpc_id):
self.mpc_id = mpc_id
self.op_params = opParams()
self.df_profiles = dfProfiles()
self.df_manager = dfManager(self.op_params)
self.df_manager = dfManager()
self.dmc_v_rel = DistanceModController(k_i=0.042, k_d=0.08, x_clip=[-1, 0, 0.66], mods=[1.15, 1., 0.95])
self.dmc_a_rel = DistanceModController(k_i=0.042 * 1.05, k_d=0.08, x_clip=[-1, 0, 0.33], mods=[1.15, 1., 0.98]) # a_lead loop is 5% faster

Expand Down
6 changes: 3 additions & 3 deletions selfdrive/controls/lib/dynamic_follow/df_manager.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import cereal.messaging as messaging
from common.op_params import opParams
from selfdrive.controls.lib.dynamic_follow.support import dfProfiles
from common.realtime import sec_since_boot

Expand All @@ -14,9 +15,8 @@ class dfReturn:


class dfManager:
def __init__(self, op_params, is_df=False):
self.op_params = op_params
self.is_df = is_df
def __init__(self):
self.op_params = opParams()
self.df_profiles = dfProfiles()
self.sm = messaging.SubMaster(['dynamicFollowButton', 'dynamicFollowData'])
self.button_updated = False
Expand Down
6 changes: 2 additions & 4 deletions selfdrive/controls/lib/lateral_planner.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,6 @@ def __init__(self, CP):
self.y_pts = np.zeros(TRAJECTORY_SIZE)

self.op_params = opParams()
self.alca_nudge_required = self.op_params.get('alca_nudge_required')
self.alca_min_speed = self.op_params.get('alca_min_speed') * CV.MPH_TO_MS

def setup_mpc(self):
self.libmpc = libmpc_py.libmpc
Expand Down Expand Up @@ -107,7 +105,7 @@ def update(self, sm, CP, VM):

# Lane change logic
one_blinker = sm['carState'].leftBlinker != sm['carState'].rightBlinker
below_lane_change_speed = v_ego < self.alca_min_speed
below_lane_change_speed = v_ego < self.op_params.get('alca_min_speed') * CV.MPH_TO_MS

if sm['carState'].leftBlinker:
self.lane_change_direction = LaneChangeDirection.left
Expand All @@ -121,7 +119,7 @@ def update(self, sm, CP, VM):
torque_applied = sm['carState'].steeringPressed and \
((sm['carState'].steeringTorque > 0 and self.lane_change_direction == LaneChangeDirection.left) or
(sm['carState'].steeringTorque < 0 and self.lane_change_direction == LaneChangeDirection.right))
if not self.alca_nudge_required:
if not self.op_params.get('alca_nudge_required'):
torque_applied = True

blindspot_detected = ((sm['carState'].leftBlindspot and self.lane_change_direction == LaneChangeDirection.left) or
Expand Down
3 changes: 1 addition & 2 deletions selfdrive/controls/lib/longcontrol.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,6 @@ def __init__(self, CP, compute_gb, candidate):
self.last_output_gb = 0.0

self.op_params = opParams()
self.enable_dg = self.op_params.get('dynamic_gas')
self.dynamic_gas = DynamicGas(CP, candidate)

def reset(self, v_pid):
Expand All @@ -82,7 +81,7 @@ def update(self, active, CS, v_target, v_target_future, a_target, CP, extras):
gas_max = interp(CS.vEgo, CP.gasMaxBP, CP.gasMaxV)
brake_max = interp(CS.vEgo, CP.brakeMaxBP, CP.brakeMaxV)

if self.enable_dg:
if self.op_params.get('dynamic_gas'):
gas_max = self.dynamic_gas.update(CS, extras)

# Update state machine
Expand Down
3 changes: 1 addition & 2 deletions selfdrive/controls/lib/pid.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,6 @@ def update(self, setpoint, measurement, speed=0.0, check_saturation=True, overri
class LongPIDController:
def __init__(self, k_p, k_i, k_d, k_f=1., pos_limit=None, neg_limit=None, rate=100, sat_limit=0.8, convert=None):
self.op_params = opParams()
self.enable_long_derivative = self.op_params.get('enable_long_derivative')
self._k_p = k_p # proportional gain
self._k_i = k_i # integral gain
self._k_d = k_d # derivative gain
Expand Down Expand Up @@ -186,7 +185,7 @@ def update(self, setpoint, measurement, speed=0.0, check_saturation=True, overri
not freeze_integrator:
self.id = i

if self.enable_long_derivative:
if self.op_params.get('enable_long_derivative'):
if abs(setpoint - self.last_setpoint) / self.rate < self.max_accel_d: # if setpoint isn't changing much
d = self.k_d * (error - self.last_error)
if (self.id > 0 and self.id + d >= 0) or (self.id < 0 and self.id + d <= 0): # if changing integral doesn't make it cross zero
Expand Down
4 changes: 2 additions & 2 deletions selfdrive/loggerd/uploader.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
allow_sleep = bool(os.getenv("UPLOADER_SLEEP", "1"))
force_wifi = os.getenv("FORCEWIFI") is not None
fake_upload = os.getenv("FAKEUPLOAD") is not None
upload_on_hotspot = opParams().get('upload_on_hotspot')
op_params = opParams()


def get_directory_sort(d):
Expand Down Expand Up @@ -226,7 +226,7 @@ def uploader_fn(exit_event):

d = None
on_hotspot = is_on_hotspot()
if (on_hotspot and upload_on_hotspot) or not on_hotspot:
if (on_hotspot and op_params.get('upload_on_hotspot')) or not on_hotspot:
d = uploader.next_file_to_upload(with_raw=allow_raw_upload and on_wifi and offroad)

if d is None: # Nothing to upload
Expand Down

0 comments on commit 848e250

Please sign in to comment.