Skip to content

Introduction

To understand the lessons in this exemplar, I'll first describe our lab's workflow and how we created problems for ourselves.

Setting the scene

Optical Tracking

Example optical tracker In our lab, we use optical tracking to capture the movement of body parts. Some systems have an active camera, sending infrared that gets reflected by markers set on the body. In other systems, the markers emit the light which the camera captures. We happen to use both, and in both cases the data output from the camera is saved to .csv file which, abridged, looks something like this:

Tools   Port 0x01   Femur tracker-Y Frame       Time [sec]          State   Q0      Qx      Qy      Qz    Tx      Ty       Tz         Error
3       Port 0x01   Femur tracker-Y 1391437402  1727257823.36817    OK      -0.18   -0.58   0.75   -0.28  57.82   -87.18   -2688.41   0.2021692
3       Port 0x01   Femur tracker-Y 1391437405  1727257823.41817    OK      -0.18   -0.58   0.75   -0.28  57.72   -87.24   -2688.37   0.217724
Frame   Femur q0    Femur qx    Femur qy    Femur qz    Femur x     Femur y     Femur z     Femur Error 
1       -0.18       -0.58       0.75        -0.28       57.82       -87.18      -2688.41    0.08        
2       -0.18       -0.58       0.75        -0.28       57.72       -87.24      -2688.37    0.08        

While the formatting of data from each camera is slightly different, they are processed to generate exactly the same outputs: joint coordinate systems. Visualisation of the coordinate system created with optical tracking cameras We look at the relative motion between these coordinate systems to calculate how joints move (kinematics).

We can also apply these coordinate systems to bone models to create animations of what happened during the experiment. Occasionally orienting models incorrectly leads to some questionable results: Optical tracking applied to bone models

The problem is that instead of creating a common camera representation, our solution was the worst possible: Create two versions of our codebase -- one for each camera.

6-DOF Robot

Another piece of equipment we use for the same purpose is a 6-degree-of-freedom robot. Kuka 6 DOF robot

The robot's software uses similar camera data, but the processing is handled directly by them. The only thing left for us to do is create the outputs.

Creating outputs

After the data are processed for either the cameras or the robot, we need to run statistical tests, create plots, etc. Traditionally, we would process the data and leave individual users to decide which tests to run and how to best plot their data. Despite wanting the same outputs, we would post-process it slightly differently and end up with data in different formats, which meant reusing code required a lot of effort.

Workflow

In summary, our workflow looks something like this Original, problematic workflow

Each section was developed months apart, expanding from a shared base. Organically, they became distinct codebases doing the same thing. This is fairly hard to maintain because over time they diverged. The core code was still the same, but naming conventions changed, features were added or removed, etc. Ultimately, it led to a lot of repeated work -- especially of improvements in one codebase that couldn't easily be ported to the other.

Ideally we would have something more streamlined, more in line with: Ideal workflow

Highlighted in green are the points of the workflow where, if we can generalise the handling of the data, we can significantly reduce complexity.