SunSucc (ikke sponsoreret af Tuborg Påskebryg)

Hardware og Robotteknologi (F21)
Porteføljeopgave 1

Kevin Lavlund Hansen – kehan18@student.sdu.dk
Rasmus Sjøholm Stamm – rasta17@student.sdu.dk
Benjamin Clement Klerens – bekle18@student.sdu.dk

Løsningen, praktisk set

Løsningens hovedsagelige dele består af en servomotor som, baseret på aflæsninger fra to photoresistorer, drejer enheden til at stå imod lysets (solens) retning. De to photoresistorer peger i hver sin retning, ca. med en vinkel på 45°. Dette betyder, at hvis lyskilden ikke står lige i mellem dem, opstår en resistansforskel, og denne kan bruges til at dirigere servoen på rette vej. Denne logiske reaktion håndteres naturligvis af en Arduino-enhed, som står for at aflæse sensorer og dreje servoen.

Komponentliste
  • Arduino UNO
  • SG90 servo motor
  • 2x photoresistor / LDR-modstand
Grov skitse af konceptet

Den faktiske konstruktion af systemet er udført med lockdown-venlige materialer såsom pap, tomme øldåser, gaffa-tape og universallim.

På billedet for neden ses et overblik over systemets komponenter, og hvor de sidder på Arduinoen (servoen sidder under, og er derved gemt). Billedet viser:

  • Servo motoren (dog ikke synlig)
  • PWM signalet, som styrer servoen
  • RBGLED’en, som fungerer som statuslysdiode
  • Photoresistorene, som aflæser belysning
Komponent overblik

Endeligt, en video-demonstration af systemet i aktion. Videon viser tydeligt hvordan servoen hele tiden drejer, så enheden “peger” på kameraet, som er den mest betydelige lyskilde.

Systemet i aktion

Løsningen, teknisk set

Det overordnede diagram for systemets kredsløb ser således ud:

SunSucc kredsløbsdiagram

Photoresistor / Light-dependent resistor

En photoresistor, eller LDR, er som navnet foreslår, en modstand, der afhænger af belysning. Kort fortalt er det en variabel modstand, som varierer efter belysning, hvor mere lys = mindre modstand. Denne funktionalitet er specielt smart, når man har et system der skal reagere på lys, såsom projektet her.

En Arduino kan som udgangspunkt ikke aflæse resistansværdier direkte, dog kan den aflæse spændingssignaler via analoge input. Derfor er LDR1 og LDR2 del af hver sin spændingsdeler, hvor Arduionen måler spændingen i mellem disse, på pins A0 og A1, som så:

På Arduinoen vil disse aflæses mellem 0-5V i en opløsning af 10 bit (0-1023). Dvs. at et analogt signal på 2.5V vil aflæses som ~512 fordi 2.5 / 5 * 1023 = 511.5. Disse aflæsninger kan derved bruges i Arduinoens kode til at
1) afgøre hvilken sensor, som modtager mest lys
2) signalere servoen til at dreje mod denne

Servo motor – SG90

En servo motor er, kort fortalt, en DC motor med indbygget gearing, hvilket tillader præcis positionsindstilling. Dette er især smart i vores tilfælde, hvor vi skal indstille enheden baseret på sensor-læsninger. Hvor en DC motor har to pins, positiv og negativ, har en servo motor i stedet typisk tre, nemlig positiv, negativ og signal. Den tredje pin, signal, bruges til at styre servoen, vha. pulse width modulation. I denne kontekst er det pulsbredden af det indgående PWM-signal, der afgør servoens position. Per komponentens datasheet, vides det at servoen drejer mellem 0-180° (eller -90-90°) over pulsbredder mellem 1-2ms.

På Arduionen sidder adskillige pins, som er i stand til outputte PWM-signaler. I vores tilfælde er servoen forbundet til pin 6, som på diagrammet er noteret med “PWM”, hvilket betyder at den har førnævnte PWM-egenskab.

RGBLED, statusdiode

For at gøre fejlfinding og testing af systemet nemmere, har vi sat en RGBLED (red-green-blue LED) i kredsløbet, som blinker i én af tre farver, alt efter hvilken tilstand Arduinoen befinder sig i. Alternativt kunne vi i stedet havet brugt tre individuelle LED’er, men vi fandt det smartere med én RGBLED, da det både var en nemmere og smartere løsning, eftersom en RBGLED principielt set kan lyse i et uendeligt antal farver.

Dioden styres med PWM, på pins 9, 10 og 11, hvor signalets duty cycle mellem 0-100% afgør den tilsvarende farves intensitet i vidden 0-255. Derved kan vi lave farver i typisk RGB-format ved at skrive forskelligt til de individuelle pins.

Programmering

Arduinoen huser al logik og programmering, som bestemmer systemets uadvendte opførsel. Filosofisk set er Arduinoen hjernen mellem sensor og aktuator. Koden, som driver systemet, er således:

#include <Servo.h>

// Photoresistors
const int sens1Pin = A0;
const int sens2Pin = A1;
// Servo
const int servoPin = 6;
// RBG LED
const int redPin = 9;
const int greenPin = 10;
const int bluePin = 11;

Servo servo;

int sens1Val;
int sens2Val;

int sens1Volt;
int sens2Volt;

int pos = 90;

void setup() {
  Serial.begin(9600);
  
  // Photoresistors
  pinMode(sens1Pin, INPUT);
  pinMode(sens2Pin, INPUT);
  // Servo
  servo.attach(servoPin);
  servo.write(pos);
  // RGB LED
  pinMode(redPin, OUTPUT);
  pinMode(greenPin, OUTPUT);
  pinMode(bluePin, OUTPUT);

  Serial.println("sens1Val,sens2Val,difference,average,pos");

  //servo.write(90);
  //delay(10000);
}

void loop() {
  setColor(255, 0, 0);
  readSensors();
  
  if (sens1Val + sens2Val <= 200) {
    reset();
  } else if (abs(sens1Val - sens2Val) >= 100) { 
    correctServo();
  }
  
  setColor(0, 0, 0);
  delay(50);
}

int mapValue(int value) {
  return 180.0 / 500.0 * value;
}

// Prints a line per reading, following the format:
// [sens1Val],[sens2Val],[difference],[average],[position]
void printReadings() {
  Serial.print(sens1Val);
  Serial.print(",");
  Serial.print(sens2Val);
  Serial.print(",");
  Serial.print(abs(sens2Val - sens1Val));
  Serial.print(",");
  Serial.print((sens2Val + sens1Val) / 2);
  Serial.print(",");
  Serial.println(pos);
}

void readSensors() {
  sens1Val = analogRead(sens1Pin);
  sens2Val = analogRead(sens2Pin);
  printReadings();
}

// Align servo in current lighting environment
void correctServo() {
  bool isCorrected = false;
  int changeDeg = 6;
  int deadzone = 50;

  while (!isCorrected) {
    setColor(0, 0, 255);
    readSensors();
    
    if (abs(sens1Val - sens2Val) <= deadzone) {
      isCorrected = true;
    } else if (sens1Val > sens2Val) {
      changePos(changeDeg);
    } else if (sens1Val < sens2Val) {
      changePos(-changeDeg);
    }
    
    servo.write(pos);
    setColor(0, 0, 0);
    delay(100);
  }
}

// Return servo to baseline (i.e. sun has gone down)
void reset() {
  setColor(0, 255, 0);
  pos = 90;
  servo.write(pos);
  delay(5000);
}

// To ensure pos never goes above 180 or below 0
void changePos(int change) {
  pos += change;

  if (pos < 0) {
    pos = 0;
  } else if (pos > 180) {
    pos = 180;
  }
}

// Changing color of RGB LED
void setColor(int red, int green, int blue) {
  analogWrite(redPin, red);
  analogWrite(greenPin, green);
  analogWrite(bluePin, blue);
}

Programmets logiske flow kan med fordel beskrives med nedenstående flow-diagram.

Flow-diagram over SunSucc’s opførsel

Diagrammet beskriver de logisker operationer, som sker i programmets loop() metode, og hvordan aktuatoren (servoen) reagerer herpå. Kort fortalt “venter” programmet på, at forskellen mellem de to photoresistorer er større end en sat grænse, hvorved den inkrementelt drejer servoen mod photoresistoren med lavest belysning, indtil photoresistor-forskellen igen er under en sat grænsevidde, hvorefter programmet vil gå tilbage til at vente.

Der er også tage højde for, at systemet skal nulstille (i.e. centrere servo), når solen er gået ned, så photoresistorene kan fange solen, når den kommer op på den modsatte side. Dette er gjort ved at tjekke om summen af aflæsning af begge photoresistorer er under, eller lig 200, hvorved servoen vil centreres på 90°.

Diagrammet viser også hvilken farve statuslysdioden har under programmets forskellige stadier. F.eks. vil den lyse blåt, mens servoen indstiller sig efter en lysændring, og rødt mens den venter.

Servo library

Til at gøre interaktion med servoen nemmere, findes der et Arduino library kaldt Servo. Denne simplificerer indstilling vha. generaliserede metoder, som kan kaldes direkte på et Servo-objekt, instansieret med servo-komponentens PWM-pin. Eksempelvis her, hvor attach() metoden “tilslutter” servo-objektet til pin servoPin, og indstiller servoen til gradtallet pos:

  servo.attach(servoPin);
  servo.write(pos);

Dette betyder at vi ikke selv behøver at håndskrive PWM-signaler til servoen, men i stedet bare give metoden det ønskede gradtal, hvorefter den selv beregner og sender det nødvendige signal. Nice.

Logging

For at få data på, hvor vidt systemet faktisk følger efter en lyskilde, er programmet skrevet til at printe en masse data som Serial output. Disse data inkluderer aflæsningsværdier af begge photoresistorer, deres forskel, gennemsnit, og servoens nuværende position i gradtal.

// Prints a line per reading, following the format:
// [sens1Val],[sens2Val],[difference],[average],[position]
void printReadings() {
  Serial.print(sens1Val);
  Serial.print(",");
  Serial.print(sens2Val);
  Serial.print(",");
  Serial.print(abs(sens2Val - sens1Val));
  Serial.print(",");
  Serial.print((sens2Val + sens1Val) / 2);
  Serial.print(",");
  Serial.println(pos);
}

Outputtet er skrevet til at følge et standard CSV-format, hvilket vil sige at dataen kan importeres direkte i et databehandlingsprogram såsom Microsoft Excel eller Google Sheets, hvoraf grafer kan genereres. For at gemme det serielle output, er det nødvendigt at bruge en seriel aflæser med logging-funktionalitet, såsom programmet PuTTY. F.eks:

Test

Under optimale forhold ville systemet blive testet under rigtig sollys, over en hel dag, da dette scenarie er systemets faktiske use case. Desværre, pga. årstidens vejrforhold, har dette vist sig at være besværligt, så i stedet er ægte solskin forsøgt simuleret vha. lygte, over et par minutter. Mens det betyder at systemet ikke er testet i realistiske forhold, giver det dog et overordnet indtryk af dets funktionalitet, da data vil vise om lyskilden bliver fulgt som ønsket. Testen resulterede i følgende diagram:

Grafen viser servoens position i grader (blå) ift. photoresistorenes gennemsnitlige læsninger, dvs. lysindfald (rød). Her håber vi at se en nogenlunde konstant rød linje, i takt med at den blå ændrer sig; dette betyder at servoen drejer for at følge lyskilden, for at holde lysindfaldet maksimalt. Dette viser diagrammet umiddelbart, så derved kan testen vurderes en success.

Leave a Reply