-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsingleIntegrator2.py
337 lines (310 loc) · 13.4 KB
/
singleIntegrator2.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
last mod 9/7/19
handles things considered by the iPDA, or multi bernoulli filters
In this case,
object existence - bernoulli probability
object genuity
object detectability x 8 - one for each camera and lidar
object previous position x - for determining long-term false positives
object previous position y - " "
current number of camera detections
current number of lidar box detections
current number of lidar frag detections
average score of camera detections
label integer, (most likely) unique for each originating msmt
object previous orientation cos
object previous orientation sin
stationaryness (previously directly in genuity score)
"""
import numpy as np
from segment import lasers2use
from singleTracker import prepSample as soPrepObject
from singleTracker import prepMeasurement as soPrepMeasurement
from singleTracker import likelihood as soLikelihood
from singleTracker import update as soUpdate
from singleTracker import likelihoodNewObject as soLikelihoodNewObject
from singleTracker import mlSample as soNewObject
from singleTracker import validSample as soValidSample
from singleTracker import predict as soPredict
from singleTracker import reOrient as soReOrient
from singleTracker import positionDistribution as soPositionDistribution
from singleTracker import report as soReport
from singleTracker import nft as sonft
NOFAKES = False
NODETECT = False
nlasers = len(lasers2use)
ft_pexist = sonft
ft_genuity = sonft+1
ft_detectability_cam = sonft+2
ft_detectability_lidar = sonft+3
ft_oldpos_x = sonft+3+nlasers
ft_oldpos_y = sonft+4+nlasers
ft_nmsmts_cam = sonft+5+nlasers
ft_nmsmts_lidarbox = sonft+6+nlasers
ft_nmsmts_lidarfrag = sonft+7+nlasers
ft_avgscore_cam = sonft+8+nlasers
ft_label = sonft+9+nlasers
ft_thisdetect = sonft+10+nlasers
ft_oldori_x = sonft+11+nlasers
ft_oldori_y = sonft+12+nlasers
ft_genuitymod = sonft+13+nlasers
nft = sonft + 14 + nlasers
ft_detectabilities = {'lidar{:d}'.format(i):ft_detectability_lidar+i
for i in range(nlasers)}
ft_detectabilities['camera'] = ft_detectability_cam
survival_probability = .997
# average probability of object being detected
detectability_steadystate = np.array([.99] + [.98]*nlasers)
detectability_timestep_ratio = .75 # amount it changes in one timestep
if NODETECT:
detectability_timestep_ratio = .99999
# log of rate of new objects, effectively multiplier on occupancy grid
newobject_rate_const = {'camera':-4., 'lidarbox':-2., 'lidarfrag':0.5}
# lidar measurements don't have quality scores -- set to .5 for no effect
# for camera, genuity ratio warps initial genuity (not just set to score)
# this doesn't actually matter anymore, could be removed
newobject_genuity_ratio = {'camera':.5, 'lidarbox':.1, 'lidarfrag':.001}
# reach of discounted moving average
oldobj_discountrate = .8
# fake objects less likely to survive -- effectively mixing in clutter
# this is on top of survival_probability
fakeobjects_survival = .99
detectability_pos2pos = 1 - (1 - detectability_steadystate) * detectability_timestep_ratio
detectability_neg2pos = detectability_steadystate * detectability_timestep_ratio
detectability_times = detectability_pos2pos - detectability_neg2pos
detectability_plus = detectability_neg2pos
def predict(sample):
soPredict(sample[:sonft])
pexist = sample[ft_pexist]
preal = sample[ft_genuity]
oldposx, oldposy = sample[[ft_oldpos_x, ft_oldpos_y]]
posx, posy = sample[:2]
newpexist = pexist*survival_probability
# remove stuff outside of kitti visible zone
if posx < 0 or posx > 55 or abs(posy) > posx*.87+1.87:
newpexist *= .5
if posx < -5 or posx > 63 or abs(posy) > posx*.87+6:
newpexist = 0.
# change genuity based on absolute and relative motion
sample[ft_genuitymod] = 1.
# extra survival (mixing) for fake objects
avgscore = sample[ft_avgscore_cam]
avgscore = avgscore / (avgscore + (1-avgscore)*fakeobjects_survival)
sample[ft_avgscore_cam] = avgscore
# adding in modifier
newpreal = avgscore
# existence is sum of exist*genuine and exist*fake
# #P(exist, genuine) is unchanged
newpexist *= fakeobjects_survival if newpreal == 0 else preal / newpreal
sample[ft_pexist] = newpexist
sample[ft_genuity] = newpreal
# mix all detectabilities
sample[ft_detectability_cam:ft_detectability_cam+nlasers+1] *= detectability_times
sample[ft_detectability_cam:ft_detectability_cam+nlasers+1] += detectability_plus
# discounted moving average for previous positions
currdist = np.hypot(sample[0], sample[1])
sample[ft_oldpos_x] = oldposx*oldobj_discountrate + posx*(1-oldobj_discountrate)
sample[ft_oldpos_y] = oldposy*oldobj_discountrate + posy*(1-oldobj_discountrate)
neworix = (sample[ft_oldori_x]*oldobj_discountrate +
posx/currdist*(1-oldobj_discountrate))
neworiy = (sample[ft_oldori_y]*oldobj_discountrate +
posy/currdist*(1-oldobj_discountrate))
oricorrection = np.hypot(neworix, neworiy)
sample[ft_oldori_x] = neworix/oricorrection
sample[ft_oldori_y] = neworiy/oricorrection
def logOdds(num,den):
return -25. if num < 1e-11 else 25. if den < 1e-11 else np.log(num/den)
# logit(k * logOdds(p))
def bernoulliWarp(p, k):
return 0. if p<1e-11 else 1. if 1-p<1e-11 else 1/(1+(1/p-1)**k)
"""
returns:
prepped object from single tracker
negative log-odds of match (for adding to NLL of match matrix)
"""
def prepObject(sample, visibility, sensorname):
soprep = soPrepObject(sample[:sonft], sensorname)
ft_detectability = ft_detectabilities[sensorname]
pexist, preal, pdetect = sample[[ft_pexist, ft_genuity, ft_detectability]]
pmatch = pexist*pdetect
punmatch = 1 - pexist*pdetect*visibility
podds = -logOdds(pmatch, punmatch)
if sensorname == 'camera' and sample[ft_nmsmts_cam]>0:
discount = min(9, sample[ft_nmsmts_cam])
weightedscore = bernoulliWarp(preal, discount/(discount+1))
else:
weightedscore = preal
return soprep + (weightedscore, podds)
""" 9/15/19 """
def prepObjectCorrector(sample, visibility, sensorname):
ft_detectability = ft_detectabilities[sensorname]
pexist, preal, pdetect = sample[[ft_pexist, ft_genuity, ft_detectability]]
punmatch = 1 - pexist*pdetect*visibility
return logOdds(1., punmatch)
"""
returns:
prepped measurement from single tracker
negative log-odds of msmt match (for adding to NLL of match matrix)
unlike VJ tracking, fake objects are disentangled from the newobjectrate
"""
def prepMeasurement(msmt, newobjectrate, sensorname):
if sensorname == 'camera':
score = msmt[5]
sosensorname = 'camera'
elif type(msmt[0]) == np.ndarray:
sosensorname = 'lidarbox'
score = .5
else:
sosensorname = 'lidarfrag'
score = .5
soprep = soPrepMeasurement(msmt, sosensorname)
newmsmtlik = soLikelihoodNewObject(msmt, sosensorname)
llnewmsmt = np.log(newobjectrate) + newobject_rate_const[sosensorname]
return soprep + (score, llnewmsmt - newmsmtlik)
"""
weights are for determining which objects to prune or report
only their relative values matter, but these are from 0 to 1
using probability of existence * probability of genuity
"""
def postMatchWeight(sample, msmt):
return sample[ft_genuity]
def postObjMissWeight(sample, visibility, sensorname):
ft_detectability = ft_detectabilities[sensorname]
pexist, preal, pdetect = sample[[ft_pexist, ft_genuity, ft_detectability]]
existdetect = pdetect * visibility
pexist = pexist*(1-existdetect) / (pexist*(1-existdetect) + 1-pexist)
return pexist * preal
" returning initial pexist * estimated genuity (which is just msmt score) "
def postMsmtMissWeight(msmt, newobjectrate, sensorname):
if sensorname == 'camera':
return msmt[5]
else:
return 0.
"""
actually negative log likelihood
this already accounts for false positive and negative probabilities
score term is 0 for fully matched or uncertain objects, up to infinite for completely off
"""
def likelihood(preppedsample, preppedmsmt):
genuity, objlogterm = preppedsample[-2:]
score, misslogterm = preppedmsmt[-2:]
scorell = -np.log(genuity*score + (1-genuity)*(1-score))
solik = soLikelihood(preppedsample[:-2], preppedmsmt[:-2])
return solik + scorell + objlogterm + misslogterm
def updateMatch(sample, msmt, sensorname):
newsample = sample.copy()
if sensorname == 'camera':
sosensorname = 'camera'
elif type(msmt[0]) == np.ndarray:
sosensorname = 'lidarbox'
else:
sosensorname = 'lidarfrag'
ft_detectability = ft_detectabilities[sensorname]
newsample[:sonft] = soUpdate(sample[:sonft], msmt, sosensorname)
oldpexist = sample[ft_pexist]
newsample[ft_pexist] = 1.
newsample[ft_detectability] = 1.
if sosensorname == 'camera':
# maintain (moving) average score
# update genuity to reflect change
score = msmt[5]
if NOFAKES:
score = 1.
npreviousdets = sample[ft_nmsmts_cam]
meanscore = sample[ft_avgscore_cam]
meanscore = logOdds(meanscore, 1-meanscore)
score = logOdds(score, 1-score)
avgweight = min(npreviousdets, 9)
newmeanscore = (meanscore*avgweight+score)/(avgweight+1)
newmeanscore = 1/(1+np.exp(-newmeanscore))
newsample[ft_genuity] = newmeanscore / (newmeanscore+(1-newmeanscore)*
sample[ft_genuitymod])
newsample[ft_nmsmts_cam] += 1
newsample[ft_avgscore_cam] = newmeanscore
elif sosensorname == 'lidarbox':
if sample[ft_nmsmts_lidarbox] == 0:
pass # do something to make genuity more likely??
newsample[ft_nmsmts_lidarbox] += 1
else:
newsample[ft_nmsmts_lidarfrag] += 1
### this is very wrong... but might fix the issue of fragments allowing
# false msmts from boxes/camera to exist indefinitely
newsample[ft_pexist] = oldpexist
return newsample
" modify features of object that was not measured this step "
def updateMiss(sample, visibility, sensorname):
newsample = sample.copy()
ft_detectability = ft_detectabilities[sensorname]
pexist, preal, pdetect = sample[[ft_pexist, ft_genuity, ft_detectability]]
existdetect = pdetect * visibility
newsample[ft_pexist] = pexist*(1-existdetect) / (1-pexist*existdetect)
newsample[ft_detectability] = pdetect*(1-visibility) / (1-existdetect)
return newsample
""" assign features to new object
"""
def updateNew(msmt, newobjectrate, sensorname):
newsample = np.zeros(nft)
if sensorname == 'camera':
sosensorname = 'camera'
score = msmt[5]
genuity_ratio = newobject_genuity_ratio['camera']
ft_nmsmts = ft_nmsmts_cam
elif type(msmt[0]) == np.ndarray:
sosensorname = 'lidarbox'
score = .5
genuity_ratio = newobject_genuity_ratio['lidarbox']
ft_nmsmts = ft_nmsmts_lidarbox
else:
sosensorname = 'lidarfrag'
score = .5
genuity_ratio = newobject_genuity_ratio['lidarfrag']
ft_nmsmts = ft_nmsmts_lidarfrag
newsample[:sonft] = soNewObject(soPrepMeasurement(msmt, sosensorname))
newsample[ft_pexist] = 1.
llexistgenu = score * genuity_ratio
llexist = llexistgenu + (1-score)*(1-genuity_ratio)
newsample[ft_genuity] = llexistgenu / llexist
newsample[ft_avgscore_cam] = llexistgenu / llexist
if NOFAKES:
newsample[ft_pexist] = llexistgenu / llexist
newsample[ft_genuity] = 1.
newsample[ft_avgscore_cam] = 1.
newsample[ft_detectability_cam:ft_detectability_cam+nlasers+1] = detectability_steadystate
newsample[ft_detectabilities[sensorname]] = 1.
newsample[ft_nmsmts] = 1
newsample[ft_label] = np.random.randint(int(1e9))
posx,posy = newsample[:2]
newsample[ft_oldpos_x] = posx
newsample[ft_oldpos_y] = posy
newsample[ft_oldori_x] = posx/np.hypot(posx,posy)
newsample[ft_oldori_y] = posy/np.hypot(posx,posy)
newsample[ft_genuitymod] = 1.
if sosensorname != 'camera':
newsample[ft_pexist] = 0.
return newsample
def validSample(sample):
valid = True
if sample[ft_pexist] > 1e-2:
valid &= soValidSample(sample[:sonft])
elif sample[ft_pexist] > 1e-5 and not soValidSample(sample[:sonft]):
print("invalid (but very unlikely) sample")
probability_features = [ft_pexist, ft_genuity, ft_avgscore_cam]
probability_features += range(ft_detectability_cam,ft_detectability_cam+nlasers+1)
valid &= np.all(sample[probability_features] >= 0)
valid &= np.all(sample[probability_features]-1 <= 1e-10)
sample[probability_features] = np.minimum(sample[probability_features], 1)
return valid
def reOrient(sample, newpose):
soReOrient(sample, newpose)
# don't reorient oldpos --- this is used to examine how object's view changes
# reorient oldori, which is used to examine change to angle of view
sample[[ft_oldori_x,ft_oldori_y]] = np.dot(newpose[:2,:2],
sample[[ft_oldori_x,ft_oldori_y]])
positionDistribution = soPositionDistribution
""" returns a float that is the prune weight, and the object in report format """
def report(sample):
return sample[ft_pexist]*sample[ft_genuity], soReport(sample[:sonft])
""" if sample has 0 pexist, no reason to perform calculations (might be empty) """
def shouldUseObject(sample): return sample[ft_pexist]*sample[ft_genuity] > 1e-10