Friday, March 18, 2016

Using Proportional Control

In the last blog post I had demonstrated that bang-bang is an effective means of control.
however,  in some instances, you don't want the suddenness of the change in bang bang control from suddenly being on to suddenly being off.  Instead you might want some smoother changes.
Enter proportional control.  In this situation, the amount of action is directly dependent on the amount error, which is the difference between the ideal situation, and the actual difference.  The error determines how much change you actually need.

Example 1:
Our first attempt at proportional control was to get our Sciborg to go straighter.
This proportional control works by using the difference between the left and right motor encoder values, which indicates the amount of deviation from a straight line, to correct the difference in motor speeds.
For example, if the left motor was ahead of the right motor, then we should feed more power and speed to the right motor and less to the left motor, and vice versa.

#include <Encoder.h>
#include <PID_v1.h>
#include <BricktronicsMotor.h>

// Select the motor port (MOTOR_1 or MOTOR_2) in the constructor below.
//
#include <Wire.h>
#include <Adafruit_MCP23017.h>
#include <BricktronicsShield.h>
BricktronicsMotor m1(BricktronicsShield::MOTOR_1);
BricktronicsMotor m2(BricktronicsShield::MOTOR_2);
  unsigned long previousMillis = 0;        // will store last time motor reversed direction
  int rpms1 = 255; //speed of motor (range is 0-225)
  int rpms2 = 255;
  int32_t left;
  int32_t right;
  int32_t error;
  int k = 4;

void setup() {
  // Be sure to set your serial console to 115200 baud
  Serial.begin(115200);
  BricktronicsShield::begin();
  // Initialize the motor connections
  m1.begin();

  m2.begin();
}

void loop() {
  // put your main code here, to run repeatedly:
  goforward();
}

void goforward()
{
 rpms1 = 160;                //initializes speed of sciborg
 rpms2 = 160;
 m1.setFixedDrive(rpms1);
 m2.setFixedDrive(rpms2);
 left = m1.getPosition();
 right = m2.getPosition();
 error = right - left; 

 if (left != right)      //while left encoder value is larger than right encoder value = too right
 {
  rpms1 = 160 + error*k;               //slow down left motor, speed up right motor 
  rpms2 = 160 - error*k;
  m1.setFixedDrive(rpms1);
  m2.setFixedDrive(rpms2);
  left = m1.getPosition();
  right = m2.getPosition();
 }
 Serial.print(" - leftm: ");
 Serial.println(left);
 Serial.print(rpms1);
 Serial.print(" - rightm: ");
 Serial.println(right);
 Serial.print(rpms2);
}

This was quite successful.
It went much straighter than trying to fidget with different motor speeds to compensate for the different motor powers.
We did it on the lab floor and it went very straight.
Going on the carpet it also went pretty straight.
Going up and down the ramps were also pretty straight.




Example 2:
Our second go at proportional control was to get our Sciborg to travel exactly 10 feet using the encoder values.   In this situation, we took the average of the left and right encoder values and that would tell us how far the sciborg had already traveled.   If we knew how much we had left to move, which is the total distance to be driven minus the distance already driven then we could calculate the speed at which the sciborg ought to move.   The more distance that was left to travel, the faster the sciborg should move.  The gain in this case was 0.015.

code for going 10 feet using encoder values
#include <Encoder.h>
#include <PID_v1.h>
#include <BricktronicsMotor.h>

// Select the motor port (MOTOR_1 or MOTOR_2) in the constructor below.
//
#include <Wire.h>
#include <Adafruit_MCP23017.h>
#include <BricktronicsShield.h>
BricktronicsMotor m1(BricktronicsShield::MOTOR_1);
BricktronicsMotor m2(BricktronicsShield::MOTOR_2);
  unsigned long previousMillis = 0;        // will store last time motor reversed direction
  float rpms1 = 255; //speed of motor (range is 0-225)
  float rpms2 = 255;
  int32_t left;
  int32_t right;
  float error;
  float k = 4;
  float DISTTOGO = 14500;
  float distk = .015;
  float avg;
  float remain_dist;
  
void setup() {
  // Be sure to set your serial console to 115200 baud
  Serial.begin(115200);
  BricktronicsShield::begin();
  // Initialize the motor connections
  m1.begin();
  m2.begin();
}

void loop() {
  // put your main code here, to run repeatedly:
   goforward();
   while ( left > DISTTOGO){
    rpms1 = 0;
    rpms2 = 0;
       m1.setFixedDrive(rpms1);
       m2.setFixedDrive(rpms2);
  } 

void goforward()
{

 left = m1.getPosition();
 right = m2.getPosition();
 avg = (left + right )/2;  // distance traveled already
 error = right - left; 
 remain_dist = DISTTOGO - avg;

 if (remain_dist*distk+75 > 160) {
  rpms1 = 160;
  rpms2 = 160;
  }
  else {
 rpms1 =  remain_dist*distk + 75;                //initializes speed of sciborg
 rpms2 =  (remain_dist*distk + 75);
   }

 m1.setFixedDrive(rpms1);
 m2.setFixedDrive(rpms2);

 Serial.print(" - leftm: ");
 //Serial.println(left);
 Serial.println(rpms1);
 Serial.print(" - rightm: ");
 //Serial.println(right);
 Serial.println(rpms2);
}


It worked okay on the lab floor.  It went pretty much 10 feet because we had programmed it to go 10 feet on the lab floor.



When testing the Sciborg on carpet and up the ramp, it didn't go the full distance.
It only reached about 9 feet of carpet.
It didn't have enough power to push to 10 feet on the carpet.
Similarly going up the ramp was challenging for the motors as the set minimum motor speed that would effect a movement on flat hard surface was too low for going up the ramp, and so, the sciborg stopped early.


When we tested it going down the ramp we saw that the sciborg traveled faster down the ramp then it had done on the flat surface because gravity was now helping to pull it along its intended direction of travel.

Part 3
The third attempt relating to proportional control was to get the sciborg to follow something using ultrasound, except how fast it was going would be dependent on how far away it was from what was in front of it.
Interestingly, the proportional approach was the first approach that I had thought of, when in thought
It is like the going 10 feet but rather than distance left to travel being the difference between a fixed 10 feet and distance already traveled the distance left to travel is simply the distance that is read by the ultrasound reader.  The farther the distance the faster the sciborg should travel until it reaches 11 cm away from what it in front of it.


#include <BricktronicsMotor.h>
#include <BricktronicsShield.h>
BricktronicsMotor m1(BricktronicsShield::MOTOR_1);
BricktronicsMotor m2(BricktronicsShield::MOTOR_2);

const int ultrasonicPin = A0;    // declare the input pin for the potentiometer

float rpms1 = 255;
float rpms2 = 255;
float A = 205/62;
float k = 435/62;
float k1 = 435/62;
int dist; 

void setup() {
  Serial.begin(9600);
  BricktronicsShield::begin();
  
  // Initialize the motor connections
  m1.begin();
  m2.begin();
}

void loop() {
  int value = analogRead(ultrasonicPin);
  Serial.print("Ultrasonic Reading = ");
  Serial.println(value);
  dist = value; 
  rpms1 = dist*A + k;  
  rpms2 = dist*A + k;
  m1.setFixedDrive(rpms1);
  m2.setFixedDrive(rpms2);
  Serial.print("left rpmps = ");
  Serial.println(rpms1);
  Serial.print("right rpmps = ");
  Serial.println(rpms2);

  delay(10);
}  

This is the result:



it worked pretty well.  it didn't go straight but that was because we didn't code to go straight, and even though we set the RPMS to be the same for both motors, it still ended up going crooked.
This video does demonstrate that it does follow and it does follow.

Part 4:
Finally we had to get our Sciborg to follow a line:
the way we got our sciborg to follow a line was was to get it to follow the boundary of light and dark. We set the light-dark boundary to be 85.  The idea is that if the light value is too light, then it is to the left of the light-dark boundary and it should turn right, and if the light value is too dark, then it is right of the light-dark boundary and it should turn left.  How much it needs to turn is determined by how much the read in light value deviates from the light value at the light dark boundary which we set to 85.

Here is the code:

#include <Encoder.h>
#include <PID_v1.h>
#include <BricktronicsMotor.h>


// Select the motor port (MOTOR_1 or MOTOR_2) in the constructor below.
//
#include <Wire.h>
#include <Adafruit_MCP23017.h>
#include <BricktronicsShield.h>
#include <BricktronicsLight.h>

BricktronicsMotor m1(BricktronicsShield::MOTOR_1);
BricktronicsMotor m2(BricktronicsShield::MOTOR_2);
BricktronicsLight ls(BricktronicsShield::SENSOR_2);

unsigned long previousMillis = 0;        // will store last time motor reversed direction
int rpms1 = 255; //speed of motor (range is 0-225)
int rpms2 = 255;
int32_t motor1;
int32_t motor2;
int light;
float k =3;
  
void setup() {
  // Be sure to set your serial console to 115200 baud
  Serial.begin(115200);
  BricktronicsShield::begin();
  // Initialize the motor connections
  m1.begin();
  m2.begin();
  // Initialize the light sensor connections
  ls.begin();
}

void loop() {
  // put your main code here, to run repeatedly:
  // if see white underneath then continue
  light = ls.scaledValue() - 85;
  Serial.print("Value :");
  Serial.println(ls.scaledValue());
  rpms1 = 160-k*light;
  rpms2 = 170+k*light;
  if (rpms1 < 100) {
    rpms1 = 100;
  }
  if (rpms2 < 105) {
    rpms2 = 105;
  }
  Serial.print("Left rpms");
  Serial.println(rpms1);
  Serial.print("Value");
  Serial.println(rpms2);
  
  m1.setFixedDrive(rpms1);
  m2.setFixedDrive(rpms2);
  delay(2);
}


and this is my code running correctly.


Actually, because I failed to communicate the working version to my partner, my partner came up with her own version of this using a different light dark boundary that worked even better  - 88 instead of 85.




Monday, March 14, 2016

9 feedback and control

Feedback and control systems include sensing, computation and actuation.
Sensing, means sensing part of the environment.
Computation, taking the sensed input and figuring out what action to do.
Actuator the action that you do as a result of the sensing

Some real life examples of feedback and control systems are found in the bathrooms.
These are some examples
Automatic hand dryer:
They sense the presence of your hands in the dryer, probably with ultrasound and then they turn on the dryer as your hands are in there, and turn it off when you aren't there
Automatic flush toilets:
These can be seen in some airports. They sense movement away from the toilet.  They don't flush when something approaches the sensor but when people leaves the toilet, they know to flush the toilets. The actuation is probably unplugging a plug to push water into the toilet bowl
automatic lights:
these lights sense any movement in the room.  When they sense movement, they turn on the lights, and then they also compute a time from the last time that they sense movements. and then cut the power to the lights to turn off the lights.
automatic water faucet:
they sense when there is a hand placed underneath the faucet and during that time, they let water run over your hands.   This is probably done with a sensor that senses distance and position of the hands and anytime something is within that range of distance and position(that should match up with just below the faucet), then it knows to turn on the water, but only during that time for which that is true.

automatic soap dispenser:
This is like the automatic water faucet, it does not dispense soap for as long as you hand is underneath it.  As soon as it senses that something is underneath the dispenser, it dispenses one serving of soap and then it waits for the next time that something comes into the correct position and distance (underneath the soap dispenser)

Automatic paper towel dispenser:
This is like the soap dispenser, except that it notices motion at a certain position and distance away from its sensor, and then dispenses one serving of paper towels.


Additionally a lot of things relating to driving and cars feature feedback and control.
Car doors in motion :
Cars will sense when it is moving and when it is moving it will automatically lock the doors to the  car such that you can't accidentally open the doors to the doors.

oil lights
oil lights senses the level of gasoline and displays it. once the level of oil reaches a critical point, then it turn on an indicator light that says you might want to add oil

check seat belt:  the car senses if there is something of significant weight in the front seat and senses if the seat belt in the front seat is buckled.    If there is something of significant weight in the front seat (indicating a person and not )




pedestrian crossings:
that sense when there are pedestrians waiting to cross and when they do sense the presence of pedestrians on the pedestrian crosswalk, not just passing by but waiting for a certain amount of time that it keeps track of at a certain location, then it turns the crossing lights to stop car traffic to let the pedestrian cross.   Using Ultrasound seems like a good idea here.

Red light cameras:
They sense when the red light has been turned and they take a picture.  They compute if there is a car in the image, then the camera saves the image of the red light passing car and then

Letting cars pass:
sometimes in the early morning, certain traffic lights at certain junctions will recognize that if there are cars in one direction and there are no cars in the other direction, that they will change the lights for the cars.  They have to sense when there are cars in all four directions in order

Letting ambulances pass:
lights can sense when there is an ambulance or a firetruck passing through and change the lights such that the lights are green for the firetrucks and red for potentially interfering lanes


Computers:
will automatically shut off if you haven't interacted with it for a certain time.
It senses the lack of keyboard or mouse or touch pad inputs.  It calculate how long that since the last interaction input, and if it is more than a certain length of time, its actuation is to lock your screen and turn off the screen, both to protect your privacy and to save power on the light intensive screen.


Thursday, March 10, 2016

Sciborgs and BangBang Control



So, if looking at just time spent running isn't accurate, what is?  Maybe if the motor could encode what distance it traveled, then we could know judge 10 feet by the distance both motors traveled?

The good news is that Sciborgs and Arduino already have this functionality.  The sciborgs motors encode what angular distance they have traveled.
• MotorEncoder

//code for MotorEncoding // Include the Bricktronics Motor library and helper libraries

#include <Encoder.h>
#include <PID_v1.h>
#include <BricktronicsMotor.h>


// Select the motor port (MOTOR_1 or MOTOR_2) in the constructor below.
//
#include <Wire.h>
#include <Adafruit_MCP23017.h>
#include <BricktronicsShield.h>
BricktronicsMotor m1(BricktronicsShield::MOTOR_1);

  unsigned long previousMillis = 0;        // will store last time motor reversed direction
  int rpms = 225; //speed of motor (range is 0-225)


void setup()
{
  // Be sure to set your serial console to 115200 baud
  Serial.begin(115200);
  BricktronicsShield::begin();
  // Initialize the motor connections
  m1.begin();

}

void loop() 
{
 // create a variable called motor1 which will be used to record the motor encoder position
  int32_t motor1;
  
 //reverse the motor direction every second; print the encoder value at the point of reversal. 
  if(millis() > previousMillis + 1000){
       rpms = -1*rpms; //reverse motor direction by multiplying its value by -1
      m1.setFixedDrive(rpms);
    previousMillis = millis();
      motor1 = m1.getPosition();
  Serial.print("motor 1 position - ");
  Serial.println(motor1);
 }
   
}
//
Interestingly enough, we found that every time that the motor switched from going forward to going backward that it would travel a little more forward than backward.


Perhaps the motor does not instantaneously switch form full speed forward to full speed backwards and a bias towards going forward causes a gradual net forward motion?  Or it could be that the motor faces more friction in one direction than in the other?

We used the encoder abilities to figure out how far to travel. We wanted to find how the encoder to actual distance traved was so that we can back calcuated how much encoder value we need to get to 1000 feet.  When we ran the Sciborg for a value of 3000, we found that the Sciborg traveled about 27 inches.  Thus if wanted the Sciborg to travel 120 inches (10 ft), then we should aim for an encoder value of 1333.   In this code, we have the sciborg travel 13400, and this successfully landed us on the 10 foot line.  This method of measuring the actual travel of the axles is much more accurate than using timing the length of travel.

//code for running a sciborg 10 feet using Motor Encoder Values
// Include the Bricktronics Motor library and helper libraries

#include <Encoder.h>
#include <PID_v1.h>
#include <BricktronicsMotor.h>


// Select the motor port (MOTOR_1 or MOTOR_2) in the constructor below.
//
#include <Wire.h>
#include <Adafruit_MCP23017.h>
#include <BricktronicsShield.h>
BricktronicsMotor m1(BricktronicsShield::MOTOR_1);
BricktronicsMotor m2(BricktronicsShield::MOTOR_2);
  unsigned long previousMillis = 0;        // will store last time motor reversed direction
  int rpms1 = 255; //speed of motor (range is 0-225)
  int rpms2 = 255;
  int32_t motor1;
  int32_t motor2;

void setup()
{
  // Be sure to set your serial console to 115200 baud
  Serial.begin(115200);
  BricktronicsShield::begin();
  // Initialize the motor connections
  m1.begin();
  m2.begin();
}

void loop() 
{

  

  while (motor1 > 16000){
    rpms1 = 0;
    rpms2 = 0;
       m1.setFixedDrive(rpms1);
       m2.setFixedDrive(rpms2);
  } 
      
      motor1 = m1.getPosition();
      motor2 = m1.getPosition();
//  Serial.print("motor 1 position - ");
//  Serial.println(motor1);
     m1.setFixedDrive(rpms1);
      m2.setFixedDrive(rpms2);

}

//





• Touch_Switch_Sciborg_Front
Another method of stopping the sciborg is to stop it when it hits something.  When it hits something it should stop running the motors.  This behavior can be accomplished by using a Touch Switch mounted to the front of the Sciborg, that sends a signal to the Arduino microcomputer when something is touching the touch switch (aka, when the front of the sciborg containing the Touch Switch has hit something).

//code for using a touch switch

const int buttonPin = 13;     // the number of the Arduino pin connected to the switch

int buttonState = 0;         // variable for reading the pushbutton status

void setup() {
  // initialize the pushbutton pin as an input:
  pinMode(buttonPin, INPUT_PULLUP);
    Serial.begin(9600);

}

void loop() {
  // read the state of the pushbutton value:
  buttonState = digitalRead(buttonPin);

  // check if the pushbutton is pressed.
  if (buttonState == HIGH) {
  Serial.println("button is unpressed");
  }
  else {
    Serial.println("button is pressed");
  }
  delay(10);  
  }
//

when the button is pressed it says the button is pressed.

example of touch sensor working!



• LightSensorNXT
Yet another way to sense the environment is to use a a light sensor.  A light sensor sense the amount of light hitting the sensor. Obviously if the sensor is too close to the surface, then the sensor is casting a black shadow that makes reading difficult, and if the sensor is too far from the surface, then outside light blur distinctions between a light and dark surface.  We found that the most accurate distance for the sensor to be from the surface that it is sensing, was just over half a centimeter away.  When it is looking at a dark surface, then little light is being reflected into the light sensor, and the light sensor will have a low reading, and when it is looking at a light surface, then much light is being reflected into the light sensor and the light sensor will have a high reading.  The only problem is with reflective dark surfaces, which depending on the angle of light, may absorb light, appearing dark, or reflect light directly into the light sensor, thus appearing light.   The range of light values ranges from 0 to 100 for a scaled value with smaller values being darker tones, and it is pretty consistent across consistent color in consistent lighting conditions.  I would have been concerned if our sciborg were required to distinguish light and dark surface in different lighting conditions, but thankfully that was not the case.    The sensor also has the option to turn on a LED light, however in my experience turning on the LED light did not help in our situation.  The strength of the LED light in conjunction with the ambient light tended to wash out the distnction between dark and light surfaces, making dark and light surfaces difficult to distinguish.

//code for light sensor

// Include the Bricktronics Light sensor library
#include <BricktronicsLight.h>


// Select the sensor port for the Light sensor (SENSOR_1 through SENSOR_4) below.
// For ports 3 and 4, use the jumpers to connect pins 2-3 and 4-5.
//
#include <Wire.h>
#include <Adafruit_MCP23017.h>
#include <BricktronicsShield.h>
BricktronicsLight ls(BricktronicsShield::SENSOR_2);


void setup() 
{
  // Be sure to set your serial console to 115200 baud
  Serial.begin(115200);

  BricktronicsShield::begin();

  // Initialize the light sensor connections
  ls.begin();
}


void loop()
{
  Serial.print("Raw value: ");
  Serial.print(ls.value());
  Serial.print(" - scaled value: ");
  Serial.println(ls.scaledValue());

  // Turn on the light sensor's built-in led (optional)
//  ls.setFloodlightAlways(true);
  delay(100);
}
//
• Ultrasonic_sensor -- LV-MaxSonar-EZ Series:
Yet another option of sensing is to use an ultrasonic sensor, which turns your sciborg into a poor mecatronic model of a bat.  ultrasonic sensors emit high frequency sound above human hearing and listen for echos, and the length of time between a sound and its echo in order to determine the sensors distance from a surface.  This is really cool because this is the only method we have seen so far to see farther than the immediate surroundings of the sciborg and see into the distance.
However, the resolution of even our high performance sonar range finder pales greatly to the sonar of living animals like bats, dolphins and the ocasional clicking "blind" person.  It detects wide flat surfaces really well because wide flat surfaces produce a clear reflection.  It doesn't detect smaller and round surfaces like chair or table or human feet.
//code for testing out ultrasonic sensor
const int ultrasonicPin = A0;    // declare the input pin for the potentiometer

void setup() {
  Serial.begin(9600);
}

void loop() {

  int value = analogRead(ultrasonicPin);

  Serial.print("Ultrasonic Reading = ");
  Serial.println(value);

  delay(100);  

}  //

//code for ultrasonic sensor being used in our sciborg
#include <BricktronicsMotor.h>
#include <BricktronicsShield.h>
BricktronicsMotor m1(BricktronicsShield::MOTOR_1);
BricktronicsMotor m2(BricktronicsShield::MOTOR_2);

const int ultrasonicPin = A0;    // declare the input pin for the potentiometer

int rpms1 = 220; //speed of motor (range is 0-225)
int rpms2 = 215;

void setup() {
  Serial.begin(9600);
  BricktronicsShield::begin();
  
  // Initialize the motor connections
  m1.begin();
  m2.begin();
}

void loop() {

  int value = analogRead(ultrasonicPin);

  Serial.print("Ultrasonic Reading = ");
  Serial.println(value);
  if (value > 12){
    m1.setFixedDrive(rpms1);
    m2.setFixedDrive(rpms2);
  }
  else{
    m1.setFixedDrive(0);
    m2.setFixedDrive(0);
    delay(10000);
  }
  delay(100);  

}   //

Example of our ultrasonic sensor working well to stop the Sciborg as it nears a surface:






Driving straight using Bang Bang control.  
Or at least trying to...
In this code, if we see that the sciborg is veering off to one side (through a mismatch of the encoder values for the two wheels) then it will make a gentle turn to correct the the mismatch


well, it works alright up and down ramps

and it works alright on carpet
and of course on our hard floor
Our results weren't very reproducible.   Each time the differences in friction on the two wheels were different, resulting in different amounts of veers, but the bangbang control helped significantly in steering the sciborg straight.

Following a line:
We tried multiple different search strategies before we got to this one.  This method follows the light dark  
We used this code and the light sensor to get our sciborg to follow a line on the ground.
// successful bang bang control to follow a path.
#include <Encoder.h>
#include <PID_v1.h>
#include <BricktronicsMotor.h>


// Select the motor port (MOTOR_1 or MOTOR_2) in the constructor below.
//
#include <Wire.h>
#include <Adafruit_MCP23017.h>
#include <BricktronicsShield.h>
#include <BricktronicsLight.h>

BricktronicsMotor m1(BricktronicsShield::MOTOR_1);
BricktronicsMotor m2(BricktronicsShield::MOTOR_2);
BricktronicsLight ls(BricktronicsShield::SENSOR_2);

  unsigned long previousMillis = 0;        // will store last time motor reversed direction
  int rpms1 = 255; //speed of motor (range is 0-225)
  int rpms2 = 255;
  int32_t motor1;
  int32_t motor2;


void setup() {
  // Be sure to set your serial console to 115200 baud
  Serial.begin(115200);
  BricktronicsShield::begin();
  // Initialize the motor connections
  m1.begin();
  m2.begin();
  // Initialize the light sensor connections
  ls.begin();
}

void goforward()
{
 rpms1 = 160;
 rpms2 = 160;
 m1.setFixedDrive(rpms1);
 m2.setFixedDrive(rpms2);
}

// pivots left
void left()
{
 rpms1 = 0;
 rpms2 = 140;
 m1.setFixedDrive(rpms1);
 m2.setFixedDrive(rpms2);
 delay(2);
}

void right()
{
 rpms1 = 140;
 rpms2 = 0;
 m1.setFixedDrive(rpms1);
 m2.setFixedDrive(rpms2);
 delay(2);
}

void loop() {
  // put your main code here, to run repeatedly:
  // if see white underneath then continue
  if(ls.scaledValue() > 95) {
    goforward();
  }
  // if don't see white underneath 
  else if (ls.scaledValue() > 80) {
    right();
  }
// if it is dark underneath
  else {
   left();
  }
}//

Monday, March 7, 2016

Sciborg, Intro

Last week we played around with Arduinos and seeing how they could control things.
This assignment we put that control to use to control sciborg. what is a sciborg?
This is a sciborg.

Its just a little robot lego cart with a computer.

This assignment was meant to introduce us to functions.  Functions are useful.  Learn to use functions judiciously.
What are functions? Functions are procedures or routines that may or may not return a result.  you can give them names and then call the functions.   when you call the function, the program does everything in the function that the function is supposed to do.   Functions are particularly useful for procedures or routines that are or can be expected to run more in more than one place in the code.  rather than typing the same code in two parts of the code file, you can place the commonalities into a function and call the same function from the two places that the function is needed and that will not only reduce the amount of code that one needs to write, but it increases abstraction of tasks and it makes finding mistakes and changing mistakes easier,


We explored functions in our SOS assignment:
In this assignment we had to rig up a LED to flash a SOS signal:
This was the code that enabled us to flash an SOS.

After this we moved on to the actually running the sciborgs.
before running the sciborgs we had to download and use a lot of libraries, that contained functions that allowed us to control and get feedback the different systems on a Sciborg.
These are the downloaded libraries.
what are libraries?  Libraries are collections of functions for a purpose.  Libraries exist so that person B, the user, can use code that person A, the author, wrote without having to rewrite that code themselves.   Ideally, the libraries are well abstracted enough the user won't need to even look at the code that author wrote, but simply use the functions and systems of the author as something that just happens.

here is the code that enabled us to run one motor:


This code enables us to run both motors simultaneously at the same (theoretical) speed.

/// Include the Bricktronics Motor library and helper libraries

#include <Encoder.h>
#include <PID_v1.h>
#include <BricktronicsMotor.h>


// Select the motor port (MOTOR_1 or MOTOR_2) in the constructor below.
//
#include <Wire.h>
#include <Adafruit_MCP23017.h>
#include <BricktronicsShield.h>
BricktronicsMotor m1(BricktronicsShield::MOTOR_1);
BricktronicsMotor m2(BricktronicsShield::MOTOR_2);

void setup()
{
  // Be sure to set your serial console to 115200 baud
  Serial.begin(115200);

  BricktronicsShield::begin();

  // Initialize the motor connections
  m1.begin();
  m2.begin();
}

void loop() 
{
  Serial.println("Going forward slowly then fast.");
  m1.setFixedDrive(75);  // speed of the motor
  m2.setFixedDrive(75);  // speed of the motor
  delay(1000);
  
  m1.setFixedDrive(255);  // speed of the motor
  m2.setFixedDrive(255);  // speed of the motor
  delay(1000);

  Serial.println("Going in reverse slowly then fast.");
  m1.setFixedDrive(-75);   // speed of the motor 
  m2.setFixedDrive(-75);   // speed of the motor 
  delay(1000);
  
  m1.setFixedDrive(-255);  // increased speed of the motor in reverse
  m2.setFixedDrive(-255);  // increased speed of the motor in reverse
  delay(1000);
}

The speed goes from -256 to 255, with the sign indicating direction
The minimum speed that allows our sciborg to move on the smooth surface of a hard floor or of a hard desk is about 80

This code gets the SciBorg to make a hard turn.  A hard turn is where the sciborg has one motor full power forward, the other full power reverse.

//code for Hard_Turn 
// Include the Bricktronics Motor library and helper libraries

#include <Encoder.h>
#include <PID_v1.h>
#include <BricktronicsMotor.h>


// Select the motor port (MOTOR_1 or MOTOR_2) in the constructor below.
//
#include <Wire.h>
#include <Adafruit_MCP23017.h>
#include <BricktronicsShield.h>
BricktronicsMotor m1(BricktronicsShield::MOTOR_1);
BricktronicsMotor m2(BricktronicsShield::MOTOR_2);

void setup()
{
  // Be sure to set your serial console to 115200 baud
  Serial.begin(115200);

  BricktronicsShield::begin();

  // Initialize the motor connections
  m1.begin();
  m2.begin();
}

void loop() 
{
  Serial.println("Going forward slowly then fast.");
  m1.setFixedDrive(-255);  // speed of the motor
  m2.setFixedDrive(255);  // speed of the motor
  delay(1000);

}
//end code for Hard_Turn


The previous code makes for a harsh turn where the sciborg turns almost like a tank(in place).  If one wanted to see a gentler turn like one might see in a car, we would want a softer turn.  This softer  turn is accomplished by making the speed of one motor slightly slower than the other.  The difference between the two motor speeds determines the arc of the turn where a smaller difference between the two motors means a larger turn radius.
// code for Soft_Turn // Include the Bricktronics Motor library and helper libraries

#include <Encoder.h>
#include <PID_v1.h>
#include <BricktronicsMotor.h>


// Select the motor port (MOTOR_1 or MOTOR_2) in the constructor below.
//
#include <Wire.h>
#include <Adafruit_MCP23017.h>
#include <BricktronicsShield.h>
BricktronicsMotor m1(BricktronicsShield::MOTOR_1);
BricktronicsMotor m2(BricktronicsShield::MOTOR_2);

void setup()
{
  // Be sure to set your serial console to 115200 baud
  Serial.begin(115200);

  BricktronicsShield::begin();

  // Initialize the motor connections
  m1.begin();
  m2.begin();
}

void loop() 
{
  Serial.println("Going forward slowly then fast.");
  m1.setFixedDrive(80);  // speed of the motor
  m2.setFixedDrive(120);  // speed of the motor
  delay(1000);

}
//end code for Soft_Turn

The way that our Sciborg is currently set, it takes about 12.3seconds to travel 10 feet, if both motors are running at full speed.
Although theoretically, if both motors are set to the same speed in the code, the sciborg should travel in a straight path.  however we found that in fact the same speed setting in the code produced different observed speed in different motors, and this observed speed changed from observation to observation.  What this meant was that in order to make the sciborg travel straight, we would have to decrease the speed of one motor in the code so that the outputted speed of that motor can match the outputted speed of the other motor.
Thus, if we wanted for our sciborg to travel exactly 10 feet, we would have to have the sciborg run for that specified amount of time(12.3 seconds) and then stop.

//code for 10_ft_using_timer// Include the Bricktronics Motor library and helper libraries

#include <Encoder.h>
#include <PID_v1.h>
#include <BricktronicsMotor.h>


// Select the motor port (MOTOR_1 or MOTOR_2) in the constructor below.
//
#include <Wire.h>
#include <Adafruit_MCP23017.h>
#include <BricktronicsShield.h>
BricktronicsMotor m1(BricktronicsShield::MOTOR_1);
BricktronicsMotor m2(BricktronicsShield::MOTOR_2);

void setup()
{
  // Be sure to set your serial console to 115200 baud
  Serial.begin(115200);

  BricktronicsShield::begin();

  // Initialize the motor connections
  m1.begin();
  m2.begin();
}

void loop() 
{
  Serial.println("Going forward slowly then fast.");
  m1.setFixedDrive(255);  // speed of the motor
  m2.setFixedDrive(245);  // speed of the motor
  delay(12300);
  stop();
}

void stop()
{
  while (true){
    m2.setFixedDrive(0);  // speed of the motor
    m1.setFixedDrive(0);  // speed of the motor
  }
}

//

The Sciborg didn't really stop at 10 feet.  In fact, it stopped 8 inches short.  It is difficult to get the sciborg to stop perfectly at 10 feet because the outputted speed is different with each run and so the distance traveled in 12.3 seconds is different for each run