STM32 ile DENEYSEL ANEMOMETRE
Mevcut anemometrelerin dezavantajını ortadan kaldıracak inovatif bir tasarım
Günümüzde mevcut olan lidar tabanlı anemometreler yüksek bir maliyete sahiptir. Doppler frekansının elde edilmesiyle kullanılan bu mevcut lidar tabanlı anemometreler rüzgâr hızını ölçmekte ve genelde tek yön prensibiyle çalışmaktadır, ayrıca bu anemometrelerin kullanımı yüksek aerosol içeriğine sahip bölgelerle sınırlıdır.
Öte yandan kepçe anemometreler de sadece rüzgâr hızını hesaplayabilmektedir, rüzgâr yönü için harici sensörler ile entegre bir biçimde kullanılması gerekmektedir. Bu da kepçe anemometre kullanımı için maliyeti arttırmaktadır.
Mevcut anemometrelerin bu gibi dezavantajını ortadan kaldıracak inovatif bir tasarım yoluna gidilmiştir. Lidar sisteminin mesafe ölçme prensibi ile kurulan bu anemometre tasarımı aşağıda yer almaktadır.
Tasarımda rüzgâr hızına bağlı olarak değişen silindir geometrisine sahip çerçeve, mikrodenetleyici ve merkezine konumlandırılmış iki adet lazer mesafe sensörü bulunmaktadır.
Oluşturulan bu sistemde rüzgâr hızına bağlı olarak değişen lazer mesafe sensörü ile silindirik çerçeve arasındaki mesafenin ölçümü yapılmaktadır. Elde edilen bu ölçümler ile matematiksel hesaplar yapılarak rüzgâr hızı ve rüzgâr yönü verilerine ulaşmak amaçlanmıştır.
Teorik Hesaplamalar
Rüzgâr hızıyla değişen silindir geometrisinin mevcut konumundan sapma miktarı, merkezine konumlandırılmış lazer mesafe sensörlerinin mikrodenetleyici yardımıyla uçuş süresini hesaplamasıyla bulunur.
Proje İnşası
-
Planlanan silindirik çerçeve için 70 mm dış çapa ve 65 mm iç çapa sahip PVC boru tercih edilmiştir
-
VL6180x optik mesafe sensörlerinin PVC boru içinde konumlandırılması için 10 mm dış çapa ve 50 cm uzunluğa sahip alüminyum boru seçilmiştir. Sensörler bu boru üstünde konumlandırılıp STM32 ve VL6180x optik mesafe sensörlerinin bağlantı kabloları bu boru içinde gizlenmektedir.
-
PVC borunun, alüminyum boru üzerinde esneme payı olacak bir biçimde yerleştirilmesi gerekmektedir. Bunun için uzatma yayı kullanılmaktır. Bu yay, alüminyum boru içirişinde bir kısmı içerde kalacak şekilde monte edilmesi gerekmektedir.
Devre Şeması
Kodlar
// Processing Kodlari=== // globals // ====================== // ----- serial port import processing.serial.*; //import the serial library Serial myPort; //the Serial port object final int Baud_rate = 115200; //communication speed String Input_string; //for incoming data boolean Connected = false; //flag // ----- display graphics PFont myFont; //name of font to be created float Scale = 0.7; // graticule size // ----- data from Arduino float Direction = 0.0; float Windspeed = 0.0; // ====================== // setup // ====================== void setup() { // ----- display window size(800, 800, P3D); /* Uncomment the following two lines to see which fonts are available */ //String[] fontList = PFont.list(); //printArray(fontList); // ------ create the screen font myFont = createFont("Arial Black", 30); textFont(myFont); // ----- initialize the serial port printArray(Serial.list()); //lists your COM ports on screen myPort = new Serial(this, Serial.list()[0], Baud_rate); myPort.bufferUntil('\n'); myPort.clear(); //clear the receive buffer } // ====================== // draw // ====================== void draw() { // ----- define colors, scale, & text background(0); //black background // ----- show wind direction pushMatrix(); translate(width/2, height/2); rotateZ(radians(-Direction)); scale(0.9); compass(); popMatrix(); // ----- show windspeed pushMatrix(); translate(width/2, height/2); scale(Scale); noStroke(); fill(255,0,0,180); circle(0,0,Windspeed*width/10); popMatrix(); // ----- draw graticule draw_graticule(); } // ======================= // serial event (called with each Arduino data string) // ======================= void serialEvent(Serial myPort) { // ----- wait for a line-feed Input_string = myPort.readStringUntil('\n'); // ----- validate if (Input_string != null) { // ----- trim whitespace Input_string = trim(Input_string); //println(Input_string); // ----- make contact if (Connected == false) { if (Input_string.equals("S")) { // ----- set flag Connected = true; //connection made // ----- request data myPort.clear(); //clear the receive buffer myPort.write("S"); //request data } } else // ----- process data { Input_string = trim(Input_string); //remove leading/trailing whitespace //println(Input_string); float[] values = float(split(Input_string, ',')); Windspeed = values[0]; Direction = values[1]; float North_offset = values[2]; float East_offset = values[3]; print(Windspeed); print("\t"); print(Direction); print("\t"); print(North_offset); print("\t"); println(East_offset); myPort.clear(); //clear the receive buffer myPort.write("S"); // request new data } } } // ======================= // draw graticule // ======================= void draw_graticule() { // ----- setup pushMatrix(); // save screen parameters translate(width/2, height/2); // move the origin scale(Scale); // scale everything // ----- draw wind viewing circle pushMatrix(); fill(255, 0, 0, 0); stroke(255, 0, 0); // red circle strokeWeight(10); circle(0, -height/1.725, width*0.17); popMatrix(); // ----- draw the arcs fill(0, 0, 0, 0); stroke(128); //use gray lines strokeWeight(2); circle(0, 0, width); circle(0, 0, width*0.75); circle(0, 0, width*0.5); circle(0, 0, width*0.35); circle(0, 0, width*0.25); circle(0, 0, width*0.15); //rotateZ(radians(45)); // rotate the screen coordinates pushMatrix(); line(-width/2, 0, width/2, 0); rotateZ(-radians(45)); line(-width/2, 0, width/2, 0); rotateZ(-radians(45)); line(-width/2, 0, width/2, 0); rotateZ(-radians(45)); line(-width/2, 0, width/2, 0); popMatrix(); // ----- label the arcs fill(255); //white text textAlign(LEFT, BOTTOM); text("7,5 m/sn", +5, -width/2*0.75); //"75 km/hr" text("5,0", +5, -width/2*0.5); //"50 km/hr" text("3,5", +5, -width/2*0.35); text("2,5", +5, -width/2*0.25); //"25 km/hr" text("1,5", +1, -width/2*0.15); text("0", +5, 0); //"0 km/hr" // ----- restore properties strokeWeight(1); fill(0); //white stroke(255); //black scale(1.0); popMatrix(); //restore screen parameters } // ========================== // compass() // ========================== void compass() { pushMatrix(); // ----- draw light grey disc pushMatrix(); noStroke(); fill(200); // light grey ellipse(0, 0, width, height); popMatrix(); // ----- blank the disc center pushMatrix(); fill(0); // black ellipse(0, 0, width*0.8, height*0.8); popMatrix(); // ----- draw the 22.5 degree markers for (int i=0; i
// ----- libraries #include #include #include // ------ Create a new instance for each sensor VL6180X North; // North-facing sensor VL6180X East; // East-facing sensor LiquidCrystal_I2C ekran(0x27,16,2); // ----- sensor override addresses /* Multiple sensors can be used by turning each sensor ON in turn and overiding the default address of 0x29 */ const byte North_address = 0x30; const byte East_address = 0x31; // ----- These Arduino pins must be wired to the VL6180X SHDN pin const int North_enable = PA5; // arduino pin A2 const int East_enable = PA6; // arduino pin A3 // ----- sensor info const float Radius = 36; // inside pipe radius (mm) const float North_offset = -6.95; // distance (mm) from centre-line const float East_offset = -35.95; // distance (mm) from centre-line const float Scale = 1.00; // scale factor float North_reading = 0.0; float East_reading = 0.0; float Windspeed = 0.0; float Direction = 0.0; // ----- Filter0 (North Sensor) float Array0[40] = {0}; // array int Index0 = 0; // position of first item float Sum0 = 0.0; // array sum float Average0 = 0.0; // moving average // ----- Filter1 (East Sensor) float Array1[40] = {0}; // array int Index1 = 0; // position of first item float Sum1 = 0.0; // array sum float Average1 = 0.0; // moving average // ----- Filter2 (Direction) float Array2[40] = {0}; // array int Index2 = 0; // position of first item float Sum2 = 0.0; // array sum float Average2 = 0.0; // moving average // ----- Communications const long Baudrate = 115200; // serial comminication speed char Char; // --------------- // setup() // --------------- void setup() { // ----- Initialise communications Serial.begin(115200); Wire.begin(); ekran.init(); ekran.backlight(); ekran.setCursor(0,0); // ----- Configure Arduino pins pinMode(North_enable, OUTPUT); pinMode(East_enable, OUTPUT); // ----- Shutdown sensors digitalWrite(North_enable, LOW); digitalWrite(East_enable, LOW); // ----- Configure North_facing sensor digitalWrite(North_enable, HIGH); // activate sensor delay(50); North.init(); // configure sensor North.configureDefault(); North.setAddress(North_address); // overide the default address North.setTimeout(500); /* ------------ Calibration: ------------ The VL6180X readings can be up to 15mm out To calibrate the sensors: - Comment out code lines 153, 162..168, 198..199 - Uncomment/comment the following two lines - Place an object 50mm in front of the sensor and note the average_reading - Calculate the offset value using the formula "offset=50-average_reading" - Replace the VL6180X::SYSRANGE__PART_TO_PART_RANGE_OFFSET value of 9 with your offset Negative offset values must be expressed in 2's-complement format - Restore/uncomment comments when you are happy with the readings. */ //North.writeReg(VL6180X::SYSRANGE__PART_TO_PART_RANGE_OFFSET,0); // use this offset when calibrating North.writeReg(VL6180X::SYSRANGE__PART_TO_PART_RANGE_OFFSET, 10); North.stopContinuous(); delay(300); North.startRangeContinuous(50); // ------ Configure East_facing sensor digitalWrite(East_enable, HIGH); // activate sensor delay(50); East.init(); // configure sensor East.configureDefault(); East.setAddress(East_address); // change default address East.setTimeout(500); /* ------------ Calibration: ------------ The VL6180X readings can be up to 15mm out To calibrate the sensors: - Comment out code lines 153, 162..168, 198..199 - Uncomment/comment the following two lines - Place an object 50mm in front of the sensor and note the average_reading - Calculate the offset value using the formula "offset=50-average_reading" - Replace the VL6180X::SYSRANGE__PART_TO_PART_RANGE_OFFSET value of 22 with your offset Negative offset values must be expressed in 2's-complement format - Restore/uncomment comments when you are happy with the readings. */ //East.writeReg(VL6180X::SYSRANGE__PART_TO_PART_RANGE_OFFSET,0); East.writeReg(VL6180X::SYSRANGE__PART_TO_PART_RANGE_OFFSET, 19); East.stopContinuous(); delay(300); East.startRangeContinuous(50); // ----- Connect display connect_to_display(); } // --------------- // loop() // --------------- void loop() { // ----- if data is available to read, if (Serial.available() > 0) { Char = (char)Serial.read(); // ----- send data whenever a send character ('S') is received if (Char == 'S') { if (!North.timeoutOccurred()) { North_reading = (float)North.readRangeContinuousMillimeters() - North_offset; Average0 = moving_average(Array0, sizeof(Array0) / sizeof(Array0[0]), &Sum0, &Index0, North_reading); } if (!East.timeoutOccurred()) { East_reading = (float)East.readRangeContinuousMillimeters() - East_offset; Average1 = moving_average(Array1, sizeof(Array1) / sizeof(Array1[0]), &Sum1, &Index1, East_reading); } calculate_speed_direction(Average0, Average1); Average2 = moving_average(Array2, sizeof(Array2) / sizeof(Array2[0]), &Sum2, &Index2, Direction); // ----- send results to the display Serial.print(Windspeed); Serial.print(','); Serial.print(Average2); // Direction Serial.print(','); /* To use these values: - Set the North_offset and East_offset in the Arduino header to zero - Replace the above offset values with these readings - The windspeed indicator should resemble a pin-prick */ Serial.print(Average0 - Radius); // Required North_offset in still air Serial.print(','); Serial.println(Average1 - Radius); // Required East_offset in still air ekran.println(Windspeed); ekran.println("--"); } } } // ---------------------------- // connect to graphics display // ---------------------------- void connect_to_display() { while (Serial.available() 0.001) dY = -dY; if (fabs(E - R * cos(Aci5 * DEG_TO_RAD) - dX) > 0.001) dX = -dX; // ----- Calculate wind direction if ((dX >= 0) && (dY >= 0)) Direction = 270 - Aci6; // quadrant 0 if ((dX = 0)) Direction = 90 + Aci6; // quadrant 1 if ((dX < 0) && (dY = 0) && (dY = array_length) { *array_index = 0; // wrap around } return *array_sum / array_length; // moving average }