Sunday, 8 December 2013

Cambridge Raspberry Jam - 7th December

Yesterday, was the 4th Cambridge Jam (CamJam) of 2013 at the Institute of Astronomy, Cambridge University.

We arrived early to set up the Armdroid display, and before we could finish unpacking everything we had a small crowd hovering around the desk.....


Above, Matthew (The Raspberry Pi Guy) was really taken by the Robotic arm, and wanted one!   Ryan Walmsley was demonstrating his RTK-000-001 motor controller board & buggy, and some of the other demonstrations was equally as impressive!

Unfortunately, my Raspberry Pi was randomly rebooting itself, so out came the manual controller to the rescue !


Eventually the Pi started behaving itself....   Perhaps there was a shift in wind direction ?

I recompiled some of the test programs with shorter delay statements to speed things up, this made the arm appear a lot more responsive when controlled by the Pi.

Daniel Bull was back with the mighty AstroBlaster arcade console...  We spent most of the day talking (fantasying) about 3D Printing technologies.   The question being... can we print replacement Armdroid components with enough precision not to require further machining ?   We will have to wait and see...


Later in the afternoon I had the pleasure of meeting Gordon Henderson, author of Wiring Pi.  This is the GPIO access library I've been using to write my Armdroid software.

I met lots of interesting people and wished I had taken more photographs.

I've been invited to the forthcoming Peterborough Raspberry Jam starting in March which I'm really looking forward to attend.

Final picture of the day....  Yulia and myself, ready for a well deserved Beer at a local pub !   It was after all a long and very successful day.



We suffered a breakage in the afternoon....



The tension equaliser had been playing up all day, and eventually lead to failure of the fingers.  I suspect one of the retaining grub screws wasn't as tight as it should, this must have slipped.   If you look closely at the Kevlar which didn't snap, literately tore itself free.

Actually, I couldn't have asked for a better proving ground - the kids absolutely loved playing with the Armdroid, and really gave it a good hammering...   I've come away with a lengthy list of problems which need to be addressed & resolved for next time.

A full write up can be found at http://www.recantha.co.uk/blog/?p=7219 with an additional update http://www.recantha.co.uk/blog/?p=7290

Finally....   Big thanks to Michael and Tim, and all the Jam Makers for their superb organisation and dedication put into running such a good show.   Next jam will be Saturday, 8 February.

Saturday, 23 November 2013

Driving Multiple Motors

I've been working on a slightly more advanced test program capable of driving multiple stepper motors simultaneously...

Of course, they don't really run at the same time - that's not possible with this interface, but with some clever programming we can make them step, so that they appear to be moving at the same time.

This is desirable for speeding up operations by running in parallel, instead of sequentially driving motors.

Another important reason is controlling the Gripper action - in order to Roll (rotation) or Pitch (up/down) the wrist mechanism - we need to carefully apply counter-rotations to the left/right hand side motors to drive the differential gearing.

In my previous test program we stepped a motor by calling the drive_motor() function.  This was a blocking function, it would not return until all the steps have completed.   Instead of this, I've written a new routine called drive_all_motors() which takes an array of motor commands.  You can still pulse just one motor with this arrangement, or pulse multiple motors as required.

The motor control array is a relatively simple structure consisting of the desired number of steps and direction  for each of the six motors:

 // define motor control structure  
 typedef struct MTR_CTRL_TAG  
 {  
   int  steps;  
   int  dir;  
 } MTR_CTRL;  
   
 // declare our motor control array  
 MTR_CTRL motor_control[6];  

A motor which is not to be driven should have steps = 0
Assigning a positive number will result in that motor being pulsed to the specified number of steps

The implementation of drive_all_motors() takes this array, and calculates the total number of iterations needed to drive the motors.  After this, we form a tight loop and pulse each of the running motors, decrementing the number of remaining steps as we proceed:

 void drive_all_motors()  
 {  
   // calculate maximum number of steps (total iterations regardless of direction)  
   // for all motors  
   int max_steps = 0;  
   int motor;  
   for (motor = 0; motor < 6; motor++)  
   {  
     if ( motor_control[ motor ].steps > max_steps )  
     {  
       max_steps = motor_control[ motor ].steps;  
     }  
   }  
   
   // repeat until all possible steps have completed  
   int step, output = 0, control = CCLK + SYNC;  
   for (step = 0; step <= max_steps * 2; step++)  
   {  
     // for each active motor, drive it!  
     for (motor = 0; motor < 6; motor++)  
     {  
       // motor is active if we have any steps remaining  
       if ( motor_control[ motor ].steps > 0 )  
       {  
         // add motor address to control pattern  
         output = control + mtr_addr[ motor ];  
   
         // now, add direction bit if necessary  
         if ( motor_control[ motor ].dir == 1 )  
         {  
           output += CDIR;  
         }  
   
         // output control byte, and delay  
         digitalWriteByte( output );  
         digitalWriteByte( output - SYNC );  
         delay( PULSE_DELAY );  
   
         // output again with sync bit restored - returning to input mode  
         digitalWriteByte( output );  
         delay( PULSE_DELAY );  
   
         // decrement motor step counter (when clock pulse is low)  
         if ( (control & CCLK) == 0 )  
         {  
           motor_control[ motor ].steps--;  
         }  
        }  
      }  
   
     // toggle clock-bit for next clock pulse  
     control = control ^ CCLK;  
   }  
 }  

The delay statements results in very slow running speeds when driven from a Raspberry Pi.  This is intentional as I'm still debugging my circuits, but we'll speed things up later.  We'll also consider speed along with acceleration/deceleration control in later versions of our software.

I'm going to start work writing a variation of the LEARN program documented in the manual allowing users to program sequences of movements & play them back.  This program will use the above technique as the bases for this work.  I also want to start abstracting the implementation details to make things easier to modify for direct-drive variants, and porting to other platforms.


The full source code for ArmTest_v2.c has been added to the software page.

Saturday, 2 November 2013

Motor Addressing Solved

Finally, I have solved the motor selection problems.   This blog update will try and cover both problems in detail....


1. PCB interconnections (only applies to prototype models)

As suspected the PCB interconnects was crossed on my Armdroid causing motor selects to drive the wrong motor.   The cables feed the the bottom two data bits (CDIR & CCLK) from the interface to driver circuitry, and look like this:


They combine two channels into a single connector at the other end.


They need to be wired such that the outputs on the interface board below feed directly into the corresponding inputs on the driver circuit board above in the same sequence.   As pictured above, the ordering is - starting from the left-hand side, motor #1 - motor #6


The colour coding of my cables are Purple, Blue, White, and Grey

The labels on all my connectors will be replaced in time to correctly reflect the motor channels they represent.


2. Motor Addressing Logic

The ordering of the Motor Address Bits was not as documented

The give away is the 74LS138 IC6
If you look closely at the pin-out for the 74LS138, you'll see that pins 1 - 3 are the input select bits, illustrated as follows:
The Truth Table says, to get output Y1 selected (ignoring the reverse logic), where Y1 happens to be our motor select for channel #1, you need to set the input pins as follows:

C B A
L L H

L = LOGIC LOW,  H = LOGIC HIGH


Looking at the interface schematics :



You can see address bits D2 - D4 are wired to the 74LS138 as follows:

D4   -->   A (pin 1)
D3   -->   B (pin 2)
D2   -->   C (pin 3)

Which means to select motor #1 the address bit pattern would have to be:

D4   HIGH
D3   LOW 
D2   LOW 

The most significant address bit is actually D2, and least significant D4.

This is not quite what the article in the ETI magazine documented for the interface specification.   Previously, I was setting addresses with the assumption D4 was the most significant bit - for example 001 in binary, instead of 100 binary.

I revisited the construction manual looking for further clarification, unfortunately the documentation is somewhat ambiguous here.

Of course, none of this is actually a problem... we're dealing with an address pattern that is essentially reversible, so if your happy to re-wire your motor assignments, everything will then fall out in the wash.

I decided not to do this, and instead will compensate in software...
By introducing an array of motor addresses, we simply index into this array to fetch the desired motor address bit pattern:

 // motor address bits  
 // 1 0x08 = 00001000  
 // 2 0x04 = 00000100  
 // 3 0x0C = 00001100  
 // 4 0x02 = 00000010  
 // 5 0x0A = 00001010  
 // 6 0x06 = 00000110  
 //              |||  
 //              ||+-- A3  
 //              |+--- A2  
 //              +---- A1  
 const int mtr_addr[] = { 0x08, 0x04, 0x0C, 0x02, 0x0A, 0x06 };


When constructing our output control byte, the new code looks like :

   int output = CCLK + mtr_addr[ mtr-1 ] + SYNC;  

I no longer need to shift the address bits into position because they are now already defined in the correct position.   The mtr-1 of course is needed because all arrays in "C" have indexes starting from zero.

Actually, I prefer this approach because it allows other readers to easily adapt to their Armdroid's configuration.   This is essentially mapping logical motor numbers to physical motor addresses.  If a motor has been incorrectly wired to the wrong channel, you only need to modify this address array to compensate.   Good eh?


The updated source code for the test program will be added later this weekend to the software section.


I still have plenty of other problems to investigate.  Some of the motors, not all, are not reversing properly, yet everything works when operated by the manual controller.   I also rigged up a test circuit to check the signals coming from the feedback switches - and not convinced this is working properly either.    But, resolving the motor addressing is certainly one step in the right direction.....

Monday, 28 October 2013

Motor Addressing

Today, bought a couple of terminal blocks which will be used to extend all stepper motor cables, and that will allow me to run with circuit boards outside of the base unit.  Only then, can I start debugging my interface circuity...


Meanwhile.... Rudi Niemeijer from the Netherlands, has also been busy hooking up an Arduino to his Armdroid clone, and seems to be experiencing similar motor addressing problems....

http://www.rudiniemeijer.nl/ort-robotic-arm/
http://www.rudiniemeijer.nl/714/
(you'll need to Google Translate these pages from Dutch)


Update:  new English pages are being maintained here

http://www.rudiniemeijer.nl/concorde-robotique-ort-robotic-arm/

Even though Rudi has a newer single interface board, the motor address logic is however roughly the same to the prototype models.


My current thinking on the addressing problems :

Motor addresses are decoded by IC6 74LS138 which selects one of eight possible outputs given an address from the interface port.  We know only six (001 - 110) of these outputs are actually used, the others are not connected.  The outputs of IC6 selects IC7 - IC12 which are 74LS175 (quad D flip-flops) used to latch the data bits.

This week... I'll be checking these addresses are correctly selecting these flip-flops.  It's quite possible the MSB (most significant bit) of the Address is not as expected, and the bit pattern needs re-arranging to suit.  But, I've not seen any examples on the internet of controlling software doing this!  Of course, another potential problem would be address lines not running in order.

The next area where the addressing can be muddled up - and only applies to the prototype variants is the wiring of the PCB interconnects.  Which is the routing from IC7 - IC12 on the interface PCB to the jumpers C1 - C12 which represent the motor drive channels for motors 1 - 6.

Finally, the physical connection of these motor channels to stepper motors assignments is another area to check.

Could be more involved... but I'll start with the above checklist....

Friday, 25 October 2013

RPi Interfacing

Progress report on the Raspberry Pi computer control & software.


A couple of evenings ago, decided to bite the bullet, and power up the Armdroid connected to my Raspberry Pi.

Needless to say, nothing happened...  but I could hear a slight clunk coming from one of the stepper motors.

Eventually, I realized everything was working, but my delay statements was initially so large, it was taking minutes just to crank a few rotations.   I gradually reduced the pulse delays to approx 500 milliseconds.

The motor addressing is a bit weird.....
Selecting motor 1, results in motor 5 spinning into life, etc.   I spent hours double checking everything, and so far, my only conclusion is that the fly leads connecting the Armdroid's interface PCB to the driver PCB is mixing up the address selection.   A couple of months ago I traced the entire interface board and was pretty happy I knew what to expect here, so I don't think the issue is with the software or that part of the interface circuity.

I really need to run everything with the board outside of the base, but to do that, I'll have to extend my motor cables and modify the power connections.   Hopefully, I can then probe away with my Logic Probe on the running circuit to see what's happening.



Anyway, here is an example of my test program written in "C"  :

 /*  
  * ArmTest.c: Armdroid Test  
  *  
  * Copyright (C) Richard Morris 2013 - http://armdroid1.blogspot.co.uk  
  ***********************************************************************  
  *  
  */  
   
 #include <stdio.h>  
 #include <wiringPi.h>  
   
   
 //// interface definitions ////  
   
 #define SYNC        0x01            // sync - output Low / input High  
 #define CDIR        0x10            // motor direction  
 #define CCLK        0x20            // clock driver circuitry  
   
 #define PULSE_DELAY    500                // delay in milliseconds  
   
   
 void setup()  
 {  
   wiringPiSetup();  
   
   // set pins 0-7 as outputs  
   int pin;  
   for (pin = 0; pin < 8; pin++)  
   {  
     pinMode( pin, OUTPUT );  
   }  
   
   // set Armdroid initially to input mode  
   digitalWriteByte( SYNC );  
 }  
   
 void drive_motor( int mtr, int steps, int dir )  
 {  
   if (mtr < 1 || mtr > 6)            // check motor number in range  
     return;  
   if (steps <= 0)                    // no steps, nothing more to do  
     return;  
   if (dir < 0 || dir > 1)            // check direction flag  
     return;  
   
     // construct control byte  
   // shift motor address into correct position  
   // add SYNC and CLCK bits  
   int output = CCLK + (mtr << 1) + SYNC;  
   
   // add direction bit if necessary  
   if (dir == 1)  
   {  
         output += CDIR;  
   }  
   
   int i;  
   for (i = 0; i < steps * 2; i++)  
   {  
     // output control byte, and delay  
     digitalWriteByte( output );  
     digitalWriteByte( output-SYNC );  
     delay( PULSE_DELAY );  
   
     // output again with sync bit - returns to input mode  
     digitalWriteByte( output );  
   
     // toggle clock-bit to generate next pulse  
     output = output ^ CCLK;  
   }  
 }  
   
 int main()  
 {  
   int motor, steps, direction;  
   
   setup();  
   printf( "Raspberry Pi - ARMDROID TEST\n" );  
   
   for (;;)    /* repeat forever */  
   {  
     printf( "Enter motor number (1 - 6) ? " );  
     scanf( "%d", &motor );  
     printf( "Enter steps ? " );  
     scanf( "%d", &steps );  
     printf( "Enter direction (0 = clockwise, 1 = counter-clockwise) ? " );  
     scanf( "%d", &direction );  
   
     drive_motor( motor, steps, direction );  
   }  
   return 0;  
 }  
   

Very much work in progress, so might not be the final cut, just yet !   The pulse delay of 500 milliseconds is still very slow, but this is good enough for debugging.

Having written this simple test program for 'prototype' variants, I fancy writing another version for Direct-Drive models.   Only the implementation of drive_motor() will need to change,  but, at the moment, I wont be in a position to test it.

I'll be adding the code to the resource page just as soon as I've figured out the best way to share files with eBlogger.




Sunday, 13 October 2013

RPi Interface - completed

Today...  Assembled my interface cable allowing connection from the Raspberry Pi interface circuitry to the Armdroid's parallel interface port:



Here's how this was assembled.....

Firstly, solder up the 10-way ribbon cable to a 20-way card edge connector - we only need to use one side as follows:



The other end is terminated with a 2x5 ribbon crimped connector:


To crimp, you need plenty of pressure...  But, you don't want to damage the pins in doing so....

I sacrificed an IC socket by removing the pins, inserted the crimp connector into the shell of this IC socket which protects the pins, inserted the ribbon cable, and pushed together using a clamp like this....



A small G-style Clamp would also do the trick, except I couldn't find one in my garage!

Now, we're ready to install onto the interface circuit:


Minor wiring rearrangements made to route correct cables through to the ribbon cable.  I have also coupled up the ground line (black) to the ground power rail on the breadboard.  The +5volts (white) power line from the Armdroid is left unconnected:


You'll notice that when I constructed the circuit last week, I adopted the colour coding of my wiring based on the ribbon cable, which should simplify troubleshooting.

Finally, one last check with the multimeter checking we're not dropping resistance, and verify continuity.  Then tested the whole circuit again with the Digital Logic Probe:




The completed interface:




Wednesday, 9 October 2013

RPi GPIO & Armdroid Interface

GPIO stands for General Purpose Input/Output, and a GPIO pin can be set to logic high, or low, with a value of 1 or 0 respectively.  The Raspberry Pi can set pins to take either value and treat them as output, or it can detect a value as input.

The GPIO header on the Raspberry Pi consists of twenty-six pins, which include power (3.3v/5v/ground) and seventeen GPIO pins.  Some of these pins have special functions as well, we won't concern ourselves with these for now.

The GPIO pins operate at 3.3volts, which means we need to do some logic-level translation in order to interface our Armdroid.

To make the make the task of breaking out the GPIO pins easier on a breadboard, I'm going to use an Adafruit Pi Cobbler breakout kit.  I soldered up mine in about 10 minutes, but looks like you can now buy these ready assembled.


The layout of my Armdroid / Raspberry Pi interface circuit.

Armdroid / RPi interface - breadboard layout

IC1 & IC2 are 74HC4050  High Speed Hex non-Inverting Buffers
The header pins (see right-hand side) represent the Armdroid connector D1 - D8
Power is taken directly from the GPIO, we could use the power source from the Armdroid interface

The circuit works using two 74HC4050 as logic level translators, converting logic high 3.3v to 5v before continuing to the Armdroid's 8-bit Parallel Interface.   This design is not bi-directional, which means we cannot read the micro-switch sensors, but we'll revisit that later in time...

GPIO ASSIGNMENTS
PIN   GPIO       ARMDROID
11    0 (brown)     D1
12    1 (red)       D2
13    2 (orange)    D3
15    3 (yellow)    D4
16    4 (green)     D5
18    5 (blue)      D6
22    6 (purple)    D7
 7    7 (grey)      D8

This table gives the physical pin numbers of each GPIO channel and mapping to Armdroid notation.



The completed circuit on the breadboard:

Armdroid / RPi interface - prototype board

Armdroid / RPi interface - connection to Raspberry Pi


Testing
As mentioned in recent updates, the gpio command-line utility can be used to test the circuit.  The following example sets pin 0 to output mode, and then sets to logic High.

gpio mode 0 out
gpio write 0 1
By substituting pin numbers, we can test all 8 output lines on our circuit with a Digital Logic Probe





All that I need to do now is solder up my ribbon cable and connectors.... and work can commence on writing software....