Saturday, 5 May 2012

Debugging the eyes

The cameras are mounted on a head, they're talking to the Arduino, it's talking to the blue tooth, the blue tooth is talking to a PC and the PC is displaying stereo images on screen:

Stereo image from MmBot's eyes
Unfortunately I'm still hitting an issue where after a few photos, something goes wrong and the PC stops getting data from the blue tooth. This post is gonna be another one of the debugging ones - I'll document my progress working out the issue as I work it out so it's gonna be rambling :)

At the moment, I have some basic code on the PC that:
  • Requests an image be taken
  • Waits until it has an image size
  • Repeatedly reads content until image is finished
On the Arduino side I've added some debug prints. The first bit of logging looks like this:

Requesting camera take picture
Unprocessed bytes: 5: 0x76 0x00 0x36 0x00 0x00 
Camera picture taken
Requesting camera file size
Camera file size received: 6244
Requesting camera take picture
Unprocessed bytes: 5: 0x76 0x00 0x36 0x00 0x00 
Camera picture taken
Requesting camera file size
Camera file size received: 6824

Here the PC has requested a picture from each eye, and then requested the file size. The unprocessed bytes are from the camera system, which show it's processing commands other than 'get content', so doesn't need to read the response. Here the unprocessed bytes are the response from the take picture request.

Next up, the Arduino starts printing out a log of what's being sent to the pc:

L[0] sz=32, strm=384
R[0] sz=32, strm=416
L[32] sz=32, strm=608
R[32] sz=32, strm=640
L[64] sz=32, strm=832
R[64] sz=32, strm=832
L[96] sz=32, strm=1024
R[96] sz=32, strm=1024
L[128] sz=32, strm=1024
R[128] sz=32, strm=1024
L[160] sz=32, strm=1024
R[160] sz=32, strm=1024

This shows the cameras (L or R), the address in the image being sent inside '[ ]', the size of the chunk, and how full the stream buffer is. Currently I can read from the camera faster than I can send to the PC, so you see the buffers gradually fill up to 1024 bytes (the total buffer size).

After a while, a camera will reach the end of the image, and it's stream buffer will start emptying as the PC reads the remaining bytes:

R[5184] sz=32, strm=1024
L[5216] sz=32, strm=1024
Unprocessed bytes: 5: 0x76 0x00 0x36 0x00 0x00 
R[5216] sz=32, strm=1024
L[5248] sz=32, strm=1023
R[5248] sz=32, strm=1024
L[5280] sz=32, strm=991
R[5280] sz=32, strm=1024

Here you can see the 'unprocessed bytes' from the 'stop picture' command that the left camera received when the left image was complete. Following that, the left stream buffer begins emptying.

A little while later, the right camera does the same:

L[5792] sz=32, strm=479
R[5792] sz=32, strm=1024
Unprocessed bytes: 5: 0x76 0x00 0x36 0x00 0x00 
L[5824] sz=32, strm=447
R[5824] sz=32, strm=1011
L[5856] sz=32, strm=415
R[5856] sz=32, strm=979

And eventually both buffers, empty, the PC shows the image, and the process repeats:

R[6784] sz=32, strm=51
R[6816] sz=19, strm=19
Requesting camera take picture
Unprocessed bytes: 5: 0x76 0x00 0x36 0x00 0x00 
Camera picture taken

OK, potentially not too interesting overview, but important to understanding what's going on. In this example everything went fine and I acquired an image similar to the one at the top.

Now it's been going wrong in a few different ways, all of which end with the PC waiting for data that isn't coming. Whether it's all the same problem just showing different symptoms, or several problems I don't yet know. Here's an example of something going wrong:

R[7232] sz=32, strm=102
R[7264] sz=32, strm=70
R[7296] sz=32, strm=38
Requesting camera take picture                        <--- succesfully requested the left camera take a picture
Unprocessed bytes: 5: 0x76 0x00 0x36 0x00 0x00 
Camera picture taken
Requesting camera file size
Camera file size received: 7828                        <--- got the file size
L[0] sz=32, strm=352                                <--- begin reading left stream
R[7328] sz=6, strm=6                                <--- eh? right feed still reading???
L[32] sz=32, strm=544                                
L[64] sz=32, strm=736
L[96] sz=32, strm=928
L[128] sz=32, strm=1024
L[160] sz=32, strm=1024
L[192] sz=32, strm=1024

What appears to have happened is the PC assumed the right image was finished before it was. I'm guessing the series of events went:
  • PC continues reading left/right images until it has got all data from both
  • PC then requests a new picture be taken from both cameras
  • For some reason, the assumption that the right camera was finished was incorrect!
  • As a result, the left state machine restarts as normal, but the right state machine does nothing, as it's still in the 'read content' state
  • The PC now requests file sizes from both cameras. It gets the new size from the left camera, but the size of the previous image (as the Arduino hasn't finished yet) from the right image
  • We now start reading content. the left works as normal - starting at address 0 and filling up the buffer. However the right simply reads the final few bytes, and then, having not got a command to take another picture, simply stops.
  • Finally, the PC reads the entire left image, then sits there waiting for the right image to come through - which isn't happening as it was never sent!
Phew! So, err, why? Well, first I do a few more tests to see if the symptoms change. Previously I've seen 2 other symptoms - sometimes the stream values seem to go complete corrupt (and I start reading from position 1827361928), and occasionally a byte just seems to get lost - the pc receives 63 bytes when it was supposed to get 64. After a few tests I see both problems occur again.

This could be to do with loss of data over blue tooth, but some of these problems stink of a logic bug somewhere - especially the first one. I make a couple of tweaks to the PC code:
  • I make sure it waits until the message comes back that the 'start picture' was succesful - my Arduino code already supported this (by sending back 1 or 0), so I may as well use it
  • I print out the image sizes it's expecting
The PC quickly gets stuck after a couple of images, failing to start taking the right photo. Basically the first problem I mentioned has occured, but I've caught it early. I check the print outs and the right camera was expecting 6176 bytes, which the Arduino agrees with. The last block requested and sent to the camera was at address 6144, of size 32 bytes, but there were 38 in the buffer. Clearly the PC made the right call - it got exactly 6176 bytes and then moved on to the next shot. So where did the extra 6 bytes come from?

Then I notice a really scary line in the Arduino output:

R[5152] sz=32, strm=1030

My 1024 byte stream buffer has 1030 bytes in it. Oh dear. It doesn't matter what the blue tooth is doing - there is no way this should ever happen if the code is correct. This explains the getting stuck due to 6 bytes, and also explains the corrupt data - memory trampling.  It's time to revisit my stream filling and see how on earth this can happen.

At first glance, I can see a line of code that would cause the problem if something else had gone wrong:


          //this is the main content bit
          //first, we bail out if we haven't yet sent the remote the data that is in the picture buffer
          if(PictureStreamUsed >= PICTURE_STREAM_SIZE)
            break;

This 'bail out' is fine provided you always read the same sized chunks (except the very last one), and PICTURE_STREAM_SIZE is divisible by the chunk size. In theory I do exactly this - only getting 32 bytes at a time. However in order for my stream to work I must ensure that:
  • The 'PictureStreamWrite' is always a multiple of 32, and never goes above 1024-32
  • The 'PictureStreamUsed' is always a multiple of 32, and never goes above 1024
If any chunk other than the last one is not 32 bytes, this code will fail. I could change the above code to handle it, but that'd just be hiding the deeper problem. Why am I getting chunks that aren't 32 bytes? First thing to do - print out what I am getting and see!

Requesting 32 bytes to camera stream at 992
Received 32 bytes to camera stream at 992
L[5120] sz=32, strm=1024
Requesting 32 bytes to camera stream at 0                     <----- left requests 32 bytes
Received 31 bytes to camera stream at 0                       <----- left gets 31 bytes back - finished?
Requesting 32 bytes to camera stream at 31                    <----- no! left requests 32 bytes again
Received 7 bytes to camera stream at 31                       <----- left gets 7 bytes back!
R[5120] sz=32, strm=1024
Requesting 32 bytes to camera stream at 0
Received 32 bytes to camera stream at 0
L[5152] sz=32, strm=1030
Unprocessed bytes: 5: 0x76 0x00 0x36 0x00 0x00 
.........
Requesting 32 bytes to camera stream at 320
Received 32 bytes to camera stream at 320
L[5472] sz=32, strm=710
R[5472] sz=32, strm=1024
Requesting 32 bytes to camera stream at 352                  <---- right requests 32 bytes
Received 11 bytes to camera stream at 352                    <---- right gets 11 bytes back - finished?
Requesting 32 bytes to camera stream at 363                  <---- no! right requests 11 bytes again
Received 3 bytes to camera stream at 363                     <---- right gets 3 bytes back
Unprocessed bytes: 5: 0x76 0x00 0x36 0x00 0x00 

Well, it seems my assumption that the camera would keep giving me the data I asked for until there was none left was incorrect. There's 2 chunks at the end of each image that are of none-32 byte multiples. This is already bad and explains corruption, but in theory, provided I end up with the right amount of data, it should work most of the time. Unless....

Requesting 32 bytes to camera stream at 928
Received 23 bytes to camera stream at 928 total read 7095 of 7096
Requesting 32 bytes to camera stream at 951
Received 7 bytes to camera stream at 951 total read 7102 of 7096
Unprocessed bytes: 5: 0x76 0x00 0x36 0x00 0x00 
R[6048] sz=32, strm=1024

Yes. It appears the camera happily returns more data than is actually in the image, and the code they suggest to mark the actual end (detect 0xff,0xd9) doesn't actually... well... work properly. I guess at this point I'd expect no less from this camera. Can you tell it's not my favourite thing in the world?

So there's 2 problems to handle:
  • The fact that I get additional bytes
  • The none-32 byte results.
The first issue is trivial. I'm already recording how many bytes have been sent, so I simply change the wait condition at the end of the camera state machine to be:

          //finally, wait for remote to drain the camera stream
          if(PictureSendAddress < PictureSize)
            break;

And for good measure, adjust the command code to avoid sending extra data:

          //clamp the amount to discard extra bytes at the end of the image
          if( (CS1.PictureSendAddress+bytes_to_send) > CS1.PictureSize )
          {
            bytes_to_send = CS1.PictureSize - CS1.PictureSendAddress;
          }

Things are much more stable now, and the code gets loads of pictures. Now all I have to deal with is the much less common issue of data corruption. This only occurs if the none-32 byte chunks arrive right at the end of the stream. This diagram shows the problem (it's got 8 byte chunks, not 32 byte, but the principle is the same):


Each row shows the buffer filling up, initially in full sized chunks nice and uniformly. However on row 4 we get a smaller chunk back, leaving a gap at the end. This stops the write pointer wrapping round. Now if the data stopped there this would be fine, but the image is not finished and another chunk is requested. The chunk is not full size, but it does go off the end of the buffer, corrupting any data that follows it. As it happens, the data following the stream buffers in my code is the image size and stream information, which explains why I occasionally end up reading from insane stream positions.

So what to do? Well first, I need to make the problem happen again. I've not seen it since the last fix which is mildly concerning - have I accidently made it go away? If so I need to know why. If not, I need to fix it. The issue occurs due to a buffer overrun, so I can make it more common by reducing the buffer size. I shrink it down to 96 bytes (from 1024). This means there's a 1 in 3 chance that a read will be at the end of the buffer. It takes a few photos to occur, but eventually I get:

Requesting 32 bytes to camera stream at 64
Received 15 bytes to camera stream at 64 total read 6319 of 6320
Requesting 32 bytes to camera stream at 79
Received 7 bytes to camera stream at 79 total read 3732081255 of 1000798054
Unprocessed bytes: 5: 0x76 0x00 0x36 0x00 0x00 

The read at byte 79 can cause up to 32 bytes to arrive (even if the camera api only gives you back 7, more can still be written). As a result, the stream buffer could be written up to byte 114, and it's only 96 bytes in size. The question is, do I actually need those extra bytes? i.e. The final 2 reads (the ones that are none-uniform) can be up to 64 bytes in total, but it's conceivable that I only need the first 32 bytes of them. It sounds weird, but print outs like this make me think it's possible:

Received 31 bytes to camera stream at 64 total read 7103 of 7104
Received 7 bytes to camera stream at 95 total read 7110 of 7104

Look carefully and you'll see the first none-uniform chunk is 31 bytes in size (1 byte off a full chunk), and we end up with 7103 bytes of a 7104 byte image. All I need is 1 byte to finish the image, and all I need is 1 byte to finish the chunk. This pattern seems to repeat - they almost always match up, and even when they don't, I never end up needing more than 1 full chunk to finish the image. If this is the case, I can simply extend the stream buffer to have 32 bytes of padding in, and allow it to overflow. I decide to try and see what happens, as this will make life much easier! You can see the small change here:

    byte PictureBuffer[PICTURE_STREAM_SIZE+32];

This seems to work and my images come through fine. The whole system is much more stable now, and head upstairs for a cup of tea. When I come back, it's still taking pictures happily!

However I'm still getting occasional image corruption, and once or twice the pc code stops responding, waiting for data that doesn't come. My gut says this is due to overloading the blue tooth and I recall reading a small note in the modem docs about wiring up the ready-to-receive and ready-to-send pins being advisable. This is because blue tooth is wireless, so isn't a perfectly stable (in terms of timing) connection, and I could easily end up overloading the transmit buffer. To test this, I remove a lot of the serial print outs (which always slow things town), increase the stream buffer, and turn up the rate at which I attempt to feed data back to the PC. With both commands set to transmit up to 256 bytes back to PC at a time and a stream buffer of 1024 bytes I start getting print outs like this:

Unprocessed bytes: 5: 0x76 0x00 0x36 0x00 0x00 
Camera picture taken
Requesting camera file size
Camera file size received: 7200
L[0] sz=256, strm=288
R[0] sz=256, strm=320
L[256] sz=256, strm=256
R[256] sz=256, strm=320
L[512] sz=256, strm=288
R[512] sz=256, strm=288
L[768] sz=256, strm=256
R[768] sz=256, strm=288
L[1024] sz=256, strm=256
R[1024] sz=256, strm=288
L[1280] sz=256, strm=256
R[1280] sz=256, strm=288

What you see here is very good! The stream buffer is no longer filling up, meaning I am transmitting to PC at the same rate as I receive from the cameras - exactly what you want from a streaming system, and I'm back to my 1 frame every 5 to 10 seconds rate. It's still very stable, and I'm tempted to say I'll leave the fancy blue tooth flow control for another day, and simply make my PC code more robust against lost data. Not by getting all clever with none-blocking code - simply by making it time out if data hasn't arrived, and tweak the PC+MmBot code so it is able to give up and restart a picture.

Wow! Eyes now work. Given image processing will be limited with this version (until I can do it onboard - either on a Raspberry Pie or some other more hardcore processor), I think I'll say that's as good as the camera feed is getting for now. 0.1fps isn't great, but I can still do stereo imaging of the background and basic face recognition.

Next up I'll get some nice controls in the PC app for driving MmBot around manually and getting the camera feed. Then I'll add some infra-red range finders and ultra sound to the equation and get MmBot wondering randomly around the office, taking pictures and feeding them back. Not long before the first hints of autonomy!

-Chris


First Sight

Back on Thursday I managed to wire up both cameras to serial connections 2 and 3 on the Arduino, and began the process of getting the images over to the PC for display / processing. Over the past few days I've been gradually progressing, but getting the code to work reliably has been a hard task. Initially I was just gonna try and send them over usb serial and then handle blue tooth later, but as it turned out, using blue tooth for communication and the normal usb serial for debugging was much more useful. This is the pair of cameras wired up:

The cameras connected to the prototyping board inside MmBot
So, over the past few blogs I've ended up with:
  • A system for remote communications over blue tooth
  • 2 link sprite cameras, mounted on a nice 'head'
  • A nice API for using the cameras
First step was to work out how to allow the PC to send a command to request a camera image, get it's information and eventually retrieve the data. Remember this whole thing has to be totally none-blocking. In other words, it needs to be a piece of code that runs once per frame, checks the state of things, and takes action if there's something to do. I settled upon writing a state machine to acheive this, which is shown below:

State machine for reading data from cameras
You can see how much more complex things get when you want them to be none blocking!

Anyhoo, the key bit is really the blue section. Here we're repeatedly grabbing some data from the camera, waiting for it to get to the pc, and then bailing out if we've reached the end of the photo. 

With a bit of debugging this process actually worked fairly well, but I wasn't entirely satisfied. The issue here is that the system has to wait for the next chunk of the image to get to the pc, before being allowed to read more from the camera. Surely it'd be better if we could be busy reading the next chunk while simultaneously sending the current chunk to the PC!

Doing the sending/receiving simultaneously isn't actually that tricky - it's basically a case of streaming. At the moment I just have 1 buffer, so after reading it from the camera, I have to wait until it's been requested by pc and transferred into the serial transmit buffer before refilling. However if I make that buffer big enough to contain 2,3 or even 32 chunks of image then I can be reading from one and writing to another! This code can easily get tricky to write, but I've written it wrong enough times over the years to finally know how to get it right first time :) Here's the state machine code I ended up with:

    void UpdateCamera()
    {
      switch(CameraStage)
      {
        case 0:
          //idle - i.e. nothing requested
          break;
        case 1:
          //remote has requested a picture be taken, so tell camera to begin taking picture
          Serial.println("Requesting camera take picture");
          Cam->StartTakingPicture();
          CameraStage++;
          //fall through
        case 2:
          //wait for camera response
          if(!Cam->Update())
            break;
          Serial.println("Camera picture taken");
          CameraStage++;
          //fall through
        case 3:
          //request the file size
          Serial.println("Requesting camera file size");
          Cam->GetFileSize(&PictureSize);
          CameraStage++;
          //fall through
        case 4:
          //wait for camera response
          if(!Cam->Update())
            break;
          Serial.print("Camera file size received: ");
          Serial.println(PictureSize);
          CameraStage++;
          //fall through
        case 5:
          //this is the main content bit
          //first, we bail out if we haven't yet sent the remote the data that is in the picture buffer
          if(PictureStreamUsed >= PICTURE_STREAM_SIZE)
            break;
          //next, we check if we have reached the end of the photo
          if(PictureReadAddress >= PictureSize)
          {
            //reached the end, so jump to stage 7 (where we stop taking the picture)
            CameraStage=7;
            break;
          }  
          //need more data, so begin getting content
          Cam->GetContent(PictureBuffer+PictureStreamWrite,&PictureAmountRead,32,PictureReadAddress);
          CameraStage++;
           //fall through
        case 6:
          //wait for camera response
          if(!Cam->Update())
            break;
          //got response, so increment the read address and loop back to stage 5
          PictureStreamWrite = (PictureStreamWrite + PictureAmountRead) % PICTURE_STREAM_SIZE;
          PictureStreamUsed += PictureAmountRead;
          PictureReadAddress += PictureAmountRead;
          CameraStage=5;
          break;
        case 7:
          //all done, so tell camera to stop taking picture
          Cam->StopTakingPicture();
          CameraStage++;
          //fall through
        case 8:
          //wait for camera
          if(!Cam->Update())
            break;    
          CameraStage++;
          //fall through
        case 9:
          //finally, wait for remote to drain the camera stream
          if(PictureStreamUsed > 0)
            break;
          //reset camera state
          CameraStage=0;
          PictureSize = 0;
          PictureAmountRead = 0;
          PictureReadAddress = 0;
          PictureSendAddress = 0;
          PictureStreamWrite = 0;
          PictureStreamRead = 0;
          PictureStreamUsed = 0;
          //fall through
        default:
          //anything that hits here just goes back to stage 0
          CameraStage = 0;
          break;  
      }
    }  

It's quite a common model in c++. You use a switch statement that falls through from case to case, only breaking out when some required condition isn't met yet (such as the camera hasn't responded yet). The code above contains the stream filling part (see cases 5 and 6), however the stream reading is in the command processing here:

    case COMMAND_PHOTO_DATA_LEFT:
      {
        //check if there's any data available
        if(CS1.PictureStreamUsed > 0)
        {
          //got data, so work out how much to send, up to a maximum of (currently) 32 bytes
          int bytes_to_send = min(CS1.PictureStreamUsed,32);
          
          //clamp the amount to avoid overrunning the end of the stream buffer
          if( (CS1.PictureStreamRead+bytes_to_send) > PICTURE_STREAM_SIZE )
          {
            bytes_to_send = PICTURE_STREAM_SIZE-CS1.PictureStreamRead;
          }
           
          //write out number of bytes to send, followed by the actual data
          WriteWord(bytes_to_send);
          Comms->write(CS1.PictureBuffer+CS1.PictureStreamRead,bytes_to_send);
          //update stream position
          CS1.PictureStreamRead = (CS1.PictureStreamRead+bytes_to_send)%PICTURE_STREAM_SIZE;
          CS1.PictureStreamUsed -= bytes_to_send;
          CS1.PictureSendAddress += bytes_to_send;
        }
        else
        {
          //no data, so write '0' (to indicate 0 bytes)
          WriteWord(0);
        }
        GCurrentCommand = TOTAL_COMMANDS;
      }
      break;

I've got one set of those commands for the left camera, and one for the right. The main command (get content) is sent by the pc to request the next chunk of the photo. Provided data is available, we pull it out of the picture buffer and send it out the serial port. Note that it's all wrapped up in a simple class. This allows me to have 2 cameras and 2 streams - 1 for each eye!

With a few tweaks to the PC side code I was able to show both images on screen. This is a photo of MmBot looking at me:

MmBot looking at me

And this is what MmBot could see (for some reason I have grey hair here - I really don't btw):

What MmBot can see when looking at me!
Awesome!

I won't bother showing the new PC code - it's basically the same stuff that I've already posted a few times. A thread sends commands to MmBot over blue tooth, waits for data to come back, and once a whole photo is received it tells the main thread to show it in an image box. The only difference is that it now reads 2 pictures instead of one.

It's now Saturday and I've come a fair way with it, but am still occasionally losing data, and eventually entering some form of deadlock, where the PC is waiting for data that isn't coming. Hopefully a few hours debugging today will solve the problem. My gut right now is that I'm overflowing the buffer on the blue tooth modem, and need to figure out / wire up the 'ready to send' pin.

Unfortunately once this is working I doubt I'm gonna be getting more than 1 frame every 5 to 10 seconds - the fact is the Arduino is cool but doesn't have the raw power needed for image processing.. Even so, it's a good experiment and once I get the raspberry pie I'll be able to do the work on-board in something closer to real-time.



Monday, 30 April 2012

Eyes and a Head

I now have cameras working and a robot that can move around. A week or 2 ago I built a prototype sensor platform, but now it's time for the real deal. Well, another prototype for the real deal. Actually another 2 prototypes for the real deal. But my latest prototype rocks! It's time to take those 2 LinkSprite cameras and mount them on their own axis, on top of a head with a proper neck.

After much pondering, I decided one of the keys to cuteness is the eyes, and crucially that they can each swivel on their own axis just like human eyes do. That doesn't mean they'll move independently (which would be creepy), but they need to rotate just like eye balls do. So looking left means each eye turns left on it's own axis. Human eyes can actually look up/down/left/right, but that would be really tricky, so I've decided to focus on just horizontal movement.

I start off by making a very simple prototype with absolutely no planning whatsoever. The cameras need to be mounted on something, so I build 2 small square frames out of balsa wood. As always, I can't find any suitable bolts with which to attach the cameras, so I improvise, and paper clips become my preffered means of attachment. They work ridiculously well, and I gain a new found respect for the wonder of paper clips.

Early prototype eye frame with cameras rotating around an axle made of a bendy metal rod

Next up, I glue a small bit of wood to the back of each camera mounting and drill a vertical hole through which I stick a bent piece of metal somebody left on my desk (when I sent an email round asking for bendy pieces of metal).

Rear view of prototype, with paper clip lever mechanism to make eyes rotate in unison 

Now building on my new love of paper clips, I straighten 2 out and attach them so they stick out of the back of the wooden mounts and can act as levers. When the levers are pushed left/right, they cause the wood to rotate, which in turn causes the cameras (aka eyes) to turn left and right. As one final step I use another paper clip to join the 2 horizontally (the yellow clip above). Now when this horizontal bar is pushed left or right, both eyes will rotate in unison!

The mechanism is wobbly, but it proves a point. This was still Saturday night and I'd done a hard days debugging of camera code, so I decide to end it there and ponder how to build a proper frame over a good nights sleep...

Ok, through the magic of writing it's now Sunday and I have a good idea of how this mechanism actually needs to work. The wooden camera mounts are good, but the pivots need to be directly above and below the cameras rather than behind them. First things first, I load up visio and properly design the camera mount with measurements and everything:



This new design will be much more robust, as the cameras are held on from above and below. They'll still be attached to the same wooden mounts, but I will attach these to a frame with some small metal axles. To turn the eyes I'll use a lever mechanism driven by a mini servo (taken from John's ex-radio-controlled-helicopter) which will be glued to the same frame.

So, armed with a design, I cut up some bits of wood to build the initial frame:

Eye frame cut and axles attached to the camera mounts, ready for fitting together

You can see it taking shape above. The small 'axles' are simply pieces of an aluminium rod someone gave me earlier, chopped up with my Dremmel (causing a lot of sparks). It's not too interesting but I'm proud of my metal cutting, so here's a photo of the chopped up axles:

My beautifully cut aluminium axles (plus my lovely Dremmel)

Right. I have my frame and I have my axles. Time to glue the axles to the camera mounts, create a couple of holes in the frame, add some washers and check things rotate as expected.

Eye frame put together with servo mounted in the middle

As you can see here, everything fits together just right (thanks to actually designing it in advance), and I glue the servo in between the 2 cameras. A subtle but crucial point is that the servo lever is positioned so that it's pivot is at the same depth as the pivots for the 2 cameras. This keeps things simple, as I'll get a 1-to-1 relationship between servo rotation and camera rotation.


Next up, I canibalize some more of John's old helicopter, which is full of handy levers that are strong and have adjustable lengths. I attach small pieces of wood to the back of the camera mounts, drill tiny holes in them and glue 2 of the shortest levers so they stick out the back. Next, I extend the servo lever using another paper clip, bent to fit rigidly through the holes on the servo lever.


Helicopter levers and paper clips to make rear mechanism that turns the eyes

All I need now is to take a couple more of the levers from the helicopter and attach them horizontally so they meet in the middle and join the extended servo lever (aka paper clip). That's the basic mechanism built - if I twist the servo lever left or right, it forces the horizontal lever left or right. This in turn pulls the levers attached to the mounts, which then rotate on their axles. The frame isn't actually held together by anything yet, but aside from that I have a working eye frame.

Next up, I effectively just build a box around the working eye frame, which turns it into a more robust and properly held together head. This is a pretty crucial point, as it'll be hard to dismantle if I get things wrong so before going ahead, I retest both cameras and the servo. All seem to be good so I build the head and wire it up to my Arduino Uno and write a simple program to get the eyes looking around:


Now for a neck. I learnt from my earlier experiments with the sensor platform that it's entirely possible to build a 2 axis system using 2 servos. However the servos need to be as low as possible, ideally both below the sensors (so they don't obscure anything). With this in mind I decide to go for the simplest possible design, and pretty much attach 2 servos to each other with a single piece of wood.

Servo attached to wood, with second servo disk embedded in wood and way too much epoxy resin! 

You can see here 1 servo glued to some wood with epoxy resin, and another servo disk embedded in the wood and glued in with rather too much resin. All I need to do is attach the servo disk to the second servo, and I have a 2 axis neck.

Finally, I take a large, flat piece of wood and cut out the shape of a servo disk to attach the upper servo. This flat piece of wood is then bolted onto the bottom of the head as you can see below:

Fully constructed neck and head

The bottom servo makes the head look up/down, while the upper servo turns it left/right. On top of that is the head itself with the eye frame and it's mini servo to turn the eyes left and right.

Some more epoxy and an Aluminium A-frame later, I have the neck attached to the front of MmBot:

Neck and head attached to MmBot

I was admittedly winging it a little bit at this point, but everything's gone fairly well so far. The only minor hindrance was that the bolts to attach the A frame needed to go through one of the aluminium reinforcements on the inside of the robot. Dave doesn't mind a bit of drilling in the background though, so I get out the old Black-And-Decker and attach a metal drill bit. In not too much time it's all bolted together and is pretty solid.

Just one more step... time for some testing! I connect all the servos to the prototyping board inside MmBot and run a simple test program to set the servos to random positions. Here it is in action:


And there you have it! A full blown 2 axis neck, controlling a head with independently mounted eyes. Let no man look at MmBot now and say she isn't cute!

What a great day :) Next up I'll get the cameras wired into the Arduino and start sending signals via blue tooth to/from the pc. Shouldn't be long before MmBot is combining  fully articulated joints with cameras and face detection to make eye contact with real people!

-Chris

Sunday, 29 April 2012

A quick camera api

By about 6 on Saturday I fully understood the cameras and had them communicating properly, so I decided to write a proper api for them, and test them in full. There's been a bit too much code on this blog lately so I don't want to spend too much time on it, but it's worth mentioning, especially as this might be useful to anyone trying to do the same thing.

This class shows my basic camera api:


class CCamera
{
public:

    CCamera();
    bool Begin(HardwareSerial* serial);
    bool Begin(SoftwareSerial* serial);
    bool Update(); //returns true if idle after update
    bool IsDone();
    ECameraState GetState();
    bool Wait(unsigned long timeout_ms=0); //pass 0 for 'infinite'
    bool Reset();
    bool StartTakingPicture();
    bool GetFileSize(unsigned long* size_buffer);
    bool GetContent(uint8_t* buffer, unsigned long* amount_read_buffer, unsigned long bytes, unsigned long address);
    bool StopTakingPicture();
    bool SetDimensions(ECameraImageDimensions dims);
    bool SetCompressionRatio(unsigned long ratio_0_to_255);
    bool EnterPowerSaving();
    bool LeavePowerSaving();
    bool SetBaudRate(ECameraBaudRate rate);
        
private:
    //... member variables here
};


Download the full code here: camera.zip

You start it up by passing a hardware or serial interface, and can then give it commands to execute. Each command simply posts a request to the camera. Once posted, you keep calling 'Update' until it returns true. This allows you to use the camera in a none-blocking manner which will be needed for MmBot, as she needs to be able to do multiple things at once and never stop responding. In the event that you do actually want to block until a command is ready, you simply call 'Wait' which will internally just keep calling Update until it's done. This little snippet shows me using the 'GetContent' function to read from camera and print it to Serial1 (which has the blue tooth connected to it!):


  while(address < fsize)
  {  
    //read data
    Camera.GetContent(buffer,&amount_read,32,address);
    Camera.Wait();
    
    //print data
    for(int i = 0; i < amount_read; i++)
    {
      printbyte(buffer[i]);
      Serial1.print(",");
    }
    Serial1.println("");
    delay(100);
    
    address += amount_read;
  }      

I got all commands working, however setting the baud rate is tricky (as you then have to change the baud rate you try and talk to it at, and then restarting confuses things etc etc). I also noticed a slight oddity with the cameras - changing compression ratio takes a few images to take effect, so don't be surprised if you set it to highly compressed and your images don't get smaller for a few frames. I think this is just a property of the LinkSprite cameras though.

One thing I have realised after much experimentation is that I'm not gonna be getting more than 1fps out of this, so the pc based image processing will be limited until my raspberry pi arrives (at which point I can use high speed web cams and process them without needing to transmit across a network).

OK, that's enough for this quick little post. Cameras are now fully functional, and I've managed to send data across blue tooth back to the pc. Next up it's time to build the eyes and head.

-Chris

Saturday, 28 April 2012

Cameras - 42 not 37

Well, as my last post detailed, I got a certain distance with cameras and then hit a brick wall trying to get hardware serial communications working. I've decided as my next step to get the much simpler linksprite sample code and get it working on an Arduino Uno.

The LinkSprite sample just consists of a few functions to send specific commands to the camera - reset, take photo and read data. Internally these quite literally just pump bytes of data down the serial port (they don't even wait for a response). The read data one is the only slightly clever bit, as it sends the address it wants to read as well. I won't bother showing these ones though. What is here, is my cleaned up and made useful version of the main loop:

void loop() 
{
  //send reset command
  SendResetCmd();
  delay(4000);                              
 
  //send take photo command
  Serial.println("Taking photo");
  SendTakePhotoCmd();

  //drain any bytes out of the serial buffer that will have come back from the previous 2 commands
  while(mySerial.available()>0)
  {
    incomingbyte=mySerial.read();
    printbyte(incomingbyte);
  }   
  Serial.println();
  
  //begin reading the photo
  Serial.println("Reading data");
  while(!EndFlag)
  {  
    //send command to read data
    SendReadDataCmd();
    
    //delay 25ms - and just hope data arrives within this time??   
    delay(25);
    
    //read whole response (5 byte response+32 byte data) into 'all' buffer
    int pos=0;
    int count=0;
    byte all[64];
    while(mySerial.available()>0)
    {
      //get response
      all[pos]=mySerial.read();
      
      //if within the actual data range
      if((pos>5)&&(pos<37)&&(!EndFlag))
      {
        //check for EOF marker (0xFF followed by 0xD9)
        if((all[pos-1]==0xFF)&&(all[pos]==0xD9))      //Check if the picture is over
          EndFlag=1;                           
        //increment bytes read count
        count++;
      }
      
      //move forwards 1 byte
      pos++;
    }
    
    //check we actually got data
    if(pos > 0)
    {
      //print out the response bytes (should always be 0x76 0x00 0x32 0x00 0x00)
      for(j=0; j < 5; j++)
      {
         printbyte(all[j]);
      }
      Serial.println();
      
      //print out the data bytes
      for(j=0;j<count;j++)
      {   
         printbyte(all[j+5]);
      }                                       //Send jpeg picture over the serial port
      Serial.println();
    }
    else
    {
      //if didn't get anything, print a warning
      Serial.println("No data!");
    }
  }      
  while(1);
}

If you've done much communications code the first thing you might see and scream in horror at is the delay(25) just after reading data. This classic noob error is basically assuming that after 25ms the data will definately be around nice and ready to use. Still, I decide to not change any key logic yet. What I do change is what gets printed. The purpose of this round is to debug what's going wrong, so rather than try to get nice images across, I print out every command response, followed by the data it provides.


Taking photo
0x76 0x00 0x26 ... response from first commands ... 0x36 0x00 0x00 
Reading data
0x76 0x00 0x32 0x00 0x00 
0xFF 0xD8 0xFF ... image data ... 0x00 0x0D 0x00 
0x76 0x00 0x32 0x00 0x00 
0x12 0x0B 0x51 ... image data ... 0x08 0x05 0x05 
0x76 0x00 0x32 0x00 0x00 
0x04 0x05 0x0A ... image data ... 0x12 0x13 0x14 
0x76 0x00 0x32 0x00 0x00 
0x15 0x15 0x0D ... image data ... 0x14 0x14 0x14 


What you see here is the initial photo being taken, followed by me draining all data from the serial buffer. Then I begin reading data in 32 byte chunks. Each read returns a response (0x76 0x00 0x32 0x00 0x00) followed by 32 bytes of image data. All good, but already just by searching through the log I see some weirdness:


0x76 0x00 0x32 0x00 0x00 
0xC4 0x2D 0x82 ... image data ... 0x54 0x75 0xAB 
0x76 0x00 0x32 0x00 0x00 
                                <---- where's the image data?
0x90 0x34 0xC5 0xB3 0x51        <---- and this is not a good response! 
0xB1 0x2D 0xD7 ... image data ... 0x00 0x32 0x00 
0x76 0x00 0x32 0x00 0x00 

Here I've already lost 32 whole bytes of image data, and suspiciously the next response is invalid. Now if I had been doing mutithreaded programming for years (and I have - yay!) I'd put this instantly down to that silly 'delay(25)' I mentioned earlier. I change this to:

    //delay until correct amount of data is here
    while(mySerial.available() < 37);

And the data coming through becomes clean as a whistle. OK. I'm back to being able to get a clean image out on the Arduino Uno using SoftwareSerial. This time though I have much simpler and debuggable code. Lets see what happens when we hook it up to a hardware serial port on the MmBot's Arduino Mega.

First up, in the spirit of sensible debugging, I take things one step at a time and get the camera working over software serial on the Arduino Mega. For this I have to tweak the code to run off GPIO pins 12  and 13, rather than 2 or 3, as not all pins support interrupts (and therefore software serial) on the Mega. After some fiddling and getting bits mixed up, I eventually attain the same result as I got from the Uno.

Now for hardware serial. This is a simple case of connecting the camera to the pins for hardware serial port 2, and changing all references to 'mySerial' in the earlier code to be 'Serial2'. I run it and lo and behold... it works. Hmmm. So what's different is the next question. It could be a timing issue, but the problem was pretty much 100% reliable on Thursday, with or without loads of debug prints to slow things down. Just to be sure, I adjust the code to only print things out if it doesn't get what it expects with a simple if statement:


      //print out the response bytes if wrong (should always be 0x76 0x00 0x32 0x00 0x00)
      if(pos != 37 || all[0] != 0x76 || all[1] != 0x00 || all[2] != 0x32 || all[3] != 0x00 || all[4] != 0x00)
      {
          Serial.print("Erroneous data received at 0x");
          Serial.println(a,HEX);
          Serial.print(pos);
          Serial.println(" bytes read");
          for(j=0;j<5;j++)
          {   
             printbyte(all[j]);
          }                                       
          Serial.println();
          for(j=5;j<pos;j++)
          {   
             printbyte(all[j]);
          }                                       
          Serial.println();
        }

I also add a little 'Serial.println("Done")' at the end so I know when it's finished.

Basically what I've done is made it run as fast as it possibly can. Unless something goes wrong the code will now just keep trying to grab data from the camera. Instantly the whole thing stops working - 'done' never gets printed out. This means that some bit of code has got stuck in an infinite loop somewhere. There's only 1 potential infinite loop in the code though - remember my 'wait for 37 bytes':

    //delay until correct amount of data is here
    while(mySerial.available() < 37);

If it's blocking somewhere it'll be here, so I add a timeout warning:

    //delay until correct amount of data is here
    unsigned long timems = millis();
    while(Serial2.available() < 37)
    {
      if((millis()-timems) > 1000)
      {
        Serial.print("Been waiting for data for ages now, only got ");
        Serial.print(Serial2.available());
        Serial.print(" current address 0x");
        Serial.print(a,HEX);
        Serial.println("");
        timems = millis();
      }
    }

This continuously checks if 1000ms have passed (i.e. 1s) and prints out a warning followed by some data. Once this is in I run the program again and lo and behold, this starts getting printed out in the serial monitor:

Reading data
Been waiting for data for ages now, only got 10 current address 0x40
Been waiting for data for ages now, only got 10 current address 0x40
Been waiting for data for ages now, only got 10 current address 0x40

Stuck as expected. Not just stuck either - stuck 0x40 (aka 64) bytes in - exactly where it was getting stuck on Thursday. So lets reason this out - what do I know?
  • I've sent off and received 2 requests for data (as I'm now on address 0x40)
  • All the requests I did receive were apparently valid - the response was correct and I got 37 bytes
  • The serial transmit buffer can't be full, as I never send more than a few command bytes before waiting for a response
  • The serial receive buffer can't be full unless for some reason the camera suddenly decided to send me massive amounts of data, which I doubt
  • Curiously, the camera has actually sent me 10 bytes back - that'll be 5 for the response, plus another 5
This rings a bell. My gut says that if I print out those 10 bytes that I have received, it'll be 2 copies of the same response. Lets see...

I add:

        while(Serial2.available())
          printbyte(Serial2.read());
        Serial.println("");

To that timeout code, so now if over 1 second passes I actually drain the serial buffer and print out what I do have so far. And shock horror, we print out...

Reading data
Been waiting for data for ages now, only got 10 current address 0x40
0x76 0x00 0x32 0x00 0x00 0x76 0x00 0x0A 0x01 0x00 

Well what do you know. Not quite an exact repetition, but I definitely get the response back, then at least 2 bytes of the same  response again (0x76 0x00). Having checked the manual there appears to be no response that contains 0x0A, so my gut says that's the start of the image. I'm now thinking there's a bug in the camera software. If you request the next read too soon after receiving the previous, it fails to communicate properly.

Adding a 10ms delay before executing the next read changes the behaviour again. This time I start getting too much data - still after 0x40. As a result, the program no longer blocks and starts printing out information about dodgy data:

Erroneous data received at 0x40
38 bytes read
0x76 0x00 0x32 0x00 0x00 
0x76 0x00 0x32 0x00 0x00 0x12 0x0B 0x51 0x04 0x51 0x04 0x00 0x00 0xFF 0xDB 0x00 0x84 0x00 0x07 0x04 0x05 0x06 0x05 0x04 0x07 0x06 0x05 0x06 0x07 0x07 0x07 0x08 0x0A 
Erroneous data received at 0x60
38 bytes read
0x10 0x0B 0x0A 0x09 0x76 
0x00 0x32 0x00 0x00 0x76 0x00 0x32 0x00 0x00 0x09 0x0A 0x14 0x0E 0x0F 0x0C 0x10 0x18 0x15 0x19 0x18 0x17 0x15 0x17 0x16 0x1A 0x1D 0x25 0x20 0x1A 0x1C 0x23 0x1C 0x16 
Erroneous data received at 0x80
38 bytes read
0x17 0x21 0x2C 0x21 0x23 
0x27 0x28 0x2A 0x76 0x00 0x32 0x00 0x00 0x76 0x00 0x32 0x00 0x00 0x2A 0x2A 0x19 0x1F 0x2E 0x31 0x2E 0x29 0x31 0x25 0x29 0x2A 0x28 0x01 0x07 0x07 0x07 0x0A 0x09 0x0A 
Erroneous data received at 0xA0

You can see on the first error I receive more data than expected, and end up with 2 whole copies of the command response. From this point on we're basically screwed, as we will always be playing catchup with this out of sync serial buffer. Timing had an effect though, so I change the 10ms delay to a 10s delay (ridiculously long) and the problem is still around. Time for more thinking. What do I know now:

  • Adding a time delay doesn't solve the problem, but does affect it in a minor way
  • It still always dies at exactly 0x40 bytes received, plus 2*5 bytes in responses
  • That's a total of 74 bytes received
  • Potentially irrelevant, but the Arduino hardware serial port has 128 byte receive buffer
  • I also work out that I send 16 bytes per request, so that's 0x20 bytes sent
  • The problem seems a lot less severe when I'm sending data out over Serial1 - still occurs sometimes though
Response length is the common denominator here. For some reason things are going wrong in between me requesting data and me getting it back. So next up I try putting a 100ms delay between request and actually attempting to read data. Now something interesting happens - see if you can spot it...

Reading data
Erroneous data received at 0x20
42 bytes read
0x76 0x00 0x32 0x00 0x00 
0xFF 0xD8 0xFF 0xFE 0x00 0x24 0x63 ... bla bla bla ... 0x00 0xF0 0x00 0x40 0x01 0x1A 0x00 0x32 0x76 0x00 0x32 0x00 0x00 
Erroneous data received at 0x40
42 bytes read
0x76 0x00 0x32 0x00 0x00 
0x12 0x0B 0x51 0x04 0x51 0x04 0x00 ... bla bla bla ... 0x07 0x07 0x08 0x0A 0x10 0x0B 0x0A 0x09 0x76 0x00 0x32 0x00 0x00 
Erroneous data received at 0x60
42 bytes read
0x76 0x00 0x32 0x00 0x00 
0x09 0x0A 0x14 0x0E 0x0F 0x0C 0x10 ... bla bla bla ... 0x17 0x21 0x2C 0x21 0x23 0x27 0x28 0x2A 0x76 0x00 0x32 0x00 0x00 
Erroneous data received at 0x80

Suddenly the going wrong is much more reliable. I get 42 bytes (i.e. the 32 data + 10) back every time. More importantly though, now that I'm waiting for the bit that is apparently going wrong to tell me everything it has to say, I realise I've got things the wrong way round...

  • I thought it was sending me the response twice, followed by the message
  • It was actually sending me a response, followed by a message, followed by the response again
If you only wait for what you need both of these appear identical, as you simply start reading the end of the last message as the start of the next one. I go and read the manual more carefully and spot the subtley worded explanation - you can expect the response to appear at the start and end of your image data after a read. Here it is:


None of the example code had to handle this. By using software serial they ran slow enough to always end up with a full 42 bytes, and just ignored the end of the message (to the extent of not even acknowledging it's existence). However, hardware serial runs fast enough to start reading the message before the end tag has arrived, and thus all existing example code (I could find anyway) breaks down.

I change the bit of code that waits for 37 bytes of data to wait for 42 bytes, and set the error check to verify 42 were received. Everything works. Wow. That was hard. Here's the final higher resolution image sent over hardware serial. i.e. MmBots first sight....

MmBot's first sight


I'm glad it was me.

Time for a rest. 6 hours is a long time to work out it should be 42 not 37. That's programming for you though. Next up I'll wrap the camera in my own nicer api, do some performance tests and hopefully get 2 camera shots (1 from each eye) being sent from MmBot to PC.

-Chris




Cameras Round 1

On Thursday I decided it was time to hook up my cameras (LinkSprite JPEG camera). I've actually had them for a while now, but unfortunately the JVC cables required to plug it in weren't part of the kit and got delayed in the post. Anyway, I now have all the bits, as you can see below:

LinkSprite JPEG camera with JVC cable plugged in

After a bit of digging I find 2 bits of source code to get this working on an Arduino.One is from the Spark Fun web site at the link I posted earlier, and the other is directly from LinkSprite. Both are designed for an older version of the Arduino libraries, so neither compile, which didn't help with deciding which one to use as a starting point. In the end I choose the Spark Fun one which provides a small library and wrapper class, which in heinsight may have been a mistake.

Anyway, I start by wiring the camera up to one of my spare Arduino Unos and spend an hour updating the example code (this consists mainly of stripping out everything except camera code, replacing use of NewSoftSerial with SoftwareSerial, and fixing a load of include files that have changed name). After some initial confusion resulting from incorrectly coloured wires, I have the camera hooked directly into the Arduino board:

Camera connected to Arduino Uno

Note that the green/blue wires are Vcc and GND, and red/black are Tx/Rx. This would normally be other way round, but I'm guessing LinkSprite didn't go with standard use of a JVC socket. The yellow wire is apparently to attach to a tv, presumably a composite connection.

Next, having got the libraries to compile I place them in the correct folder, and setup the main arduino code. This snippet is roughly what I had at the time:


#include <JPEGCamera.h>
#include <SoftwareSerial.h>

char response[512];
unsigned int count=0;
int size=0;
int address=0;
int eof=0;
JPEGCamera camera;

void setup()
{
     //Setup the camera and serial port
    Serial.begin(9600);
    Serial.println("Hello");
  
    camera.begin();
    camera.reset(response);
    delay(3000);
    
    //take a picture
    Serial.println("Taking picture");
    camera.takePicture(response);
    delay(2000);
    Serial.println("");

    //Get + print the size of the picture
    Serial.println("Size");
    count = camera.getSize(response, &size);
    Serial.println(size);
    Serial.println("");
}

void loop()
{
}

The camera works through serial communication, so internally the JPEGCamera library is using SoftwareSerial to talk to it over IO ports 2 and 3. All this code does is tell the library to send a few commands and wait for the responses - first to reset the camera, then take a picture, and finally retreive and print out it's size. After not too much time I get this being printed out in the serial monitor:

Taking picture
Size
3600

A 3.5k jpeg image has been taken! Hurray :)

OK, time to get some images out of this baby. Problem is, I'm just sending data back to PC as text through the serial monitor, so how to send the jpeg? Well at this point I turn to an old c++ trick which gets used all over the place. After reading the size, I add the following loop to extract actual data:

    //Get + print data
    Serial.println("Data");
    Serial.println("{");
    while(address < size)
    {               
        //pull the data out that we requested earlier
        count=camera.readData(response,address);
        for(int i = 0; i < count; i++)
        {
            Serial.print("0x");
            Serial.print(uint8_t(response[i]),HEX);
            Serial.print(", ");
        }
        Serial.println();
        address+=count;
    }
    Serial.println("}");

This reads data in 32 byte chunks from the camera and sends it as text to the serial monitor. What it actually prints looks something like this:


Hello
Taking picture

Size
3652

Data
{
0xFF, 0xD8, 0xFF, 0xFE, 
.... lots more data here ....
0x58, 0xF, 0xFF, 0xD9, 
}


The nice thing is, the section after data is formatted just like a standard c++ array definition. It can be copy and pasted into any c++ or c# application in it's raw form and compiled into the executable. In games this trick is often used to store start up screens inside the exe, allowing you to get around restrictions on boot time. Here though I'm gonna plonk it into my c# mmbot app and dump it to a jpeg in the first few lines of code:


        byte[] data = new byte[] { ... copy and pasted data here ... }
        
        public MainWindow()
        {
            using (FileStream f = File.OpenWrite("myimg.jpg"))
            {
                f.Write(data, 0, data.Length);
            }


And here it is - the resulting image - me looking pleased with myself:

First photo taken from the camera (me looking smug)

The camera is configurable to do less compression and higher resolution than this image, but it's a good start!

OK, doing well, but the text based image sending is a bit silly frankly. Instead I make 2 tiny tweaks to the Arduino code. First, a little wait at the start for any data to arrive from the serial port:

    //wait for pc
    while(!Serial.available());
    while(Serial.available()) Serial.read();

And the inner loop now posts data in raw binary form back to the pc:

    for(int i = 0; i < count; i++)
    {
      Serial.write(uint8_t(response[i]));
    }

Now I modify my MmBot c# app to read the size and data from the serial port and decode / display it in a window:

       public void DoneJpegRead(byte[] data)
        {
            try
            {
                MemoryStream memstream = new MemoryStream(data);

                JpegBitmapDecoder decoder = new JpegBitmapDecoder(memstream, BitmapCreateOptions.None, BitmapCacheOption.Default);
                MainImage.Source = decoder.Frames[0];
            }
            catch (System.Exception e)
            {
            }
         }

        public void DoPortThread()
        {
            //open serial port
            SerialPort port = new SerialPort("COM9", 9600);
            port.Open();

            //clear any bytes waiting in it
            while (port.BytesToRead > 0)
                port.ReadByte();

            byte[] buff = null;
            while (true)
            {
                port.Write(new byte[] { 0 }, 0, 1);
 
                string val = port.ReadLine().Trim();
                System.Diagnostics.Debug.WriteLine(val);
                if (val == "Size")
                {
                    int size = Convert.ToInt32(port.ReadLine().Trim());
                    buff = new byte[size];
                }
                else if (val == "Data")
                {
                    int idx = 0;
                    int lastdisplay = 0;
                    System.Diagnostics.Debug.WriteLine("Reading " + buff.Length.ToString() + " bytes");
                    while (idx < buff.Length)
                    {
                        idx += port.Read(buff, idx, buff.Length - idx);
                        if (idx > (lastdisplay + 1))
                        {
                            lastdisplay = idx;
                        }
                    }
                    Dispatcher.Invoke(new Action(delegate { DoneJpegRead(buff); }), new object[] { });
                }
            }

            port.Close();
        }

This results in the following screen shot:

Slightly corrupted image sent to and displayed in a c# app

Not entirely sure where that corruption is coming from - images seem to come through fine using my earlier copy-and-paste technique, which suggests a bug in my c# code or some weirdness with the .net jpeg decoder. Edit: I later worked out this was because the serial buffer was overflowing. Calling Serial.flush() before sending data to pc fixed it.

For one final trick, I load up Face SDK - the face recognition system I intend to use (at least to begin with). This is a great little piece of software, which the developers very kindly let me have cheap when I said I just wanted to build a robot. Here it is looking at my photo and identifying my face!

Luxand FaceSDK picking out my face from the first photo

So, all good right? Camera working? Everything wonderful. Well.... this is where things got a bit hairy. After some testing I discover it takes a few seconds to get data back from the camera all the way to the pc. As a first step to speed things up I knock the serial port baud rate up to 115200 and test again. It's a little bit faster but still not great, so I add some code in the Arduino to time things. As it turns out the Camera->Arduino communication is taking about 10 times longer than the Arduino->PC communication. I put this partially down to the fact that I'm still using software serial communication, and decide it's time to move onto MmBot, which has an Arduino Mega in, with 4 hardware serial ports. In theory these should work better and faster right? Well.... No. You can see the camera wired to the robot here:

Camera plugged into MmBot

I'm not going to go through all the random things I tried to get this to work. It seems to communciate for a bit and then give up. Unplugging everything other than camera didn't work. Trying different ports didn't work. Head butting my desk didn't work. After digging around I eventually discover that sometimes I'm receiving the same data twice. The process for getting data from the camera goes something like:

  • Send 'read data' command (which includes how much data you want - in this case 32 bytes)
  • Receive 5 byte response which is basically 'ok'
  • Receive 32 bytes of data
For some reason, having switched to hardware serial communication I am now getting:
  • Send 'read data' command (which includes how much data you want - in this case 32 bytes)
  • I receive the 5 byte response
  • Sometimes I receive the 5 byte response again for no particular reason 
  • Receive 32 bytes of data
As a result, every time this happens I get 5 bytes out of sync. Eventually serial buffers overflow, or even if they don't, I just get a completely corrupted image. 

So what to do? Well, I don't know yet. I did all this on Thursday and have basically redone it all up to here again today (Saturday) to see if I made any mistakes. If it was a general serial port issue I'd expect the problem to be fairly random, but it seems specific to 5 bytes after the read data request. This makes me think there's something special about how you need to talk to the camera itself. I've tried both cameras and they both have the same issue, so it's unlikely to be hardware problems. Next I think I'm going to try the simpler code from Link Sprite to see if there's anything the Spark Fun guys missed out. 

Hopefully by the end of the day I'll get the hardware serial working. If not, I'll probably just give up on that and make do with slow software serial comms for now (once I get my raspberry pie this'll all be hi def usb web cams anyway!). 

Tuesday, 24 April 2012

Coding

Didn't get a great deal of hardware stuff done today, aside from using the epoxy resin to glue down the Aluminium frame and get everyone within 10 metres feeling a little funny from solvent fumes. I still need some wires to arrive in order to connect the cameras (unless I get impatient and resort to a pair of pliers and a soldering iron). However I did get to work on writing the actual code for the Arduino. This'll be a short post with a fair bit of code :)

The Arduino code basically has 2 purposes. First and foremost is to communicate with the PC, allowing me to write a complex brain using a high powered computer that controls the robot remotely. Second to that is to act as an auto pilot for when the pc connection gets lost or (more likely) some bug in my pc code causes it to stop responding.

I've divided the code up into 2 'modes'. One is a simple text based mode that allows me to take control over the robot via hyper terminal. The second is a more advanced mode that has more commands and uses binary data to communicate:

///////////////////////////////////////////////////////////////////
//Main loop in advanced mode
///////////////////////////////////////////////////////////////////
void LoopAdvancedMode()
{
  //read incoming binary commands
  ProcessCommands();
  
  //do advanced logic and autopilot
}

///////////////////////////////////////////////////////////////////
//Main loop in simple (text) mode
///////////////////////////////////////////////////////////////////
void LoopSimpleMode()
{
  //simple text based commands + logic here
  while(Serial1.available())
  {
    int val = Serial1.read();
    if(val == '8')
    {
      //blabla
    }
    //lots more if statements

    //special case for switching to advanced mode
    else if(val == 'T')
    {
      GMode = 1;
    }      
}

///////////////////////////////////////////////////////////////////
//Main loop - just calls simple or advanced version
///////////////////////////////////////////////////////////////////
void loop()
{
  if(GMode == 0)
  {
    LoopSimpleMode();
  }
  else
  {
    LoopAdvancedMode();
  }
}

Basically, the main loop either calls the simple loop function, or the advanced loop function. It defaults to simple mode, however you send it 't' to switch to advanced mode. Once in advanced mode you can send it a command to switch back into simple mode.

Interestingly, the code has a lot in common with standard game code. For example, it can not stall at any point. In a game this is to avoid ugly frame rate issues. However the MmBot could cause herself (or others) damage if left in a blocking state, as the motors may be running at the time and she could drive straight into a wall. Just like in a game the code needs to be able to receive commands over a network (aka the blu tooth) and respond to them in an entirely none blocking way. It needs to respond to keep alive requests and handle scenarios where a connection is lost. In a game (especially one like LBP) you can never predict all the possible problems the user will create, so you have to design for things you haven't thought of! In just the same way, the MmBot will be driving around a constantly changing and unpredictable world so needs to respond quickly - or totally fail to respond correctly but do it in a cute way!

This code shows the command processing:



enum ECommands
{
  COMMAND_KEEPALIVE,
  COMMAND_PING,
  COMMAND_MOTION,
  COMMAND_COMPASS,
  COMMAND_SAY,
  COMMAND_SET_SENSOR_POSITION,
  COMMAND_FORWARDS,
  COMMAND_LEFT,
  COMMAND_RIGHT,
  COMMAND_HORIZONTAL_SENSOR_SWEEP,
  COMMAND_SIMPLE_MODE,
  TOTAL_COMMANDS
};
ECommands GCurrentCommand = TOTAL_COMMANDS; //command currently being read

///////////////////////////////////////////////////////////////////
//reads commands from pc
///////////////////////////////////////////////////////////////////
void ProcessCommands()
{
  //if not currently reading a command, check if there is one available to be read
  if(GCurrentCommand == TOTAL_COMMANDS)
  {
    if(Serial1.available() >= 2)
    {
      //read the new command
      GCurrentCommand = (ECommands)ReadWord();
    }
  }
    
  //will now execute the command, assuming there's enough data available
  switch(GCurrentCommand)
  {
   case COMMAND_SET_SENSOR_POSITION:
      {
        //sets sensor positions - requires 2 integers so doesn't execute until 4 bytes are available
        if(Serial1.available() >= 4)
        {
          ServoTargetX = ReadWord();
          ServoTargetY = ReadWord();
          GCurrentCommand = TOTAL_COMMANDS;
        }
        break;
      }

   case COMMAND_SIMPLE_MODE:
      {
        //switches back to simple mode
        GMode = 0;
        GCurrentCommand = TOTAL_COMMANDS;
        break; 
      }
      
   case COMMAND_FORWARDS:
      {
        //move forwards and return 1
        LeftMotorActiveTimer = 10;
        RightMotorActiveTimer = 10;        
        WriteWord(1);
        GCurrentCommand = TOTAL_COMMANDS;
      }
      break;
   
   ///////////////////////////////////////////////////////////
   /// SIMILAR STUFF FOR ALL THE OTHER COMMAND TYPES HERE
            
   default:
      GCurrentCommand = TOTAL_COMMANDS;
      break;
  }   
}

The key here is that it never blocks. Each frame, if no commands are being processed, it checks if 2 bytes are available (the size of a command code). Once a command code is received it'll check each frame to see if all the data required to execute the command is available. Many commands need no extra data, however with something like 'set sensor position' you needs 4 bytes to know where to move to. Using this none blocking approach the robot can maintain constant control over the motors.

Speaking of motors, they too have a safety mechanism built in. Rather than directly turning motors on/off, the commands simply request to keep the motors on for another few frames. You can see how it works from this function that updates the motors / servos:
///////////////////////////////////////////////////////////////////
//updates the servos and motors
///////////////////////////////////////////////////////////////////
void UpdateMotorTargets()
{
  SensorServoX.write(ServoTargetX);
  SensorServoY.write(ServoTargetY);
  
  if(LeftMotorActiveTimer > 0)
  {
    digitalWrite(PIN_MOTOR_LEFT,HIGH);
    LeftMotorActiveTimer--;
  }
  else
  {
    digitalWrite(PIN_MOTOR_LEFT,LOW);
  }
  
  if(RightMotorActiveTimer > 0)
  {
    digitalWrite(PIN_MOTOR_RIGHT,HIGH);
    RightMotorActiveTimer--;
  }
  else
  {
    digitalWrite(PIN_MOTOR_RIGHT,LOW);
  }  
}

This 'UpdateMotorTargets' function is called once per frame in either mode. Providing nothing actually blocks, this means that the motors will be turned off by default unless they are repeatedly told to stay on. All with the intent of ensuring that in the event of epic failure, the first thing MmBot does is stop moving!

Anyhoo, that's the lot for today. Tomorrow I'll either get a bit more coding done, or if wires arrive, get some vision going!

-Chris