2013-08-19

Robot leg anatomy

Here is (mostly for my own reference) a simple glossary for the parts that go into a very simple robot leg, with illustration.

The leg is divided into 4 parts:
  1. Hip (Coxa)
  2. Thigh (Femur)
  3. Shin (Tibia)
  4. Foot (Tarsus)
I prefer simplicity so I will use the names in English, while others will prefer the names in Latin.

In my robot, there is no Foot, I just included that for completeness. Also this referece lacks any mention of joints or muscles (actuators). I simply lists the bones or "links".

Anatomy of robot legg showing Hip, Thigh, Shin, Foot (Coxa, Femur, Tibia, Tarsus).
I used wikipedia and this site for inspiration and reference: http://bugs.osu.edu/~bugdoc/Shetlar/462/462InsectMorphology/Morph03.htm

2013-08-18

Good bye ODE, hello GEAR!

I eventually grew tired of ODE's inability to handle cyclic graphs well, and looked for alternatives. Apparently cyclic dependencies in the constraints is a difficult problem to solve so very few solutions exist that will work. Luckily I found an alternative called GEAR (Geometric Engine for Articulated Rigid-body simulation) which was made with cyclic graphs in mind. GEAR is an implementation of a a novelty algorithm using some fancy new stuff called "lie group formulation for articulated rigid body dynamics". The code speaks for itself. Mr. Jinwook Kim who made the original, and Mr. Junggon Kim who adapted it to his use (and from what I can see, extended/simplified the code a bit) really know what they were up to. It seems to use more is nearer to use some basic C++ features and lacks the silly "C <--> C++ <--> C" wrapping of the ODE API. There seems to be no documentation what-so-ever, except for the extremely math-intensive and otherwise dense paper, and code comments are sparse. But the demos are stunning! Please check out the videos on this page.

"Sloop" demo of GEAR
I will make my best effort to use this code in my project, and if I am lucky, it will perform under my demands. One hurdle that I will have to get around is the lack of collision detection and handling, however there is some code for that on the video page that I might be able to adapt.

UPDATE: gear was moved to github.

2013-08-13

Riftcam

I have previously written about OpenGL code for the ocular rift, and Now after receiving my own I have yet to go further with writing the perspective correction code. The truth is that even if the rift is one awesome piece of gadget that I believe will change the way we interact with computers in the near future, I still have a strong mandate in this project that I have to prioritize, and that is to build my robot. Until I actually need the rift for something in my project, It will be sitting in its box.

Oculus rift illustration 
That being said, I have contacted several surveillance camera manufacturers on Alibaba with requests about creating a stereo vision PTZ dome camera with oculus compatible wide angle lenses. I actually had some response in this regard. I will keep this blog updated.


ODE instability

Adding the actuators to my ODE model proved much harder than first anticipated. Even with all the stability-enhancing features of ODE cranked up to the max (really small step size, CFM and ERP set high, unit size bodies, masses etc.) my model completely explodes. I think the reason is that when I pose the model, it is not completely still, and the abrupt movement caused by the spike in error correction at simulation startup simply blows out of proportions.

Open Dynamics Engine (ODE) exploded simulation due to instability.

I will work on it some more before giving up on it. One idea is to engage the actuator bodies after the rest of the model has settled. Maybe assembling the model in zero gravity will help as well. But so far it is not looking good for this project. :-( I was hoping that ODE would play nice. I have used it a lot before, but then I mostly played with the collision detection and building large structures out of blocks to see how they would collapse, which ODE handled splendidly.

In my frustration I went looking for alternatives and found this excellent collection of hexapod code by 12Cent Dwarf

2013-08-12

First ODE based IK screenshot

I finally managed to coax ODE into accepting my IK rig. It was not easy, I can tel you that. The ODE documentation is a nightmare. I still haven't figured out why some of my code does not work. But I decided to leave it be and focus on what's more important. I have built the rig without actuators. It runs in a simulator that draws out a simple representation of the world in OpenGL. I also have a Qt5 form where I can add some widgets to control the simulation as I go along. In the screen-shot, the robot is actually in free fall, because it becomes really unpleasant to look at after it lands.

Devol Robot Simulator first screenshot
And here is the code that actually assembles the robot:

void LimbRigController::buildRig(){
    Vector center(-2,0,5);
    int NUM_LEGS=6;
    Vector axisBase(1,0,0);
    float thighToShinRatio=0.75;
    float legLength=1.0;
    float innerRad=0.5;
    float kneeRad=innerRad+(legLength*thighToShinRatio)/(legLength*thighToShinRatio+legLength/thighToShinRatio);
    float toeRad=kneeRad+(legLength/thighToShinRatio)/(legLength*thighToShinRatio+legLength/thighToShinRatio);
    float kneeHeight=0.0;
    float stepAng=(360)/NUM_LEGS;
    float ang=0;
    float legRadius=0.1;
    float baseRadius=2;
    DynamicObject *base=new DynamicObject(world,space,center,center +Vector(0,0.01,innerRad/baseRadius),Vector(0,0,1),baseRadius,0x008888ff,DynamicObject::CYL);
    //base->join(0,center,Vector(0,0,1));
    dobs.push_back(base);
    for(int i=0;i pelvis(0,innerRad,0);
        pelvis.rotate(ang,0,0,1);
        pelvis+=center;
        Vector knee(0,kneeRad,kneeHeight);
        knee.rotate(ang,0,0,1);
        knee+=center;
        Vector toe(0,toeRad,0);
        toe.rotate(ang,0,0,1);
        toe+=center;
        Vectoraxis=axisBase;
        axis.rotate(ang,0,0,1);
        DynamicObject *thig=new DynamicObject(world,space,pelvis,knee,axis,legRadius,0x00ff8888,DynamicObject::CYL);
        DynamicObject *shin=new DynamicObject(world,space,knee,toe,axis,legRadius,0x0088ff88,DynamicObject::CYL);
        base->join(thig,pelvis,axis,-M_PI*0.5,M_PI*0.5);
        shin->join(thig,knee,axis,-M_PI*0.5,0);
        dobs.push_back(thig);
        dobs.push_back(shin);
        ang+=stepAng;
    }
}

2013-08-09

Sourcing radio modem

I have combed alibaba again. That site is really full of interesting prospects!

This time I went looking for radio modems. The robot will have conventional WIFI and GPRS/3G/LTE access, but I thought that as a more general purpose, fail-safe and cheaper alternative (3G is expensive), It would be neat to have a FM radio on board. Just look at all the goodies!

Radio modem with 20km range from alibaba.com

I made sure to hide my VISA card someplace safe first.

ODE instead of IKFast

After working with IKFast for a while, I realized that the OpenRAVE + IKFast combo is overkill for my purpose, and It might not do what I need/want. I wrote my robot definition in XML based on one of the examples, loaded it in OpenRAVE and all was dany, except that it was really hard to work with. I have instead shifted my effort to creating a custom software in C++ / OpenGL / Qt5 / ODE.

Open Dynamics Engine joint types


The idea is to construct a model of the robot using ODE bodies connected with hinges, and then fit the model with linear motors in place of the linear actuators. The model will be constructed with realistic masses, sizes and motor speeds and torques in an environment with realistic ground friction and gravity. So this model will in many ways simulate a real hardware robot, with quirks and numerical errors in place.

Then the next step is to write a layered stack of controller logic that only receive as input the physical properties of the robot such as the limb sizes, masses and so on plus the robot state in the form of linear actuator position readings and the actual position of the limbs in the simulation.

By carefully crafting the logic in each layer of the stack, I can create a feedback loop that continuously "improves" the simulated state of the robot so that it resembles the desired configuration as much as possible.

Each layer will have its separate well defined set of responsibilities and well defined interfaces to the neighboring layers, allowing for experimentation within each confined layer bu exchanging modules.

This model will most likely make the work of finding good parameters for the robot much easier. Trusting the results will be easier.

2013-08-04

Anatomy of a robot limb

For simplicity I have decided to keep all 6 limbs of Devol identical. Each limb will be interchangeable with any other. This I hope will make building and maintaining the limbs much quicker and cheaper due to reuse, but the design process might take longer because now I have to ensure that the one design i decide upon actually will work for all 6 legs.

I have been thinking a lot, and created a 3d model for simulating the possible limb configurations. I have also managed to install OpenRave, and I have started learning to use it by adapting one of the example robot definition XML files while reading some of its tutorials on-line.

I have also looked at realistic actuators that might fit into the designs on global sourcing sites such as alibaba. For example I have received a quotation at 22USD per piece for 20pieces of this nice little actuator. Since I will need 18 of them, this is good news.

FY011C Linear actuator 12VDC at FOB 22USD/20pcs
The idea is that instead of having geared motors at the joins, I will have linear electro-mechanical actuators in a configuration that is typical for hydraulic and pneumatic systems. The benefits are many:
  • The design is simple
  • The resulting robot will be more solid due to the separation of hinges and force in joints.
  • Reaching higher precision in the actuators is easier than with geared motors due to the available flexibility of mounting positions.
  • The actuators will actually be cheaper due to their outspread use in furniture.
  • The actuators will remain static under load with 0 power drain.
  • The actuators are available with built in position sensors and end switches
  • The actuators are available with IP ratings for dust and water resistance. The one I found was rated IP66
  • The choice of available actuators is huge with all sorts of quality, precision, speed and load requirements.
 But how can I calculate the required dynamic and static forces? I found this great site that appears to be centered around building another hexapod. They have a head start, I better shape up!

Anyhow they made a series of tutorials, one which was very useful for my project, namely the PDF about simple hydraulics joints. I can warmly recommend it due to its simplicity and completeness.

My take-away was this figure with the matching formula:
Shamelessly stolen without permission from http://projecthexapod.com


Linear Piston Force * Moment Arm = Torque @ Joint 


Now I'll just have to find out the value for all my variables...


2013-08-01

3D rig

I have used  Light Wave since version 5.0, and it is a great 3D modeling and animation program with really long traditions. It has support for inverse kinematics, so it is perfect for experimenting with different models of the robot.  I have made a 3D model and animated it.

The purpose is to find optimum leg configurations, sizes and placements. I can also use the model to predict what kind of forces and precision I will need in my actuators. This was the greatest take-away from my first limb prototype, the fact that I need to have actuators with more torque and a design with much more sturdy joints. This is my first result. Its a single walk cylcle:



2013-07-24

Android on HP tx1000 update

My effort to install Android on a HP tx1000 convertible tablet was a success. Essentially i duplicated the example configuration files that were made for a later HP laptop model to suit the more humble specs of my device, and after a few rounds of trial and error it booted android and everything was swell.

However, just as it was starting to get fun, the device died on me. I have put my plans for x86 Android device on hold while working on other much more important and pressing software components like IK.

IKFast

Right now I am working on the software part of the robot. The area I have worked most on is related to vision, audio and other rather complex areas. I have hit a wall because the single most important algorithm for the vision subsytem, namely the SURF has an implementation in OpenCL that refuses to run on my nVidia hardware. Apparently nVidia has inferior OpenCL support to the likes of AMD. I have struggled with a hodgepodge of drivers, libraries and versions before essentially giving up til I get my hands on some better hardware.

In the meantime I have shifted focus to another important part of the software, namely motion. How will the position of the legs be calculated? I have worked with 3D graphics for a while earlier in my carrier, and I instantly googled "IK" for "Inverse Kinematics", which is the art and science of calculating where a bunch of stuff under constraints will move when receiving input.

It turns out that to make IK work I will have to create an accurate 3D model of the robot body first. How else would the IK solver know what and how to operate?




After looking around for a bit for C++ libraries for solving IK problems, I came across IKFast which is a plugin to the extensive OpenRAVE project that takes the whole making a model and simulating it very seriously. Actually IKFast is a compiler that will generate C++ code that can be incorporated into my project directly. It needs a robot description as input, and it will output code that is fast, accurate and robust and that fits my robot perfectly.

This is exactly the kind of tool I love. Performance in the front seat. The downside (or upside if you will) is that I need to know exaclty how my robot should look like, and although I have all these dreams resembling scenes from the sci-fi movies, I havent put much effort into finding a resonable working design.

I guess it is time that I start designing the body of my robot. Maybe I can use the robot model to simulate my way to a better design?



2013-07-23

Honda GX25 Update

The Honda GX25 that I un-boxed for quite some time ago has been started and runs great. I have since moved and it is in storage. The plan is to find a suitable alternator for it and also to create a simple ECU that can be controlled from the roboard. To begin with I will most likely just write the ECU as a routine in C on the roboard directly and use one of its many ports to controll a little RC servo on the throttle.

CNC Update

The EMCO F1 CNC machine that I received for some time ago has not yet been put to good use. I have moved lately, and thus I needed a place for this 200kg beast to stay. It has been stored in the garage of my good friend.

We have been busy remodelling his house and haven’t had the time to look at the CNC machine before now. I must say that this machine is really retro. It looks like it came right out of TRON. Even the key is a rather spectacularly designe specimen. Everything has the "robot from the future in the eyes of 1984" feel to it. All the while being a very high quality product made in Austria.

But the best part by far is the sound when you switch it on. It spends several seconds winding up the computer fan, and you feel like an astronaut from some sci-fi movie every time.

Status on the machine is that the software, computer and control panel works great. So does two of the axes, which will jog manually. The Z axis and the spindle motor are however not responding. We are troubleshooting this, but given the limited space and time (it has to fit in both our busy schedules) progress is slow.

The way ahead on this is most likely replacing the 1984 beast-computer that weighs in on around 120kg with a linux based control computer and some matching stepper/spindle motor controller boards. But we will try our best to diagnose it first and see if we can get it working using the existing hardware. For that job, this site has been an absolute god-send resource. It has all manuals, programming guides and specifications for the machine.

I might get some pictures or videos of our effort in here.

Paralella: Cheap supercomputer in credit-card form factor

My robot will require a lot of on-board processing, and this is a challenge given that it should be able to go on for days without charging. So finding the hardware with the right cycle to power ratio is a challenge.

While looking around the internet I found  the paralella board.



In short it is a 5watt board with 66 OpenCL compatible CPU/GPU cores that apparently delivers up top 90Gflops! The form-factor is the size of a regular credit card.

I have previously contemplated using an AMD 7990 graphics card in combination with an AMD g-series SBC. While delivering a staggering amount of raw OpenCL performance, that solution will also draw an excruciating amount of current. Given that these little paralella boards can be stacked in clusters, I would be able to write software that would scale the current draw with the required amount of computing power.

An intriguing thought indeed!

2013-03-27

KURIO USB deviceID

I am writing some software for android, and I wanted to test it on my KURIO pad.

It has device ID: 18d1:0003

You need to put the following line in /etc/udev/rules.d/51-android.rules file:

# kurio 7
SUBSYSTEM=="usb", ATTR{idVendor}=="18d1", ATTR{idProduct}=="0003", MODE="0666", OWNER="<username>"

Be thankful, I just saved you several milliseconds worth of command-line hassle:


2013-02-09

OpenGL source code for Oculus Rift

If you are at all interested in gadgets, graphics, games you have surely picked up news about the up and coming disruptive technology that is oculus rift. If you haven't, then you should definitely check it out.

I have ordered my developer kit already, and I am looking forward to using the head mount display as a way to display the UI for my  robot.

Since there isn't really much to go on yet when it comes to code examples and such, I decided to create example source code for oculus rift using OpenGL. A little tutorial if you will, for rendering a scene in OpenGL in a way that will be adaptible to the VR gadget when it arrives.

First some explanation. When you render in 3D, you will create what is called a frustum, which means "view volume". This is usually shaped as a pyramid protruding from your viewpoint in the scene, in the direction you are "viewing".


Regular perspective frustum in OpenGL

This is fine when you are rendering to a regular monitor. However when you want to display the result in any form of stereoscopic display such as a 3DTV, VR goggles or similar, you will have to render TWO frustums, one for each eye.

Stereoscopic perspective frustum in OpenGL

Since most stereoscopic displays today have moderate field of view, the image will not be very immersive at all. The Oculus Rift changes this by boosting the field of view (also known as the view angle) to 110 degrees. This goes beyond what we are able to preceive, and will together with the stereoscopic 3D effect give a very immersive effect.

Wide angled stereoscopic perspective frustum (Oculus Rift style) in OpenGL

So how is this done in OpenGL? This entry in the OpenGL FAQ sums it up really nicely.

What are the pros and cons of using glFrustum() versus gluPerspective()? Why would I want to use one over the other?
glFrustum() and gluPerspective() both produce perspective projection matrices that you can use to transform from eye coordinate space to clip coordinate space. The primary difference between the two is that glFrustum() is more general and allows off-axis projections, while gluPerspective() only produces symmetrical (on-axis) projections. Indeed, you can use glFrustum() to implement gluPerspective(). However, aside from the layering of function calls that is a natural part of the GLU interface, there is no performance advantage to using matrices generated by glFrustum() over gluPerspective().
Since glFrustum() is more general than gluPerspective(), you can use it in cases when gluPerspective() can't be used. Some examples include projection shadows, tiled renderings, and stereo views.
Tiled rendering uses multiple off-axis projections to render different sections of a scene. The results are assembled into one large image array to produce the final image. This is often necessary when the desired dimensions of the final rendering exceed the OpenGL implementation's maximum viewport size.
In a stereo view, two renderings of the same scene are done with the view location slightly shifted. Since the view axis is right between the “eyes”, each view must use a slightly off-axis projection to either side to achieve correct visual results.

The glFrustum call will in other words allow you to set up a view matrix that with the necessary offset. But how should we go about rendering the scene? The oculus rift expects the image for each eye to be rendered side by side, so we simply render the scene twice, using the proper viewport each time. Again, from the OpenGL FAQ:

9.060 How can I draw more than one view of the same scene?
You can draw two views into the same window by using the glViewport() call. Set glViewport() to the area that you want the first view, set your scene’s view, and render. Then set glViewport() to the area for the second view, again set your scene’s view, and render.
You need to be aware that some operations don't pay attention to the glViewport, such as SwapBuffers and glClear(). SwapBuffers always swaps the entire window. However, you can restrain glClear() to a rectangular window by using the scissor rectangle.
Your application might only allow different views in separate windows. If so, you need to perform a MakeCurrent operation between the two renderings. If the two windows share a context, you need to change the scene’s view as described above. This might not be necessary if your application uses separate contexts for each window.
With no further ado, here is my working code for a stereoscopic view, which I think will work pretty well with the oculus rift from what I have gathered. It might need some tweaking with respect to projection mapping as they have been talking about  "adjusting for fisheye effect". However, I assume that it will be easy to perform with a custom projection matrix.

/*
 * StereoView.hpp
 *
 *  Created on: Feb 7, 2013
 *      Author: Lennart Rolland
 */

#ifndef STEREO_VIEW_HPP_
#define STEREO_VIEW_HPP_

#include "GLStuff.hpp"
#include "View.hpp"

using namespace std;
// Magic constant
const float DTR = 0.0174532925f;
// Intraocular distance (distance between eyes, should match the real distance between the eyes of the viewer when realism is a goal)
const float IOD = 0.5f;

class StereoView: public View {
private:

 class Eye {
 private:
  float left;
  float right;
  float bot;
  float top;
  float translation;
  float near;
  float far;
 public:

  Eye(float lf, float rf, float bf, float tf, float mt, float near, float far) :
    left(lf), right(rf), bot(bf), top(tf), translation(mt), near(near), far(far) {
  }

  void apply() {
   glMatrixMode (GL_PROJECTION);
   glLoadIdentity();
   //Set view frustum
   glFrustum(left, right, bot, top, near, far);
   //Translate to cancel parallax
   glTranslatef(translation, 0.0, 0.0);
   glMatrixMode (GL_MODELVIEW);
  }
 };

 int w, h;
 float aspect, top, right, shift, distance;
 Eye eyeLeft, eyeRight;
 bool useViewports;

 void init(void) {
  glMatrixMode (GL_PROJECTION);
  glLoadIdentity();
  glMatrixMode (GL_MODELVIEW);
  glLoadIdentity();
 }

 void drawSceneInstance(Scene &scene, Engine &e) {
  glPushMatrix();
  //Translate to screen plane
  glTranslatef(0.0, 0.0, distance);
  scene.render(e);
  glPopMatrix();
 }

 void selectEye(bool left) {
  //Use viewports
  if (useViewports) {
   const int w2 = w / 2;
   glViewport(left ? 0 : w2, 0, w2, h);
   glScissor(left ? 0 : w2, 0, w2, h);

   glEnable (GL_SCISSOR_TEST);
   glClearColor(left ? 1.0 : 0, 0, left ? 0 : 1.0, 1.0);
   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
   glDisable(GL_SCISSOR_TEST);

   //cout << "viewport left:" << left << "\n";
  }
  //Use native OpenGL stereo back buffers
  else {
   glDrawBuffer(left ? GL_BACK_LEFT : GL_BACK_RIGHT);
   //cout << "buffer left:" << left << "\n";
  }
 }

public:
 StereoView(int w = 1280, int h = 720, bool useViewports = true, float near = 3.0, float far = 30.0, float fov = 110, float screenZ = 10.0, float distance = -10.0) :
   w(w), h(h), aspect(double(w) / double(h)), top(near * tan(DTR * fov / 2)), right(aspect * top), shift((IOD / 2) * near / screenZ), distance(distance), eyeLeft(top, -top, -right + shift, right + shift, IOD / 2, near, far), eyeRight(top, -top, -right - shift, right - shift, -IOD / 2, near, far), useViewports(useViewports) {
 }

 virtual ~StereoView() {
 }

 void resize(int w, int h) {
  float fAspect, fHalfWorldSize = (float) (1.4142135623730950488016887242097 / 2);
  glViewport(0, 0, w, h);
  glMatrixMode (GL_PROJECTION);
  glLoadIdentity();
  if (w <= h) {
   fAspect = (GLfloat) h / (GLfloat) w;
   glOrtho(-fHalfWorldSize, fHalfWorldSize, -fHalfWorldSize * fAspect, fHalfWorldSize * fAspect, -10 * fHalfWorldSize, 10 * fHalfWorldSize);
  } else {
   fAspect = (GLfloat) w / (GLfloat) h;
   glOrtho(-fHalfWorldSize * fAspect, fHalfWorldSize * fAspect, -fHalfWorldSize, fHalfWorldSize, -10 * fHalfWorldSize, 10 * fHalfWorldSize);
  }
  glMatrixMode (GL_MODELVIEW);
 }

 void renderView(Scene &scene, Engine &e) {
  init();
  gluLookAt(pos.x, pos.y, pos.z, dir.x, dir.y, dir.z, up.x, up.y, up.z);
  //Clear color and depth for all buffers
  glDrawBuffer (GL_BACK);
  glViewport(0, 0, w, h);
  //Left eye
  selectEye(true);
  eyeLeft.apply();
  drawSceneInstance(scene, e);
  //Right eye
  selectEye(false);
  eyeRight.apply();
  drawSceneInstance(scene, e);
  glDrawBuffer(GL_BACK);
  glViewport(0, 0, w, h);
  glDisable (GL_SCISSOR_TEST);
 }

};

#endif /* STEREO_VIEW_HPP_ */