Final Project
So the project turned out to be fairly successful. The
project was basically building upon the carcass of an Exkate electric
skateboard (the deck, trucks, and battery). I was mainly
concerned with the electronics and electronics box (the old one
couldn't fit my electronics). The main components of the class I used
were PCB making (I made a board on the Modela with parallel voltage
dividers, a Hall effect current sensor, and voltage regulator to step
36V down to 12 for the microncontroller, which in turn had a 5V
regulator), molding/casting (I cast my electronics box out of urethane
plastic, making a halving mold out of polystyrene on the ShopBot),
waterjet cutter (the supporting plate for the battery, the battery
terminals, and some of the plates for the trucks), I/O devices (the
sensors and motor), and microcontroller programming (using the Arduino,
with a non-final build of my code appended (I'm still messing with
calibration constants and scaling factors)). The LED ended up
being quite useful. It's blue during calibration or while it's
waiting for you to get back on, green to varying degrees depending on
the PWM signal you're sending to the motor, and red to varying degrees
depending on the brake signal you're sending to the motor (a
combination of 0 PWM on the motor pin and variable PWM on the DISABLE
pin).
http://www.youtube.com/watch?v=UTdc1Z80crQ


Problems:
This project has several major flaws which need to be addressed in a
v2.
The first flaw is the FlexiForce variable resistor force sensors, which
tend to drift in my setup. Tightening them down between trucks is
not the best way to do it. I tried to use tape in the interstice
where the force sensors were laid, to let the plates act like a spring
across the fulcrum of the tape. This worked well enough that I
have been able to ride it around extensively without breaking or
dislodging anything. However, it's not very elegant. I have
some calibration routines and offset constants I use to more or less
correct this, but it's not great.
It's a bit jumpy. I have no way of telling how fast I'm going so
my ramping scheme is tough to implement completely smoothly. It's
also inherent to the design of having shifting weight accomplish
acceleration. You need to be skilled to provide smooth
input. This will be alleviated somewhat when imbalances in my
system are removed.
The board is heavy. It is heavier than it needs to be. I am
grateful for the heavy-duty axles and protective plating, but some of
the structural plating on the trucks, as well as the board itself, are
a bit overdone (better than underdone). The SLA battery is also
god-awful. The next one gets designed around one of the better
Chinese LiFePO4 packs.
Paths Forward:
Key off strain gauges on flexures, where the entire board is a flexure
pivoting around a central fulcrum. Attach the trucks to each
other by a stiff structural element, and let the flexure ride above
this.
Put in a decent 10-15Ah LiFePO4 battery (flat), which cuts about 6-8lbs
and nearly doubles range (from 4-6 to 10-12). Make the flexure
out of carbon fiber, not wood. Keep the exkate trucks, but
consider machining some structural elements down.
Make electronics surface-mount and monolithic on one board, using
structural steel as a heat sink for the N-MOSFETS on the H-bridge
(means developing a Modela-able surface mount version of the
OSMC). If the arduino is not machined on the same board (no
reason not to), use the Pro instead of the Diecimila. Essentially
cut the size of the electronics box to 2x3x1" (from 4x7x2.5").
Make the code open and see who wants to improve and build their own.
Code: (some of it is still ugly) (it comes in at 4-5kB)
Note: I have a Processing analog of this code that lets me output any
of these values to a graph in real time (with computer
connected). This has proven quite useful to get me up and going.
//Stephen Kuhn
//MAS 863 Final Project
int motorPin = 9; //sends the PWM control signal to the motor
int brakePin = 10; //pulsing full 255 bits of PWM = cruise, pulse down
from 255 for variable brake
int redPin = 3;
int greenPin = 5;
int bluePin = 6;
int speakerPin = 11; //make this a digital out pin
//int HEpin = 4; //Hall Effect current sensor pin
int front1 = 0; //front variable resistor force sensors, read off a 5V
voltage divider
int front2 = 1;
int back1 = 3; //rear force sensors
int back2 = 4;
int front; //these will be the front averaged values during operation
int back;
int start_front; //these will be used to take the riderless
baseline of values
int start_back;
boolean calibrated = false; //this is the riderless calibration
boolean rider = false; //this is a
flag which ends calibration() and starts operation (after a delay) i.e.
someone gets on
boolean rider_thrown = false;
int offset=0; //this is going to be used to counter drift in the
force sensor balance, initialized off the startup calibration routine
int balance=0;
int pwm_speed=0;
int pwm_brake=0;
int red_LED=0; //PWM for the status LEDs
int green_LED=0;
int blue_LED=0;
int pause = 140; //pause 50 milliseconds between taking readings
int prior_pwm=0; //this is a holder variable used in ramping
int prior_brake=0;
int prior_front=0;
int prior_back=0;
void beep(int n_secs) {
int x;
int freq = 1432; //freq = period/2 (in microseconds) of the tone
to be played (in this case f)
long delayAmount = (long)(1000000/freq);
long loopTime = (long)(n_secs*1000000/(delayAmount*2));
for (x=0;x<loopTime;x++) {
digitalWrite(speakerPin,HIGH);
delayMicroseconds(delayAmount);
digitalWrite(speakerPin,LOW);
delayMicroseconds(delayAmount);
}
}
void calibrate() { //Get your baseline values (here, average over
3 seconds);
analogWrite(motorPin, 0);
analogWrite(brakePin, 0);
analogWrite(bluePin, 255);
delay(2000);
int n_secs = 3;
int length_start_vals=15;
int start_front_left[length_start_vals]; //will hold some number
of values of the four sensors to be time averaged to get a start value
int start_front_right[length_start_vals];
int start_back_left[length_start_vals];
int start_back_right[length_start_vals];
int sum_front=0; //temp variables to compute an average
int sum_back=0;
int cal_pause = round((float)n_secs*1000/length_start_vals);
//in milliseconds
for (int i=0;i<length_start_vals;i++) { //in this case, we're
averaging 30 values for each of the four sensors, over 3 seconds
start_front_right[i] =
analogRead(front1);
start_front_left[i] = analogRead(front2);
start_back_right[i] = analogRead(back1);
start_back_left[i] = analogRead(back2);
analogWrite(bluePin,
0);
delay(cal_pause/2); //delay 100ms
analogWrite(bluePin, 255);
delay(cal_pause/2);
}
for(int i=0;i<length_start_vals;i++) { //average the
two front sensors and the two rear sensors, then we'll create a sum
over the warmup period to get a start average
sum_front += (start_front_right[i] +
start_front_left[i])/2;
sum_back += (start_back_right[i] +
start_back_left[i])/2;
}
start_front = round(sum_front/length_start_vals);
start_back = round(sum_back/length_start_vals);
offset = start_front-start_back;
int margin = 50; //Exceeding this amount of analogRead()
value (out of 1023) above the startup values will trigger the end of
calibration i.e. someone has gotten on
//Note: how tight you tighten the trucks will affect this
margin. Ideally, you'd want a low startup value (around 100-200
out of 1023), so that
//you have some resolution when someone gets on i.e. you're not
saturating the sensor with the force of tightening down the truck
while (!rider) {
analogWrite(bluePin, 255);
front = (analogRead(front1) + analogRead(front2))/2;
back = (analogRead(back1) + analogRead(back2))/2;
if ( front > start_front + margin || back >
start_back + margin)
rider = true;
calibrated = true;
delay(1000); //wait to get settled
}
analogWrite(bluePin, 0);
}
void setup()
{
//Don't need to set analog pins to INPUT or OUTPUT
pinMode(speakerPin, OUTPUT);
analogWrite(brakePin,0);
analogWrite(motorPin,0); //we want to have the brakes on
before and while we get on
}
void loop()
{
if (!calibrated) {
beep(1);
calibrate();
beep(1);
}
front = (analogRead(front1) + analogRead(front2))/2;
back = (analogRead(back1) + analogRead(back2))/2;
//this is designed to moderately stop the board if the rider is
thrown
int riderless_constant = 30;
while ((front + back)/2 < ((start_front + start_back)/2 +
riderless_constant) && (prior_front + prior_back)/2 <
((start_front + start_back)/2 + riderless_constant) ) { //you're going
to have to play around with this
analogWrite(motorPin, 0);
analogWrite(brakePin, 30);
analogWrite(bluePin, 255);
analogWrite(greenPin, 0);
analogWrite(redPin, 0);
rider_thrown = true;
front = (analogRead(front1) + analogRead(front2))/2;
back = (analogRead(back1) + analogRead(back2))/2;
}
//someone has tried to get on after being thrown
if (rider_thrown == true) {
analogWrite(motorPin, 0);
analogWrite(brakePin, 0);
analogWrite(greenPin, 0);
analogWrite(redPin, 0);
analogWrite(bluePin, 255);
delay(200);
rider_thrown = false;
front = (analogRead(front1) + analogRead(front2))/2;
back = (analogRead(back1) + analogRead(back2))/2;
prior_pwm = 0;
prior_brake = 0;
analogWrite(bluePin, 0);
}
int scaling_offset = 30;
balance = front - back - offset - scaling_offset;
if (balance > 0) {
pwm_speed = round(balance*1.9);
//limit the slope of brake or accelerate to help
alleviate current concern
float a = 0.88;
pwm_speed = round(((a * prior_pwm) + ((1.0 - a) *
pwm_speed)));
if (pwm_speed > 255)
pwm_speed = 255;
prior_pwm = pwm_speed; //setting prior_pwm for the
next loop
analogWrite(brakePin, 0);
analogWrite(motorPin, pwm_speed);
}
else {
pwm_speed = 0;
prior_pwm += round(0.2*pwm_brake);
//this is decrementing prior_pwm only a bit to reduce jumpiness due to
ramping while already at high speed
if (prior_pwm < 0)
prior_pwm = 0;
if (balance > -15)
pwm_brake = round(0.8*balance);
else if (balance > -30)
pwm_brake = round(1.3*balance);
else if (balance > -50)
pwm_brake = round(1.8*balance);
else if (balance > -80)
pwm_brake = round(2.3*balance);
else pwm_brake = round(2.8*balance);
//pwm_brake will be a negative value
float b = 0.25;
pwm_brake = round(((b * prior_brake) + ((1.0 -
b) * pwm_brake)));
if (pwm_brake < -255)
pwm_brake = -255;
prior_brake = pwm_brake;
analogWrite(motorPin, 0);
analogWrite(brakePin, 255 + pwm_brake);
//brakes are maximum for a PWM signal of 0 (with motorPin at 0), cruise
is PWM of 255
}
//Use Hall Effect Sensor to limit the current
/*if (pwm_speed > 0) { //this will be positive current
while(analogRead(currentPin) > 150)
{ //150 will have to be determined from datasheet, ideally this
should correspond to 20-25A
analogWrite(motorPin,
pwm_speed--);
}
else { //this will be negative current (motor acting as
sink)
while(analogRead(currentPin) < 100)
{ //100 will have to be determined from datasheet, corresponding
to -15A (this is unlikely)
analogWrite(brakePin,
pwm_brake++); //Remember here that pwm_brake = 255 is no brake
and incrementing pwm_brake reduces brake
}
}*/
prior_front = front;
prior_back = back;
if (balance > 0) {
analogWrite(redPin, 0);
green_LED = pwm_speed; //get rid of this 3 later or
replace this metric with acceleration
if (green_LED > 255)
green_LED = 255;
else if (green_LED < 0)
green_LED = 0;
analogWrite(greenPin, green_LED);
}
else {
analogWrite(greenPin, 0);
red_LED = pwm_brake*-1;
if (red_LED > 255)
red_LED = 255;
else if (red_LED < 0)
red_LED = 0;
analogWrite(redPin, red_LED);
}
delay(pause); //delay x milliseconds (quantization necessary in
establishing ramping criteria)
}