Saturday, April 9, 2016

Deep Dive Into PulsedLight LIDAR-Lite V2 Device

LIDAR-Lite V2 Device is produced by http://pulsedlight3d.com/  This excellent device uses laser to measure the distance. The max distance is 40m. You can use it for both indoor and outdoor. And it is quite convenient to hook it up with open-source platform like Arduino. As the name suggested, its main usage is for LIDAR sensor. You can mount it to a robot or a drone to collect surrounding information. But people also use it for other purposes like generating point cloud.





















This is what we see when open the device. Inside it has two parts: the upper part is the laser diode, which is the transmitter. The lower part is receiver, which can sense the laser light. It works like radar: the upper part sends a laser beam. The beam is reflected by object and the lower part detects the reflection. By correlating the signal sent and signal received, it can identify the peak of correlation and derive the distance based on the peak location. When the object moves farther, the reflection peak will be detected later. 


















The SW library of LIDAR Lite allows to retrieve the correlation values. For each correlation, it has 256 elements. The plot below includes correlation of a few distance measurements. "corr50" curve has the max distance of 4.82m (482cm). Clearly the correlation peak occurs later for larger distance.

lidar_lite_corr.jpg

Monday, April 4, 2016

Quaternion-Based Rotation in OpenGL

This is yet another blog about quaternion. Assuming that we have quaternion [q0, q1, q2, q3]. Can we directly use it to rotation object in OpenGL? The answer is yes. At first, let us normalize this quaternion (written with Java)

     norm = (float) Math.sqrt(q0*q0 + q1*q1 + q2*q2 + q3*q3);
    if (norm == 0)
    {
    return 0;
    }
    else
    {
    q0 = q0 / norm;
    q1 = q1 / norm;
    q2 = q2 / norm;
    q3 = q3 / norm;
    }

Then, glRotatef function in OpenGL is a natural fit for rotation using quaternion.
 
        // Rotate the object according to quaternion
        theta = (float) (Math.acos(q0) * 2);
        
        aNorm = (float) Math.sqrt(q1 * q1 + q2 * q2 + q3 * q3);
        if (aNorm != 0)
        {
        gl.glRotatef(theta*180f/PI, q1/aNorm, q2/aNorm, q3/aNorm);
        }
        else
        {
        gl.glRotatef(theta*180f/PI, q1, q2, q3);
        }

Note that except for quaternion, glRotatef can also be used for rotation along x, y or z axis. In that way, glRotatef needs to be called three times instead of once with quaternion-based method.

    glRotatef(xRot , 1.0, 0.0, 0.0);
    glRotatef(yRot , 0.0, 1.0, 0.0);
    glRotatef(zRot , 0.0, 0.0, 1.0);

Sunday, April 3, 2016

How To Find A Point's Screen Correspondence in OpenGL

To project a point, we can use gluProject function. What the function does is to project a 3D coordinate to the window screen.

GLfloat x,y,z;
double wx,wy,wz,pixel_x,pixel_y;
double modelMatrix[16];
double projMatrix[16];
GLint viewport[4];

// To find the projection
glGetDoublev(GL_MODELVIEW_MATRIX,modelMatrix);
glGetDoublev(GL_PROJECTION_MATRIX,projMatrix);
glGetIntegerv(GL_VIEWPORT,viewport); 
gluProject(x, y, z, modelMatrix, projMatrix, viewport, &wx, &wy, &wz); 

If we want to compare the screen coordinate of the point with the location of the mouse, a little bit extra processing is needed before comparing with event->x()/y(), which are mouse locations.

pixel_x = wx;
pixel_y = viewport[1] + viewport[3] - wy;
dist = (pixel_x - event->x()) * (pixel_x - event->x()) + (pixel_y - event->y()) * (pixel_y - event->y());

How To Zoom In in OpenGL + Qt


Recently I am working on a project which utilizes Qt5.3+OpenGL. I need to implement a function in which by spinning the mouse wheel, the 3D object in OpenGL view can be enlarged or shrunk. After some research, it is found that we can implement the function in two ways: glScaled or  glOrthof.

Approach 1: glScaled

glScaled function scales the x, y, z by a factor. In my implementation, all three axis are scaled by the same factor.

First, the mouse wheel function is written in such a way that wheel rotation changes a zoom factor named zoomScale. After that, it will call the paintGL() function where galScaled is embedded.

void GLWidget::wheelEvent(QWheelEvent *event)
{
QPoint numDegrees = event->angleDelta();
if (numDegrees.y() < 0)  zoomScale = zoomScale/1.1;
if (numDegrees.y() > 0)  zoomScale = zoomScale*1.1;

updateGL(); // call paintGL()
}

In paintGL() function, galScaled() is called and zoomScale is used to scale x, y, and z axis. We should note that in order to avoid unnecessary clipping, the near and far planes of OpenGL view need to be set with some redundancy.

void GLWidget::paintGL()
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glLoadIdentity();
    glScaled(zoomScale, zoomScale, zoomScale);
    glRotatef(xRot / 16.0, 1.0, 0.0, 0.0);
    glRotatef(yRot / 16.0, 0.0, 1.0, 0.0);
    glRotatef(zRot / 16.0, 0.0, 0.0, 1.0);



Approach 2: glOrthof

glOrthof function defines a box where the objects are shown in OpenGL. The box has left, right, up, down, near, far boundaries. Again, we starts from the wheelEvent function which calls resizeGL().

void GLWidget::wheelEvent(QWheelEvent *event)
{
QPoint numDegrees = event->angleDelta();
if (numDegrees.y() < 0)  zoomScale = zoomScale/1.1;
if (numDegrees.y() > 0)  zoomScale = zoomScale*1.1;
GLint viewport[4];
glGetIntegerv(GL_VIEWPORT,viewport); 
resizeGL(viewport[2], viewport[3]);

updateGL(); // call paintGL()
}

In resizeGL, zoomScale is used to adjust the boundaries in x and y directions. In this way, we can also implement the zoom in/out functions.

void GLWidget::resizeGL(int width, int height)
{
    int side = qMin(width, height);
int range = 1000;
    //glViewport((width - side) / 2, (height - side) / 2, side, side);
glViewport(0, 0, width, height);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();

    glOrthof(-0.5*range*zoomScale, +0.5*range*zoomScale, -0.5*height/width*range*zoomScale, +0.5*height/width*range*zoomScale, -5*range, +5*range);

    glMatrixMode(GL_MODELVIEW);
}