Accompanying source code for my bachelor's thesis "Animated Sparse Voxel Octrees".

Course Website for HY475 Robotic Navigation '21. CSD-UOC

C++ Master Class

A state of the art clock-based spiking neural network simulator

Safely convert between arithmetic types

Personal fork of BSim

personal website

Personal fork of genn-team/genn for benchmarking purposes

Personal fork of NeuronGPU

startedAnthonyCalandra/modern-cpp-features

started time in 12 days

push eventdenniskb/genn

commit sha d9fc736b3399e575759805b017273ec7a1e384cc

Fix Brunel scaling

push time in 2 months

push eventdenniskb/denniskb.github.io

commit sha 2e24523c5e79b1fda1c9938d2ed6978720397829

Update paper title and venue

push time in 2 months

issue closeddenniskb/hy475

```
// Part 1
function ff(pos, obs) {
var dir = math.normalize(math.subtract(dst, pos));
for (var o of obs) {
var dirObs = math.subtract(pos, o[0]);
var strength = math.max(0, o[1] + 40 - math.norm(dirObs)) / 40;
dir = math.add(dir, math.multiply(math.normalize(dirObs), strength));
}
return math.normalize(dir);
}
function plan(obs) {
var path = [[50, 50]];
while (math.distance(path.back(), dst) > 10) {
path.push(math.add(path.back(), ff(path.back(), obs)));
}
return path;
}
// Part 2
if (typeof this.x === 'undefined') {
this.x = 50;
this.y = 50;
this.dir = 0;
}
if (math.distance([this.x, this.y], dst) < 10) return [0, 0];
// Treat every measurement as a small obstacle
var obs = [];
for (var p of pcl) {
obs.push([p, 10]);
}
var ffDir = ff([this.x, this.y], obs);
var ffDirRad = math.atan2(-ffDir[1], ffDir[0]);
var u;
if (this.dir > ffDirRad) {
u = [-1, 1];
this.dir -= math.PI/120;
} else {
u = [1, 1];
this.dir += math.PI/120;
}
this.x += math.cos(this.dir);
this.y -= math.sin(this.dir);
return u;
```

closed time in 2 months

denniskbpush eventdenniskb/genn

commit sha e8eb8ca2e2628b3dd8e00e6e8795c1f3eaab0f0f

Add Synth model

push time in 2 months

push eventdenniskb/genn

commit sha b86f91a54bfd8c783150bf47bb299d2a04ecc0c6

Increase max. network size for Brunel(+)

push time in 2 months

push eventdenniskb/genn

commit sha 2c2adc594e12d15658f0a44e26b74568a1de184a

Optimize matrix connect. type for Brunel

push time in 2 months

created repositorydenniskb/genn

Personal fork of genn-team/genn for benchmarking purposes

created time in 2 months

issue openeddenniskb/hy475

```
// Part 1
function ff(pos, obs) {
var dir = math.normalize(math.subtract(dst, pos));
for (var o of obs) {
var dirObs = math.subtract(pos, o[0]);
var strength = math.max(0, o[1] + 40 - math.norm(dirObs)) / 40;
dir = math.add(dir, math.multiply(math.normalize(dirObs), strength));
}
return math.normalize(dir);
}
function plan(obs) {
var path = [[50, 50]];
while (math.distance(path.back(), dst) > 10) {
path.push(math.add(path.back(), ff(path.back(), obs)));
}
return path;
}
// Part 2
if (typeof this.x === 'undefined') {
this.x = 50;
this.y = 50;
this.dir = 0;
}
if (math.distance([this.x, this.y], dst) < 10) return [0, 0];
// Treat every measurement as a small obstacle
var obs = [];
for (var p of pcl) {
obs.push([p, 10]);
}
var ffDir = ff([this.x, this.y], obs);
var ffDirRad = math.atan2(-ffDir[1], ffDir[0]);
var u;
if (this.dir > ffDirRad) {
u = [-1, 1];
this.dir -= math.PI/120;
} else {
u = [1, 1];
this.dir += math.PI/120;
}
this.x += math.cos(this.dir);
this.y -= math.sin(this.dir);
return u;
```

created time in 2 months

issue closeddenniskb/hy475

```
var N = 5;
if (typeof this.particles === 'undefined') {
this.particles = [];
for (var i = 0; i < N; i++) {
var grid = new Array(1024);
for (var j = 0; j < 1024; j++) grid[j] = [0, 0];
this.particles.push([1/N, 50, 200, 0, grid]);
}
}
var total = 0;
for (var p of this.particles) {
p[3] += math.normrnd(deltaDir, 0.1 * math.abs(deltaDir));
p[1] += 2 * math.cos(p[3]);
p[2] -= 2 * math.sin(p[3]);
var worldToEye = math.world2eye(p[1], p[2], p[3]);
var clipToWorld = math.inv(math.multiply(eyeToClip, worldToEye));
var mydepth = math.raycast(p[4], p[1], p[2], clipToWorld);
var mae = math.mean(math.abs(math.subtract(depth, mydepth)));
var w = 10 - math.min(10, mae);
p[0] = w;
total += w;
math.dmf(p[4], depth, worldToEye, eyeToClip);
}
// rest copy-pasted from assign. 3
// except for p[2]->p[0] and clone()
for (var p of this.particles) {
p[0] /= total;
}
var buckets = [];
total = 0;
for (var p of this.particles) {
total += p[0];
buckets.push(total);
}
var newParticles = [];
var r = math.random();
for (var i = 0; i < N; i++) {
newParticles.push(clone(this.particles[buckets.lowerBound(r)]));
r += 1/N;
if (r >= 1) r -= 1;
}
this.particles = newParticles;
return this.particles;
```

There's no "correct" weight formula, this one I found with trial and error but there were many that worked. One student for example simply kept track of the maximum error and then caclulated `w = max_error - error[i]`

in a separate loop, which worked great too. Whatever formula you come up with it also has to harmonize well with the randomization!

closed time in 2 months

denniskbpush eventdenniskb/denniskb.github.io

commit sha b6aba09ba904f356f44056636a2c18aab4d691b6

Add video presentation for multi-gpu Spice

push time in 2 months

issue closeddenniskb/hy475

Hello, I sent you an email regarding my grade on Monday but I did not get an answer. I got 0 on assignment 5 while I had very similar solution to the one you uploaded. Maybe you didn't receive my submission or went to your spam folder?

Antonis Ntroumpogiannis 4014

closed time in 2 months

drouboissue openeddenniskb/hy475

```
var N = 5;
if (typeof this.particles === 'undefined') {
this.particles = [];
for (var i = 0; i < N; i++) {
var grid = new Array(1024);
for (var j = 0; j < 1024; j++) grid[j] = [0, 0];
this.particles.push([1/N, 50, 200, 0, grid]);
}
}
var total = 0;
for (var p of this.particles) {
p[3] += math.normrnd(deltaDir, 0.1 * math.abs(deltaDir));
p[1] += 2 * math.cos(p[3]);
p[2] -= 2 * math.sin(p[3]);
var worldToEye = math.world2eye(p[1], p[2], p[3]);
var clipToWorld = math.inv(math.multiply(eyeToClip, worldToEye));
var mydepth = math.raycast(p[4], p[1], p[2], clipToWorld);
var mae = math.mean(math.abs(math.subtract(depth, mydepth)));
var w = 10 - math.min(10, mae);
p[0] = w;
total += w;
math.dmf(p[4], depth, worldToEye, eyeToClip);
}
// rest copy-pasted from assign. 3
// except for p[2]->p[0] and clone()
for (var p of this.particles) {
p[0] /= total;
}
var buckets = [];
total = 0;
for (var p of this.particles) {
total += p[0];
buckets.push(total);
}
var newParticles = [];
var r = math.random();
for (var i = 0; i < N; i++) {
newParticles.push(clone(this.particles[buckets.lowerBound(r)]));
r += 1/N;
if (r >= 1) r -= 1;
}
this.particles = newParticles;
return this.particles;
```

There's no "correct" weight formula, this one I found with trial and error but there were many that worked. One student for example simply kept track of the maximum error and then caclulated `w = max_error - error[i]`

in a separate loop, which worked great too. Whatever formula you come up with it also has to harmonize well with the randomization!

created time in 2 months

issue closeddenniskb/hy475

I tried different metrics for depth distance , different N for particles but many times the solution is very bad. Is this normal due to the randomization or should we get near perfect results like in the final picture all the time? I am pretty sure the problem is STEP 4 and clipToworld but still asking to better know what to expect as the perfect solution

closed time in 2 months

CreaperLostissue closeddenniskb/hy475

worldToEye means Eye with respect to World coordinate system?

closed time in 2 months

mcal1xissue closeddenniskb/hy475

No separation between ghosts, all maps identical

Hi, I have tried different solutions for the third step but I don't get the right results. In all of them there is no separation between the ghosts. In the last implementation I wrote, for every particle, I choose a random value for dir,x,y using the function math.normrnd(mu, std), where mu is this.dir,this.x,this.y and std is 0.07 * abs(deltaDir) for dir and stdx and stdy (which I compute separately) for x,y. Is this the correct way of thinking, to solve the problem? I have my doubts about stdx,stdy....

Thank you

closed time in 2 months

grasstr8issue closeddenniskb/hy475

JavaScript pass by value/reference

**Pass by value**: primitive types (`1`

, `-2`

, `3.14`

, `'hello world'`

, `true`

, `false`

)
**Pass by reference**: everything else (arrays `[]`

, objects `{}`

, ...)

Meaning, if you initialize your particles with `this.grid`

(which is an array after all), all your particles will share the same grid, meaning there will exist only one grid/map and it will be 'integrated' `N`

times for every update step (once per particle).

closed time in 2 months

denniskbissue closeddenniskb/hy475

```
var N = 100;
var NOISE = 2.5;
if (typeof this.particles === 'undefined') {
this.particles = [];
for (var i = 0; i < N; i++)
this.particles.push([math.random(0, 500), math.random(0, 500), 1/N]);
}
// Update particles (move with airplane)
this.particles.forEach(function(p) {
p[0] += dx + math.random(-NOISE, NOISE);
p[1] += dy + math.random(-NOISE, NOISE);
for (var i = 0; i < 2; i++) {
if (p[i] < 0) p[i] += 500;
if (p[i] >= 500) p[i] -= 500;
}
});
// Calculate weights, normalize
var total = 0;
this.particles.forEach(function(p) {
var pAlt = altSea - terrain.at(p[0], p[1]);
var w = 250 - math.abs(altGround - pAlt);
p[2] = w;
total += w;
});
this.particles.forEach(function(p) {
p[2] /= total;
});
// Resample particles (low-variance resampler)
var buckets = [];
var total = 0;
this.particles.forEach(function(p) {
total += p[2];
buckets.push(total);
});
var newParticles = [];
var r = math.random();
for (var i = 0; i < N; i++) {
var particleID = buckets.lowerBound(r);
var particle = this.particles[particleID];
newParticles.push(particle.clone());
r += 1/N;
if (r >= 1) r -= 1;
}
this.particles = newParticles;
return this.particles;
```

closed time in 2 months

denniskbissue closeddenniskb/hy475

```
if (typeof this.grid === 'undefined') {
this.grid = new Array(4900);
for (var i = 0; i < 4900; i++)
this.grid[i] = [0, 0];
}
for (var y = 0; y < 70; y++) {
for (var x = 0; x < 70; x++) {
var gridWorld = [10 * x + 5, 10 * y + 5, 1];
var gridEye = math.multiply(worldToEye, gridWorld);
var gridClip = math.multiply(eyeToClip, gridEye);
if (gridClip[0] < -gridClip[2] || gridClip[0] >= gridClip[2] || gridClip[1] < -gridClip[2])
continue;
var xNDC = gridClip[0] / gridClip[2];
var xScreen = math.floor((xNDC + 1) / 2 * depth.length);
var sd = depth[xScreen] - -gridEye[1];
if (sd >= -20) {
var i = x + y * 70;
this.grid[i][0] += math.min(20, sd);
this.grid[i][1]++;
}
} // end for x
} // end for y
return this.grid;
```

closed time in 2 months

denniskbissue commentdenniskb/hy475

De-activate re-sampling for now (simply comment out `this.particles = newParticles;`

. Your particles should spread out (and the maps should be different). Is this the case? If, as you say, the maps are the same, perhaps you didn't generate an independent map per particle (initialization)? Once you activate re-sampling again ofc. you want your particles to converge to the ground truth but it's a good check.

Yes the grid (array) is passed by reference. Only primitive types are passed by value in JS, everything else is by reference.

comment created time in 2 months

issue commentdenniskb/hy475

`worldToEye`

is a 3x3 matrix which transforms a point from world space to (the depth sensor's) eye space.

comment created time in 2 months

issue commentdenniskb/hy475

You should be able to get fairly close, my metric isn't that complex (just took some playing around to find it) and a bunch of students did it as well (with different metrics). Of course you should be sure that your code is bug free before you waste time tuning your weights. A good sanity check is step 3 'randomization': notice that the particles are spread out **and** that each particle's map is *different*.

comment created time in 2 months

issue commentdenniskb/hy475

Forget about the concrete formula for now. You're adding (hopefully) *different* random numbers to the locations of 5 *independent* (?) particles, yet they end up identical. Either

- the randomization isn't big enough. You could plug in a crazy value to test this. Are you getting at least separation from the ground truth bot?
- you don't draw different random numbers, or
- your particles aren't independent for some reason

The fact that you mention `this.x`

, `this.y`

, etc. makes me think it has something to do with the last point. By implementing step 2 you should've gotten rid of all your `this.x`

, `this.y`

, ... state variables and only be left with `this.particles`

(or however you call your particles array). Look at assign. 3, there is no `this.x`

or `this.y`

either--the x and y are stored per particle.

comment created time in 2 months

issue openeddenniskb/hy475

JavaScript pass by value/reference

**Pass by value**: primitive types (`1`

, `-2`

, `3.14`

, `'hello world'`

, `true`

, `false`

)
**Pass by reference**: everything else (arrays `[]`

, objects `{}`

, ...)

created time in 2 months

push eventdenniskb/hy475

commit sha 23b997cd9a283f6fc226b66c87ddc10d2c8cdfe7

Update readme

push time in 3 months

push eventdenniskb/hy475

commit sha 03acfd88de00b13eb649428a06d0649a77408828

Improve figures (assign. 6 wip)

push time in 3 months

push eventdenniskb/hy475

commit sha cbf79870fe37c53645f2703cd77d54ed66976782

Typos (assign. 6 wip)

commit sha ed4204d795b6460764b7960c71d51765517d6a94

Merge branch 'master' of github.com:denniskb/hy475

push time in 3 months

push eventdenniskb/hy475

commit sha c5499d3a44295043a50800c8f553ca3e054251e4

Force jekyll rebuild

push time in 3 months