Skip to content

Commit 7e6903b

Browse files
authored
locationd, paramsd: Check conditions before updating kalman filters (commaai#23789)
* update filters only when all messages are alivbe and valid * update message valid and fix unit test * update refs * move check outside loop * modify fake message fn in test * deprecate inputsOK and resolve PR comments * avoid double looped list comprehension * follow import conventions * modify paramsd valid to only be invalid in case of commIssue * update refs
1 parent 87849f9 commit 7e6903b

File tree

5 files changed

+55
-36
lines changed

5 files changed

+55
-36
lines changed

cereal

selfdrive/locationd/locationd.cc

+8-6
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,6 @@ void Localizer::input_fake_gps_observations(double current_time) {
262262

263263
void Localizer::handle_gps(double current_time, const cereal::GpsLocationData::Reader& log) {
264264
// ignore the message if the fix is invalid
265-
266265
bool gps_invalid_flag = (log.getFlags() % 2 == 0);
267266
bool gps_unreasonable = (Vector2d(log.getAccuracy(), log.getVerticalAccuracy()).norm() >= SANE_GPS_UNCERTAINTY);
268267
bool gps_accuracy_insane = ((log.getVerticalAccuracy() <= 0) || (log.getSpeedAccuracy() <= 0) || (log.getBearingAccuracyDeg() <= 0));
@@ -462,9 +461,9 @@ kj::ArrayPtr<capnp::byte> Localizer::get_message_bytes(MessageBuilder& msg_build
462461
{
463462
cereal::Event::Builder evt = msg_builder.initEvent();
464463
evt.setLogMonoTime(logMonoTime);
464+
evt.setValid(inputsOK);
465465
cereal::LiveLocationKalman::Builder liveLoc = evt.initLiveLocationKalman();
466466
this->build_live_location(liveLoc);
467-
liveLoc.setInputsOK(inputsOK);
468467
liveLoc.setSensorsOK(sensorsOK);
469468
liveLoc.setGpsOK(gpsOK);
470469
return msg_builder.toBytes();
@@ -499,12 +498,15 @@ int Localizer::locationd_thread() {
499498

500499
while (!do_exit) {
501500
sm.update();
502-
for (const char* service : service_list) {
503-
if (sm.updated(service) && sm.valid(service)) {
504-
const cereal::Event::Reader log = sm[service];
505-
this->handle_msg(log);
501+
if (sm.allAliveAndValid()){
502+
for (const char* service : service_list) {
503+
if (sm.updated(service)){
504+
const cereal::Event::Reader log = sm[service];
505+
this->handle_msg(log);
506+
}
506507
}
507508
}
509+
508510

509511
if (sm.updated("cameraOdometry")) {
510512
uint64_t logMonoTime = sm["cameraOdometry"].getLogMonoTime();

selfdrive/locationd/paramsd.py

+8-5
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ def handle_log(self, t, which, msg):
6262
yaw_rate_valid = yaw_rate_valid and abs(yaw_rate) < 1 # rad/s
6363

6464
if self.active:
65-
if msg.inputsOK and msg.posenetOK:
65+
if msg.posenetOK:
6666

6767
if yaw_rate_valid:
6868
self.kf.predict_and_observe(t,
@@ -160,10 +160,11 @@ def main(sm=None, pm=None):
160160

161161
while True:
162162
sm.update()
163-
for which in sorted(sm.updated.keys(), key=lambda x: sm.logMonoTime[x]):
164-
if sm.updated[which]:
165-
t = sm.logMonoTime[which] * 1e-9
166-
learner.handle_log(t, which, sm[which])
163+
if sm.all_alive_and_valid():
164+
for which in sorted(sm.updated.keys(), key=lambda x: sm.logMonoTime[x]):
165+
if sm.updated[which]:
166+
t = sm.logMonoTime[which] * 1e-9
167+
learner.handle_log(t, which, sm[which])
167168

168169
if sm.updated['liveLocationKalman']:
169170
x = learner.kf.x
@@ -198,6 +199,8 @@ def main(sm=None, pm=None):
198199
liveParameters.angleOffsetAverageStd = float(P[States.ANGLE_OFFSET])
199200
liveParameters.angleOffsetFastStd = float(P[States.ANGLE_OFFSET_FAST])
200201

202+
msg.valid = sm.all_alive_and_valid()
203+
201204
if sm.frame % 1200 == 0: # once a minute
202205
params = {
203206
'carFingerprint': CP.carFingerprint,

selfdrive/locationd/test/test_locationd.py

+37-23
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@
44
import random
55
import unittest
66
import time
7+
import capnp
78
from cffi import FFI
89

910
from cereal import log
1011
import cereal.messaging as messaging
12+
from cereal.services import service_list
1113
from common.params import Params
1214

1315
from selfdrive.manager.process_config import managed_processes
@@ -103,13 +105,15 @@ def test_posenet_spike(self):
103105
ret = self.localizer_get_msg()
104106
self.assertFalse(ret.liveLocationKalman.posenetOK)
105107

108+
106109
class TestLocationdProc(unittest.TestCase):
107110
MAX_WAITS = 1000
111+
LLD_MSGS = {'gpsLocationExternal', 'cameraOdometry', 'carState', 'sensorEvents', 'liveCalibration'}
108112

109113
def setUp(self):
110114
random.seed(123489234)
111115

112-
self.pm = messaging.PubMaster({'gpsLocationExternal', 'cameraOdometry'})
116+
self.pm = messaging.PubMaster(self.LLD_MSGS)
113117

114118
managed_processes['locationd'].prepare()
115119
managed_processes['locationd'].start()
@@ -127,43 +131,53 @@ def send_msg(self, msg):
127131
waits_left -= 1
128132
time.sleep(0.0001)
129133

130-
def test_params_gps(self):
131-
# first reset params
132-
Params().put('LastGPSPosition', json.dumps({"latitude": 0.0, "longitude": 0.0, "altitude": 0.0}))
133-
134-
lat = 30 + (random.random() * 10.0)
135-
lon = -70 + (random.random() * 10.0)
136-
alt = 5 + (random.random() * 10.0)
134+
def get_fake_msg(self, name, t):
135+
try:
136+
msg = messaging.new_message(name)
137+
except capnp.lib.capnp.KjException:
138+
msg = messaging.new_message(name, 0)
137139

138-
for _ in range(1000): # because of kalman filter, send often
139-
msg = messaging.new_message('gpsLocationExternal')
140-
msg.logMonoTime = 0
140+
if name == "gpsLocationExternal":
141141
msg.gpsLocationExternal.flags = 1
142142
msg.gpsLocationExternal.verticalAccuracy = 1.0
143143
msg.gpsLocationExternal.speedAccuracy = 1.0
144144
msg.gpsLocationExternal.bearingAccuracyDeg = 1.0
145145
msg.gpsLocationExternal.vNED = [0.0, 0.0, 0.0]
146-
msg.gpsLocationExternal.latitude = lat
147-
msg.gpsLocationExternal.longitude = lon
148-
msg.gpsLocationExternal.altitude = alt
149-
self.send_msg(msg)
150-
151-
for _ in range(250): # params is only written so often
152-
msg = messaging.new_message('cameraOdometry')
153-
msg.logMonoTime = 0
146+
msg.gpsLocationExternal.latitude = self.lat
147+
msg.gpsLocationExternal.longitude = self.lon
148+
msg.gpsLocationExternal.altitude = self.alt
149+
elif name == 'cameraOdometry':
154150
msg.cameraOdometry.rot = [0.0, 0.0, 0.0]
155151
msg.cameraOdometry.rotStd = [0.0, 0.0, 0.0]
156152
msg.cameraOdometry.trans = [0.0, 0.0, 0.0]
157153
msg.cameraOdometry.transStd = [0.0, 0.0, 0.0]
158-
self.send_msg(msg)
154+
msg.logMonoTime = t
155+
return msg
159156

157+
def test_params_gps(self):
158+
# first reset params
159+
Params().delete('LastGPSPosition')
160+
161+
self.lat = 30 + (random.random() * 10.0)
162+
self.lon = -70 + (random.random() * 10.0)
163+
self.alt = 5 + (random.random() * 10.0)
164+
self.fake_duration = 90 # secs
165+
# get fake messages at the correct frequency, listed in services.py
166+
fake_msgs = []
167+
for sec in range(self.fake_duration):
168+
for name in self.LLD_MSGS:
169+
for j in range(int(service_list[name].frequency)):
170+
fake_msgs.append(self.get_fake_msg(name, int((sec + j / service_list[name].frequency) * 1e9)))
171+
172+
for fake_msg in sorted(fake_msgs, key=lambda x: x.logMonoTime):
173+
self.send_msg(fake_msg)
160174
time.sleep(1) # wait for async params write
161175

162176
lastGPS = json.loads(Params().get('LastGPSPosition'))
163177

164-
self.assertAlmostEqual(lastGPS['latitude'], lat, places=3)
165-
self.assertAlmostEqual(lastGPS['longitude'], lon, places=3)
166-
self.assertAlmostEqual(lastGPS['altitude'], alt, places=3)
178+
self.assertAlmostEqual(lastGPS['latitude'], self.lat, places=3)
179+
self.assertAlmostEqual(lastGPS['longitude'], self.lon, places=3)
180+
self.assertAlmostEqual(lastGPS['altitude'], self.alt, places=3)
167181

168182

169183
if __name__ == "__main__":
+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
7e6072a254791e4106a15ecbf94c16f40d54b459
1+
072ee2ba6bca1ea5943fef27b0cc764e40275568

0 commit comments

Comments
 (0)