Skip to content
This repository was archived by the owner on Jan 9, 2022. It is now read-only.

Commit 90db0a1

Browse files
sshanemitchellgoffpcComma Device
committed
model cruise control button (commaai#453)
* show model cruise control button * handle in update function * model-based pos/speed targets for cruise mpc New model: 09633f87-a95c-4dfd-9992-ffbaa97b269f/600 Whoops Fixed some more stuff longitudinal_planner.update on modelV2 update Retuned MPC Clip speed and position to v_cruise * split MPCs that require different tuning * clean up * ui fixes * fix for cereal * update releases Co-authored-by: mitchellgoffpc <mitchellgoffpc@gmail.com> Co-authored-by: Comma Device <device@comma.ai>
1 parent e622529 commit 90db0a1

File tree

10 files changed

+67
-68
lines changed

10 files changed

+67
-68
lines changed

SA_RELEASES.md

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
Stock Additions Update 2 - 2021-08-15 (0.8.8)
22
===
33
* Update SA to 0.8.8 with new model from upstream!
4+
* Experimental model cruise control button is back
45

56
Stock Additions Update 1 - 2021-08-15 (0.8.7)
67
===

selfdrive/controls/lib/lead_mpc.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ def set_cur_state(self, v, a):
4949
self.cur_state[0].v_ego = v_safe
5050
self.cur_state[0].a_ego = a_safe
5151

52-
def update(self, CS, radarstate, v_cruise):
52+
def update(self, CS, radarstate, modelstate, v_cruise):
5353
v_ego = CS.vEgo
5454
if self.lead_id == 0:
5555
lead = radarstate.leadOne

selfdrive/controls/lib/long_mpc.py

+15-6
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,23 @@
99

1010

1111
class LongitudinalMpc():
12-
def __init__(self):
12+
def __init__(self, mpc_id):
13+
self.mpc_id = mpc_id
14+
1315
self.reset_mpc()
1416
self.last_cloudlog_t = 0.0
1517
self.ts = list(range(10))
1618
self.status = True
17-
self.min_a = -1.2
19+
self.min_a = -1.2 if self.mpc_id == 0 else -3.5
1820
self.max_a = 1.2
1921

2022

2123
def reset_mpc(self):
2224
self.libmpc = libmpc_py.libmpc
23-
self.libmpc.init(0.0, 1.0, 0.0, 50.0, 10000.0)
25+
if self.mpc_id == 0:
26+
self.libmpc.init(0.0, 1.0, 0.0, 50.0, 10000.0)
27+
else:
28+
self.libmpc.init(1.0, 1.0, 0.0, 5.0, 10000.0)
2429

2530
self.mpc_solution = libmpc_py.ffi.new("log_t *")
2631
self.cur_state = libmpc_py.ffi.new("state_t *")
@@ -45,10 +50,14 @@ def set_cur_state(self, v, a):
4550
self.cur_state[0].v_ego = v_safe
4651
self.cur_state[0].a_ego = a_safe
4752

48-
def update(self, carstate, radarstate, v_cruise):
53+
def update(self, carstate, radarstate, modelstate, v_cruise):
4954
v_cruise_clipped = np.clip(v_cruise, self.cur_state[0].v_ego - 10., self.cur_state[0].v_ego + 10.0)
50-
poss = v_cruise_clipped * np.array(T_IDXS[:LON_MPC_N+1])
51-
speeds = v_cruise_clipped * np.ones(LON_MPC_N+1)
55+
if self.mpc_id == 0:
56+
poss = v_cruise_clipped * np.array(T_IDXS[:LON_MPC_N+1])
57+
speeds = v_cruise_clipped * np.ones(LON_MPC_N+1)
58+
else:
59+
poss = np.minimum(np.array(modelstate.position.x)[:LON_MPC_N+1], v_cruise_clipped * np.array(T_IDXS[:LON_MPC_N+1]))
60+
speeds = np.minimum(np.array(modelstate.velocity.x)[:LON_MPC_N+1], v_cruise_clipped)
5261
accels = np.zeros(LON_MPC_N+1)
5362
self.update_with_xva(poss, speeds, accels)
5463

selfdrive/controls/lib/longitudinal_planner.py

+5-34
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,6 @@
1515
from selfdrive.controls.lib.long_mpc import LongitudinalMpc
1616
from selfdrive.controls.lib.drive_helpers import V_CRUISE_MAX, CONTROL_N
1717
from selfdrive.swaglog import cloudlog
18-
# from selfdrive.controls.lib.long_mpc_model import LongitudinalMpcModel
19-
# from common.op_params import opParams
2018

2119
LON_MPC_STEP = 0.2 # first step is 0.2s
2220
AWARENESS_DECEL = -0.2 # car smoothly decel at .2m/s^2 when user is distracted
@@ -46,44 +44,17 @@ def limit_accel_in_turns(v_ego, angle_steers, a_target, CP):
4644
return [a_target[0], min(a_target[1], a_x_allowed)]
4745

4846

49-
class ModelMpcHelper:
50-
def __init__(self):
51-
self.model_t = [i ** 2 / 102.4 for i in range(33)] # the timesteps of the model predictions
52-
self.mpc_t = list(range(10)) # the timesteps of what the LongMpcModel class takes in, 1 sec intervels to 10
53-
self.model_t_idx = [sorted(range(len(self.model_t)), key=[abs(idx - t) for t in self.model_t].__getitem__)[0] for idx in self.mpc_t] # matches 0 to 9 interval to idx from t
54-
assert len(self.model_t_idx) == 10, 'Needs to be length 10 for mpc'
55-
56-
def convert_data(self, sm):
57-
modelV2 = sm['modelV2']
58-
distances, speeds, accelerations = [], [], []
59-
if not sm.alive['modelV2'] or len(modelV2.position.x) == 0:
60-
return distances, speeds, accelerations
61-
62-
speeds = [modelV2.velocity.x[t] for t in self.model_t_idx]
63-
distances = [modelV2.position.x[t] for t in self.model_t_idx]
64-
for t in self.mpc_t: # todo these three in one loop
65-
if 0 < t < 9:
66-
accelerations.append((speeds[t + 1] - speeds[t - 1]) / 2)
67-
68-
# Extrapolate forward and backward at edges
69-
accelerations.append(accelerations[-1] - (accelerations[-2] - accelerations[-1]))
70-
accelerations.insert(0, accelerations[0] - (accelerations[1] - accelerations[0]))
71-
return distances, speeds, accelerations
72-
73-
7447
class Planner():
7548
def __init__(self, CP):
7649
self.CP = CP
7750
self.mpcs = {}
7851
self.mpcs['lead0'] = LeadMpc(0)
7952
self.mpcs['lead1'] = LeadMpc(1)
80-
self.mpcs['cruise'] = LongitudinalMpc()
81-
# self.mpcs['model'] = LongitudinalMpcModel()
53+
self.mpcs['cruise'] = LongitudinalMpc(0)
54+
self.mpcs['e2e'] = LongitudinalMpc(1)
8255

8356
self.fcw = False
8457
self.fcw_checker = FCWChecker()
85-
# self.op_params = opParams()
86-
self.model_mpc_helper = ModelMpcHelper()
8758

8859
self.v_desired = 0.0
8960
self.a_desired = 0.0
@@ -134,9 +105,9 @@ def update(self, sm, CP):
134105
next_a = np.inf
135106
for key in self.mpcs:
136107
self.mpcs[key].set_cur_state(self.v_desired, self.a_desired)
137-
self.mpcs[key].update(sm['carState'], sm['radarState'], v_cruise)
138-
# TODO: handle model long enabled check
139-
if self.mpcs[key].status and self.mpcs[key].a_solution[5] < next_a: # picks slowest solution from accel in ~0.2 seconds
108+
self.mpcs[key].update(sm['carState'], sm['radarState'], sm['modelV2'], v_cruise)
109+
if (self.mpcs[key].status and self.mpcs[key].a_solution[5] < next_a and # picks slowest solution from accel in ~0.2 seconds
110+
((key == 'e2e' and sm['modelLongButton'].enabled) or key != 'e2e')):
140111
self.longitudinalPlanSource = key
141112
self.v_desired_trajectory = self.mpcs[key].v_solution[:CONTROL_N]
142113
self.a_desired_trajectory = self.mpcs[key].a_solution[:CONTROL_N]

selfdrive/controls/plannerd.py

-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ def plannerd_thread(sm=None, pm=None):
3838
if sm.updated['modelV2']:
3939
lateral_planner.update(sm, CP)
4040
lateral_planner.publish(sm, pm)
41-
if sm.updated['radarState']:
4241
longitudinal_planner.update(sm, CP)
4342
longitudinal_planner.publish(sm, pm)
4443

selfdrive/controls/tests/test_following_distance.py

+14-3
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
import cereal.messaging as messaging
77
from selfdrive.config import Conversions as CV
88
from selfdrive.controls.lib.lead_mpc import LeadMpc
9+
from selfdrive.controls.lib.drive_helpers import LON_MPC_N
10+
from selfdrive.modeld.constants import T_IDXS
911

1012

1113
def RW(v_ego, v_l):
@@ -39,7 +41,7 @@ def run_following_distance_simulation(v_lead, t_end=200.0):
3941
CS.carState.vEgo = v_ego
4042
CS.carState.aEgo = a_ego
4143

42-
# Setup model packet
44+
# Setup radarstate packet
4345
radarstate = messaging.new_message('radarState')
4446
lead = log.RadarState.LeadData.new_message()
4547
lead.modelProb = .75
@@ -49,12 +51,21 @@ def run_following_distance_simulation(v_lead, t_end=200.0):
4951
lead.status = True
5052
radarstate.radarState.leadOne = lead
5153

54+
# Setup model packet
55+
modelstate = messaging.new_message('modelV2')
56+
positions = log.ModelDataV2.XYZTData.new_message()
57+
velocities = log.ModelDataV2.XYZTData.new_message()
58+
positions.x = (v_ego * np.array(T_IDXS[:LON_MPC_N+1])).tolist()
59+
velocities.x = (v_ego * np.ones(LON_MPC_N+1)).tolist()
60+
modelstate.modelV2.position = positions
61+
modelstate.modelV2.velocity = velocities
62+
5263
# Run MPC
5364
mpc.set_cur_state(v_ego, a_ego)
5465
if first: # Make sure MPC is converged on first timestep
5566
for _ in range(20):
56-
mpc.update(CS.carState, radarstate.radarState, 0)
57-
mpc.update(CS.carState, radarstate.radarState, 0)
67+
mpc.update(CS.carState, radarstate.radarState, modelstate.modelV2, 0)
68+
mpc.update(CS.carState, radarstate.radarState, modelstate.modelV2, 0)
5869

5970
# Choose slowest of two solutions
6071
v_ego, a_ego = mpc.mpc_solution.v_ego[5], mpc.mpc_solution.a_ego[5]

selfdrive/test/process_replay/process_replay.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -264,8 +264,8 @@ def ublox_rcv_callback(msg):
264264
ProcessConfig(
265265
proc_name="plannerd",
266266
pub_sub={
267-
"modelV2": ["lateralPlan"], "radarState": ["longitudinalPlan"],
268-
"carState": [], "controlsState": [], 'modelLongButton': [],
267+
"modelV2": ["lateralPlan", "longitudinalPlan"],
268+
"carState": [], "controlsState": [], "radarState": [],
269269
},
270270
ignore=["logMonoTime", "valid", "longitudinalPlan.processingDelay"],
271271
init_callback=get_car_params,

selfdrive/ui/qt/onroad.cc

+22-18
Original file line numberDiff line numberDiff line change
@@ -113,27 +113,26 @@ ButtonsWindow::ButtonsWindow(QWidget *parent) : QWidget(parent) {
113113
QWidget *btns_wrapper = new QWidget;
114114
QHBoxLayout *btns_layout = new QHBoxLayout(btns_wrapper);
115115
btns_layout->setSpacing(0);
116-
btns_layout->setContentsMargins(0, 0, 30, 30);
116+
btns_layout->setContentsMargins(30, 0, 30, 30);
117117

118118
main_layout->addWidget(btns_wrapper, 0, Qt::AlignBottom);
119119

120-
// mlButton = new QPushButton("Toggle Model Long");
121-
// mlButton->setStyleSheet("font-size: 50px; border-radius: 25px; border-color: #b83737;");
122-
// QObject::connect(mlButton, &QPushButton::clicked, [=]() {
123-
// mlButton->setStyleSheet("font-size: 50px; border-radius: 25px; border-color: #37b868;");
124-
// });
125-
// mlButton->setFixedWidth(525);
126-
// mlButton->setFixedHeight(150);
127-
// btns_layout->addStretch();
128-
// btns_layout->addWidget(mlButton, 0, Qt::AlignCenter);
120+
mlButton = new QPushButton("Model Cruise Control");
121+
QObject::connect(mlButton, &QPushButton::clicked, [=]() {
122+
QUIState::ui_state.scene.mlButtonEnabled = !mlEnabled;
123+
});
124+
mlButton->setFixedWidth(575);
125+
mlButton->setFixedHeight(150);
126+
btns_layout->addStretch(4);
127+
btns_layout->addWidget(mlButton, 0, Qt::AlignHCenter | Qt::AlignBottom);
128+
btns_layout->addStretch(3);
129129

130130
dfButton = new QPushButton("DF\nprofile");
131131
QObject::connect(dfButton, &QPushButton::clicked, [=]() {
132132
QUIState::ui_state.scene.dfButtonStatus = dfStatus < 3 ? dfStatus + 1 : 0; // wrap back around
133133
});
134134
dfButton->setFixedWidth(200);
135135
dfButton->setFixedHeight(200);
136-
// btns_layout->addStretch();
137136
btns_layout->addWidget(dfButton, 0, Qt::AlignRight);
138137

139138
setStyleSheet(R"(
@@ -149,20 +148,25 @@ ButtonsWindow::ButtonsWindow(QWidget *parent) : QWidget(parent) {
149148
}
150149

151150
void ButtonsWindow::updateState(const UIState &s) {
152-
updateDfButton(s.scene.dfButtonStatus); // update dynamic follow profile button
153-
// updateMlButton(s.scene.dfButtonStatus); // update model longitudinal button // TODO: add model long back first
154-
}
155-
156-
void ButtonsWindow::updateDfButton(int status) {
157-
if (dfStatus != status) { // updates (resets) on car start, or on button press
158-
dfStatus = status;
151+
if (dfStatus != s.scene.dfButtonStatus) { // update dynamic follow profile button
152+
dfStatus = s.scene.dfButtonStatus;
159153
dfButton->setStyleSheet(QString("font-size: 45px; border-radius: 100px; border-color: %1").arg(dfButtonColors.at(dfStatus)));
160154

161155
MessageBuilder msg;
162156
auto dfButtonStatus = msg.initEvent().initDynamicFollowButton();
163157
dfButtonStatus.setStatus(dfStatus);
164158
QUIState::ui_state.pm->send("dynamicFollowButton", msg);
165159
}
160+
161+
if (mlEnabled != s.scene.mlButtonEnabled) { // update model longitudinal button
162+
mlEnabled = s.scene.mlButtonEnabled;
163+
mlButton->setStyleSheet(QString("font-size: 50px; border-radius: 25px; border-color: %1").arg(mlButtonColors.at(mlEnabled)));
164+
165+
MessageBuilder msg;
166+
auto mlButtonEnabled = msg.initEvent().initModelLongButton();
167+
mlButtonEnabled.setEnabled(mlEnabled);
168+
QUIState::ui_state.pm->send("modelLongButton", msg);
169+
}
166170
}
167171

168172
void OnroadAlerts::updateAlert(const Alert &a, const QColor &color) {

selfdrive/ui/qt/onroad.h

+6-2
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,15 @@ class ButtonsWindow : public QWidget {
2121

2222
private:
2323
QPushButton *dfButton;
24-
// QPushButton *mlButton;
24+
QPushButton *mlButton;
2525

26+
// dynamic follow button
2627
int dfStatus = -1; // always initialize style sheet and send msg
2728
const QStringList dfButtonColors = {"#044389", "#24a8bc", "#fcff4b", "#37b868"};
28-
void updateDfButton(int status);
29+
30+
// model long button
31+
bool mlEnabled = true; // triggers initialization
32+
const QStringList mlButtonColors = {"#b83737", "#37b868"};
2933

3034
public slots:
3135
void updateState(const UIState &s);

selfdrive/ui/ui.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ typedef struct UIScene {
102102

103103
int dfButtonStatus = 0;
104104
int lsButtonStatus = 0;
105-
bool mlButtonEnabled;
105+
bool mlButtonEnabled = false;
106106

107107
mat3 view_from_calib;
108108
bool world_objects_visible;

0 commit comments

Comments
 (0)