An LSTM can be used to recognise words from audio data (sequence to one).
I recorded a random sequence of 320 utterances of 5 words (“left”, “right”, “stop”, “go”, and “?” for background noise) with a sampling rate of 16kHz.
The audio data was split into chunks of 512 samples.
The word start and end were identified using a rising and a falling threshold for the root-mean-square value of each chunk.
The logarithm of the Fourier spectrum of each chunk was used as input for the LSTM.
The initial state and output of the LSTM (16 units each) was set to zero.
The final output state of the LSTM was used as input for a fully connected layer with 5 output units followed by a softmax classifier.
The network was trained in a sequence-to-one setting to recognise a word given a sequence of audio chunks as shown in the following figure.
The example was trained using gradient descent with multiplier alpha = 0.001.
The training took seven hours on a CPU.
The sequence-to-one setting handles words of different lengths gracefully.
The resulting example is more minimalistic than the Tensorflow audio recognition example.
In the following video the speech recognition is used to control a robot.
The Gilbert-Johnson-Keerthi (GJK) algorithm is a method for collision detection of convex polyhedra.
The method is used in multiple rigid body simulations for computer games.
Given:
a set of points defining the convex polyhedron A
a set of points defining the convex polyhedron B
The algorithm returns:
the distance of the polyhedra
two closest points
the two simplices (convex subsets of up to 4 points in 3 dimensions) of A and B containing the two closest points
An n-dimensional simplex is the convex hull of n+1 points as shown in the figure below.
The algorithm makes use of the fact, that the distance between two sets A and B is the distance of the Minkowski difference to the origin:
where
The GJK algorithm iteratively updates a simplex until the closest point to the origin is found.
The algorithm iterates using support points.
Given a set M and a vector d, the support point is defined as the furthest point of M in direction d:
The GJK algorithm detects the two closest points of A and B as follows:
Choose any point m in M(A,B) in the Minkowski difference of A and B.
Set the initial simplex w0 to w0={m}.
Let d be the closest point of wk to the origin.
If d s(wk, -d)>=d s(M,-d) then return d.
Set w’k+1=wk∪{s(M,-d)}
Set wk+1 to the smallest simplex in w’k+1 still containing s(M,-d).
Continue with step 3.
Note that step 3 requires finding the closest point of the simplex wk to the origin.
Most implementations of the GJK algorithm seem to use the following approach:
Check if the origin is contained in the 3D simplex.
If not, check whether the origin is near one of the 4 surfaces of the simplex.
If the origin is not in the Delaunay region of any surface, check whether the origin is near one of the 6 edges.
If not in a Delaunay region of an edge, find the closest point of the 4 points.
A much more compact implementation can be obtained using a divide-and-conquer approach:
Let wk={wk0,wk1,…,wkn}
Solve the least squares equation system
If all ti>=0 and t1+t2+…+tn<=1 (or n=0), then wk0+Ht is the closest point.
Otherwise take the closest point of all sub-simplices with n-1 dimensions using the approach above (i.e. recursion).
Note that this approach will visit each edge up to two times, and each point up to three times.
The performance is not optimal, but it makes for a much more concise implementation.
The least squares solution is:
Here follows an implementation in Scheme (GNU Guile).
By using pairs of points from A and B instead of the Minkowski difference, one can keep track of the information required to determine the pair of closest points of A and B (instead of the closest point of M to the origin).
(use-modules(oopgoops)(srfisrfi-1)(srfisrfi-26))(define-method(-(a<list>))"Negate vector."(map-a))(define-method(+(a<list>)(b<list>))"Add two vectors."(map+ab))(define-method(+(a<list>)(b<real>))"Add vector and scalar."(map(cut+<>b)a))(define-method(+(a<real>)(b<list>))"Add vector and scalar."(map(cut+a<>)b))(define-method(-(a<list>)(b<list>))"Subtract a vector from another."(map-ab))(define-method(-(a<list>)(b<real>))"Subtract a vector from another."(map(cut-<>b)a))(define-method(-(a<real>)(b<list>))"Subtract a vector from another."(map(cut-a<>)b))(define-method(*(a<list>)(b<number>))"Multiply a vector with a scalar."(map(cut*<>b)a))(define-method(*(a<number>)(b<list>))"Multiply a scalar with a vector."(map(cut*<>a)b))(define(argopopfunlst)(let*[(vals(mapfunlst))(opval(applyopvals))](list-ref(reverselst)(1-(length(memberopvalvals))))))(define(argminfunlst)(argopminfunlst))(define(argmaxfunlst)(argopmaxfunlst))(define(leave-one-outlst)(map(lambda(i)(append(takelsti)(droplst(1+i))))(iota(lengthlst))))(define(inner-productab)"Compute inner product of two vectors."(reduce+0(map*ab)))(define(normv)"Return norm of a vector."(sqrt(inner-productvv)))(define(transposemat)"Transpose a matrix"(if(null?mat)'()(map(lambda(i)(map(cutlist-ref<>i)mat))(iota(length(carmat))))))(define(dotmatvec)"Multiply a matrix with another matrix or a vector"(map(cutinner-product<>vec)mat))(define(permutationslst)"Return all permutations of list LST. The permutations are ordered so that every alternate permutation is even."(if(zero?(lengthlst))'(())(concatenate(map(lambda(itemindex)(let[(remaining(deleteitemlst))(order(if(even?index)identityreverse))](map(cutconsitem<>)(permutations(orderremaining)))))lst(iota(lengthlst))))))(define(determinantmat)"Compute determinant of a matrix"(let*[(n(lengthmat))(indices(iotan))(perms(permutationsindices))](reduce+0(map(lambda(permk)(*(reduce*1(map(lambda(ji)(list-ref(list-refmatj)i))indicesperm))(if(even?k)1-1)))perms(iota(lengthperms))))))(define(submatrixmatrowcolumn)"Return submatrix with specified ROW and COLUMN removed."(let[(rows(deleterow(iota(lengthmat))))(columns(deletecolumn(iota(length(carmat)))))](map(lambda(j)(map(lambda(i)(list-ref(list-refmatj)i))columns))rows)))(define(inversemat)"Compute inverse of matrix"(let[(det(determinantmat))(indices(iota(lengthmat)))(sgn(lambda(vji)(if(eq?(even?j)(even?i))v(-v))))](map(lambda(j)(map(lambda(i)(sgn(/(determinant(submatrixmatij))det)ji))indices))indices)))(define(least-squaresdesign-matrixobservation)"Least-squares solver"(if(null?design-matrix)'()(let[(design-matrix-transposed(transposedesign-matrix))](dot(inverse(dotdesign-matrix-transposeddesign-matrix))(dotdesign-matrix-transposedobservation)))))(define(support-pointdirectionpoints)"Get outermost point of POINTS in given DIRECTION."(argmax(cutinner-productdirection<>)points))(define(center-of-gravitypoints)"Compute average of given points"(*(reduce+#fpoints)(/1(lengthpoints))))(define(closest-simplex-pointssimplex-asimplex-b)"Determine closest point pair of two simplices"(let*[(observation(-(carsimplex-a)(carsimplex-b)))(design-matrix(-observation(transpose(-(cdrsimplex-a)(cdrsimplex-b)))))(factors(least-squaresdesign-matrixobservation))](if(and(everypositive?factors)(<(reduce+0factors)1))(cons(cons(fold+(carsimplex-a)(map*factors(map(cut-<>(carsimplex-a))(cdrsimplex-a))))(fold+(carsimplex-b)(map*factors(map(cut-<>(carsimplex-b))(cdrsimplex-b)))))(conssimplex-asimplex-b))(argmin(lambda(result)(norm(-(caarresult)(cdarresult))))(mapclosest-simplex-points(leave-one-outsimplex-a)(leave-one-outsimplex-b))))))(define(gjk-algorithmconvex-aconvex-b)"Get pair of closest points of two convex hulls each defined by a set of points"(let[(simplex-a'())(simplex-b'())(closest(cons(center-of-gravityconvex-a)(center-of-gravityconvex-b)))](while#t(let*[(direction(-(carclosest)(cdrclosest)))(candidates(cons(support-point(-direction)convex-a)(support-pointdirectionconvex-b)))](if(>=(+(inner-productdirection(-direction))1e-12)(inner-product(-(carcandidates)(cdrcandidates))(-direction)))(breakclosest))(let[(result(closest-simplex-points(cons(carcandidates)simplex-a)(cons(cdrcandidates)simplex-b)))](set!closest(carresult))(set!simplex-a(cadrresult))(set!simplex-b(cddrresult)))))))
Here an example of two colliding cuboids simulated using this implementation is shown:
Any feedback, comments, and suggestions are welcome.
OpenGL is a powerful cross-platform standard for 3D visualisation.
OpenGL libraries use a domain specific language (shader language) to describe element-wise operations on vertices (vertex shader) and pixel values (fragment shader).
More recent OpenGL versions also support geometry shaders and tesselation shaders (see OpenGL article on Wikipedia).
The learning curve for OpenGL is quite steep at the beginning.
The reason is, that a program to draw a triangle is almost as complex as a program drawing thousands of triangles.
It is also important to add code for retrieving error messages in order to be able to do development.
I haven’t found many minimal examples to understand OpenGL, so I am posting one here.
The example draws a coloured triangle on the screen.
// Minimal OpenGL shader example using OpenGL directly#include<math.h>
#include<stdio.h>
#include<GL/glew.h>
#include<GL/glut.h>constchar*vertexSource="#version 130\n\
in mediump vec3 point;\n\
in mediump vec2 texcoord;\n\
out mediump vec2 UV;\n\
void main()\n\
{\n\
gl_Position = vec4(point, 1);\n\
UV = texcoord;\n\
}";constchar*fragmentSource="#version 130\n\
in mediump vec2 UV;\n\
out mediump vec3 fragColor;\n\
uniform sampler2D tex;\n\
void main()\n\
{\n\
fragColor = texture(tex, UV).rgb;\n\
}";GLuintvao;GLuintvbo;GLuintidx;GLuinttex;GLuintprogram;intwidth=320;intheight=240;voidonDisplay(void){glClearColor(0.0f,0.0f,0.0f,0.0f);glClear(GL_COLOR_BUFFER_BIT);glUseProgram(program);glDrawElements(GL_TRIANGLES,3,GL_UNSIGNED_INT,(void*)0);glutSwapBuffers();}voidonResize(intw,inth){width=w;height=h;glViewport(0,0,(GLsizei)w,(GLsizei)h);}voidprintError(constchar*context){GLenumerror=glGetError();if(error!=GL_NO_ERROR){fprintf(stderr,"%s: %s\n",context,gluErrorString(error));};}voidprintStatus(constchar*step,GLuintcontext,GLuintstatus){GLintresult=GL_FALSE;if(status==GL_COMPILE_STATUS)glGetShaderiv(context,status,&result);elseglGetProgramiv(context,status,&result);if(result==GL_FALSE){charbuffer[1024];if(status==GL_COMPILE_STATUS)glGetShaderInfoLog(context,1024,NULL,buffer);elseglGetProgramInfoLog(context,1024,NULL,buffer);if(buffer[0])fprintf(stderr,"%s: %s\n",step,buffer);};}voidprintCompileStatus(constchar*step,GLuintcontext){printStatus(step,context,GL_COMPILE_STATUS);}voidprintLinkStatus(constchar*step,GLuintcontext){printStatus(step,context,GL_LINK_STATUS);}GLfloatvertices[]={0.5f,0.5f,0.0f,1.0f,1.0f,-0.5f,0.5f,0.0f,0.0f,1.0f,-0.5f,-0.5f,0.0f,0.0f,0.0f};unsignedintindices[]={0,1,2};floatpixels[]={0.0f,0.0f,1.0f,0.0f,1.0f,0.0f,1.0f,0.0f,0.0f,1.0f,1.0f,1.0f};intmain(intargc,char**argv){glutInit(&argc,argv);glutInitDisplayMode(GLUT_DOUBLE|GLUT_RGB);glutInitWindowSize(width,height);glutCreateWindow("mini");glewExperimental=GL_TRUE;glewInit();GLuintvertexShader=glCreateShader(GL_VERTEX_SHADER);glShaderSource(vertexShader,1,&vertexSource,NULL);glCompileShader(vertexShader);printCompileStatus("Vertex shader",vertexShader);GLuintfragmentShader=glCreateShader(GL_FRAGMENT_SHADER);glShaderSource(fragmentShader,1,&fragmentSource,NULL);glCompileShader(fragmentShader);printCompileStatus("Fragment shader",fragmentShader);program=glCreateProgram();glAttachShader(program,vertexShader);glAttachShader(program,fragmentShader);glLinkProgram(program);printLinkStatus("Shader program",program);glGenVertexArrays(1,&vao);glBindVertexArray(vao);glGenBuffers(1,&vbo);glBindBuffer(GL_ARRAY_BUFFER,vbo);glBufferData(GL_ARRAY_BUFFER,sizeof(vertices),vertices,GL_STATIC_DRAW);glGenBuffers(1,&idx);glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,idx);glBufferData(GL_ELEMENT_ARRAY_BUFFER,sizeof(indices),indices,GL_STATIC_DRAW);glVertexAttribPointer(glGetAttribLocation(program,"point"),3,GL_FLOAT,GL_FALSE,5*sizeof(float),(void*)0);glVertexAttribPointer(glGetAttribLocation(program,"texcoord"),2,GL_FLOAT,GL_FALSE,5*sizeof(float),(void*)(3*sizeof(float)));glEnable(GL_DEPTH_TEST);glUseProgram(program);glEnableVertexAttribArray(0);glEnableVertexAttribArray(1);glGenTextures(1,&tex);glActiveTexture(GL_TEXTURE0);glBindTexture(GL_TEXTURE_2D,tex);glUniform1i(glGetUniformLocation(program,"tex"),0);glTexImage2D(GL_TEXTURE_2D,0,GL_RGB,2,2,0,GL_BGR,GL_FLOAT,pixels);glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT);glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);glGenerateMipmap(GL_TEXTURE_2D);glutDisplayFunc(onDisplay);glutReshapeFunc(onResize);glutMainLoop();glDisableVertexAttribArray(1);glDisableVertexAttribArray(0);glBindTexture(GL_TEXTURE_2D,0);glDeleteTextures(1,&tex);glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0);glDeleteBuffers(1,&idx);glBindBuffer(GL_ARRAY_BUFFER,0);glDeleteBuffers(1,&vbo);glBindVertexArray(0);glDeleteVertexArrays(1,&vao);glDetachShader(program,vertexShader);glDetachShader(program,fragmentShader);glDeleteProgram(program);glDeleteShader(vertexShader);glDeleteShader(fragmentShader);return0;}
The example uses the widely supported OpenGL version 3.1 (which has the version tag 130).
You can download, compile, and run the example as follows:
I am quite interested in how simulators such as the Orbiter space simulator are implemented.
A spacecraft can be seen as a rigid object with a moments of inertia tensor.
Without any forces acting on the object, the rotational moment of the object does not change.
In general the moments of inertia tensor causes the direction of the rotation vector to be different at each point in time even if the rotational moment is not changing.
This motion can be numerically simulated using a higher order integration method such as 4th order Runge-Kutta.
Here is a video showing the resulting simulation of a cuboid tumbling in space:
Brian Vincent Mirtich’s thesis demonstrates how to simulate collisions of two convex polyhedra.
Furthermore micro-collisions are used as a simple but powerful method to simulate resting contacts.
If the micro-collisions are sufficietly small, a resting object can be approximated with sufficient accuracy:
One still needs to implement friction (also shown in Mirtich’s thesis) which requires a numerical integral to compute the friction occuring during a micro-collision.
Collisions of polyhedra are demonstrated in Mirtich’s thesis as well, however it might be simpler to make use of the GJK algorithm.
Planetary bodies, spacecraft, and other non-convex objects could be handled by dividing them into multiple convex objects.
It would also be interesting to integrate soft body physics as shown in Rigs of Rods.
However the accuracy of Rigs of Rods is not sufficiently high for space simulation.
E.g. an object tumbling in space would not preserve its momentum.
Update:
In the following examples, dynamic Coloumb friction with the ground is simulated.
Hi, I decided to update the design of my homepage to a responsive HTML design.
This makes the web page look nicer on mobile phones.
I ended up using the Hyde theme which is based on Poole which in turn is based on Jekyll.
I hope you will enjoy the new layout.
Here is a demonstration of what Jekyll can do (also to remind myself).
See Hyde example for more.