From e93932e0c17c85c34a44df5727a82487a824f2fe Mon Sep 17 00:00:00 2001 From: Shane Date: Thu, 23 Jan 2020 14:44:07 -0600 Subject: [PATCH 01/33] move dynamic gas to a separate file --- selfdrive/controls/lib/dynamic_gas.py | 67 +++++++++++++++++++++++ selfdrive/controls/lib/longcontrol.py | 79 +++------------------------ 2 files changed, 74 insertions(+), 72 deletions(-) create mode 100644 selfdrive/controls/lib/dynamic_gas.py diff --git a/selfdrive/controls/lib/dynamic_gas.py b/selfdrive/controls/lib/dynamic_gas.py new file mode 100644 index 00000000000000..790f4ab909ae35 --- /dev/null +++ b/selfdrive/controls/lib/dynamic_gas.py @@ -0,0 +1,67 @@ +from selfdrive.car.toyota.values import CAR as CAR_TOYOTA +from selfdrive.car.honda.values import CAR as CAR_HONDA +from common.numpy_fast import clip, interp + + +class DynamicGas: + def __init__(self, CP, candidate): + self.toyota_candidates = [attr for attr in dir(CAR_TOYOTA) if not attr.startswith("__")] + self.honda_candidates = [attr for attr in dir(CAR_HONDA) if not attr.startswith("__")] + + self.candidate = candidate + self.CP = CP + + def update(self, v_ego, lead_data, mpc_TR): + x, y = [], [] # gasMaxBP, gasMaxV + if self.CP.enableGasInterceptor: # todo: make different profiles for different vehicles + if self.candidate == CAR_TOYOTA.COROLLA: + x = [0.0, 1.4082, 2.80311, 4.22661, 5.38271, 6.16561, 7.24781, 8.28308, 10.24465, 12.96402, 15.42303, 18.11903, 20.11703, 24.46614, 29.05805, 32.71015, 35.76326] + y = [0.2, 0.20443, 0.21592, 0.23334, 0.25734, 0.27916, 0.3229, 0.35, 0.368, 0.377, 0.389, 0.399, 0.411, 0.45, 0.504, 0.558, 0.617] + elif self.candidate == CAR_TOYOTA.RAV4: + x = [0.0, 1.4082, 2.80311, 4.22661, 5.38271, 6.16561, 7.24781, 8.28308, 10.24465, 12.96402, 15.42303, 18.11903, 20.11703, 24.46614, 29.05805, 32.71015, 35.76326] + y = [0.234, 0.237, 0.246, 0.26, 0.279, 0.297, 0.332, 0.354, 0.368, 0.377, 0.389, 0.399, 0.411, 0.45, 0.504, 0.558, 0.617] + elif self.candidate == CAR_HONDA.PILOT_2019: + x = [0.0, 1.4082, 2.80311, 4.22661, 5.38271, 6.16561, 7.24781, 8.28308, 10.24465, 12.96402, 15.42303, 18.11903, 20.11703, 24.46614, 29.05805, 32.71015, 35.76326] + y = [0.234, 0.237, 0.246, 0.26, 0.279, 0.297, 0.332, 0.354, 0.368, 0.377, 0.389, 0.399, 0.411, 0.45, 0.504, 0.558, 0.617] + # else: + # if self.candidate in [CAR_TOYOTA.CAMRY, CAR_TOYOTA.CAMRYH]: + # x = [0.] + # y = [0.5] + + if not x: # if unsupported car, don't use dynamic gas + # x, y = CP.gasMaxBP, CP.gasMaxV # if unsupported car, use stock. + return interp(v_ego, self.CP.gasMaxBP, self.CP.gasMaxV) + + gas = interp(v_ego, x, y) + + if lead_data['status']: # if lead + if v_ego <= 8.9408: # if under 20 mph + x = [0.0, 0.24588812499999999, 0.432818589, 0.593044697, 0.730381365, 1.050833588, 1.3965, 1.714627481] # relative velocity mod + y = [gas * 0.9901, gas * 0.905, gas * 0.8045, gas * 0.625, gas * 0.431, gas * 0.2083, gas * .0667, 0] + gas_mod = -interp(lead_data['v_rel'], x, y) + + x = [0.44704, 1.78816] # lead accel mod + y = [0.0, gas_mod * .4] # maximum we can reduce gas_mod is 40 percent of it + gas_mod -= interp(lead_data['a_lead'], x, y) # reduce the reduction of the above mod (the max this will ouput is the original gas value, it never increases it) + + # x = [TR * 0.5, TR, TR * 1.5] # as lead gets further from car, lessen gas mod # todo: this + # y = [gas_mod * 1.5, gas_mod, gas_mod * 0.5] + # gas_mod += (interp(current_TR, x, y)) + new_gas = gas + gas_mod + + x = [1.78816, 6.0, 8.9408] # slowly ramp mods down as we approach 20 mph + y = [new_gas, (new_gas * 0.8 + gas * 0.2), gas] + gas = interp(v_ego, x, y) + else: + x = [-1.78816, -0.89408, 0, 1.78816, 2.68224] # relative velocity mod + y = [-gas * 0.35, -gas * 0.25, -gas * 0.075, gas * 0.1575, gas * 0.2025] + gas_mod = interp(lead_data['v_rel'], x, y) + + current_TR = lead_data['x_lead'] / v_ego + x = [mpc_TR - 0.22, mpc_TR, mpc_TR + 0.2, mpc_TR + 0.4] + y = [-gas_mod * 0.36, 0.0, gas_mod * 0.15, gas_mod * 0.4] + gas_mod -= interp(current_TR, x, y) + + gas += gas_mod + + return clip(gas, 0.0, 1.0) diff --git a/selfdrive/controls/lib/longcontrol.py b/selfdrive/controls/lib/longcontrol.py index 969bf46dca83a5..1a5aa7801cca2c 100644 --- a/selfdrive/controls/lib/longcontrol.py +++ b/selfdrive/controls/lib/longcontrol.py @@ -2,10 +2,8 @@ from common.numpy_fast import clip, interp from selfdrive.controls.lib.pid_long import PIController from common.travis_checker import travis -from selfdrive.car.toyota.values import CAR as CAR_TOYOTA -from selfdrive.car.honda.values import CAR as CAR_HONDA -from common.op_params import opParams from selfdrive.controls.lib.dynamic_lane_speed import DynamicLaneSpeed +from selfdrive.controls.lib.dynamic_gas import DynamicGas LongCtrlState = log.ControlsState.LongControlState @@ -71,80 +69,20 @@ def __init__(self, CP, compute_gb, candidate): self.v_pid = 0.0 self.last_output_gb = 0.0 - self.op_params = opParams() - self.candidate = candidate - self.toyota_candidates = [attr for attr in dir(CAR_TOYOTA) if not attr.startswith("__")] - self.honda_candidates = [attr for attr in dir(CAR_HONDA) if not attr.startswith("__")] + self.dynamic_gas = DynamicGas(CP, candidate) self.gas_pressed = False self.lead_data = {'v_rel': None, 'a_lead': None, 'x_lead': None, 'status': False} self.track_data = [] self.mpc_TR = 1.8 - self.v_ego = 0.0 self.dynamic_lane_speed = DynamicLaneSpeed() - self.last_v_target = 0 def reset(self, v_pid): """Reset PID controller and change setpoint""" self.pid.reset() self.v_pid = v_pid - def dynamic_gas(self, CP): - x, y = [], [] # gasMaxBP, gasMaxV - if CP.enableGasInterceptor: # todo: make different profiles for different vehicles - if self.candidate == CAR_TOYOTA.COROLLA: - x = [0.0, 1.4082, 2.80311, 4.22661, 5.38271, 6.16561, 7.24781, 8.28308, 10.24465, 12.96402, 15.42303, 18.11903, 20.11703, 24.46614, 29.05805, 32.71015, 35.76326] - y = [0.2, 0.20443, 0.21592, 0.23334, 0.25734, 0.27916, 0.3229, 0.35, 0.368, 0.377, 0.389, 0.399, 0.411, 0.45, 0.504, 0.558, 0.617] - elif self.candidate == CAR_TOYOTA.RAV4: - x = [0.0, 1.4082, 2.80311, 4.22661, 5.38271, 6.16561, 7.24781, 8.28308, 10.24465, 12.96402, 15.42303, 18.11903, 20.11703, 24.46614, 29.05805, 32.71015, 35.76326] - y = [0.234, 0.237, 0.246, 0.26, 0.279, 0.297, 0.332, 0.354, 0.368, 0.377, 0.389, 0.399, 0.411, 0.45, 0.504, 0.558, 0.617] - elif self.candidate == CAR_HONDA.PILOT_2019: - x = [0.0, 1.4082, 2.80311, 4.22661, 5.38271, 6.16561, 7.24781, 8.28308, 10.24465, 12.96402, 15.42303, 18.11903, 20.11703, 24.46614, 29.05805, 32.71015, 35.76326] - y = [0.234, 0.237, 0.246, 0.26, 0.279, 0.297, 0.332, 0.354, 0.368, 0.377, 0.389, 0.399, 0.411, 0.45, 0.504, 0.558, 0.617] - # else: - # if self.candidate in [CAR_TOYOTA.CAMRY, CAR_TOYOTA.CAMRYH]: - # x = [0.] - # y = [0.5] - - if not x: - # x, y = CP.gasMaxBP, CP.gasMaxV # if unsupported car, use stock. - return interp(self.v_ego, CP.gasMaxBP, CP.gasMaxV) - - gas = interp(self.v_ego, x, y) - - if self.lead_data['status']: # if lead - if self.v_ego <= 8.9408: # if under 20 mph - x = [0.0, 0.24588812499999999, 0.432818589, 0.593044697, 0.730381365, 1.050833588, 1.3965, 1.714627481] # relative velocity mod - y = [gas * 0.9901, gas * 0.905, gas * 0.8045, gas * 0.625, gas * 0.431, gas * 0.2083, gas * .0667, 0] - gas_mod = -interp(self.lead_data['v_rel'], x, y) - - x = [0.44704, 1.78816] # lead accel mod - y = [0.0, gas_mod * .4] # maximum we can reduce gas_mod is 40 percent of it - gas_mod -= interp(self.lead_data['a_lead'], x, y) # reduce the reduction of the above mod (the max this will ouput is the original gas value, it never increases it) - - # x = [TR * 0.5, TR, TR * 1.5] # as lead gets further from car, lessen gas mod # todo: this - # y = [gas_mod * 1.5, gas_mod, gas_mod * 0.5] - # gas_mod += (interp(current_TR, x, y)) - new_gas = gas + gas_mod - - x = [1.78816, 6.0, 8.9408] # slowly ramp mods down as we approach 20 mph - y = [new_gas, (new_gas * 0.8 + gas * 0.2), gas] - gas = interp(self.v_ego, x, y) - else: - x = [-1.78816, -0.89408, 0, 1.78816, 2.68224] # relative velocity mod - y = [-gas * 0.35, -gas * 0.25, -gas * 0.075, gas * 0.1575, gas * 0.2025] - gas_mod = interp(self.lead_data['v_rel'], x, y) - - current_TR = self.lead_data['x_lead'] / self.v_ego - x = [self.mpc_TR - 0.22, self.mpc_TR, self.mpc_TR + 0.2, self.mpc_TR + 0.4] - y = [-gas_mod * 0.36, 0.0, gas_mod * 0.15, gas_mod * 0.4] - gas_mod -= interp(current_TR, x, y) - - gas += gas_mod - - return clip(gas, 0.0, 1.0) - - def handle_passable(self, passable): + def handle_passable(self, passable, v_ego): self.gas_pressed = passable['gas_pressed'] self.lead_data['v_rel'] = passable['lead_one'].vRel self.lead_data['a_lead'] = passable['lead_one'].aLeadK @@ -153,18 +91,15 @@ def handle_passable(self, passable): self.mpc_TR = passable['mpc_TR'] self.track_data = [] for track in passable['live_tracks']: - self.track_data.append({'v_lead': self.v_ego + track.vRel, 'y_rel': track.yRel, 'x_lead': track.dRel}) + self.track_data.append({'v_lead': v_ego + track.vRel, 'y_rel': track.yRel, 'x_lead': track.dRel}) def update(self, active, v_ego, brake_pressed, standstill, cruise_standstill, v_cruise, v_target, v_target_future, a_target, CP, passable): """Update longitudinal control. This updates the state machine and runs a PID loop""" - self.last_v_target = v_target - self.v_ego = v_ego - # Actuation limits if not travis: - self.handle_passable(passable) - gas_max = self.dynamic_gas(CP) - # v_target, v_target_future, a_target = self.dynamic_lane_speed.update(v_target, v_target_future, v_cruise, a_target, self.v_ego, self.track_data, self.lead_data) + self.handle_passable(passable, v_ego) + gas_max = self.dynamic_gas.update(v_ego, self.lead_data, self.mpc_TR) + # v_target, v_target_future, a_target = self.dynamic_lane_speed.update(v_target, v_target_future, v_cruise, a_target, v_ego, self.track_data, self.lead_data) else: gas_max = interp(v_ego, CP.gasMaxBP, CP.gasMaxV) brake_max = interp(v_ego, CP.brakeMaxBP, CP.brakeMaxV) From 1c1647f1bf95fc5fe9414651fdada560ee722690 Mon Sep 17 00:00:00 2001 From: Shane Date: Thu, 23 Jan 2020 14:54:18 -0600 Subject: [PATCH 02/33] update readme --- README.md | 6 ++++-- common/op_params.py | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index ba426834e773da..af4bce840da118 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,8 @@ This is my dynamic follow from 0.5, where it changes your TR (following distance Dynamic lane speed ----- +*This feature is disabled until I can figure out how to improve it.* + This is a new feature that reduces your cruising speed if many vehicles around you are significantly slower than you. This works with and without an openpilot-identified lead. Ex.: It will slow you down if traveling in an open lane with cars in adjacent lanes that are slower than you. Or if the lead in front of the lead is slowing down, as well as cars in other lanes far ahead. The most it will slow you down is some average of: (the set speed and the average of the surrounding cars) The more the radar points, the more weight goes to the speeds of surrounding vehicles. ~~Two PID loops to control gas and brakes independently~~ @@ -46,9 +48,9 @@ This is a new feature that reduces your cruising speed if many vehicles around y If you have a Toyota Corolla with a comma pedal, you'll love this addition. Two longitudinal PID loops are set up in `longcontrol.py` so that one is running with comma pedal tuning to control the gas, and the other is running stock non-pedal tuning for better braking control. In the car, this feels miles better than stock openpilot, and nearly as good as your stock Toyota cruise control before you pulled out your DSU! It won't accelerate up to stopped cars and brake at the last moment anymore. -~~Custom wheel offset to reduce lane hugging~~ +Custom wheel offset to reduce lane hugging ----- -***Update**: This also may be removed, I was able to get good results live tuning camera offset. Perhaps angle offset isn't needed?* +***Update**: The performance of this modification is iffy at best, but it definitely is more apparant than just tuning your camera offset value. Removing the immediate angle offset can have some weird oscilating effect when it's windy or on roads with camber (slant to one side). Will be left in, but disabled.* Stock openpilot doesn't seem to be able to identify your car's true angle offset. With the `LaneHugging` module you can specify a custom angle offset to be added to your desired steering angle. Simply find the angle your wheel is at when you're driving on a straight highway. By default, this is disabled, to enable you can: - Use the `opEdit` class in the root directory of openpilot. To use it, simply open an `ssh` shell and enter the commands below: diff --git a/common/op_params.py b/common/op_params.py index 6af7f5691c9fa5..6685c557b3e516 100644 --- a/common/op_params.py +++ b/common/op_params.py @@ -26,7 +26,7 @@ def read_params(params_file, default_params): class opParams: def __init__(self): self.default_params = {'camera_offset': {'default': 0.06, 'allowed_types': [float, int], 'description': 'Your camera offset to use in lane_planner.py', 'live': True}, - 'awareness_factor': {'default': 2.0, 'allowed_types': [float, int], 'description': 'Multiplier for the awareness times', 'live': False}, + 'awareness_factor': {'default': 3.0, 'allowed_types': [float, int], 'description': 'Multiplier for the awareness times', 'live': False}, 'lane_hug_direction': {'default': None, 'allowed_types': [type(None), str], 'description': "(NoneType, 'left', 'right'): Direction of your lane hugging, if present. None will disable this modification", 'live': False}, 'lane_hug_angle_offset': {'default': 0.0, 'allowed_types': [float, int], 'description': ('This is the angle your wheel reads when driving straight at highway speeds.\n' 'Replaces both offsets generated by the calibration learner to help fix lane hugging.\n' From 64192387d744ed5d698aa2fcf41eb47be12afc27 Mon Sep 17 00:00:00 2001 From: Shane Date: Thu, 23 Jan 2020 15:02:56 -0600 Subject: [PATCH 03/33] fix lane hugging mod, it would make all offsets 0 if lane hug mod was disabled. weird behavior should be fixed --- common/op_params.py | 6 +++--- selfdrive/controls/lane_hugging.py | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/common/op_params.py b/common/op_params.py index 6685c557b3e516..56516bbf10fd61 100644 --- a/common/op_params.py +++ b/common/op_params.py @@ -27,12 +27,12 @@ class opParams: def __init__(self): self.default_params = {'camera_offset': {'default': 0.06, 'allowed_types': [float, int], 'description': 'Your camera offset to use in lane_planner.py', 'live': True}, 'awareness_factor': {'default': 3.0, 'allowed_types': [float, int], 'description': 'Multiplier for the awareness times', 'live': False}, - 'lane_hug_direction': {'default': None, 'allowed_types': [type(None), str], 'description': "(NoneType, 'left', 'right'): Direction of your lane hugging, if present. None will disable this modification", 'live': False}, + 'lane_hug_direction': {'default': None, 'allowed_types': [type(None), str], 'description': "(None, 'left', 'right'): Direction of your lane hugging, if present. None will disable this modification", 'live': False}, 'lane_hug_angle_offset': {'default': 0.0, 'allowed_types': [float, int], 'description': ('This is the angle your wheel reads when driving straight at highway speeds.\n' 'Replaces both offsets generated by the calibration learner to help fix lane hugging.\n' 'Enter absolute value here, direction is determined by parameter \'lane_hug_direction\''), 'live': True}, 'following_distance': {'default': None, 'allowed_types': [type(None), float], 'description': 'None has no effect, while setting this to a float will let you change\n' - 'the TR (0.9 to 2.7, if set dynamic follow will be disabled)', 'live': False}, + 'the TR (0.9 to 2.7). If this isn\'t None, dynamic follow will be disabled as well)', 'live': False}, 'alca_nudge_required': {'default': True, 'allowed_types': [bool], 'description': ('Whether to wait for applied torque to the wheel (nudge) before making lane changes. ' 'If False, lane change will occur IMMEDIATELY after signaling'), 'live': False}, 'alca_min_speed': {'default': 25.0, 'allowed_types': [float, int], 'description': 'The minimum speed allowed for an automatic lane change (in MPH)', 'live': False}, @@ -40,7 +40,7 @@ def __init__(self): 'automatically learned steering ratio. If True, it will use the static value in interface.py', 'live': False}, 'use_dynamic_lane_speed': {'default': True, 'allowed_types': [bool], 'description': 'Whether you want openpilot to adjust your speed based on surrounding vehicles', 'live': False}, 'min_dynamic_lane_speed': {'default': 20.0, 'allowed_types': [float, int], 'description': 'The minimum speed to allow dynamic lane speed to operate (in MPH)', 'live': False}, - 'upload_on_hotspot': {'default': False, 'allowed_types': [bool], 'description': 'If False, openpilot will send absolutely no data when connected to your phone\'s hotspot', 'live': False}, + 'upload_on_hotspot': {'default': False, 'allowed_types': [bool], 'description': 'If False, openpilot will not upload driving data while connected to your phone\'s hotspot', 'live': False}, 'reset_integral': {'default': False, 'allowed_types': [bool], 'description': 'This resets integral whenever the longitudinal PID error crosses or is zero.\nShould help it recover from overshoot quicker', 'live': False}} self.params = {} diff --git a/selfdrive/controls/lane_hugging.py b/selfdrive/controls/lane_hugging.py index be0ed46934f4bb..a51b589e1e68c5 100644 --- a/selfdrive/controls/lane_hugging.py +++ b/selfdrive/controls/lane_hugging.py @@ -10,6 +10,7 @@ class LaneHugging: def __init__(self): self.op_params = opParams() self.direction = self.op_params.get('lane_hug_direction', None) # if lane hugging is present and which side. None, 'left', or 'right' + self.angle_offset = abs(self.op_params.get('lane_hug_angle_offset', 0.0)) if isinstance(self.direction, str): self.direction = self.direction.lower() @@ -17,7 +18,7 @@ def modify_offset(self, offset, lane_change_direction, lane_change_state): # negative angles: right # positive angles: left self.angle_offset = abs(self.op_params.get('lane_hug_angle_offset', 0.0)) - if not travis: + if not travis and self.direction is not None: offset = 0.0 starting = LaneChangeState.laneChangeStarting if self.direction == 'left' and ((lane_change_state == starting and lane_change_direction != LaneChangeDirection.left) or lane_change_state != starting): From ee578e8145830228411e97b71cb02294b4c923a0 Mon Sep 17 00:00:00 2001 From: Shane Date: Thu, 23 Jan 2020 15:06:53 -0600 Subject: [PATCH 04/33] update readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index af4bce840da118..b0cf6d383c3b79 100644 --- a/README.md +++ b/README.md @@ -84,7 +84,7 @@ Parameters are stored at `/data/op_params.json` Live tuning support ----- -This has just been added and currently only the `camera_offset` parameter is officially supported. +This has just been added and currently only the `camera_offset` and `lane_hug_angle_offset` parameters are supported. - Just start opEdit with the instructions above and pick a parameter. It will let you know if it supports live tuning, if so, updates will take affect within 5 seconds! - Alternatively, you can use the new opTune module to live tune quicker and easier! It stays in the parameter edit view so you can more easily experiment with values. opTune show below: From 3aa69b78f18d814507ad7826e0b41389d7b3b357 Mon Sep 17 00:00:00 2001 From: Shane Date: Thu, 23 Jan 2020 15:07:23 -0600 Subject: [PATCH 05/33] update readme --- common/op_params.py | 1 + 1 file changed, 1 insertion(+) diff --git a/common/op_params.py b/common/op_params.py index 56516bbf10fd61..b4527260f29cd8 100644 --- a/common/op_params.py +++ b/common/op_params.py @@ -5,6 +5,7 @@ import random from common.travis_checker import travis + def write_params(params, params_file): if not travis: with open(params_file, "w") as f: From ce9eed21e0b4b3cb43629b8c5d3ca43730fb3d51 Mon Sep 17 00:00:00 2001 From: Shane Date: Thu, 23 Jan 2020 15:11:56 -0600 Subject: [PATCH 06/33] shorten annoying message --- op_edit.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/op_edit.py b/op_edit.py index 5a2ef64f910d69..9dadebad387787 100644 --- a/op_edit.py +++ b/op_edit.py @@ -80,10 +80,8 @@ def change_parameter(self, choice): print('- Allowed types: {}'.format(', '.join([str(i).split("'")[1] for i in allowed_types]))) if live: print('- This parameter supports live tuning! Updates should take affect within 5 seconds.\n') - print('It\'s recommended to use the new opTune module! It\'s been streamlined to make live tuning easier and quicker.') - print('Just exit out of this and type:') - print('python op_tune.py') - print('In the directory /data/openpilot\n') + print('Try out opTune! It\'s designed to help you live tune parameters quicker.') + print('Just exit out of this and type: \'python op_tune.py\'') else: print() print('Enter your new value:') From 1e8a7a494f74c587c1661148141074ebd2268992 Mon Sep 17 00:00:00 2001 From: Shane Date: Thu, 23 Jan 2020 15:13:45 -0600 Subject: [PATCH 07/33] test if opedit will crash with this parameter --- common/op_params.py | 3 ++- op_edit.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/common/op_params.py b/common/op_params.py index b4527260f29cd8..67eaa22e98ee8e 100644 --- a/common/op_params.py +++ b/common/op_params.py @@ -42,7 +42,8 @@ def __init__(self): 'use_dynamic_lane_speed': {'default': True, 'allowed_types': [bool], 'description': 'Whether you want openpilot to adjust your speed based on surrounding vehicles', 'live': False}, 'min_dynamic_lane_speed': {'default': 20.0, 'allowed_types': [float, int], 'description': 'The minimum speed to allow dynamic lane speed to operate (in MPH)', 'live': False}, 'upload_on_hotspot': {'default': False, 'allowed_types': [bool], 'description': 'If False, openpilot will not upload driving data while connected to your phone\'s hotspot', 'live': False}, - 'reset_integral': {'default': False, 'allowed_types': [bool], 'description': 'This resets integral whenever the longitudinal PID error crosses or is zero.\nShould help it recover from overshoot quicker', 'live': False}} + 'reset_integral': {'default': False, 'allowed_types': [bool], 'description': 'This resets integral whenever the longitudinal PID error crosses or is zero.\nShould help it recover from overshoot quicker', 'live': False}, + 'test_parameter': {'default': False, 'allowed_types': [bool]}} self.params = {} self.params_file = "/data/op_params.json" diff --git a/op_edit.py b/op_edit.py index 9dadebad387787..66a05b0e253fa7 100644 --- a/op_edit.py +++ b/op_edit.py @@ -81,7 +81,7 @@ def change_parameter(self, choice): if live: print('- This parameter supports live tuning! Updates should take affect within 5 seconds.\n') print('Try out opTune! It\'s designed to help you live tune parameters quicker.') - print('Just exit out of this and type: \'python op_tune.py\'') + print('Just exit out of this and type: \'python op_tune.py\'\n') else: print() print('Enter your new value:') From ffd45c43d060b8e074f176801f2043c81427993e Mon Sep 17 00:00:00 2001 From: Shane Date: Thu, 23 Jan 2020 15:26:00 -0600 Subject: [PATCH 08/33] make op_params more robust when checking if key is live or not and updating params from file --- common/op_params.py | 20 +++++++++++--------- op_edit.py | 1 - 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/common/op_params.py b/common/op_params.py index 67eaa22e98ee8e..7729eca07d6395 100644 --- a/common/op_params.py +++ b/common/op_params.py @@ -117,18 +117,20 @@ def put(self, key, value): self.params.update({key: value}) write_params(self.params, self.params_file) - def get(self, key=None, default=None): # can specify a default value if key doesn't exist - if key is None: + def get(self, key=None, default=None, all_params=False): # can specify a default value if key doesn't exist + self.update_params(key, all_params) + if key is None or all_params: return self.params - if not travis and key in self.default_params and self.default_params[key]['live']: # if is a live param, we want to get updates while openpilot is running - if time.time() - self.last_read_time >= self.read_frequency: # make sure we aren't reading file too often + return self.params[key] if key in self.params else default + + def update_params(self, key, all_params): + if all_params or (key in self.default_params and 'live' in self.default_params[key] and self.default_params[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 self.params, read_status = read_params(self.params_file, self.format_default_params()) if not read_status: - time.sleep(0.01) - self.params, read_status = read_params(self.params_file, self.format_default_params()) # if the file was being written to, retry once - self.last_read_time = time.time() - - return self.params[key] if key in self.params else default + time.sleep(1/100.) + self.params, _ = read_params(self.params_file, self.format_default_params()) # if the file was being written to, retry once + self.last_read_time = time.time() def delete(self, key): if key in self.params: diff --git a/op_edit.py b/op_edit.py index 66a05b0e253fa7..26ad4df396c642 100644 --- a/op_edit.py +++ b/op_edit.py @@ -15,7 +15,6 @@ def run_loop(self): print('Here are your parameters:\n') while True: self.params = self.op_params.get() - 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 i in self.op_params.default_params and self.op_params.default_params[i]['live'] else '' for i in self.params] From cbeedf13443aa8914e3fc9a6ab2328d1ecb0c042 Mon Sep 17 00:00:00 2001 From: Shane Date: Thu, 23 Jan 2020 15:27:12 -0600 Subject: [PATCH 09/33] make op_params more robust when checking if key is live or not and updating params from file --- common/op_params.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/common/op_params.py b/common/op_params.py index 7729eca07d6395..cf45663ce84788 100644 --- a/common/op_params.py +++ b/common/op_params.py @@ -117,14 +117,14 @@ def put(self, key, value): self.params.update({key: value}) write_params(self.params, self.params_file) - def get(self, key=None, default=None, all_params=False): # can specify a default value if key doesn't exist - self.update_params(key, all_params) - if key is None or all_params: + 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 or force_update: return self.params return self.params[key] if key in self.params else default - def update_params(self, key, all_params): - if all_params or (key in self.default_params and 'live' in self.default_params[key] and self.default_params[key]['live']): # if is a live param, we want to get updates while openpilot is running + def update_params(self, key, force_update): + if force_update or (key in self.default_params and 'live' in self.default_params[key] and self.default_params[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 self.params, read_status = read_params(self.params_file, self.format_default_params()) if not read_status: From 2a19f69df068556e6c36aba0550b7fc073c77e88 Mon Sep 17 00:00:00 2001 From: Shane Date: Thu, 23 Jan 2020 15:28:54 -0600 Subject: [PATCH 10/33] test --- common/op_params.py | 1 + 1 file changed, 1 insertion(+) diff --git a/common/op_params.py b/common/op_params.py index cf45663ce84788..20ffd8d690f029 100644 --- a/common/op_params.py +++ b/common/op_params.py @@ -127,6 +127,7 @@ def update_params(self, key, force_update): if force_update or (key in self.default_params and 'live' in self.default_params[key] and self.default_params[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 self.params, read_status = read_params(self.params_file, self.format_default_params()) + print('read op file') if not read_status: time.sleep(1/100.) self.params, _ = read_params(self.params_file, self.format_default_params()) # if the file was being written to, retry once From b3e54a4ffbc300bf6c2ae2689e7073070e1a2f5d Mon Sep 17 00:00:00 2001 From: Shane Date: Thu, 23 Jan 2020 15:30:15 -0600 Subject: [PATCH 11/33] test --- common/op_params.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/op_params.py b/common/op_params.py index 20ffd8d690f029..33f793ea26b514 100644 --- a/common/op_params.py +++ b/common/op_params.py @@ -119,7 +119,7 @@ def put(self, key, value): 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 or force_update: + if key is None: return self.params return self.params[key] if key in self.params else default From b4918848ca013f97d0c9f508eff6c0861dc98960 Mon Sep 17 00:00:00 2001 From: Shane Date: Thu, 23 Jan 2020 15:32:06 -0600 Subject: [PATCH 12/33] fix --- common/op_params.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/op_params.py b/common/op_params.py index 33f793ea26b514..1a315ba7bee093 100644 --- a/common/op_params.py +++ b/common/op_params.py @@ -131,7 +131,7 @@ def update_params(self, key, force_update): if not read_status: time.sleep(1/100.) self.params, _ = read_params(self.params_file, self.format_default_params()) # if the file was being written to, retry once - self.last_read_time = time.time() + self.last_read_time = time.time() def delete(self, key): if key in self.params: From e4181d17f009962093b7b945a62eda6290f559bc Mon Sep 17 00:00:00 2001 From: Shane Date: Thu, 23 Jan 2020 15:35:45 -0600 Subject: [PATCH 13/33] fix bug in op_edit where if user adds parameter to default params but doesn't add live key --- common/op_params.py | 1 - op_edit.py | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/common/op_params.py b/common/op_params.py index 1a315ba7bee093..debc48caca58b5 100644 --- a/common/op_params.py +++ b/common/op_params.py @@ -127,7 +127,6 @@ def update_params(self, key, force_update): if force_update or (key in self.default_params and 'live' in self.default_params[key] and self.default_params[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 self.params, read_status = read_params(self.params_file, self.format_default_params()) - print('read op file') if not read_status: time.sleep(1/100.) self.params, _ = read_params(self.params_file, self.format_default_params()) # if the file was being written to, retry once diff --git a/op_edit.py b/op_edit.py index 26ad4df396c642..aaa1d4c1a7a781 100644 --- a/op_edit.py +++ b/op_edit.py @@ -14,9 +14,9 @@ def run_loop(self): print('Welcome to the opParams command line editor!') print('Here are your parameters:\n') while True: - self.params = self.op_params.get() + self.params = self.op_params.get(force_update=True) 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 i in self.op_params.default_params and self.op_params.default_params[i]['live'] else '' for i in self.params] + live = [' (live!)' if i in self.op_params.default_params and 'live' in self.op_params.default_params[i] and self.op_params.default_params[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)) From 12a01eca37c849fc1938bc8dd08e4afa8d6e482c Mon Sep 17 00:00:00 2001 From: Shane Date: Thu, 23 Jan 2020 15:43:15 -0600 Subject: [PATCH 14/33] make op_params more robust when checking if parameter has additional information, it's more modular --- op_edit.py | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/op_edit.py b/op_edit.py index aaa1d4c1a7a781..b83e7b6f85f484 100644 --- a/op_edit.py +++ b/op_edit.py @@ -63,26 +63,30 @@ def parse_choice(self, choice): def change_parameter(self, choice): while True: chosen_key = list(self.params)[choice] - extra_info = False + has_description = False + has_allowed_types = False live = False if chosen_key in self.op_params.default_params: - extra_info = True - allowed_types = self.op_params.default_params[chosen_key]['allowed_types'] - description = self.op_params.default_params[chosen_key]['description'] - live = self.op_params.default_params[chosen_key]['live'] + has_description = 'description' in self.op_params.default_params[chosen_key] + has_allowed_types = 'allowed_types' in self.op_params.default_params[chosen_key] + live = 'live' in self.op_params.default_params[chosen_key] and self.op_params.default_params[chosen_key]['live'] old_value = self.params[chosen_key] print('Chosen parameter: {}'.format(chosen_key)) print('Current value: {} (type: {})'.format(old_value, str(type(old_value)).split("'")[1])) - if extra_info: - print('\n- Description: {}'.format(description.replace('\n', '\n '))) + + if has_description: + print('\n- Description: {}'.format(self.op_params.default_params[chosen_key]['description'].replace('\n', '\n '))) + if has_allowed_types: + allowed_types = self.op_params.default_params[chosen_key]['allowed_types'] print('- Allowed types: {}'.format(', '.join([str(i).split("'")[1] for i in allowed_types]))) - if live: - print('- This parameter supports live tuning! Updates should take affect within 5 seconds.\n') - print('Try out opTune! It\'s designed to help you live tune parameters quicker.') - print('Just exit out of this and type: \'python op_tune.py\'\n') - else: - print() + if live: + print('- This parameter supports live tuning! Updates should take affect within 5 seconds.\n') + print('Try out opTune! It\'s designed to help you live tune parameters quicker.') + print('Just exit out of this and type: \'python op_tune.py\'\n') + else: + print() + print('Enter your new value:') new_value = input('>> ').strip() if new_value == '': From ed4ec396f42f3219b1c286e8c4441e01d85594bb Mon Sep 17 00:00:00 2001 From: Shane Date: Thu, 23 Jan 2020 15:48:52 -0600 Subject: [PATCH 15/33] better formatting for the displaying of the modular messages --- op_edit.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/op_edit.py b/op_edit.py index b83e7b6f85f484..ad4db769944987 100644 --- a/op_edit.py +++ b/op_edit.py @@ -75,17 +75,18 @@ def change_parameter(self, choice): print('Chosen parameter: {}'.format(chosen_key)) print('Current value: {} (type: {})'.format(old_value, str(type(old_value)).split("'")[1])) + to_print = [] if has_description: - print('\n- Description: {}'.format(self.op_params.default_params[chosen_key]['description'].replace('\n', '\n '))) + to_print.append('- Description: {}'.format(self.op_params.default_params[chosen_key]['description'].replace('\n', '\n '))) if has_allowed_types: allowed_types = self.op_params.default_params[chosen_key]['allowed_types'] - print('- Allowed types: {}'.format(', '.join([str(i).split("'")[1] for i in allowed_types]))) + to_print.append('- Allowed types: {}'.format(', '.join([str(i).split("'")[1] for i in allowed_types]))) if live: - print('- This parameter supports live tuning! Updates should take affect within 5 seconds.\n') - print('Try out opTune! It\'s designed to help you live tune parameters quicker.') - print('Just exit out of this and type: \'python op_tune.py\'\n') - else: - print() + to_print.append('- This parameter supports live tuning! Updates should take affect within 5 seconds.\n') + to_print.append('Try out opTune! It\'s designed to help you live tune parameters quicker.') + to_print.append('Just exit out of this and type: \'python op_tune.py\'') + + print('\n{}\n'.format('\n'.join(to_print))) print('Enter your new value:') new_value = input('>> ').strip() From 34b3292220155e337be205ec3942d0b9db193c0b Mon Sep 17 00:00:00 2001 From: Shane Date: Thu, 23 Jan 2020 15:49:46 -0600 Subject: [PATCH 16/33] this might look more pronounced --- op_edit.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/op_edit.py b/op_edit.py index ad4db769944987..4df6fe37f27e07 100644 --- a/op_edit.py +++ b/op_edit.py @@ -77,12 +77,12 @@ def change_parameter(self, choice): to_print = [] if has_description: - to_print.append('- Description: {}'.format(self.op_params.default_params[chosen_key]['description'].replace('\n', '\n '))) + to_print.append('>> Description: {}'.format(self.op_params.default_params[chosen_key]['description'].replace('\n', '\n '))) if has_allowed_types: allowed_types = self.op_params.default_params[chosen_key]['allowed_types'] - to_print.append('- Allowed types: {}'.format(', '.join([str(i).split("'")[1] for i in allowed_types]))) + to_print.append('>> Allowed types: {}'.format(', '.join([str(i).split("'")[1] for i in allowed_types]))) if live: - to_print.append('- This parameter supports live tuning! Updates should take affect within 5 seconds.\n') + to_print.append('>> This parameter supports live tuning! Updates should take affect within 5 seconds.\n') to_print.append('Try out opTune! It\'s designed to help you live tune parameters quicker.') to_print.append('Just exit out of this and type: \'python op_tune.py\'') From 81ac0b4b1866bc4f7521e4b5bfcb53709a7b544c Mon Sep 17 00:00:00 2001 From: Shane Date: Thu, 23 Jan 2020 15:50:40 -0600 Subject: [PATCH 17/33] fix for parameters with no extra data --- op_edit.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/op_edit.py b/op_edit.py index 4df6fe37f27e07..bf5b0fcbe1f7c4 100644 --- a/op_edit.py +++ b/op_edit.py @@ -77,16 +77,17 @@ def change_parameter(self, choice): to_print = [] if has_description: - to_print.append('>> Description: {}'.format(self.op_params.default_params[chosen_key]['description'].replace('\n', '\n '))) + to_print.append('>> Description: {}'.format(self.op_params.default_params[chosen_key]['description'].replace('\n', '\n '))) if has_allowed_types: allowed_types = self.op_params.default_params[chosen_key]['allowed_types'] - to_print.append('>> Allowed types: {}'.format(', '.join([str(i).split("'")[1] for i in allowed_types]))) + to_print.append('>> Allowed types: {}'.format(', '.join([str(i).split("'")[1] for i in allowed_types]))) if live: - to_print.append('>> This parameter supports live tuning! Updates should take affect within 5 seconds.\n') + to_print.append('>> This parameter supports live tuning! Updates should take affect within 5 seconds.\n') to_print.append('Try out opTune! It\'s designed to help you live tune parameters quicker.') to_print.append('Just exit out of this and type: \'python op_tune.py\'') - print('\n{}\n'.format('\n'.join(to_print))) + if to_print: + print('\n{}\n'.format('\n'.join(to_print))) print('Enter your new value:') new_value = input('>> ').strip() From 634490700f060d174993103ce958a28f89a85d1c Mon Sep 17 00:00:00 2001 From: Shane Date: Thu, 23 Jan 2020 15:51:17 -0600 Subject: [PATCH 18/33] fix --- op_edit.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/op_edit.py b/op_edit.py index bf5b0fcbe1f7c4..5ee14b87f148f1 100644 --- a/op_edit.py +++ b/op_edit.py @@ -77,7 +77,7 @@ def change_parameter(self, choice): to_print = [] if has_description: - to_print.append('>> Description: {}'.format(self.op_params.default_params[chosen_key]['description'].replace('\n', '\n '))) + to_print.append('>> Description: {}'.format(self.op_params.default_params[chosen_key]['description'].replace('\n', '\n '))) if has_allowed_types: allowed_types = self.op_params.default_params[chosen_key]['allowed_types'] to_print.append('>> Allowed types: {}'.format(', '.join([str(i).split("'")[1] for i in allowed_types]))) From 23aa237701c5a415e4f7af7898b2da67dbe77631 Mon Sep 17 00:00:00 2001 From: Shane Date: Thu, 23 Jan 2020 15:52:13 -0600 Subject: [PATCH 19/33] fix --- op_edit.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/op_edit.py b/op_edit.py index 5ee14b87f148f1..bb0b4571e04f6f 100644 --- a/op_edit.py +++ b/op_edit.py @@ -99,7 +99,7 @@ def change_parameter(self, choice): if not status: continue - if extra_info and not any([isinstance(new_value, typ) for typ in allowed_types]): + if live and not any([isinstance(new_value, typ) for typ in allowed_types]): self.message('The type of data you entered ({}) is not allowed with this parameter!\n'.format(str(type(new_value)).split("'")[1])) continue From c575ab3d82148dad4b654b2a1f49ce1769b0adef Mon Sep 17 00:00:00 2001 From: Shane Date: Thu, 23 Jan 2020 15:52:39 -0600 Subject: [PATCH 20/33] woops lol --- op_edit.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/op_edit.py b/op_edit.py index bb0b4571e04f6f..cbdcce7a9f7a34 100644 --- a/op_edit.py +++ b/op_edit.py @@ -99,7 +99,7 @@ def change_parameter(self, choice): if not status: continue - if live and not any([isinstance(new_value, typ) for typ in allowed_types]): + if has_allowed_types and not any([isinstance(new_value, typ) for typ in allowed_types]): self.message('The type of data you entered ({}) is not allowed with this parameter!\n'.format(str(type(new_value)).split("'")[1])) continue From 44bf1d1b740abab3957bf66e6e0d84b9949241dd Mon Sep 17 00:00:00 2001 From: Shane Date: Thu, 23 Jan 2020 15:55:28 -0600 Subject: [PATCH 21/33] test --- op_edit.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/op_edit.py b/op_edit.py index cbdcce7a9f7a34..24a31707b6f552 100644 --- a/op_edit.py +++ b/op_edit.py @@ -77,7 +77,7 @@ def change_parameter(self, choice): to_print = [] if has_description: - to_print.append('>> Description: {}'.format(self.op_params.default_params[chosen_key]['description'].replace('\n', '\n '))) + to_print.append('>> Description: {}'.format(self.op_params.default_params[chosen_key]['description'].replace('\n', '\n '))) if has_allowed_types: allowed_types = self.op_params.default_params[chosen_key]['allowed_types'] to_print.append('>> Allowed types: {}'.format(', '.join([str(i).split("'")[1] for i in allowed_types]))) @@ -100,7 +100,7 @@ def change_parameter(self, choice): continue if has_allowed_types and not any([isinstance(new_value, typ) for typ in allowed_types]): - self.message('The type of data you entered ({}) is not allowed with this parameter!\n'.format(str(type(new_value)).split("'")[1])) + self.message('The type of data you entered ({}) is not allowed with this parameter!'.format(str(type(new_value)).split("'")[1])) continue print('\nOld value: {} (type: {})'.format(old_value, str(type(old_value)).split("'")[1])) From 94545f061f5a0f060760cb0ad511d69cc1553745 Mon Sep 17 00:00:00 2001 From: Shane Date: Thu, 23 Jan 2020 15:57:04 -0600 Subject: [PATCH 22/33] test --- op_edit.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/op_edit.py b/op_edit.py index 24a31707b6f552..fe9503c3cd67d9 100644 --- a/op_edit.py +++ b/op_edit.py @@ -7,7 +7,7 @@ class opEdit: # use by running `python /data/openpilot/op_edit.py` def __init__(self): self.op_params = opParams() self.params = None - self.sleep_time = 1.0 + self.sleep_time = 1.25 self.run_loop() def run_loop(self): @@ -77,7 +77,7 @@ def change_parameter(self, choice): to_print = [] if has_description: - to_print.append('>> Description: {}'.format(self.op_params.default_params[chosen_key]['description'].replace('\n', '\n '))) + to_print.append('>> Description: {}'.format(self.op_params.default_params[chosen_key]['description'].replace('\n', '\n '))) if has_allowed_types: allowed_types = self.op_params.default_params[chosen_key]['allowed_types'] to_print.append('>> Allowed types: {}'.format(', '.join([str(i).split("'")[1] for i in allowed_types]))) From 0b6a301171c4ed7363821d7218a7c8205c214624 Mon Sep 17 00:00:00 2001 From: Shane Date: Thu, 23 Jan 2020 15:59:03 -0600 Subject: [PATCH 23/33] fix opTune --- op_tune.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/op_tune.py b/op_tune.py index e9a3ee22acb7f3..ac289d5f5ed83f 100644 --- a/op_tune.py +++ b/op_tune.py @@ -11,7 +11,7 @@ def __init__(self): def start(self): print('Welcome to the opParams command line live tuner!') - editable = [p for p in self.op_params.get() if p in self.op_params.default_params and self.op_params.default_params[p]['live']] + editable = [p for p in self.op_params.get(force_update=True) if p in self.op_params.default_params and 'live' in self.op_params.default_params[p] and self.op_params.default_params[p]['live']] while True: print('Choose a parameter to tune:') print('\n'.join(['{}. {}'.format(idx + 1, p) for idx, p in enumerate(editable)])) From 412708ac81bb51da771051679ef5d67f1c331a54 Mon Sep 17 00:00:00 2001 From: Shane Date: Thu, 23 Jan 2020 16:10:15 -0600 Subject: [PATCH 24/33] test opTune --- common/op_params.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/op_params.py b/common/op_params.py index debc48caca58b5..18e050598bafee 100644 --- a/common/op_params.py +++ b/common/op_params.py @@ -43,7 +43,7 @@ def __init__(self): 'min_dynamic_lane_speed': {'default': 20.0, 'allowed_types': [float, int], 'description': 'The minimum speed to allow dynamic lane speed to operate (in MPH)', 'live': False}, 'upload_on_hotspot': {'default': False, 'allowed_types': [bool], 'description': 'If False, openpilot will not upload driving data while connected to your phone\'s hotspot', 'live': False}, 'reset_integral': {'default': False, 'allowed_types': [bool], 'description': 'This resets integral whenever the longitudinal PID error crosses or is zero.\nShould help it recover from overshoot quicker', 'live': False}, - 'test_parameter': {'default': False, 'allowed_types': [bool]}} + 'test_parameter': {'default': False, 'description': 'Test', 'live': True}} self.params = {} self.params_file = "/data/op_params.json" From 067fdc4a22c5d0da4bee67bf9157add139d7c7b4 Mon Sep 17 00:00:00 2001 From: Shane Date: Thu, 23 Jan 2020 16:18:22 -0600 Subject: [PATCH 25/33] improve the checking of parameter info --- op_tune.py | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/op_tune.py b/op_tune.py index ac289d5f5ed83f..8513970690d54a 100644 --- a/op_tune.py +++ b/op_tune.py @@ -25,12 +25,24 @@ def start(self): continue self.chosen(editable[choice]) - def chosen(self, param): - allowed_types = self.op_params.default_params[param]['allowed_types'] - print('\nChosen parameter: {}'.format(param)) - print('Current value: {}'.format(self.op_params.get(param))) - print('\n- Description: {}'.format(self.op_params.default_params[param]['description'])) - print('- Allowed types: {}\n'.format(', '.join([str(i).split("'")[1] for i in allowed_types]))) + def chosen(self, chosen_key): + old_value = self.op_params.get(chosen_key) + print('Chosen parameter: {}'.format(chosen_key)) + print('Current value: {} (type: {})'.format(old_value, str(type(old_value)).split("'")[1])) + + has_description = 'description' in self.op_params.default_params[chosen_key] + has_allowed_types = 'allowed_types' in self.op_params.default_params[chosen_key] + + to_print = [] + if has_description: + to_print.append('>> Description: {}'.format(self.op_params.default_params[chosen_key]['description'].replace('\n', '\n '))) + if has_allowed_types: + allowed_types = self.op_params.default_params[chosen_key]['allowed_types'] + to_print.append('>> Allowed types: {}'.format(', '.join([str(i).split("'")[1] for i in allowed_types]))) + + if to_print: + print('\n{}\n'.format('\n'.join(to_print))) + while True: value = input('Enter value: ') if value == '': @@ -45,8 +57,8 @@ def chosen(self, param): if not any([isinstance(value, typ) for typ in allowed_types]): self.message('The type of data you entered ({}) is not allowed with this parameter!\n'.format(str(type(value)).split("'")[1])) continue - self.op_params.put(param, value) - print('Saved {} with value: {}! (type: {})\n'.format(param, value, str(type(value)).split("'")[1])) + self.op_params.put(chosen_key, value) + print('Saved {} with value: {}! (type: {})\n'.format(chosen_key, value, str(type(value)).split("'")[1])) def message(self, msg): print('--------\n{}\n--------'.format(msg), flush=True) From d3221135ae9a1f8aecfbc3ba3540b4bf7e1535c3 Mon Sep 17 00:00:00 2001 From: Shane Date: Thu, 23 Jan 2020 16:19:06 -0600 Subject: [PATCH 26/33] extra check --- op_tune.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/op_tune.py b/op_tune.py index 8513970690d54a..355672763eaf78 100644 --- a/op_tune.py +++ b/op_tune.py @@ -54,7 +54,7 @@ def chosen(self, chosen_key): self.message('Cannot parse input!') continue - if not any([isinstance(value, typ) for typ in allowed_types]): + if has_allowed_types and not any([isinstance(value, typ) for typ in allowed_types]): self.message('The type of data you entered ({}) is not allowed with this parameter!\n'.format(str(type(value)).split("'")[1])) continue self.op_params.put(chosen_key, value) From 44b43cd67c68ffdd62d5f1853a3f985aea1b143b Mon Sep 17 00:00:00 2001 From: Shane Date: Thu, 23 Jan 2020 16:19:41 -0600 Subject: [PATCH 27/33] this might look better --- op_tune.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/op_tune.py b/op_tune.py index 355672763eaf78..b204018a36a0da 100644 --- a/op_tune.py +++ b/op_tune.py @@ -35,7 +35,7 @@ def chosen(self, chosen_key): to_print = [] if has_description: - to_print.append('>> Description: {}'.format(self.op_params.default_params[chosen_key]['description'].replace('\n', '\n '))) + to_print.append('>> Description: {}'.format(self.op_params.default_params[chosen_key]['description'].replace('\n', '\n '))) if has_allowed_types: allowed_types = self.op_params.default_params[chosen_key]['allowed_types'] to_print.append('>> Allowed types: {}'.format(', '.join([str(i).split("'")[1] for i in allowed_types]))) From 2062506e951b1564ca97d92bb89bbad87cfa4bfb Mon Sep 17 00:00:00 2001 From: Shane Date: Thu, 23 Jan 2020 16:21:07 -0600 Subject: [PATCH 28/33] test --- common/op_params.py | 4 ++-- op_edit.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/common/op_params.py b/common/op_params.py index 18e050598bafee..3d600ff3d1bdfe 100644 --- a/common/op_params.py +++ b/common/op_params.py @@ -30,7 +30,7 @@ def __init__(self): 'awareness_factor': {'default': 3.0, 'allowed_types': [float, int], 'description': 'Multiplier for the awareness times', 'live': False}, 'lane_hug_direction': {'default': None, 'allowed_types': [type(None), str], 'description': "(None, 'left', 'right'): Direction of your lane hugging, if present. None will disable this modification", 'live': False}, 'lane_hug_angle_offset': {'default': 0.0, 'allowed_types': [float, int], 'description': ('This is the angle your wheel reads when driving straight at highway speeds.\n' - 'Replaces both offsets generated by the calibration learner to help fix lane hugging.\n' + 'Replaces both offsets from the calibration learner to help fix lane hugging.\n' 'Enter absolute value here, direction is determined by parameter \'lane_hug_direction\''), 'live': True}, 'following_distance': {'default': None, 'allowed_types': [type(None), float], 'description': 'None has no effect, while setting this to a float will let you change\n' 'the TR (0.9 to 2.7). If this isn\'t None, dynamic follow will be disabled as well)', 'live': False}, @@ -43,7 +43,7 @@ def __init__(self): 'min_dynamic_lane_speed': {'default': 20.0, 'allowed_types': [float, int], 'description': 'The minimum speed to allow dynamic lane speed to operate (in MPH)', 'live': False}, 'upload_on_hotspot': {'default': False, 'allowed_types': [bool], 'description': 'If False, openpilot will not upload driving data while connected to your phone\'s hotspot', 'live': False}, 'reset_integral': {'default': False, 'allowed_types': [bool], 'description': 'This resets integral whenever the longitudinal PID error crosses or is zero.\nShould help it recover from overshoot quicker', 'live': False}, - 'test_parameter': {'default': False, 'description': 'Test', 'live': True}} + 'test_parameter': {'default': False, 'live': True}} self.params = {} self.params_file = "/data/op_params.json" diff --git a/op_edit.py b/op_edit.py index fe9503c3cd67d9..8bfbb18da44ab6 100644 --- a/op_edit.py +++ b/op_edit.py @@ -77,7 +77,7 @@ def change_parameter(self, choice): to_print = [] if has_description: - to_print.append('>> Description: {}'.format(self.op_params.default_params[chosen_key]['description'].replace('\n', '\n '))) + to_print.append('>> Description: {}'.format(self.op_params.default_params[chosen_key]['description'].replace('\n', '\n '))) if has_allowed_types: allowed_types = self.op_params.default_params[chosen_key]['allowed_types'] to_print.append('>> Allowed types: {}'.format(', '.join([str(i).split("'")[1] for i in allowed_types]))) From fc9cef6f0b1e740b09a91f0ab4ee039f272863fb Mon Sep 17 00:00:00 2001 From: Shane Date: Thu, 23 Jan 2020 16:22:09 -0600 Subject: [PATCH 29/33] test just allowed types --- common/op_params.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/op_params.py b/common/op_params.py index 3d600ff3d1bdfe..f2b1006e4bbeac 100644 --- a/common/op_params.py +++ b/common/op_params.py @@ -43,7 +43,7 @@ def __init__(self): 'min_dynamic_lane_speed': {'default': 20.0, 'allowed_types': [float, int], 'description': 'The minimum speed to allow dynamic lane speed to operate (in MPH)', 'live': False}, 'upload_on_hotspot': {'default': False, 'allowed_types': [bool], 'description': 'If False, openpilot will not upload driving data while connected to your phone\'s hotspot', 'live': False}, 'reset_integral': {'default': False, 'allowed_types': [bool], 'description': 'This resets integral whenever the longitudinal PID error crosses or is zero.\nShould help it recover from overshoot quicker', 'live': False}, - 'test_parameter': {'default': False, 'live': True}} + 'test_parameter': {'default': False, 'allowed_types': [bool], 'live': True}} self.params = {} self.params_file = "/data/op_params.json" From 7cd1f6b8626497049cb2185a699b87fd4b527321 Mon Sep 17 00:00:00 2001 From: Shane Date: Thu, 23 Jan 2020 16:25:15 -0600 Subject: [PATCH 30/33] update to use message function --- op_edit.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/op_edit.py b/op_edit.py index 8bfbb18da44ab6..90f3d28b74ecf4 100644 --- a/op_edit.py +++ b/op_edit.py @@ -44,11 +44,11 @@ def parse_choice(self, choice): print('Exiting opEdit!') return 'error', choice else: - print('\nNot an integer!\n', flush=True) + self.message('Not an integer!') time.sleep(self.sleep_time) return 'retry', choice if choice not in range(0, len(self.params) + 2): # three for add/delete parameter - print('Not in range!\n', flush=True) + self.message('Not in range!') time.sleep(self.sleep_time) return 'continue', choice @@ -109,9 +109,9 @@ def change_parameter(self, choice): choice = input('[Y/n]: ').lower().strip() if choice == 'y': self.op_params.put(chosen_key, new_value) - print('\nSaved!\n', flush=True) + self.message('Saved!') else: - print('\nNot saved!\n', flush=True) + self.message('Not saved!') time.sleep(self.sleep_time) return @@ -152,7 +152,7 @@ def delete_parameter(self): self.op_params.delete(key) print('\nDeleted!\n') else: - print('\nNot saved!\n', flush=True) + self.message('Not saved!') time.sleep(self.sleep_time) return @@ -184,9 +184,9 @@ def add_parameter(self): choice = input('[Y/n]: ').lower().strip() if choice == 'y': self.op_params.put(key, value) - print('\nSaved!\n', flush=True) + self.message('Saved!') else: - print('\nNot saved!\n', flush=True) + self.message('Not saved!') time.sleep(self.sleep_time) return From 798cf96699a9664646086ac2eddce0b578bef654 Mon Sep 17 00:00:00 2001 From: Shane Date: Thu, 23 Jan 2020 16:25:53 -0600 Subject: [PATCH 31/33] fix --- op_tune.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/op_tune.py b/op_tune.py index b204018a36a0da..1e340d1d718d7b 100644 --- a/op_tune.py +++ b/op_tune.py @@ -55,7 +55,7 @@ def chosen(self, chosen_key): continue if has_allowed_types and not any([isinstance(value, typ) for typ in allowed_types]): - self.message('The type of data you entered ({}) is not allowed with this parameter!\n'.format(str(type(value)).split("'")[1])) + self.message('The type of data you entered ({}) is not allowed with this parameter!'.format(str(type(value)).split("'")[1])) continue self.op_params.put(chosen_key, value) print('Saved {} with value: {}! (type: {})\n'.format(chosen_key, value, str(type(value)).split("'")[1])) From ab5326d8ece48a0bc4ab13a3dc8901eb97a7a797 Mon Sep 17 00:00:00 2001 From: Shane Date: Thu, 23 Jan 2020 16:28:03 -0600 Subject: [PATCH 32/33] fixes --- op_edit.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/op_edit.py b/op_edit.py index 90f3d28b74ecf4..0cec60de0f0a5e 100644 --- a/op_edit.py +++ b/op_edit.py @@ -45,11 +45,9 @@ def parse_choice(self, choice): return 'error', choice else: self.message('Not an integer!') - time.sleep(self.sleep_time) return 'retry', choice if choice not in range(0, len(self.params) + 2): # three for add/delete parameter self.message('Not in range!') - time.sleep(self.sleep_time) return 'continue', choice if choice == len(self.params): # add new parameter @@ -112,7 +110,6 @@ def change_parameter(self, choice): self.message('Saved!') else: self.message('Not saved!') - time.sleep(self.sleep_time) return def parse_input(self, dat): @@ -150,10 +147,9 @@ def delete_parameter(self): choice = input('[Y/n]: ').lower().strip() if choice == 'y': self.op_params.delete(key) - print('\nDeleted!\n') + self.message('Deleted!') else: self.message('Not saved!') - time.sleep(self.sleep_time) return def add_parameter(self): @@ -187,7 +183,6 @@ def add_parameter(self): self.message('Saved!') else: self.message('Not saved!') - time.sleep(self.sleep_time) return def message(self, msg): From 2c4197d7bad62e6480d058a424f06a9b72c2302f Mon Sep 17 00:00:00 2001 From: Shane Date: Thu, 23 Jan 2020 16:42:37 -0600 Subject: [PATCH 33/33] add a description to op_params describing how to use it --- README.md | 2 +- common/op_params.py | 16 ++++++++++++++-- op_edit.py | 1 + 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index b0cf6d383c3b79..13de580f5135e6 100644 --- a/README.md +++ b/README.md @@ -78,7 +78,7 @@ This is a handy tool to change your `opParams` parameters without diving into an cd /data/openpilot python op_edit.py ``` -A list of parameters that you can change are located [here](common/op_params.py#L29). +A list of parameters that you can modify are located [here](common/op_params.py#L42). Parameters are stored at `/data/op_params.json` diff --git a/common/op_params.py b/common/op_params.py index f2b1006e4bbeac..6692a24046b9e3 100644 --- a/common/op_params.py +++ b/common/op_params.py @@ -26,6 +26,19 @@ def read_params(params_file, default_params): class opParams: def __init__(self): + """ + To add your own parameter to opParams in your fork, simply add a new dictionary entry with the name of your parameter and its default value to save to new users' op_params.json file. + The description, allowed_types, and live keys are no longer required but recommended to help users edit their parameters with opEdit and opTune correctly. + - The description value will be shown to users when they use opEdit or opTune to change the value of the parameter. + - The allowed_types key is used to restrict what kinds of values can be entered with opEdit so that users can't reasonably break the fork with unintended behavior. + Limiting the range of floats or integers is still recommended when `.get`ting the parameter. + When a None value is allowed, use `type(None)` instead of None, as opEdit checks the type against the values in the key with `isinstance()`. + - Finally, the live key tells both opParams and opTune that it's a live parameter that will change. Therefore, you must place the `op_params.get()` call in the update function so that it can update. + Here's an example of the minimum required dictionary: + + self.default_params = {'camera_offset': {'default': 0.06}} + """ + self.default_params = {'camera_offset': {'default': 0.06, 'allowed_types': [float, int], 'description': 'Your camera offset to use in lane_planner.py', 'live': True}, 'awareness_factor': {'default': 3.0, 'allowed_types': [float, int], 'description': 'Multiplier for the awareness times', 'live': False}, 'lane_hug_direction': {'default': None, 'allowed_types': [type(None), str], 'description': "(None, 'left', 'right'): Direction of your lane hugging, if present. None will disable this modification", 'live': False}, @@ -42,8 +55,7 @@ def __init__(self): 'use_dynamic_lane_speed': {'default': True, 'allowed_types': [bool], 'description': 'Whether you want openpilot to adjust your speed based on surrounding vehicles', 'live': False}, 'min_dynamic_lane_speed': {'default': 20.0, 'allowed_types': [float, int], 'description': 'The minimum speed to allow dynamic lane speed to operate (in MPH)', 'live': False}, 'upload_on_hotspot': {'default': False, 'allowed_types': [bool], 'description': 'If False, openpilot will not upload driving data while connected to your phone\'s hotspot', 'live': False}, - 'reset_integral': {'default': False, 'allowed_types': [bool], 'description': 'This resets integral whenever the longitudinal PID error crosses or is zero.\nShould help it recover from overshoot quicker', 'live': False}, - 'test_parameter': {'default': False, 'allowed_types': [bool], 'live': True}} + 'reset_integral': {'default': False, 'allowed_types': [bool], 'description': 'This resets integral whenever the longitudinal PID error crosses or is zero.\nShould help it recover from overshoot quicker', 'live': False}} self.params = {} self.params_file = "/data/op_params.json" diff --git a/op_edit.py b/op_edit.py index 0cec60de0f0a5e..e303d0025bc1fb 100644 --- a/op_edit.py +++ b/op_edit.py @@ -90,6 +90,7 @@ def change_parameter(self, choice): print('Enter your new value:') new_value = input('>> ').strip() if new_value == '': + self.message('Exiting this parameter...') return status, new_value = self.parse_input(new_value)