Introduction: Hamsterloop


Want to get your hamster from one end of his cage to the other in milliseconds? Here's what you need. And all for the cost and complexity of a new Ferrari.

The idea of a Hyper-loop, propelling people at high speed between San Francisco & LA in 1/2 hour, caught my attention. So as a Hyper-loop enthusiast, retired software geek, and odd project builder, I thought it would be cool to build a working model of the system. It should have as many of the technical facets of the real project as possible so as to be a proof of concept, add to the enthusiasm for the real project, and be a kinetic art piece that would amuse and amaze.

  • The model would be a car zooming at high speed within a tube.
  • The car would ride on air bearings to minimize friction within the tube.
  • Propulsion would be by linear induction motors.
  • If possible, the tube would have at least a partial vacuum, with the intent to reduce air friction and allow super-sonic speeds.

All this must be done without breaking the bank, though as in full scale projects, and as you can see if you read on, the cost and effort tended to creep up.

I proposed this idea to a friend who is a retired electrical engineer and he readily agreed to help. His electronics expertise has been crucial to the effort, and his continued enthusiasm and support have kept me going.

What are shown as "steps" below are are components in the system.

Step 1: The Car - Overview

The car in its final form is about 15 inches long and two inches in diameter plus attachments. A ducted fan pulls air in at the front and pushes it out a set of feet. When running, air flows out through holes in the feet and into pockets. These pockets are the air bearings that eliminate friction and allow the car to glide over small bumps in the tube.

An Arduino Nano collects readings from a pressure sensor and acceleration/gyro sensor, and sends motor control signals to the fan speed controller. A Bluetooth module allows monitoring and control via Android phone.

General List of Miscellaneous Tools
   •	Set of drill bits
   •	Cordless Drill
   •	Fine blade hand saw
   •	Lots of blue tape (temp labels, lines, holding)
   •	Marking pens
   •	Electrical tape (the black stuff)
   •	Tape measure
   •	Calipers
   •	Soldering Iron, solder & shrink tubing
   •	Misc hand tools
   •	PC with Arduino IDE
   •	Android phone with BlueTerm app

Step 2: The Car - Housing

For the car housing, after initial attempts using 2 inch ABS plumbing pipe we settled on 2 inch clear acrylic tubing. Clear tubing lets you see what’s inside, and it turned out that the next smaller size tubing (1 3/4 inch) slides closely inside the 2” OD tubing. This made for neat assembly of the car front and back. Fan motor housing on the front, and plug with small hole in the back are fitted with sleeves of smaller diameter tubing and are held in place with set screws.

The acrylic tubing machines well and can be cut with woodworking tools. We were able to drill holes, cut large slots using a Dremel saw tool, and use a tap to cut threads for attaching feet and magnets.

A warning to any who work with acrylic tubing. Do not use denatured alcohol to clean off marks, etc. Although normally fine for cleaning flat acrylic, there must be a styrene component in the tubing that causes cracks to form when holes or cuts are touched with the methanol. The cracks didn’t hurt the structure, but marred the clean look.

List of Tools & Components
	Tools
	o	Cut-off saw – for accurate cutting lengths of acrylic tubes
	o	Dremel rotary tool with saw blade – for large cuts in acrylic tube
	o	Files – for cleaning up saw cuts and melted plastic
	o	Step drill – for larger holes (large drill bits are a bad bet) 
	o	#4 – 40 tap – for threads in tube to attach feet and magnets
	o	2” Hole saw - for plug for back of car
	o	Acrylic cement and applicator
	Components
	o	2” OD acrylic tubing - 12”, 2”, 1/2” lengths (body, front & back)
 		TAP Plastics Store $15
	o	1 ¾ OD acrylic tubing – 1” and ½” lengths (for sleeves to fit front
		 & back of car ) – TAP $10
	o	2” circle of 1/8” acrylic sheet – back of car (hole from hole 
		saw is fine)  – scrap from motor boxes
	o	#4 – 40 flat head screws 3/8 long x 14 for magnets, x 16 for feet 
		(Get spares. They’re tiny) – $3

Step 3: The Car - Feet

Air Bearings

Initially I had the notion of using compressed air captured in the car pushing out through the feet to form the air bearings. This was not feasible due to how much air it takes to make the car float. We quickly adopted the idea of a ducted fan at the front of the car. This aligns with the full sized hyper-loop design and provides a large volume of air.

Building the Feet

The four feet are affixed to the car at 90 angles and are built to conform to the convex curve of the car and the concave curve of the tube. There was a lot of trial and error getting the car to float successfully on it feet. After trying "flat" feet, we learned that air bearings need recessed pockets. This spreads out and contains the air that is compressed between the foot and the tube wall. Also, a large flow of air is more effective than a higher pressure jet.

Initially the car was concentric with the tube, but to make room for the linear induction motors, it was necessary to drop the car down a bit, still maintaining matching curves. This made the geometry a bit more complex, and left feet became different from right feet.

The feet were drawn in Sketchup and made in ABS plastic using my friend's 3D printer. This was a great technique since it allowed trial and error. In first attempts we glued the feet to the car. In the final design a flange was extended to allow the feet to be screwed onto the car. The Sketchup model had to avoid any unsupported horizontals for successful 3D printing.

List of Tools & Components

	Tools
	o	Sketchup PC program
	o	3D Printer
	o	Full sheets sandpaper – for truing up foot bottoms (& tops)
	o	18" scrap of 4” ABS (or PVC) pipe – for sanding bottoms to shape 
	o	Length of 2” tubing – for sanding foot bottoms to shape

	Components
	o	ABS filament for 3D printer about 250g - $7
		(this is for many tries at getting it right)

Step 4: The Car - Monitoring and Control

I wanted to measure the acceleration of the car and also the air pressure in the car. I placed an Arduino Nano in the car with pressure sensor and acceleration sensor. Addition of a Bluetooth module allowed the car system to be read and controlled from outside the tube (via Blue Term Android app).

Initially I had the Bluetooth module connected via Software Serial. This caused wild fluctuations in the signal sent to the fan controller pin, and we burned out a couple of electronic speed controls. Using the standard Serial pins 0 and 1 eventually solved this problem, but now requires unplugging the Bluetooth when loading an updated sketch.

List of Tools & Components

•	Tools - see general tools list
•	Components
	o	Arduino Nano V3 – Ebay $5
	o	28 pin DIP Socket – Wide – (30 pin not available) Ebay $2
	o	Prototyping board – Ebay $3
	o	HC-06 Bluetooth Slave Module – Ebay $7
	o	BMP180 Barometric Sensor Module – Ebay $9
	o	MPU–6050 3 Axis Gyro/Accel Module – Banggood China $3

This is the Arduino sketch used in the car.

// HyperloopCar
// Version 02 add accelerometer MPU6050
// Version 03 add ducted fan speed control (servo lib)
//            back off on SortwareSerial - just use pins 0 & 1
// Version 04 add gyro reading and roll calc for setting balancing servo
//       04b go back to SoftwareSerial 
//       04c add timer to turn off fan after x seconds 
// Version 05 comment out balancing servo code, display pressure when it changes 
//      05b  elim software serial since it caused interference on signal to ESC

#include "Servo.h"            //lib for running fan & servo
#include "MPU6050.h"           // accel/gyro sensor
///#include "SoftwareSerial.h"   //for BT connection to other pins



#include "SFE_BMP180.h"          // temp.pressure sensor
#include "Wire.h"		// less than and greater than signs get lost in
#include "I2Cdev.h"		// the crude Instructables editor

Servo esc;
Servo servo;
int throttle = 1300;    // default.  value to be set by BT serial input
#define runtime 15000   // time until it shuts down automatically
#define fanstop 700    // microsecond pulse with to stop fan 
long fanTime = 0;      // how long fan has been on

MPU6050 accelgyro;  //0x68 is default I2C address
                    //tie AD0 pin to 5v+ to set 0x69
     // for Roll calculation               
#define M_PI 3.14159265359
#define dt 0.01
#define gyroSens 65.536  //copied from web example ??? seems to work
                    
int16_t ax, ay, az;
int16_t gx, gy, gz;
int16_t ayMaxPlus, ayMaxMinus, axMax, azMax;  //save max values
//#define OUTPUT_READABLE_ACCELGYRO
#define cvtFtSec2 62  //conversion from raw to ft/sec sq
#define OUTPUT_BINARY_ACCELGYRO
float roll = 0;
  
//SoftwareSerial mySerial(5, 6); // RX, TX  Objst for BT serial
  
  
int ledPin = 2; 
char state = 0;
int flag = 0; 
char stat;   // variables for temp/pressure
double T,P,p0,a;
float psi;
float temp = 0;
float savePressure;
char ctlChar = '0';

SFE_BMP180 pressure;    //object for temp/pressure sensor
#define ALTITUDE 137.0 // Est Altitude in meters 
int pressureCtr = 0; 
//  BMP-180 Wiring
//Any Arduino pins labeled:  SDA  SCL  5+  Gnd
//Uno, Redboard, Pro:        A4   A5
//Mega2560, Due:             20   21
//Leonardo:                   2    3

int serialCtr = 0;  //don't look for serial i/p every time thru loop

void setup() {
  
   esc.attach(9, fanstop, 2000);    // pin for electronic speed control 
   // for ducted fan, min microsec pulse with,  and max to set range
   esc.writeMicroseconds(fanstop);  //set to off
      
   // servo.attach(10);    // pin for balancing servo 

   Wire.begin();  //needed?

   pinMode(ledPin, OUTPUT);
   digitalWrite(ledPin, LOW);
 
   Serial.begin(9600); // Default connection rate for my BT module
   Serial.println("HyperloopCar05b");

   if (!pressure.begin()) {
    Serial.println("BMP180 init fail\n\n");
    while(1); // Pause forever.
   }
   Serial.println("got to here");
    accelgyro.initialize();
    accelgyro.setFullScaleAccelRange(MPU6050_ACCEL_FS_16);  //change from default 2g range
    // verify connection
    Serial.println(accelgyro.testConnection() ? "MPU6050 connection successful" : "MPU6050 connection failed");
    Serial.print("0->9 = max fan");
    Serial.print(" s=fan start  o=fan off");
    Serial.println(" p=print readings  i=initialize ESC");
    pinMode(13, OUTPUT);
    digitalWrite(13, HIGH);  // all's well indicator
 
}
 
void loop() {
  serialCtr++;
  if (serialCtr > 10) {;  //check for user input occasionally 
    checkInput();
    serialCtr = 0;
  }
   
  if ((fanTime != 0) && (millis() - fanTime > runtime)) {
     stopFan();    // turn it off automatically
  }  
    
  
     // get info rom MPU6050, acceleration & gyro
  accelgyro.getMotion6(&ax, &ay, &az, &gx, &gy, &gz);  //get readings from MPU6050
  if (ay > 0 && ay > ayMaxPlus) ayMaxPlus = ay; //save fwd/bkwd accel raw value 
  if (ay < 0 && ay < ayMaxMinus) ayMaxMinus = ay;
  
/*  calcRoll(ax, ay, az, gy, &roll);  // calc roll. pass a & g values and ptr to roll
  int degRoll = roll * 100;
  // Serial.print("degRoll=");Serial.println(degRoll);
  //delay(100);
  int servoangle = map(degRoll, -50, 50, 60, 135);  //map +-50deg angle to servo maxes
                      // adj mapping to tune balance
  if (abs(degRoll < 50)) {
   // Serial.print("angle=");Serial.println(servoangle);
    servo.write(servoangle); // move trim servo to keep balance
  }  
*/
  pressureCtr++;
  if (pressureCtr > 100) {  //only do this every once in a while
    pressureCtr = 0;
    checkPressure();    //get current psi & temp
   // Serial.print("savePressure=");Serial.print(savePressure);
  //  Serial.print("  psi=");Serial.println(psi);
    if (abs(savePressure - psi) > .5) {
       savePressure = psi;
       dispReadings();
    }  
  }
}


int ms;
void startFan()  {  //increase speed gradually to max
  fanTime = millis();    //save the time when we turn it on 
  Serial.print(" Up to "); Serial.print(throttle); Serial.print(" max for "); 
  Serial.print(runtime / 1000);Serial.println(" seconds");
  for (ms=fanstop; ms <= throttle; ms+=12) {
    //  Serial.print(" pulse ms="); 
    //  Serial.println(ms);
      esc.writeMicroseconds(ms);
      delay(125);
  }  
}

void stopFan()  {
  Serial.println("Stopping the fan");
  for (ms=ms-100; ms >= fanstop; ms-=25) {
//      Serial.print(" pulse ms="); 
//      Serial.println(ms);
      esc.writeMicroseconds(ms);
      delay(125);
  }
  fanTime = 0;
}

void checkInput() {  
  if (Serial.available() > 0){  // see if any user input
      ctlChar = Serial.read();
      Serial.println();
      int i = ctlChar - '0';  //convert singe char to integer
      if (i >= 0 && i <= 9)  { //set throttle
             throttle = map(i, 0, 9, 1300, 1800);  //vary bewteen low & high
             Serial.print(" max throttle pulse set to "); 
             Serial.println(throttle);
      } else if (ctlChar == 's')  {
          startFan();
      } else if (ctlChar == 'o')  {
          stopFan();
      } else if (ctlChar == 'p')  {
          dispReadings();
      } else if (ctlChar == 'i')  {
          initializeESC();
      }
   } 
}
/*
void calcRoll(int16_t aax, int16_t aay, int16_t aaz, int16_t ggy, float *roll) {
  float rollAcc;
  // integrate gyro data , cvt angular speed to angle
  *roll -= ((float)ggy / gyroSens) * dt;  // angle 
  // compensate for drift wil accel data if valid
  int forceMagnitudeApprox = abs(aax) + abs(aay) + abs(aaz);
  if (forceMagnitudeApprox > 8192 * forceMagnitudeApprox < 32768) {
    // turning around the x & z axes results in vector on the y axis
    rollAcc = atan2((float)aax, (float)aaz) * 180 / M_PI;
   *roll = *roll * 0.98 + rollAcc * 0.02;  // smooth it in
  }  
}
*/

void initializeESC()  {  // "train" esc to recognize arduino throttle
//   long savTime = millis();
   esc.writeMicroseconds(2000);  //set to max
   Serial.println("Now sending max power to ESC");
   Serial.println("Turn on power to ESC, wait 2 sec, and hit any key when ready");
   while (!Serial.available()); // wait for input 
   Serial.read();
 //    delay(2000);    // for >2 seconds
   esc.writeMicroseconds(fanstop);  //set to min
   Serial.print("...");
   delay(2000);
   Serial.println("done");
}


void dispReadings()  {   // diplay readings on Serial device


    Serial.print(" ayMax="); Serial.print(ayMaxPlus / cvtFtSec2);
    Serial.print("  ");  // temp & pressure
    Serial.print(temp);
    Serial.print("F  ");
    Serial.print(psi,1);
    Serial.println(" psi");
}

void checkPressure()  {
  stat = pressure.startTemperature();
  if (stat != 0)
  {
    delay(stat);  // Wait for the measurement to complete:

    // Retrieve the completed temperature measurement:
    // Note that the measurement is stored in the variable T.
    // Function returns 1 if successful, 0 if failure.

    stat = pressure.getTemperature(T); // Retrieve the completed temp
    if (stat != 0)
    {
      // Start a pressure measurement:
      // The parameter is the oversampling setting, from 0 to 3 (highest res, longest wait).
      // If request is successful, the number of ms to wait is returned.
      // If request is unsuccessful, 0 is returned.

      stat = pressure.startPressure(3);
      if (stat != 0)
      {
        // Wait for the measurement to complete:
        delay(stat);
        temp = T;      //Farenheit it turns out
   //     temp = (((9.0/5.0)*T)+32.0);      //needed for Farenheit

        // Retrieve the completed pressure measurement:
        // Note that the measurement is stored in the variable P.
        // Note also that the function requires the previous temperature measurement (T).
        // (If temperature is stable, you can do one temperature measurement for a number of pressure measurements.)
        // Function returns 1 if successful, 0 if failure.

        stat = pressure.getPressure(P,T);
        if (stat != 0)
        {
           psi = P*0.014503773773;
        }
        else Serial.println("error retrieving pressure measurement\n");
      }
      else Serial.println("error starting pressure measurement\n");
    }
    else Serial.println("error retrieving temperature measurement\n");
  }
  else Serial.println("error starting temperature measurement\n");
  // delay(2000);
}

Step 5: The Car - Fan and Power

I was able to buy a 40mm ducted fan that fit perfectly within the 2” OD acrylic tubing. The fan motor has a large high amperage wire for each of three phases. The power to the fan is provided by a specialized Electronic Speed Controller (ESC). The fan and its controller are model aircraft hobbyist technology, and because of this, are relatively inexpensive for being quite high tech gear.

To run the fan, initially we used a radio control transmitter and receiver. But the Arduino and its Bluetooth link to the outside world allowed us to have the fan motor under program control.

The Arduino uses a Servo library to send the ESC pulses of different microsecond durations to determine motor speed. We noticed that when the fan started and even as it ran, the floating car tended to twist in one direction. To minimize the start-up torque we had the Arduino ramp up the fan speed gradually. To counteract the ongoing torque we designed a backward fan and glued it in place behind the motor. This serves to counteract the twist the fan is putting on the column of moving air. We went so far at one point as to use the acceleration/gyro sensor to have the Arduino move a fin in response to car movement (yaw). Although a great learning experience, this was way too complicated and also ineffective. The most effective way to counteract the torque turned out to be a slot cut slightly offset in the bottom of the car so the heavy battery could project downward at an angle and act as ballast.

The fan and its controller (ESC) are a set of lore unto themselves. The ESC requires initialization to set the "servo angles" (actually the microsecond pulse widths) at which the motor is Off and at which the motor is at full speed. The ESC communicates via a series of beeps, amazingly using the fan motor itself as a speaker! The ESC is rated for 30 Amps. They get very hot and we burned up more than one ESC during the learning process - when the fan was obstructed for instance.

To power all this is a Lipo (Lithium Polymer) battery. We are using a 3 cell, 11.1 volt battery which promises 800 milliAmpereHours. The first battery we bought did fit in the car but at 1800 mAh was easily twice as heavy and quite a bit larger. Of course one needs a special charger and adapters for these rascals.

List of Tools & Components

Tools - see general tool list plus
   o	Lipo Battery charger – local hobby store $50-$60 (oof)
Components
   o	Ducted fan – brushless motor - 40mm – Ebay $32
   o	Electronic Speed Control (30A for brushless motors) – Ebay $7
   o	EC3 Female to JST Male Battery Adapter Ebay $7
   o	11.1V 25C 800MAh Lipo Battery – Ebay $9
   o	15A Lighted Rocker Switch – auto parts store $5


Step 6: The Car - Magnets

For the car portion of the linear induction motor I attached two lines of permanent magnets at 90 degree angles. The magnets are Neodymium (rare earth), 5/8 inch diameter 1/8 inch thick with countersunk holes. The are mounted with alternating polarity. More on this in the section describing the linear induction motor.

List of Tools & Components
Tools - see general list
Components
    o	Niodymium Disc Magnets 5/8” dia 1/8” thick w hole 10N & 10S 
		- AmazingMagnets.com $35

Step 7: Linear Induction Motor - Overview

A linear induction motor is an electric motor set out in a line rather than turning around an axis. The are surprisingly few good examples on the web for us to shamelessly copy, so we were forced to do rather a lot of trial and error.

One of the few examples of linear induction motors we found used the motors to move a mag-lev car a short distance under joystick control. Our motor however needs to accelerate the car as quickly as it can so that the speed gained can carry it a distance through the tube. So the motor doesn't need to be the length of the tube, only long enough to get the car moving fast.

For early testing we used a little wooden car on wheels and a single set of coils. This proved that we could make it happen with our own coils and control logic.

The system we settled on uses independent sensor/coil pairs. When a sensor sees a permanent magnet (PM), its coil is energized (along with the matching coil on the other side of the tube) independent of all other sensor coil pairs. Spacing of PMs versus coils is arranged so that not all coils will come on at once. This makes power demands more manageable and smooths out forces applied to the car.

The motors we ended up with are two pairs of acrylic boxes, one pair about 12 inches by 1 inch wide by 3/4 inch deep, the other pair (our first attempt) about 8 inches long. Each motor pair has one box containing sensors and coils and another box containing coils only. The boxes are mounted in the tube so that the PMs on the car pass close to sensors and coils. An Arduino Mega and specially built power board respond to the sensors and send power to the coils to draw the PMs to the coils. When one PM is being drawn in, its predecessor PM is pushed away from the same coil (PMs are arrayed in alternating polarity).

Step 8: Linear Induction Motor - Coils

We had to wind our own coils since there didn't seem to be any available for purchase. Note our funky setup for winding. Magnet wire (#30) was tensioned by a sewing machine bobbin winder and the winding is done with an electric drill controlled by a variac power supply to moderate the speed. Magnetic strength is a function of Amps and turns, so for uniformity we attached a bicycle speedometer to the drill hub to count the turns. Our coils have about 840 turns and measure 10-12 Ohms each.

Each coil is wound around a 1/4 inch ID brass tube 5/8 inch long. We applied super glue as we wound so the coil would stay together. The brass tubes worked well for mounting onto 1/4 inch OD acrylic rods, cemented into the motor boxes.

Coils with iron cores generate stronger magnetic fields, but we thought that the permanent magnets on the car would be drawn to the core whether the coil was turned on or not. So we went with hollow core "solenoid" coils.

When soldering leads onto the coil magnet wire it takes a bit of scraping to remove the insulation. It's important to color code the wires so wires from the same coil have the same color. Coil sequence and polarity can be figured out by testing later.

List of Tools & Components

Tools
   o	Sewing machine – to dispense and keep tension on wire
   o	Electric drill
   o	Bicycle speedometer – to count revolutions (winds)
   o	Variac – to control speed of electric drill while winding
   o	Plastic or nylon disks 1 1/2” dia, 1/4” hole – temporary bobbin ends while 
		winding coils
   o	1/4 diameter bolt and nut to hold bobbin together and for drill to turn 
Components
   o	Magnet wire 30 AWG – 1lb (>3000feet) – Ebay $18
   o	¼ OD brass tubing – cut to 5/8” for coil cores - $4
   o	White paper circles – glue to ends of coil while winding
   o	Waxed Paper – prevent glue from sticking to nylon disks
   o	Superglue – several tubes
   o	#22 wire, at least 4 colors
   o	Wire protector/wrap  1/4”  - Harbor Freight $2


Step 9: Linear Induction Motor - Sensors

Initially we tried a three phase approach where three sets of coils would come on when called for by proximity of permanent magnets, but this made spacing critical and resulted in a complex system. It was a far simpler to have a sensor for each coil pair and energize the coil pairs independently.

The sensors are 3503 Ratiometric linear Hall Effect sensors. Ratiometric means the signal they generate varies depending on (the square of) the distance and that they distinguish North from South (via plus or minus readings).

It's important when soldering sensor leads to keep them skinny so they will fit into the hollow tube. It's also important for the sensors to use appropriately colored wires so when they're all bunched together you can tell which is 5v+, which is ground, and which is the signal. Which signal is which sensor can be figured out later in testing.

List of Tools & Components

Tools - see list of general tools
Components
   o	3503 Hall Effect Ratiometric Sensors – x 8  - Ebay 10 for $2.50
   o	#22 wire, at least 4 colors
   o	Wire protector/wrap  ¼”  - Harbor Freight $2

Step 10: Linear Induction Motor - Housing

Acrylic boxes contain and organize the coils and sensors. The boxes (one 12 inch and one 8 inch) are in two pieces: a cover and a base. The cover has a 1" thin (1/16) strip of acrylic as the face cemented to two sides 3/4" (1/8) acrylic. The thin cover also has little pieces of thick (1/4) acrylic cemented in at the ends and center which are tapped to accept mounting screws. The base is 1/8 acrylic with 1/4 holes drilled to accept solid rod to hold the coils and 1/4 hollow tube to hold the sensors.

Our first motor (pair of boxes) has the coils spaced at 1 1/4 inch centers. In the second motor, the coils are spaced twice as far apart at 2 1/2. We don't think that this spacing is significant other than keeping it so permanent magnets on the car don't line up at the same time (PMs are at 1 1/2 inch centers).

List of Tools & Components

Tools
   o	Table saw w fine tooth (plywood) blade for slicing acrylic
   o	Acrylic cement and applicator

Components (for 2 motors of 4 coil pairs each) 
   o	Acrylic sheet pieces for 4 boxes – scraps from TAP Plastics - $5
     o	   1/16   1” wide 12” long x 4 bottoms of boxes (face downward toward car)
     o     1/8   3/4” wide 12” long x 8 sides of boxes
     o	   1/4 thick 3/4 x 1/2  x 12 - glued to center & ends of box 
		bottoms to accept screws
     o     1/8 3/4 wide 12” long x 4 – base to hold rods for coils 
		(& tubes for sensors)
   o	1/4 OD acrylic rod – cut to 5/8” x 16 – posts to hold coils – TAP $4
   o	1/4 OD acrylic tube – cut to 5/8” x 8 posts to hold sensors – TAP $4


Step 11: Linear Induction Motor - Power and Controls

At first we used Adafruit Motor Controller shields to provide power to the coils. The Arduino digital output pins provide 5volts at a rather low current. The motor controller shield uses the small digital signal to send up to 1 Amp of power to motors, in this case our coils. To use the Adafruit boards we also used the library of Arduino code they provide. Although this did work and moved the car, it did not move it strongly.

Eventually we opted to build out own driver board using parts from electronic salvage stores in the area. With our own board, each coil's circuit can accommodate a peak of 8 Amps of current, and each driver can power the coils on both sides of the car that are activated together (wired in parallel). We had been using a 12 volt power supply. To increase the power to the coils we purchased a 10 Amp 24 volt power supply. They sell these quite cheaply online to power LED strips, and they work fine for our purpose.

For the final approach for controls we used an Arduino Mega since it had plenty of analog and digital pins available. The Arduino sketch (program) is rather simple in that it loops around looking at each sensor and deciding if there is a significant reading and if so, is it North or South. If it's North, that sensor's coil is turned on to South (so it will pull). And vice-versa.

For early testing we used a little wooden car on wheels and a single set of coils. At one point as it was starting to work, we observed "cogging", stuttering of the coils pulling on the magnets in an uneven way. In the Arduino sketch we had Serial.print commands sending readings to the console to see what was happening. Eliminating the Serial.print statements sped up the sketch so it moved the little wooden car nicely.

At another point in testing, we put in timestamps at the beginning and the end of the loop to see how long everything was taking. The loop took 20 to 25 milliseconds. This is way too slow to react to a car moving at our hoped for 20 feet a second. I tried taking out the calls to the Adafruit library and the time went down to between 0 and 1 millisecond. OK then. That confirmed the decision to make our own power driver.

List of Tools & Components

Tools - see general list of Tools

Components
   o	24v 10A Power Supply – Ebay $22
   o	Arduino Mega – Ebay $20
   o	Toggle switch
   o	Controller/Driver Board 
	o  Part #	quantity	price	cost
	o  TIP122	16		0.59	9.44
	o  Tip125	16		0.49	7.84
	o  1n4148	16		0.05	0.8
	o  IN5231	16		0.09	1.44
	o  2n3904	8		0.12	0.96
	o  resistors	40		0.04	1.6
	o  Proto PCB	1		5	5
					TOTAL	27.08

This is the sketch used to run the motors.

/* 
LinMotorIndepCoil
For use with the Adafruit Motor Shield v2 
Version 01 - Independent Sensor/Coil sets, up to 4 sets per motor board
   Assumes car with PMs is pushed into first sensor/coil pair
   Permanent magnets are spaced w alternating polarity on car 
   so when pulling on one PM, there will be pushing on the previous.
Version 02 - abandon resing/falling tests and use static value of sensors
   to set coils. Use Serial input to adjust sensor sensitivity
Version 03 - log times for coils on/off etc and 
             save timings for print later when interrupt tripped                      
             Set timeouts to prevent coils from staying on too long
Version 04 - no more Adafruit motor shields - 8 coil pairs
*/

//#include <Wire.h>

int hallPin[] = {0,1,2,3,4,5,6,7};  // analog pins for Hall sensors
int coilN[] = { 34, 30, 28, 22, 36, 32, 26, 24 }; 
                              // digital pins for setting coil to N
int coilS[] = { 35, 31, 29, 23, 37, 33, 27, 25 }; 
                              // digital pins for setting coil to S

#define HowMany 8     // how many sensor/coil pairs we have 
int hallVal[] = {0,0,0,0,0,0,0,0 };    //saved values for Hall sensors
int hallTrim[] = {0,0,0,0,0,0,0,0 };   // value to be subtracted so 
                                       // "no field detected" will read 0
#define hallThresh 60                  // what reading is significant

#define coilMaxTime 1000   //one second should be plenty of time
long coilHoldTimeS[HowMany] = {0,0,0,0,0,0,0,0};  // to prevent staying on too long
long coilHoldTimeN[HowMany] = {0,0,0,0,0,0,0,0};  // to prevent staying on too long

long coilOnTimeS[HowMany] =  {0,0,0,0,0,0,0,0};  // when coil came on - first S magnet only
long coilOffTimeS[HowMany] = {0,0,0,0,0,0,0,0};  // when coil came off 
long coilOnTimeN[HowMany] =  {0,0,0,0,0,0,0,0};  // when coil came on - first N magnet only
long coilOffTimeN[HowMany] = {0,0,0,0,0,0,0,0};  // when coil came off 

volatile boolean printFlag = false;  // flag to indicate button pushed to start printing


//long testStart = 0;
//long testEnd = 0;
//String sense = "0";


void setup() {
  Serial.begin(9600);           // set up Serial library at 9600 bps
  Serial.println("Starting LinMotorIndepCoil04");
  
  for (int i=0; i< HowMany; i++)  {   //make sure coils are off
      pinMode(coilN[i], OUTPUT);    
      digitalWrite(coilN[i], LOW);    //  set N pin to low 
      pinMode(coilS[i], OUTPUT);    
      digitalWrite(coilS[i], LOW);    //  set S pin to low 
  }
  
  for (int i=0; i < HowMany; i++)  {    //set sensor trim values assuming no field initially
    hallTrim[i]  = analogRead(hallPin[i]);  //sb close to 0 if no field
  }  
  
  pinMode(2, INPUT);    //  set up interrupt on pin 2
  digitalWrite(2, HIGH);
  attachInterrupt(0, setPrintFlag, FALLING);  //pin 2 is interrupt 0

}
 

void loop()  {
 //testStart = millis();
   if (printFlag == true) printStats();  
         // button pushed, take time to print stats
 
      // look at each of the active sensors to see how its coil should be set
   for (int i=0; i < HowMany; i++)  {  
     int testVal = analogRead(hallPin[i]) - hallTrim[i];  //sb close to 0 if no field
     if (testVal > hallThresh) {    // South pole PM detected
        // sense = "S";
        //Serial.print("South testVal=");Serial.print(testVal);
        //Serial.print(" i=");Serial.println(i);
        digitalWrite(coilN[i], HIGH);    //  set N pin to high 
        digitalWrite(coilS[i], LOW);     //  set S pin to low 
        coilHoldTimeN[i] = 0;          // reset N hold time
        if (coilHoldTimeS[i] == 0) {
          coilHoldTimeS[i] = millis();  //save the time
        }
        if (coilOnTimeS[i] == 0) {
          coilOnTimeS[i] = millis();  //save the time for 1st magnet only
        }
        if (millis()- coilHoldTimeS[i] > coilMaxTime) {
           turnOffCoils(i);  // if coil has been S too long, turn it off
        }  
     } else if (testVal < -hallThresh) { // North
      //sense = "N";
        //Serial.print("North testVal=");Serial.print(testVal);
        //Serial.print(" i=");Serial.println(i);
        digitalWrite(coilN[i], LOW);    //  set N pin to low 
        digitalWrite(coilS[i], HIGH);     //  set S pin to high 
        coilHoldTimeS[i] = 0;          // reset S hold time
        if (coilHoldTimeN[i] == 0) {
          coilHoldTimeN[i] = millis();  //save the time
        }
        if (coilOnTimeN[i] == 0) {
          coilOnTimeN[i] = millis();  //save the time for 1st magnet only
        }
        if (millis()- coilHoldTimeN[i] > coilMaxTime) {
           turnOffCoils(i);  // if coil has been N too long, turn it off
        }  
      } 
      else {    // no signigicant reading
  //   sense = "0";
        //Serial.print("Neither testVal=");Serial.print(testVal);
        //Serial.print(" i=");Serial.println(i);
         turnOffCoils(i);  //they get turned off, 
                 // but the coilHoldTime doesn't get reset
         
      } 
   } 
 //testEnd = millis();
// Serial.print("test ms=");Serial.print(testEnd - testStart);Serial.print(" ");Serial.println(sense);
// delay(2000);  
}  

void  turnOffCoils(int i)  {
  //Serial.println("turnOffCoils");
        digitalWrite(coilN[i], LOW);    //turn off the coil 
        digitalWrite(coilS[i], LOW);    //
        if (coilOnTimeS[i] > 0 && coilOffTimeS[i] == 0) {     //first time S off for this coil
           coilOffTimeS[i] = millis();   //time stamp it
        }   
        if (coilOnTimeN[i] > 0 && coilOffTimeN[i] == 0) {     //first time N off for this coil
           coilOffTimeN[i] = millis();   //time stamp it
        }   
        // leave coilHoldTime as it is until magnet polarity changes
}  
  
void setPrintFlag() {    //interrupt service routine
   printFlag = true;
}

void printStats() {      //only do this if interrupt happens
   for (int i=0; i < HowMany; i++) {  //for each of 4 coils
      Serial.print("Coil "); Serial.print(i+1); 
      Serial.print(" S On Time "); Serial.println(coilOffTimeS[i] - coilOnTimeS[i]);
      Serial.print("Coil "); Serial.print(i+1); 
      Serial.print(" N On Time "); Serial.println(coilOffTimeN[i] - coilOnTimeN[i]);
    }
    printFlag = false;
    for (int i=0; i < HowMany; i++) {  //reset counters after printing
       coilOnTimeS[i] = 0; coilOnTimeN[i] = 0;
       coilOffTimeS[i] = 0; coilOffTimeN[i] = 0;
    }   
 }

This is the sketch used to test the sensors and coils.

/* CoilTest3 to work with hall sensors and our own motor driver board
  reads all the sensors and prints the value
  reads input 1-8 from serial and turns that coil to North for 5 seconds

*/
int hallsave[8]; //no-reading value for setting sensor reading to zero
int halls[8];

int coilN[] = { 34,30,28,22,36,32,26,24}; 
// digital pins for setting coil to N - arranged for jumper geography
int coilS[] = { 35,31,29,23,37,33,27,25}; 
// digital pins for setting coil to S
#define howMany 8

void setup() {
  //while (!Serial);
  Serial.begin(9600);           // set up Serial library at 9600 bps
  Serial.println("CoilTest3");
  Serial.println("Input 1 thru 8 to run specific coil pair for 5 seconds");

  for(int i=0;i<howMany;i++) {      // save no-reading values
      hallsave[i] = analogRead(i);  
  } 
    
     // make sure all coils are off
  for(int i=0;i<howMany;i++) {  
      pinMode(coilN[i], OUTPUT);
      digitalWrite(coilN[i], LOW);  
      pinMode(coilS[i], OUTPUT);
      digitalWrite(coilS[i], LOW);  
  }
}

// int i;

void loop()  {
    for(int i=0;i<howMany;i++) {  
      halls[i] = analogRead(i) - hallsave[i];  //sb close to 0 if no field
      Serial.print(halls[i]);  Serial.print("  ");       // debug value
    }
    Serial.println("");  
    delay(1000);
    
    char cmd ;
    if (Serial.available()) {
        cmd = Serial.read();    //read character from serial
    }  else return;
    
    if (cmd >= '1' && cmd <='8') { 
      int x=cmd-48;
      digitalWrite(coilN[x - 1], HIGH);  // turn on coil set to N
      Serial.print("testing #"); Serial.println(x);
      delay(2000);
      Serial.println("off");
      digitalWrite(coilN[x-1], LOW);  // turn off both coils of pair 
    }  
}  



Step 12: The Tube

Early in the design we settled on 4 inch corrugated drainage pipe (non perforated) as a reasonable tube for the
model. Rigid PVC or ABS plumbing pipe is more expensive and the joints between sections are not as even. Four inch clear acrylic tubing would be wonderful, but would be prohibitively costly.

The drainage tubing is not perfectly smooth inside due to the corrugations, but my assumption was right that the car does in fact ride above the bumps on its air bearings.

The drainage tubing is inexpensive and light, and I can visualize laying out a very long stretch of it to demonstrate speed. It is opaque however and it was necessary to cut viewing slots in various portions. For a vacuum, these will have to be covered with a transparent material and sealed.

To maintain a vacuum in the tube we will need an air lock. This would allow the car to be placed into the tube without losing existing vacuum. It would also simulate a real system with passenger stations. For an air lock door, we will try a “blast door” from a shop dust collection system. To pull the vacuum, we will initially use a shop vac. This moves a lot of air but does not bring the pressure down much. If the air lock system is successful, we will try an air conditioning system vacuum pump. As the reader might determine from verb tense, the vacuum portion of the project is still in the future.

List of Tools & Components

Tools - see general list of tools

Components
   o	4” Drainage Pipe –  10 foot section – Home Depot $7


Step 13: Connections

Cables from Coils

Coil pairs are wired in parallel. For each motor of 4 coil pairs, I brought together the same end (e.g. North) of each coil pair and soldered them to a single lead going to the control box. In that way, the number of wires going into the controller was cut in half. I kept all the North ends together, making one cable with the North leads for each of 4 coils. Same process for the South ends yielded a cable with South leads for the same 4 coils. Repeated for the second motor, gave a total of 4 4-wire cables heading toward the control box.

Cables from Sensors

The leads coming from the sensors contained 3 wires each: 5v+, Ground, and Signal. For each motor (4 sensors) I combined the 5v leads and combined the ground leads. This made a cable with only 6 wires going to the control box, this repeated for the second motor.

Jumpers From Arduino to Driver Board

For the 8 coil pairs (4 pairs in each "motor"), there are 16 Arduino pins that tell the driver board which coil to energize and which at what polarity. For example, for given coil, if the even numbered pin is set High (5v+) and the odd numbered pin set Low, the coil is turned on to North. It should never happen that both odd and even pins are both High, although there are extra transistors in the driver board to prevent disaster if this occurs. The 16 jumpers also have protection in the form of diodes, to prevent any damaging current from flowing toward the Arduino. We arrived at this cautious approach the hard way.

Control Box

To keep the electronics contained and neat I put them into a box I had from a previous decommissioned project. My goal was to make the whole system transportable and storeable. This required being able to unplug the electronics from the tube where the motors are mounted. The Arduino Mega, Driver Board, and Power Supply were mounted onto the 3/4 inch plywood base. The acrylic cover already had 3 4-pin jacks so I bought a 4th and that allowed all the coil cables to come in nicely. I bought two sets of 9-pin D-sub connectors (male & female) and soldered them onto the sensor cables. This is all on the "back".

On the from are the ammeter (useful to see what the coils are drawing), an On/Off power switch, and a button to trigger the interrupt that prints statistics.

List of Tools & Components

Tools - see general list of tools

Components
   o	Component Box left over from previous project
     o	  Plywood base to hold Arduino, Motor Controller Board, Power Supply 
     o    Acrylic Cover
   o    4 pin Microphone Jack and Socket x 4 – 
		connect coils to Controller Board – Radio Shack $35
   o	9 pin D-sub connectors (M & F) x 2 – connect sensors to Arduino Mega – 
		Radio Shack $10 
   o	Crimp lugs – connect wire ends to Controller Board – Hardware Store $5
   o    Wire - 4 colors
   o    Jumpers to connect Arduino pins to Controller Board - $1
   o    Ammeter - with shunt added to handle 20Amps - from "inventory"
   o    Toggle switch - also from inventory (previous projects) - $1
   o    Push button for interrupt - also from inventory


Step 14: Testing and Tuning

One of the requirements is that the car move slowly under fan power so it will move into range of the linear motors. The first video clip shows it needing a nudge.

Once it gets hold, the motor it really does shove the car quickly down the tube. This is prior to any tuning of sensor thresholds. Once the geniuses leveled the table so the car didn't have to start uphill it does move slowly on its own into the motor. Now the tuning begins.

Tuning consisted of establishing the best sensor thresholds for turning on and turning off the coils. The Arduino sketch that runs the coil records the time when each coil is first turned on and also when turned off. Looking only at the duration between coil 1 and coil 8 and also coil 5 and coil 8 we found that a sensor reading of 60 was best for the turn-on threshold. For the turn-off, the best sensor threshold was about 70. (60 and 70 are millivolts above or below the no-reading level (about 512) on the sensor Analog pins.

Since we were recording all coil Ons and Offs, I was curious to see if there were any surprises. The chart above shows the raw millisecond readings and some Excel arithmetic. There wasn't much acceleration for the final 3 coils. On the theory that the processor wasn't switching the coils fast enough we replaced the Mega with a much faster Due. There was not much difference in performance.

I think our approach to the linear induction motor ran up against some inherent limitations. That is, as the car accelerates, it spends less and less time being pulled by the coils. This reduces the force being exerted by each coil thereby reducing acceleration. If anyone out there has better ideas for using linear induction motors for acceleration, not just propulsion, I'd be happy to hear about them.

Overall however, even without trying to pull a vacuum in the tube, I feel the project was successful if not stellar. Fun too!

Coded Creations

Participated in the
Coded Creations

Move It

Participated in the
Move It

3D Printing Contest

Participated in the
3D Printing Contest