Elise Chapman, Remington Frank, Sharon Li, Sarah Yun
Simple Narrative Description
The machine sees the color on the paper strip, then uses that color to extend a motor a certain distance. That distance is detected and is used to angle a stick to a proportional degree.
Technical Description
Our double transducer involves the following components: an RGB sensor, a linear actuator, a VL53L0X distance sensor, a servo motor, LCD, and an Arduino Uno. The RGB sensor reads the RGB values of a printed color off of a given gradient sheet. These values are then used to calculate the respective hue (HSV) value for said color. From here, the hue value (range 0 – 360) is mapped to the time the linear actuator extends (0 – 3 seconds for full range of motion). The extension of the linear actuator is read as a change in distance by the VL53L0X time of flight distance sensor. The range of the linear actuators extension is zero to two inches, so the time of flight sensor reads a range of zero to two inches as well (approximately 0 – 50 millimeters). This distance is then mapped to the servo motor’s rotation (with a range of 0 – 90°). The LCD display shows all the aforementioned outputs as mapped to a 0 – 99 range.
Final Double Transducers
Detail Photos
Working Videos
Process Photos
Discussion
Despite the simplicity of this double transducer in concept, its execution was harder than expected–especially its functionality. Although there were many late additions to our team, we were able to help each other solve similar problems and catch one another up (in which Sharon is very grateful and appreciative) in order to build four double transducers in a short period of time.
For our input, the color sensor was reading the RGB values of the given color strip because that is how computers perceive color, but we had to convert it to HSV color values, which is how humans perceive color. It was an interesting experience to introduce device the way we perceive color in order to give it a singular value input (hue) to work with the linear actuator.
Another problem we encountered that wasn’t an apparent one to us was the correct and neat soldering of our parts, specifically on the theVL53L0X sensor. This ultimately led to many problems when testing the parts individually as well as together as one entity with the other inputs and outputs. We had to make sure that our VL53L0X distance sensor was facing the right direction and that none of the metals bridged between each other. This trial and error process allowed us to understand how important it was to solder carefully and neatly as well as check our schematics beforehand.
Lastly, mapping the values so that each of the parts works cohesively was a struggle. Because there were multiple values being transferred into the next action and another action after that, it was crucial that we map each value to fit the other components’ range in order to make the board functional. We had to initially test the VL53L0X distance sensor to calculate the distance it reads and limit our range to that, then maps that value to a degree the motor arm should move so that we can visually see its change. Although we got it in the end, it required some calculations and test trials to make the entire system work.
However, overall we were able to get all the aspects working, and our team finished with functional double transducers.
Functional Block Diagram and Electrical Schematic
Code
/* Double Transducer: From Printed Color to Servo Motor Rotation {Sarah Yun, Elise Chapman, Sharon Li, Remington Frank} The code below processes and translates an input RGB reading from a printed color scale to a servo motor rotation (0 - 90°). It being a double transducer, the middle step is a linear actuator that extends and has its extension distance then read by a VL53L0x distance sensor. Pin Map: Pin | role | Description ---------------------------------------- 6 | output| VCC input for linear actuator 7 | input | GND for linear actuator 9 | output| Servo motor middle pin SCL | input | SCL pin on VL53L0X sensor SDA | input | SDA pin on VL53L0X sensor A4 | input | SCL pin on I2C LCD display A5 | input | SDA pin on I2C LCD display 5V | output| VL53L0X sensor power 3.3V | output| Power for remaining components GND | input | Ground for all components RGB Sensor code sourced from the Adafruit TCS34725 RGB Sensor example code. For further reference, refer to the following: https://learn.adafruit.com/adafruit-color-sensors/arduino-code VL53L0X sensor code is sourced from the Pololu library. This library can be found at the following link: https://github.com/pololu/vl53l0x-arduino Conversion from RGB to HSV values makes use of an algorithm sourced from: https://www.rapidtables.com/convert/color/rgb-to-hsv.html This has also been done as shown on the GeeksForGeeks site, which our code is very akin (identical) to: https://www.geeksforgeeks.org/program-change-rgb-color-model-hsv-color-model/ All credit is due to the above for the respective sections mentioned. */ #include <Wire.h> #include "Adafruit_TCS34725.h" #include <Servo.h> #include <LiquidCrystal_I2C.h> #include <Wire.h> #include <VL53L0X.h> VL53L0X sensor; /* Initialise with default values (int time = 2.4ms, gain = 1x) */ // Adafruit_TCS34725 tcs = Adafruit_TCS34725(); /* Initialise with specific int time and gain values */ Adafruit_TCS34725 tcs = Adafruit_TCS34725(TCS34725_INTEGRATIONTIME_614MS, TCS34725_GAIN_1X); Servo myServo; LiquidCrystal_I2C lcd(0x27, 16, 2); const int SERV = 9; int FWD = 6; int REV = 7; // Initialize evaluation criteria and other values double hue, saturation, value; long duration; double distance; float prevRunTime; float runTime; unsigned long trueTime; unsigned long currTime; unsigned long delayTime; unsigned long printTime; int turn; int prevTurn; int counter; int h_print; int la_print; int dist_print; int s_print; bool printer = 1; void setup(void) { Serial.begin(115200); Wire.begin(); lcd.begin(); lcd.backlight(); bool start = 1; // Begin color sensing sensor if (tcs.begin()) { Serial.println("Found sensor"); } else { Serial.println("No TCS34725 found ... check your connections"); while (1); } // Initilize pin modes pinMode(trigPin, OUTPUT); pinMode(echoPin, INPUT); pinMode(FWD, OUTPUT); pinMode(REV, OUTPUT); digitalWrite(FWD, LOW); digitalWrite(REV, HIGH); delay(3000); myServo.attach(SERV); sensor.setAddress(0x52); sensor.setTimeout(500); if (!sensor.init()) { Serial.println("Failed to detect and initialize sensor!"); while (1) {} } // Start continuous back-to-back mode (take readings as // fast as possible). To use continuous timed mode // instead, provide a desired inter-measurement period in // ms (e.g. sensor.startContinuous(100)). sensor.startContinuous(); } void loop(void) { trueTime = millis(); uint16_t r, g, b, c, colorTemp, lux; // Gather various color data from RGB sensor tcs.getRawData(&r, &g, &b, &c); // colorTemp = tcs.calculateColorTemperature(r, g, b); colorTemp = tcs.calculateColorTemperature_dn40(r, g, b, c); lux = tcs.calculateLux(r, g, b); Serial.print("Color Temp: "); Serial.print(colorTemp, DEC); Serial.print(" K - "); Serial.print("Lux: "); Serial.print(lux, DEC); Serial.print(" - "); Serial.print("R: "); Serial.print(r, DEC); Serial.print(" "); Serial.print("G: "); Serial.print(g, DEC); Serial.print(" "); Serial.print("B: "); Serial.print(b, DEC); Serial.print(" "); Serial.print("C: "); Serial.print(c, DEC); Serial.print(" "); Serial.println(" "); // RGB to HSV conversion double rWeight = r / 65535.0; double gWeight = g / 65535.0; double bWeight = b / 65535.0; Serial.println("Red Weight: "); Serial.print(rWeight); Serial.println(" "); // RGB to HSV algorithm double cmax = max(rWeight, max(gWeight, bWeight)); // maximum of r, g, b double cmin = min(rWeight, min(gWeight, bWeight)); // minimum of r, g, b double diff = cmax - cmin; // diff of cmax and cmin. double h = -1; // if cmax and cmax are equal then h = 0 if (cmax == cmin) { h = 0; } // if cmax equal r then compute h else if (cmax == rWeight) { h = fmod(60 * ((gWeight - bWeight) / diff) + 360, 360); } // if cmax equal g then compute h else if (cmax == gWeight) { h = fmod(60 * ((bWeight - rWeight) / diff) + 120, 360); } // if cmax equal b then compute h else if (cmax == bWeight) { h = fmod(60 * ((rWeight - gWeight) / diff) + 240, 360); } Serial.println(" "); Serial.print("Hue: "); Serial.print(h); Serial.print(" "); Serial.println(" "); //-------------------------------------------------------------------------------------------------------- // Map Hue value to linear actuator distance extended. Linear actuator has an operating time // range of 0 to 3 seconds, hence the mapping to 0 to 3000 milliseconds. runTime = map(h, 0, 360, 0, 3000); if(runTime > prevRunTime) { digitalWrite(FWD, HIGH); digitalWrite(REV, LOW); delay(runTime - prevRunTime); } else if (runTime < prevRunTime){ digitalWrite(FWD, LOW); digitalWrite(REV, HIGH); delay(prevRunTime - runTime); } prevRunTime = runTime; digitalWrite(FWD, LOW); digitalWrite(REV, LOW); //-------------------------------------------------------------------------------------------------------- // Read distance extended of linear actuator by the time of flight sensor distance = sensor.readRangeContinuousMillimeters() - 63; Serial.print(distance); if (sensor.timeoutOccurred()) { Serial.print(" TIMEOUT"); } Serial.println(); //-------------------------------------------------------------------------------------------------------- // Map distance detected into 90° rotation of servo turn = map(distance, 0, 50, 0, 90); if(turn > 90) turn = 90; myServo.write(turn); //-------------------------------------------------------------------------------------------------------- // Print inputs and outputs to LCD display h_print = map(h, 0, 360, 0, 99); la_print = map(runTime, 0, 3000, 0, 99); dist_print = map(distance, 0, 50, 0, 99); s_print = map(turn, 0, 90, 0, 99); // Update the LCD display every 250 milliseconds (4 times per second) if(trueTime - printTime >= 250){ lcd.home(); lcd.print(String("i:") + String(int(h_print))); lcd.setCursor(6,0); lcd.print(String("m:") + String(la_print)); lcd.setCursor(8, 1); lcd.print(String(dist_print)); lcd.setCursor(12, 1); lcd.print(String("o:") + String(s_print)); } }