Wednesday, 21 January 2009

End-project. PART XIII

Date: 20 01 2009
Duration of activity: 7 hours + (6 + 8 hours of homework)
Group members participating: all group members (Anders B. + Anders D. of homework)

1. The Goal

The goal of this lab session is to put all the efforts in making the robot to draw its name ``THOMAS''.

2. The Plan

  • Run many tests a and adjust code to improve accuracy to its maximum.

  • Tryout shape drawing.

  • Define letters for the name.

  • Tryout drawing letters.

  • Plot the name 'THOMAS' to see if the definition is right.

3. The Results

3.1. Rounded square

Before, we have had some troubles while drawing a square with rounded corners. This may be due to the fact that we didn't define it correctly. The robot actually was trying to draw what was given:

On the other hand---this figure shows a very different figure than the one that was apparently attempted printed, earlier in the process. It could also have been a bug in the description that has snook in.

3.2. Robot's art

Along the way of testing how to manage and balance all the motor controls and corrections, we got some drawings that can be called a robot's ``art''.

3.3. Multiple drawing

In order to measure how precise our robot can do, we tried to draw the same shapes several times on top of each other. The results we got were surprising in a good way. The arrow points to a line, where at least five overlapping lines were drawn (yet one cannot distinguish that many lines, visually).

3.4. Shapes

To look at some shapes, we tested both drawing a triangle and a square with rounded corners a lot (after the definition was fixed, we got that shape very precisely).

3.5. Letters

After we had success with shapes, we decided to try drawing letters. This is more of a challenge, as the letters (if we want to write a word) have to be relatively small and more complicated. These are examples of letter ``S'' and letters ``TH'' together. We were intending for the robot to be able to write it's name ``THOMAS''.

3.6. PC-side plotting

Seemingly it was possible to write letters pretty nicely, it was time to write a whole name. To be sure that everything is done correctly, and instead of wasting time testing that fact while actually drawing, plotting descriptions were a more easy and time saving thing to do. The below image shows this in action.

This is a very interesting prospect: We're able to, to some degree, simulate the robot's actions, since we can have GNUplot plot the data in a three-dimensional coordinate system, and spot the errors in the descriptions (if any) already at this stage.

3.7. Calibration layer

  • reCalibrate() has been extended to give off an annoying (very error-ish) sound if any axis is touching an endpoint, when reCalibrate() is run.

  • findMinAndMax() enables using previously recorded calibration data. The data is written through a FileUtil file.

3.8. Navigation layer

  • motorPowerFromFraction (float fraction) minor corrections were made: conditions determining speed sign were deleted.

  • motorPowerFromDistance (float[] distance) the way calculations are made was corrected: An acceleration vector is introduced. It used to be a single common-to-all-axes value, but now the urgency of getting back on track is calculated coordinate-wise. (For completeness, it should be noted that this vector also appeared in the previous quoting of the code).

  • getMaxVelocity() returns max velocity value from a calibration layer

The change of using a vector of acceleration factors was a significant one: The error magnitude (the error vector's norm, really) used to hover around 5mm with peaks of up to around 7mm, but when the urgency became adjusted per-axis, this norm started to hover around 0.5mm with peaks of strictly below 2mm. This is perhaps the greatest statement about our achieved precision -- a typical error of around half a millimeter is an extreme degree of precision for this project.

Note however, that this precision is by no means spectacular in general. All environmental effects are under control in an x-y-coordinator, and the movements are known beforehand. If this was an engineering course, we probably would be striving for being 1-5 tacho counts from the ideal value.

3.9. Gradient interpreter layer

  • GradientInterpreterPCSideDumper is the PC-side GradientInterpreter implementation, which does nothing but to enable the dumping of data points for plotting by GNUplot.

3.10. Handler layer

The InteractiveHandler class handles doing ``interactive'' movements with the robot. Thus, it synchronously does operations on its GradientInterpreter, with the purpose of being readily-usable for Bluetooth remote control.

  • move(float[] change) the parameter ``change'' is the signed change in position (in mm) to move. Returns after the movement has completed


  • drawTShape (float[] at) draws a letter 'T'.

  • drawHShape (float[] at) draws a letter 'H'.

  • drawOShape (float[] at) draws a letter 'O'.

  • drawMShape (float[] at) draws a letter 'M'.

  • drawAShape (float[] at) draws a letter 'A'.

  • drawSShape (float[] at) draws a letter 'S'.

  • drawTHOMAS () handles the drawing of the whole word. (in a crude way, that would be improved upon in the LetterHelper.)


3.11. Reading from and writing to the flash memory

During development much of the robot's time was spent recalibrating (i.e. inside CalibrationLayer's reCalibrate(). Every test case from the CalibrationLayer and up, had to recalibrate Thomas on boot-up, to be able to distinguish between top and bottom safety breaks, the size of the work area, maximum velocity of each axis and the position of the pen.


  1. The minimum tacho count is always zero.

  2. The maximum tacho seldom varies greatly, because this would imply a reconstruction of Thomas.

  3. By hitting the bottom-most touch sensor, the number of tachos the carriage must travel to the top is so significant, that we can deduct which touch sensors are being pressed in the future.

  4. The size of the work area is calculated from the constant amount of tacho's per millimeter, and the max tacho count.

  5. The maximum velocity of Thomas does not vary if the gearing of the motors isn't changed and he is on a constant power-supply.

With all these observations in our hands we decide to reduce the time Thomas takes to recalibrate, by storing enough information achieved from the last calibration for to only require a calibration run to make the carriage go to origo. It turns out that to make a complete calibration, Thomas only needs to know the maximum tacho count of each axis and their max velocity. The size of the work areas is easily calculated. And as long as we force the carriage to drive downward until the bottom safety-breaks are being pressed, we will reset the tacho count to zero and we are certain that the robot can distinguish between top-most and bottom-most touchsensors. As soon as (0,0) is reached, Thomas also knows its own position, and can keep on knowing it from tachos pr. millimeter.

We now know what we need to store.


  • nextLine(FileInputStream stream) returns next line of a file.

  • parseFloatInLine(String line) parse characters in order to read floats.

  • writeString(String filename, String toBeWritten) implements actual string writing to the file.

For reading and writing from the flash we made the utility class This class should contain all commonly used I/O interaction methods, but as of now we have only implemented the specific method that we needed to solve our problem.

The protocol for parsing information from the flash memory is based on the notion that the flash memory is shared by all programs and browsed with nxjbrowse. Stored information should contain enough information for it to be human readable when possible.

From this we implemented a solution where every recalibration run checks if as certain file exists on the flash memory: ``lastCalibration''. Here's an example of how this file looks:

maxTachoNoX = 21218
maxTachoNoY = 15228
maxVelX = 0.0075553244
maxVelY = 0.0069913477

Creation and parsing of this file is simply done with calling writeString(line) per line to be written, and parsing by calling nextLine(stream) and parseFloatInLine(line) to get the float values stored.


  • testWritingToFlash() tests the ability to being able to write something to the flash.

  • testReadingFromFlash() test the ability to being able to read from the flash.

  • reCalibrateByResuseSavedValues() runs the calibration with aforesaved file with calibration values.

4. Conclusion

This has been a very successful lab session. We went all the way from Thomas's art to a very precise shape drawing. We also defined letters of the name 'THOMAS' and we tried drawing 'S', 'T', and 'H'. We have also started working on making the robot get to know what to draw using a bluetooth connection instead of uploading a new program every time. The time required for recalibration was also significantly reduced as soon as we were able to read and write to the flash memory.

No comments: