All posts by Gordon

I'm a polymath inventor/engineer/musician/artist living in Pittsburgh Pennsylvania. I've had the priviledge to get to do some interesting thing in my life, and made this blog to share some of them. I welcome collaboration and invite you to say hello if you like anything I've done.

Thoughts after teaching a 3 month long intensive CNC Machining and CAD design class

For the last three months I have been teaching an intensive,  twenty-hours-a-week class for underprivileged adult students in CAD design with Solidworks and Fusion 360,   and metalwork starting with manual milling machines and working up to modern HAAS CNC machines.   I first gained these skills by self-teaching myself at makerspaces like Techshop and Autodesk Pier 9, and consider these skills amongst the most empowering abilities to a designer or person with creative ambitions,    so I was excited to share  my enthusiasm with the students.   But it was also incredibly daunting-  this was an incredibly ambitious undertaking.  Many of my students had not previously had any experience with metal work or machining,  and a normal course covering the breadth of topics I set out to,  budgets a year or more for all this.   However,  all of the students were interested and motivated,    so I took them on a whirlwind tour over the 12 week class.    At the end of the class, each student had designed and manufactured their own part, start to finish:   from creating their own 3d CAD drawings, to setting up the machines,   generating toolpaths,   and correctly setting work and tool offsets and speeds and feeds.   100% of my students achieved their HAAS basic CNC mill operator certification,    too.   Over the next few weeks I’ll be collecting my reflections about the course in this post with frequent updates,    especially attempting to explain in the right words:  What worked well,   and what didn’t,  in this experience?

Amphibious Bicycle Construction Prep: Scanning the Hull

I’ve been having some powerful dreams of building a custom bicycle for bicycle touring adventures such as I’ve enjoyed a few times before—    but this time,  I want to incorporate a few really novel elements into the design,   so that the resulting vehicle can effectively meet the serious(!) challenges (and experience the opportunities) of both the all-terrain  amphibious Kinetic Sculpture Race (KSR),  and the at-times brutal Race to Alaska –  a 750 mile coastal boat race up the entire length of British Columbia,    in the wild Pacific ocean, with one rule: no motors.    So:  what I’ll build will not look exactly like a typical bicycle,    but it will function like one-  as well as a (fast?  fast.) and seaworthy small sailboat.

what I want to do
Artist’s Rendering of What I’m Going For (in Water-Mode)

Whatever I build will need to be able to  traverse steep and soft sand-dunes,  rocky river shores,  roll along miles-long coastal beaches,  pedal easily over  50+ miles of paved roads,  or,  in the Race to Alaska,   weather incredibly challenging conditions including steep waves and at times gale-force winds in the north Pacific.      (Several boats in the 2019 R2AK snapped centerboards off,  and broke rudders,  due to  the violence of the conditions encountered at the start of the epic 2019 R2AK).

 

doug shoup in R2AK
Yeah, I want to go here. This is Doug Shoup, sailing a Colin Angus Row-Cruiser (which is 18′ long), in the first day of the 2019 R2AK, where vessels and pilots are winnowed by the grueling 40 mile open-ocean crossing of the Strait of Juan de Fuca, between Port Townsend Washington and Victoria BC, Canada.  Winds were at times over 40mph.  Doug’s centerboard snapped off when a powerfuil wave caught him abeam shortly after this picture was taken-  although the failure of the centerboard was almost certainly contributed to heavily also by a manufacturing defect (a nick in the wood,  AKA a “stress riser” right where the stresses on the board were greatest,   where it emerged from the base of the boat).  Just goes to show that the engineering and fabrication,   for my purpose here,  must be A-grade.

 

Can I design, fabricate, and pilot a very-seaworthy bicycle/watercraft through all these conditions?   I think so, and I’ll share here my progress towards this objective.

COVID IMPACTING GCKSR and R2AK

Unfortunately,  because of the pandemic,  both of my best motivating deadlines,   the Kinetic Sculpture Race and the Race to Alaska,  have been called off due to concerns of contagion  and the closed international borders with Canada, respectively.    But,   there is an online group of weird bicycle-building creative souls, called “FreakBikers Unite!“,   which is conveniently having a build-a-thon,   internationally via the internet,    and so this will be the framing and helpful-deadline to keep this project motivated.   (To be honest my goals are beyond any of these events,   but they each provide unique framing and motivation).      The rules of the so called “LOLBO” or “Laugh out Loud Build Off”,   are that it must be Rideable,   and it must make people laugh.     (No mention of Amphibious there,  but it’s not ruled out, certainly).    (More on why this will make people laugh,   and what art it is/carries during the Kinetic Sculpture Race,   later.)

The remainder of these posts will be at times highly technical,  as I need to teach myself (I get to teach myself)  a lot of new skills to pull off what I have in mind-  but I’ll try to come up for air from time to time and put all the technicalities into context,   if you bear with me.    Skills like working with composite materials (fiberglass, carbon fiber)  and figuring out the boat-builder tricks of how to efficiently work with the curvy shapes that water flows most kindly over.   (As a rule,   boats are nowhere flat or square,  and so designing and building them takes some real out-of-the-box thinking compared to most other branch of engineering).  Molding,  in particular,   is going to be something I’ll get much more familiar with over this project.

CHALLENGE #1

MEASURE THE DONOR KAYAK:

Fenn Mako Millenium 5
This is a (much cleaner, undamaged) kayak of the same model as I’ve acquired. See how sleek it is?
fenn millenium 2
it’s 14 times longer than it is wide. (21 feet long, 17″ wide)

I’m going to be building upon a donor kayak,    thus saving myself incalculable hours sanding and fairing.   The donor kayak I found is pretty spiffy, too-   a Fenn “Millenium”.  At 21′ long and only 17″ at it’s widest, and with a wave-piercing axe bow,   it’s quite easy on the eyes,  and the water.  (A friend who used to professionally race sea kayaks said it was among the fastest kayaks in the world).   The length-to-beam ratio is 14(!):   just slightly broader than a needle in the water.  And the thing is incredibly light –  it’s built of carbon fiber,  and weighs maybe 35 pounds,   all 21 feet of it.     It won’t stay that weight for long, as I repair and add to it,   but it’s a good start.

Before you go assume I’m either Mr. Moneybags (I think these things sell new for on the order of $2k) or Mr. Incredibly Wasteful (ie. how dare he cut into a perfectly good kayak!),    let me mention that I saved this kayak from the trash heap;  It blew off a vehicle and has two big cracks,  like beltlines,   running around the hull. Its previous owner was happy to drive it to me in exchange for gas money and a steak lunch.

Before I describe what I’m going to add to it,   and how,  and why,   I need to measure what I have already:

STEP 1:  3D SCAN THE HULL

(STEP 1.1:  BUILD A 3D SCANNER)

Given that I want to leverage digital fabrication techniques like CAD, CAM, and CNC tools like 3d printing and CNC milling/routing,    I really need accurate geometry for what I’m starting with.   Also,   since repairing the hull will require me to cut the top off the kayak,    there’s a risk it could fall out of shape,   since it’ll then be quite flexible.    So I need to make an accurate cradle to support the hull while it’s weakened.  To make this cradle,   as well as model accurate additions to the hull (which I’ll get into later),    I need to measure the shape of the hull very accurately.    But boats are nowhere flat or square, so how do you measure the 3D shape of a boat to make an intimately fitting cradle for it along it’s whole length?    For starters,   having a support every foot or so along the length will be sufficient.   I only need to make 20 or so frames to cradle the hull in cross section,   every foot or so.   There’s many ways this could be done (“ticking stick” template making,   or bending a lead bar around it which is then removed and traced,  were two considered options)   but I ultimately decided to leverage my extensive collection of robotics gizmos like linear slides,   bearings and shafts,  and highly accurate angle-encoders.

IMG_7450 IMG_7448 IMG_7447

IMG_7458
Electronics package reads the encoders (gray cables), has several modes (selected by the rotary switch), and a multifunction button that controls spot-sampling, calibration, and new-file creation. The XLR connector is for an optional remote pushbutton (so I can be far from the box, guiding the probe over the boat, and trigger measurements)
IMG_7454
the Y axis encoder is protected by the rounded enclosure at the left. a 12mm shaft supports the feeler arm. The whole assembly with feeler-arm slides along the linear rail below.
IMG_7456
at the end of the linear slide is a calibration cradle, into which the ball can be placed. In that position, X and Y are set to zero as the master reference position against which boat-hull measurements are taken.

 

Anyway… today (4/2/21) I got it all working and now I have a gizmo that can scan curvy boat hulls very accurately,  allowing me to make parts that fit and extend the hull very smoothly.   Hooray!  Hull Scans will be updated tomorrow:-)

Screenshot 2021-04-02 164920
Hoorah! First cross-sectional hull scan (serial output, which is deliberately verbose for debugging. The actual data file is a simple text file with three columns of numbers).

 

Source Code for the Data Logger Touch Probe:

/* Two-Encoder Data Logger for Measuring Hull Shape
 *  Gordon Kirkwood March-April 2021
 *  based on two-encoder reader example code,   and SD library data logger source code
  42  black LED+
  40  white LED-
  38  gray  button sw
  36  purple  button sw
  34  blue  COMMON return of selector switch- ground it
  32  green continuous record mode-  (?while button pressed,  or until pressed again?   At ~10hz or every 5mm pathlength from last point?)
  30  yellow  spot record mode – record a datapoint upon each button press (rising edge)
  28  orange  “file” mode-   button press initiates a new file.   Press between each segment.
  26  red “Y=0” calibration zero – button press sets Y value to zero (do this while ball on stand)
  24  brown “X=0” calibration zero – button press sets X value to zero (do this while ball on stand)

  18,19-  X channel encoder's quadrature inputs
  20,21- Y channel encoder's quadrature inputs
*/


//included libraries
//NOTE:   The most recent SD card library does NOT work with my older SD shield on the mega,  I had to hunt around, find the older library,   
//uninstall (move out of the libraries folder) the default (current) SD library,  and install the older version before it would recognize my SD shield
//link to the older SD shield library and explanation:  https://learn.adafruit.com/adafruit-data-logger-shield/for-the-mega-and-leonardo

#include <Encoder.h>
#include <SPI.h>
#include <SD.h>


//values used for the return of modes from the identify_mode() function

#define MODE_CAL_X 1
#define MODE_CAL_Y 2
#define MODE_FILE 3
#define MODE_SPOT 4
#define MODE_CONT 5
#define MODE_UNKNOWN 0


//define the two input encoders on the scanner

Encoder knobX(18, 19); //on the arduino mega,   pins 2,3,18,19,20,21 are able to external-interrupt, giving fastest response to encoders.
Encoder knobY(20, 21);



const int chipSelect = 10;  //setup for SD shield

unsigned long time;                   //for tracking how frequently to output coordinates,  when time-limiting the output rate
unsigned long nexttime;               //this value is set when a value is output,   as the soonest the next output should occur\

//these values store millisecond counts

unsigned long save_every_X_ms = 1000;  //time interval at which position is logged to datafile, if limited free-running sampling mode (not used)

//in milliseconds,  presuming one or more values has changed since the last logged coordinates.
//if values haven't changed since last last logged coordinates, no new value is recorded.
//FOR FUTURE MOD:  compute the approximate distance traveled since last data coordinates,
//and attempt to log datapoints at semiuniform intervals of spatial coordinates,   possibly with
//allowance for corners and where there are sharp changes in coordinates,  where values might be recorded
//at finer spatial increments.
//otherwise I will perform this resampling in matlab or similar postprocessing software.

File dataFile;                        //sd card data structure

char filename[16];                    //string to read in and manipulate filenames in

/*  pins for mode selector (grayhill multipole switch) user input knob, and the wire colors connected to them,   are included in the following pinout table
 *   MEGA PIN wire color  function
 
42  black LED+
40  white LED-
38  gray  button switch
36  purple  button switch (short the two leads to trigger spot recording, or select various options.  
34  blue  COMMON return of selector switch- ground it
32  green continuous record mode-  (?while button pressed,  or until pressed again?   At ~10hz or every 5mm pathlength from last point?)
30  yellow  spot record mode – record a datapoint upon each button press (rising edge)
28  orange  “file” mode-   button press initiates a new file.   Press between each segment.
26  red “Y=0” calibration zero – button press sets Y value to zero (do this while ball on stand)
24  brown “X=0” calibration zero – button press sets X value to zero (do this while ball on stand)

 */

int LED_HIGH_PIN =42;
int LED_LOW_PIN =40;

int BUTTON_PULL_PIN =38;  //write low on this
int BUTTON_READ_PIN =36;  //input_pullup on this - when connected by bytton to BUTTON_PULL_PIN it will be pulled down

int MODE_RETURN_PIN = 34;  //set to low,  then set input mode pins to input pullup,    mode pins are active when grounded

int MODE_CAL_X_PIN = 24;   //grounded to MODE_RETURN_PIN if CAL_X mode selected
int MODE_CAL_Y_PIN = 26;   //grounded to MODE_CAL_X_PIN if CAL_Y mode selected
int MODE_FILE_PIN = 28;    //etc...
int MODE_SPOT_PIN = 30;
int MODE_CONT_PIN = 32; 

long inter_blink_interval = 200;
long blink_duration = 50;

//main loop variables

  long newX, newY;  //to tell if values are changed,  I compare new values and old values at each update. 

   long x = 0;               //initialize local variables for both enncoders,  here called x and y (channel).  note that the encoder datastructure maintains a separate internal private variable for position,  so 
                             //to calibrate or zero values requires encoder.write(val); to be called,  not just setting these values to zero. 
   long y = 0;
  
  long  min_delta_Y = 10;  //minimum increment of Y encoder between recorded datapoints -  the larger this value,  the less frequently datapoints will be written
  long  min_delta_X = 10;  //minimum increment of X encoder between recorded datapoints. 
  
  int mode = 0;            //use mode definitions in header for various operating modes 
  int last_mode = 0;       //track if mode has changed 
  int button = 1;          //button status.  input pullup defaults to 1 in case of no buttonpress.
  int last_button = 1;     //track if button changed from last evaluation. initialize to 1 because default (without button press) is input_pullup,   so defaults high.

//************************************************************************************

void beep_LED(){  //turns on LED for 100ms.
  digitalWrite(LED_HIGH_PIN,HIGH);
  digitalWrite(LED_LOW_PIN,LOW);
  delay(100);
  digitalWrite(LED_HIGH_PIN,LOW);
}


//************************************************************************************
void increment_filename()
//this function searches for files with a name matching a certain pattern,  incrementing a counter variable present in the filename until it encounters a name which doesn't exist\\
//note:  if files are removed from the directory,  so that there is not a full sequence of files prefix000,  prefix001,  etc... up to prefixNNN,   this code will fill those gaps, resulting in nonlinear sequence of files. 
{
    
    long lastblink=0,nextblink = 0;
    lastblink = millis();
    nextblink = lastblink + inter_blink_interval;
    
    if (dataFile) {//close file if it is open, because we'll be creating a new file.
 
      Serial.println("closing previously open datafile.");
      dataFile.close();
  
      Serial.print("echoing contents of previous file:");
      Serial.println(filename);
     
      Serial.println("****************************************** begin echoing file>>");
      dataFile = SD.open(filename);
     
      while (dataFile.available()) {
        Serial.write(dataFile.read());
        if (millis() > nextblink)   //blink LED while data file being echoed over serial port,  so that you know that device is not yet ready to record new data.
        {
          beep_LED();
          nextblink = millis() + inter_blink_interval;
        }
      
        
      }
  
      Serial.println();
      Serial.println("****************************************** Done echoing file.");

    }//if datafile exists
    else
    {
      Serial.println("datafile was not open");
    }
  

  
  
  
  
  
  //below code finds initial file name to begin with (may be repeated in main loop if button pressed while in "file" mode, to increment filename. 
  
  int n = 0;  //terating variable for file numbers in names with a consistent pattern
  
  //  initialize filename in the consistent pattern chosen
  snprintf(filename, sizeof(filename), "data%03d.txt", n); // includes a three-digit sequence number in the file name


  //search for filenames in given pattern until the name DOESNT exist-  thats the next file. 
  while (SD.exists(filename)) {
    n++;
    snprintf(filename, sizeof(filename), "crv%03d.txt", n);
  }

  //now filename[] contains the name of a file that doesn't exist


  //to open file for writing eg in main loop:
  Serial.print("opening file: ");
  Serial.println(filename);
  dataFile = SD.open(filename, FILE_WRITE);  //THERE WAS MY PROBLEM:  I HAD variable type before dataFile (ie. File dataFile),  which compiler understood to define a LOCAL variable, overriding / taking precedence over global dataFile variable.
  if (dataFile) { //if file was opened OK

    //  Serial.println(n);//number of file
    Serial.println("Successfully opened file.");
    //do not close file;  it must remain open to accept data written.
    dataFile.println(filename);  //for testing purposes write the file's name into the file itself as a header.
    //dataFile.close();
  }
  else {//error opening file
    Serial.println("error opening file");
    while (1);  // end execution.
  }
}
//************************************************************************************

void setup() {


  
  Serial.begin(9600);
  Serial.println("FormScanner 3/29/2021 Gordon C Kirkwood");

  //now setup the data card
  Serial.print("Initializing SD card...");
  pinMode(SS, OUTPUT);

  //setup LED as output
  pinMode(LED_HIGH_PIN,OUTPUT); //LED high pin
  pinMode(LED_LOW_PIN,OUTPUT);  //LED low pin
  
  //blink LED once to indicate circuit coming alive.
  digitalWrite(LED_HIGH_PIN,HIGH);  //this pin will toggle to turn LED on and off
  digitalWrite(LED_LOW_PIN,LOW);  //this pin will remain low
  delay(100);
  digitalWrite(LED_HIGH_PIN,LOW);
  
  //button press detection
  pinMode(BUTTON_PULL_PIN,OUTPUT); //button pulldown
  digitalWrite(BUTTON_PULL_PIN,LOW);  //leave this pin low;  it will short out the weakly-pulled-high button_read_pin input.
  pinMode(BUTTON_READ_PIN,INPUT_PULLUP);  //button read,  active low (button press causes button_read_pin to go low. 

  //setup mode selector knob
  pinMode(MODE_RETURN_PIN, OUTPUT);
  digitalWrite(MODE_RETURN_PIN, LOW);  //pull low

  pinMode(MODE_CAL_X_PIN,INPUT_PULLUP);   //grounded to MODE_RETURN_PIN if CAL_X mode selected
  pinMode(MODE_CAL_Y_PIN,INPUT_PULLUP);   //grounded to MODE_CAL_X_PIN if CAL_Y mode selected
  pinMode(MODE_FILE_PIN,INPUT_PULLUP);    //etc...
  pinMode(MODE_SPOT_PIN,INPUT_PULLUP);
  pinMode(MODE_CONT_PIN,INPUT_PULLUP);
  
  // see if the card is present and can be initialized:   
  if (!SD.begin(10, 11, 12, 13)) {   //these are the pins for connection to older adafruid arduino SD shield;   not same for current generation of boards.
    Serial.println("Card failed, or not present, halting.");
    // don't do anything more:
    while (1) ;
  }
  Serial.println("card initialized.");

  //timer initialization for write-every-so-on timing
  time = millis();
  nexttime = time;

  increment_filename();  //find a filename that is new,  and open it.  


  knobX.write(0);
  knobY.write(0);  //ensure both encoders start with a default zero value.
  
}  //setup ends with a data file opened,   a file structure variable named "dataFile", and a character array "filename[]" containint it's name.   
   //in the subsequent loop,   it may be necessary to find a "next file name",  by closing the existing file.  

// \code to open and test file.  ****






//************************************************************************************

void echo_mode(int m)  //for debugging over serial port,  echo what the mode select switch is
{
  switch (m) {
  case MODE_CAL_X:
    Serial.println("CAL_X");   
    break;
  case MODE_CAL_Y:
    Serial.println("CAL_Y");
    break;
  case MODE_FILE:
    Serial.println("FILE"); 
    break;
  case MODE_SPOT:
    Serial.println("SPOT");
    break;
  case MODE_CONT:
    Serial.println("CONT");
    break;
  default:
    Serial.println("mode unknown");
    break;
  }//switch  
}

//************************************************************************************

int read_mode() {
//function identify_mode() reads the mode pins by setting the common pin low,  and all the poles of the selector go to input_pullup pins.  Returns an integer MODE code,  as defined in header,  signifying which mode. 

/*  pins for mode selector knob are included in the following pinout table
 *   MEGA PIN wire color  function
42  black LED+
40  white LED-
38  gray  button sw
36  purple  button sw
34  blue  COMMON return of selector switch- ground it
32  green continuous record mode-  (?while button pressed,  or until pressed again?   At ~10hz or every 5mm pathlength from last point?)
30  yellow  spot record mode – record a datapoint upon each button press (rising edge)
28  orange  “file” mode-   button press initiates a new file.   Press between each segment.
26  red “Y=0” calibration zero – button press sets Y value to zero (do this while ball on stand)
24  brown “X=0” calibration zero – button press sets X value to zero (do this while ball on stand)
 */
  digitalWrite(MODE_RETURN_PIN,LOW);  //make sure the mode_return_pin is able to pull any selector contact low
  if (0==digitalRead(MODE_CAL_X_PIN)){return MODE_CAL_X;}
  if (0==digitalRead(MODE_CAL_Y_PIN)){return MODE_CAL_Y;}
  if (0==digitalRead(MODE_FILE_PIN)){return MODE_FILE;}
  if (0==digitalRead(MODE_CONT_PIN)){return MODE_CONT;}
  if (0==digitalRead(MODE_SPOT_PIN)){return MODE_SPOT;}
  return MODE_UNKNOWN;
  
  
}

//************************************************************************************

int read_button(){
//I need to modify so I only detect rising edges, and debounce button.   for now,  debouncing in the calling-code, with a short delay. 
  digitalWrite(BUTTON_PULL_PIN, 0);
  return(digitalRead(BUTTON_READ_PIN));
}

//************************************************************************************

void save_values(long thisx, long thisy, long thisz){
//writes an XYZ triplet to the already open datafile.
//assumes that datafile is already open for writing to.

    // make a string for assembling the data to log:
    String dataString = "";

    // read encoder values and put into a string to write to file
    dataString += String(newX);
    dataString += " ";
    dataString += String(newY);
    dataString += " ";
    dataString += String(0);  //solidworks wants triplets, ie XYZ values,  so write a constant 0 at the end of each row. (Z placeholder)
    dataFile.println(dataString);  //************
    //dataFile.flush();   //************

    Serial.println(dataString);

}


//************************************************************************************


void loop() {

  
  newX = knobX.read();
  newY = knobY.read();
  //x and y record the last values of these,   and are only updated to the newX and newY values upon a data-save event,   so that I don't record duplicate values in the file. 

  mode = read_mode();
  while (mode == MODE_UNKNOWN) mode = read_mode();  //wait until the value settles when switching between settings;   
  //because the switch breaks contact first,  then makes next contact ("break before make") so transitions are noisy


  //update mode display only when the mode changes    

  if (last_mode != mode){
    echo_mode(mode);
    last_mode = mode;
  }

  //echo if rising edge on button press

  button = read_button();
  if (button && !last_button) //rising edge
  {
   Serial.println("button pressed!");
   delay(100);  //crude debounce, but effective since data collection rate is slow. 
  
    //insert code here to handle various modes.  
   switch (mode) {
   case MODE_CAL_X:
        Serial.println("button detected in CAL_X mode,  setting X-> 0");   
        knobX.write(0);
        break;
     case MODE_CAL_Y:
        Serial.println("button detected in CAL_Y mode,  setting X-> 0");   
        knobY.write(0);
        break;
     case MODE_FILE:
       Serial.println("button detected in FILE mode,  starting a new file.");   
       increment_filename();
       break;
     case MODE_SPOT:
       //write a line of text to the file
       //newX and newY were already read at the start of the loop, no need to re-read them. 
        //check if (position has changed on either encoder since x and y were last written to the file (and newx value propaged to stored X, and newY propaged to stored Y)

        if ((x != newX) || (y != newY)) //only save values if different from before.
        {
          Serial.println("button detected in SPOT mode.  Position has changed since last recorded,  saving coordinates.");
          x = newX; //update position trackers so the "value changed" test above can detect subsequent changes. 
          y = newY;
    
          save_values(x,y,0);
        }
        else
        {
          Serial.println("button detected in SPOT mode.  However, position has changed since last datapoint,  so no new data written.");
        }
        break;   
     case MODE_CONT:
       Serial.println("button detected in CONTINUOUS mode.  However,  button has no effect in continuous mode. Data is recording by default");
       break;
   }//switch
  
  
  }//if button pressed
  last_button = button; 

  if (mode==MODE_CONT)
        //continuous data logging means no button press required to trigger it.  
       //plan:  check if X or Y encoder has changed by at least min_X_increment or min_Y_increment.   if either encoder has changed by at least this much,  then record the datapoint to the file. 
       //also record to file regardless of change in X or Y since last recorded,  if the mode has JUST changed to MODE_CONT. 
       if (((abs(x-newX) >= min_delta_X) || (abs(y-newY) >= min_delta_Y))  || (last_mode!= MODE_CONT)){
         x = newX;
         y = newY;
         save_values(x,y,0);
         Serial.println("CONT mode, motion detected, datapoint written.");
       }       

}//loop

 

 

Rapid prototyping of Composite Aerospace Parts with 3d printed molds

I needed a lightweight,  impact resistant, streamlined fairing for a flying robot.    I could 3d print a part with the right shape,  but 3d printed materials would either be  too weak,  or too heavy.   I’ve been considering a hybrid workflow which this was the perfect opportunity to try out:     3d printing a mold for composite layup.

Carbon Kevlar Nacelle half made in a 3d printed mold
Carbon Kevlar Nacelle half made in a 3d printed mold.  The shape is a solid of revolution using a scaled NACA airfoil shape that has 33% chord-to-length ratio,  about 8″ long.  The only fault is that some of the mold-filling red paint transferred to the part –  a purely cosmetic issue.

It worked wonderfully!   The mold printed overnight,    and after some sanding primer was applied and sanded down to a 600 grit finish,  I waxed the mold,  and it was ready to invest with fabric.   I vacuum bagged the parts  for good consolidation,  and am very pleased with the results,  shown above.

The nacelle mold was 3d printed in about 8 hours on my Prusa I3 mk3 with an 0.6mm nozzle
The nacelle mold was 3d printed in about 8 hours on my Prusa I3 mk3 with an 0.6mm nozzle
The mold after sanding,   filling with sanding primer,   sanding again,   and lastly waxing
The mold after sanding, filling with sanding primer, sanding again, and lastly waxing

 

US Patent #10,710,247 Issued to Kirkwood et al for new Robotic “Conical Iris Gripper”

Overview drawing of the Kirkwoood Conical Iris Gripper
Overview drawing of the Kirkwoood Conical Iris Gripper

 

I was issued US Patent number 10,710,247 on July 14th 2020 for a new type of robotic gripper device uniquely able to grasp difficult-to-grasp food items.

Robotic preparation of food is hard, in part, because of the difficulty of grasping and manipulating  irregular shaped,  slippery, and loose aggregated cooking ingredients like chopped onions, shrimp, or loose lettuce.    I was asked to consider if any new design could meet the needs of this task, and prototype it.      From my previous work with mechanical iris development inspired by the international space station’s Canadarm gripper,  (see: CNC Bubble Iris), I realized that  a novel 3-degree-of-freedom gripper could meet this need:  a conical cage of slender spears, which could change shape in three different ways:   taper angle,   diameter, and extension.   With these motions, I realized it was possible to make a highly effective gripper with a wide repertoire of potential grasping strategies,   which could allow for a huge range of ingredients to be handled by a single gripper.  The gripper can  either open or close like a hand gathering and grabbing a handful of food, or stab like a fork, push food off of that fork, or dispense loose food items gradually, e.g. ‘sprinkling’.  This gripper went on to become the primary end effector for a $1M+ commercial research project

 

fig 2bsideview

The conical iris gripper was rapid prototyped with 3d printing,  laser cutting,  OEM parts, and a minimum of machining,  over the span of about two weeks with other projects going on. The initial prototype was made using mostly laser cut acrylic and off the shelf parts, with a materials cost of less than $150.

“Enabling Biomimetic Morphing UAVS” paper published in Twenty-Second International Conference on Composite Materials (ICCM22)

I’m pleased to release a new publication in the Twenty-Second International Conference on Composite Materials (ICCM22) entitled “Enabling Biomimetic Morphing UAVs”  with colleagues at SRI,  the University of Southern California,  and NextGen Aeronautics.

This publication details one application of a technology I have helped develop with my colleague Roy Kornbluh at SRI:  ultra-lightweight,   ultra-low power,    electrolaminate clutches and mechanical multiplexers for reconfigurable systems in aerospace, astronautical, and terrestrial robotic applications.

My role in this project was to mechanically design a demonstrator tailfin assembly using these new lightweight, low power, electrolaminate clutches to lock control surfaces of aerial vehicles into variable area configurations.    I did all the mechanical and electrical design as well as composite fabrication of the carbon fiber tail and rear fuselage assembly.

Internal structure of prototype electrolaminate clutched tailfin assembly I designed and built
Internal structure of prototype electrolaminate clutched tailfin assembly that I designed and built.
Screenshot_2021-01-07 Spedding_Enabling-Biomorphic-UAVs pdf
Molded carbon fiber tail assembly that I designed and built for the Air Force Office of Special Research as a demonstrator of new application of lightweight, low power, electrolaminate technology.

Abstract:       This paper reports on the design and testing of a practical, morphing wing aircraft. We intentionally mix and match elements of avian inspired design with novel technologies and proven mechanical components to provide a demonstrator aircraft that shows, in the simplest way, what benefits accrue from basic morphing changes. The simplest and most beneficial morphing concept is to change the wing area and aspect ratio, and it is easy to show that, for an otherwise fixed example configuration, a factor of three decrease in wing planform area can sustain a predicted lift:drag ratio, L/D = 9, when the vehicle flight speed U doubles from 12 to 24 m/s. Without morphing, L/D would be 6.5. Such a large area change can be achieved with a telescoping wing, which is not biomimetic, but is practicable and achievable using standard and custom 3D printed components. We combine this variation with a tail-body configuration that is bio-inspired, and suggested by previous and continuing work on the vehicle-level flight efficiency of tailless aircraft, where a standard tail geometry is replaced by a trailing edge flap that converts the cargo-carrying body into a lifting body. The practical shape-changing is enabled by the use of novel electrolaminate materials that can quickly change stiffness at varying positions/postures.

A small concert for solo cello, deep inside the Grand Canyon, during a meteor shower.

redwall_sunrise
Sunrise seen from The Coconino Plateau off the North Kaibab trail. This is a beautiful natural promontory and stage, but experiences high winds which, at least during the morning as I hiked past, made it unfit for performance.
redwall_best2
First sighting of my podium, the Redwall Bridge on the North Kaibab trail, 2.8 miles and 2000 vertical feet below the North Kaibab trailhead. The Redwall bridge is seen as a straight line deep in the canyon seen here.

Six years ago,    while hiking rim-to-rim in the Grand Canyon the first time,  I discovered a profoundly spectacular location with interesting acoustics.    A wooden footbridge crossed a chasm,   forming a convenient stage,    with sheer rock walls ascending close together towards the north rim of the canyon,   2000′  overhead.   The Kaibab trail is even, in spots, blasted as a “C” shaped ledge into a sheer-vertical cliff face.  It is the largest and most topographically interesting  space I’ve ever gotten to sing in.

What stood out, to my ears, was the unusual quiet and echo.   It is free of almost any noise,   except the sounds of small ground squirrels or birds traipsing in the underbrush.       But when a sound was made, the echoes were unusually distinct and lively.   Echoes would echo, and echo,  and it seemed you could hear the fourth or fifth reflection of a handclap.    I stopped and whistled  for a few minutes,     and remember thinking to myself,   wistfully:

“If only I had my cello”.  

Six years passed.  A time of incredible growth and flux,    and now I find myself living on the west coast,  theoretically within a day’s drive of this spot.    (13 hours from the San Francisco bay).   The idea is still high among my crowded backlog of adventures / projects / enthusiasms.  And then the news arrives,  that the Perseid meteor shower this year (a regular August occurrence) was expected to be unusually brilliant this year,  “The brightest in a decade” even.        This finally pushed the idea (of revisiting the North Kaibab trail with my Cello) from “A good idea worth doing sometime”  to “A unique opportunity to do now“.    I messaged a few friends to inquire if anyone wanted to go,  and join me in a middle-of-the-night cello recital on a footbridge in the canyon,   beneath the brightest meteor shower of a decade.    Many were interested but could not attend due to prior commitments,  or the extreme driving:hiking ratio (near 2:1).   but the invite met an enthusastic “yes” from a friend who, it turns out,  was more apt a fellow adventurer than I could have known beforehand:  not only a willing impromptu adventurer,  but an aerialist/gymnast/theatrical producer,  with extraordinary insights and ideas about unconventional audience/performer relationships.

Talking to Strangers: Lou Fischer

BuB motorcycle
The BuB streamliner motorcycle, piloted by Chris Carr, reached speeds in excess of 367 mph with a 3 liter, 500 horsepower turbocharged V4 engine on the Bonneville Salt Flats.

I was bicycling by starlight in the middle of the desert at midnight, 20 miles from anywhere in the middle of Goblin Valley Utah,  in late August.   Crossing deserts during the night is necessary when the daytime shade temperature is between 110 and 120F   (but there is no shade) and ground temperatures reach 150F before noon.  But it is also a treasure to ride in the desert at night:   the skies are the clearest of anywhere on the continent.  One day before the August full moon, I was riding by star and moon light alone.  Nobody was on the road. Cars would pass maybe once every half hour or two, and when they did, I could see their headlights ten minutes away, hear their roar minutes away.  I’d been riding this way, solo, for a few hours,   racing towards Moab Utah and Arches NP trying to arrive on my birthday and see the full moon rise over these incredible landscapes,   when I saw a parked car on the side of the road up ahead,  lights off, with it’s trunk open,   and a fellow standing next to it.

I should mention that,  when asking locals about what to expect,  before crossing the 100 miles of open Mojave desert a few weeks back,  from Joshua Tree  to the Colorado river,  multiple independent sources repeated several times “It’s where people go to bury bodies”,  or “Ever seen ‘the Hills have Eyes’?”.

But I saw his camera tripod, and no bodies. I had also benefited previously from a trucker who stopped and shared an ice chest of gatorade with me in the middle of the Mojave desert; here was an opportunity to pay it forward. And I was curious.

He certainly did not expect a jovial Gordon “hello, howdy! Are you okay, need any water?”.      Not when the loudest thing he’d likely experienced for his last hour was the click of his camera shutter,  sand underfoot, and what night creature sounds as occur in August, in the deep sand and sagebrush desert, at midnight. I tried to mitigate his shock by speaking from a respectful safe distance of 50 feet or so.

To say he was “Startled” would be an understatement.  We were in one of the most desolate places in the country, after midnight, in the dark. He’d probably felt himself the only person for miles,  ten seconds prior.  Bicycles riding by starlight are stealthy!  I saw him reassure  himself (discretely) of the location of a bottle of bear-spray on his hip,  his countermeasures.   I had sympathy for this;  I had done similar before hailing him.  We were both assessing each other. And then we talked.

We progressed quickly from threat-assessment  to rapport and shared enthusiasm. Two gearhead adventurers, alone in the middle of the desert at midnight talking under starlight .  We talked for over an hour.  Before I resumed bicycling,   he made me promise to message him before I arrived in Chicago, where he would host me.   He also took this picture of me,  which is one of my favorites of the whole trip:

Gordan
Photo by Lou Fischer. Gordon Kirkwood bicycle crossing Goblin Valley Utah through the night in late August.

Lou is a photography buff and documentary filmmaker, his youtube channel is “Bonneville Stories”.   Some of his work is linked below.   His brother held a land – speed record on motorcycles;  He was in town to document the fastest motorcycle in the world, the BuB streamliner motorcycle,   which reaches in excess of 360MPH  / Mach 0.5.    I often think of this encounter as one of the more rewarding “talk to strangers” lessons in good faith optimism… especially since pessimism could easily have prejudiced this introduction to nonexistence. It would have been easy to make an excuse to pass a car in a desolate area.

This story, from six years ago, came to mind in the context of two especially significant meetings this last week and next, discussing character development and education in science, technology, engineering and math with DARPA,  the Navy,   and a large private philanthropy whose director has honored me by asking for my input.  As I refine my thoughts I’m enjoying revisiting a few of these experiences which in retrospect seem like formative decision points or character building moments.    Stay tuned! 

Gordon Kirkwood Presents at historic NoiseBridge Hacker Space for DorkBot SF #82

It was an honor to be invited to present my work to the distinguished audience of DorkBot SF,  held at Noisebridge, a storied and significant Hacker space in San Fransico.

440px-Noisebridge_logo
DorkBot is an international speaker series whose self-summary is “People doing strange things with electricity“.  In this context I presented some of the more interesting robotic, high voltage photography,  and other technical art projects I’ve worked on, especially the recently completed CNC bubble iris.

Fellow presenters included the international director of the Electronic Freedom Foundation,  Danny O’Brien,   and Jesse Silver, the creator of BlumenLumen,   a kinetic origami sculpture featured at Burning Man.

Screenshot 2015-07-16 12.00.46
IMG_5321


IMG_5313

Screenshot 2015-07-16 12.02.41 Screenshot 2015-07-16 12.03.24

CNC Bubble Iris Article Released

http://www.instructables.com/id/CNC-bubble-machine
Photo of the CNC Bubble Iris taken at Autodesk’s Pier-9 facility by Nathan Hurst, Editor at Make Magazine.

 

It is a real pleasure to see Make Magazine,  Hackaday, and Adafruit #ArtTuesday all re-blog the publication of the apparatus I’ve built to bring new precision, repeatability, and automation to the craft of giant soap bubble making.  Stay tuned for all the interesting things I’ll use this instrument for!