An Agent-Based Approach to Adaptive Facade Control

The following post is an overview of my progress on the design, development, and realization of an agent-based adaptive building facade system.

System Components

Diagram 1. Representation of functionality and data flow between the three primary agents in the Agent-Based Adaptive Facade mock-up

An agent-based system consists of multiple¬†agents that are capable of acting alone, and in concert with one another. ¬†It’s purpose is to employ the various technologies discussed here, and to provide a platform on which to gather performative data. ¬†The mock-up will be deployed for a period of¬†approximately¬†one month,¬†after which¬†the results will be collected for analysis. ¬†In the case of this Adaptive Facade System mock-up
(Diagram 1) , the agents consist of:

  • Agent Computing Mechanism
  • Database Agent
  • Adaptive Facade Agent

The Agent Computing Mechanism performs two types of tasks that integrate the other two agents into¬†the system. ¬† ¬†The more direct basic set of tasks involves the gathering and distribution of weather, sensor, and actuator-state data. ¬†In this role the computing mechanism accesses and parses current weather observations from a web-based XML source, and stores them to two tables kept in the database (current weather &¬†long-term¬†weather). ¬†The “current weather” table temporarily stores the most up-to-date observations, and consists of only one row. ¬†This table is accessed¬†routinely to inform the computing mechanism, which in turn delivers the observation data to the facade agent. ¬†The “long-term weather” table is meant to store the data until the deployment of the whole system has terminated. ¬†The computing mechanism also collects sensor and adaptive behavior information from the facade agent, and stores it to tables in the database. ¬†At this point the long-term data will be sourced for analysis on the systems performance, and later compared to data gained through computational simulation of similar systems.

The second, more advanced set of tasks involves the real-time computational analysis of interior/exterior observations and adaptive performance results.  This information is used to critically analyze the effectiveness of the adaptive changes made by the facade, and to develop more efficient behaviors.  In this way the computing mechanism becomes intelligent by learning from its environment.  This is an aspect of the system that makes it intelligent.

The Database Agent¬†accessed by the computing mechanism alone (outside the scope of this system the database could still be used to perform other tasks, as in this class’ case). ¬†The previously mentioned data is stored here in four different tables, and accessed as needed for the operation of the system:

  1. adaptive_arduino_components
  2. adaptive_comp_mechanism_decisions
  3. adaptive_weather_current
  4. adaptive_weather_longterm

The Adaptive Facade Agent is made up of a mechanically adaptive facade, enclosed experimental environment (Figure 2), Arduino micro-controller, sensors, and actuator components (Figure 3).  This agent is able to sense and respond to changing conditions autonomously, but is also capable of receiving more refined commands from the computing mechanism.  The information from its sensors, actuators, and adaptive changes are stored within a table on the database.

Sensors and Actuator groups consist of:

  • photocell resistors (8)
  • Miga NanoMuscle (4)
  • Sensirion Temperature and Humidity sensor (1)

Figure 2. Adaptive Facade and enclosure

Figure 3. Arduino micro-controller and sensors (actuators are off screen)

Software Development

The¬†customized¬†application of the¬†aforementioned¬†technologies has facilitated the need to¬†develop¬†equally customized software for managing them. ¬†Up to this point, development has been carried out in the Ardiuno IDE and Processing “PDE”.

The Arduino program (facadeAgent4) is uploaded to the micro-controller (which stores the data in its on-board RAM), and manages the input/output functions of each sensor and actuator.  Additionally, it facilitates the transfer of data between the micro-controller and computing mechanism via communications port.

/*facadeAgent4
coded by Kai Hansen
May, 2012
*/

//temp and bmp sensor start
#include <Sensirion.h>

#define ENA_ERRCHK  // Enable error checking code

const byte dataPin =  2;                 // SHTxx serial data
const byte sclkPin =  3;                 // SHTxx serial clock
const byte ledPin  = 13;                 // Arduino built-in LED
const unsigned long TRHSTEP   = 5000UL;  // Sensor query period
const unsigned long BLINKSTEP =  250UL;  // LED blink period

Sensirion sht = Sensirion(dataPin, sclkPin);

unsigned int rawData;
float temp_c_indoor;
float temp_f_indoor;
float relative_humidity_indoor;
float dewpoint_c_indoor;
float dewpoint_f_indoor;

byte ledState = 0;
byte measActive = false;
byte measType = TEMP;

unsigned long trhMillis = 0;             // Time interval tracking
unsigned long blinkMillis = 0;

int getIndexOfMaximumValue(int* array, int size){  //indexes array to find max value
  int maxIndex = 0;
  int max = array[maxIndex];
  for (int i=0; i<size; i++){
    if (max<array[i]){
	max = array[i];
	maxIndex = i;
    }
  }
  return maxIndex;
} 

#ifdef ENA_ERRCHK
//temp and bmp sensor end

// This version of the code checks return codes for errors
byte error = 0;

int interLight1 = A0;  //interior photocells
int interLight2 = A1;
int interLight3 = A2;

int exterLight1 = A3;  //exterior photocell array
int exterLight2 = A4;
int exterLight3 = A5;
int exterLight4 = A6;
int exterLight5 = A7;

//frit actuators start 
int fritAct1 = 5;
int fritAct2 = 6;
//frit actuators end

//vent actuators start 
int ventAct1 = 9;
int ventAct2 = 10;
//vent actuators end 

//weather import start
float temp_f;
float temp_c;
float relative_humidity;
float dewpoint_c;
float dewpoint_f;
//weather import end

void setup(){
  Serial.begin(57600);

  //weather import start

  //weather import end

  pinMode(interLight1, INPUT);
  pinMode(interLight2, INPUT);
  pinMode(interLight3, INPUT);

  pinMode(exterLight1, INPUT);
  pinMode(exterLight2, INPUT);
  pinMode(exterLight3, INPUT);
  pinMode(exterLight4, INPUT);
  pinMode(exterLight5, INPUT);

  //frit start
  pinMode(fritAct1, OUTPUT);
  pinMode(fritAct2, OUTPUT);
  //frit end

  //vent start
  pinMode(ventAct1, OUTPUT);
  pinMode(ventAct2, OUTPUT);
  //vent end

  //temp and bmp sensor start
  byte stat;
  byte error = 0;
  //Serial.begin(9600);
  pinMode(ledPin, OUTPUT);
  delay(15);                             // Wait >= 11 ms before first cmd
// Demonstrate status register read/write
  sht.readSR(&stat);                     // Read sensor status register
  Serial.print("Status reg = 0x");
  Serial.println(stat, HEX);
  sht.writeSR(LOW_RES);                  // Set sensor to low resolution
  sht.readSR(&stat);                     // Read sensor status register again
  Serial.print("Status reg = 0x");
  Serial.println(stat, HEX);
// Demonstrate blocking calls
  sht.measTemp(&rawData);                // sht.meas(TEMP, &rawData, BLOCK)
  temp_c_indoor = sht.calcTempC(rawData);
  sht.measHumi(&rawData);                // sht.meas(HUMI, &rawData, BLOCK)
  relative_humidity_indoor = sht.calcHumi(rawData, temp_c_indoor);
  dewpoint_c_indoor = sht.calcDewpointC(relative_humidity_indoor, temp_c_indoor);
  dewpoint_f_indoor = sht.calcDewpointF(relative_humidity_indoor, temp_f_indoor);
  logData();
  //temp and bmp sensor end
}

void loop(){
  //solar tracking start
  int photoCell1 = analogRead(interLight1);
  int photoCell2 = analogRead(interLight2);
  int photoCell3 = analogRead(interLight3);

  int avgInteriorLight = (photoCell1 + photoCell2 + photoCell3)/3;
  int photoCell4 = analogRead(exterLight1);
  int photoCell5 = analogRead(exterLight2);
  int photoCell6 = analogRead(exterLight3);
  int photoCell7 = analogRead(exterLight4);
  int photoCell8 = analogRead(exterLight5);

  Serial.println(" ");
  Serial.println("Begin");
  Serial.println("interior Light Sensor 1: ");
  Serial.println(photoCell1, DEC);
  Serial.println("interior Light Sensor 2: ");
  Serial.println(photoCell2, DEC);
  Serial.println("interior Light Sensor 3: ");
  Serial.println(photoCell3, DEC);

  Serial.println("interior Light Average Value: ");
  Serial.println(avgInteriorLight, DEC);
  Serial.println(" ");

  Serial.println("Exterior Light Sensor 1: ");
  Serial.println(photoCell4, DEC);
  Serial.println("Exterior Light Sensor 2: ");
  Serial.println(photoCell5, DEC);
  Serial.println("Exterior Light Sensor 3: ");
  Serial.println(photoCell6, DEC);
  Serial.println("Exterior Light Sensor 4: ");
  Serial.println(photoCell7, DEC);
  Serial.println("Exterior Light Sensor 5: ");
  Serial.println(photoCell8, DEC);

  int photoArray[] = {photoCell4, photoCell5, photoCell6, photoCell7, photoCell8};

  int solarPosition = getIndexOfMaximumValue(photoArray, 5) + 1;

  Serial.println(" ");
  Serial.println("Solar position relative to array index: ");
  Serial.println(solarPosition, DEC);
  //solar traking end

  //frit actuators start
  switch (solarPosition){
    case 1:
      digitalWrite(fritAct1, LOW);				
      digitalWrite(fritAct2, LOW);
      break;
    case 2:
      analogWrite(fritAct1, 50);	// change this latter to reflect PWM
      analogWrite(fritAct2, 50);
      break;
    case 3:
      analogWrite(fritAct1, 100);	// change this latter to reflect PWM
      analogWrite(fritAct2, 100);
      break;
    case 4:
      analogWrite(fritAct1, 50);	// change this latter to reflect PWM
      analogWrite(fritAct2, 50);
      break;
    case 5:
      digitalWrite(fritAct1, LOW);				
      digitalWrite(fritAct2, LOW);
      break;
  }

  //int desiredInteriorLight = 750;

  /*if (avgInteriorLight != desiredInteriorLight){
   //adjustments to meet desired lighting
  }*/
  //frit actuators end

  //weather import start

  //weather import end

  //temp_bmp sensor start
  unsigned long curMillis = millis();          // Get current time

  // Rapidly blink LED. Blocking calls take too long to allow this.
  if (curMillis - blinkMillis >= BLINKSTEP) {  // Time to toggle the LED state?
    ledState ^= 1;
    digitalWrite(ledPin, ledState);
    blinkMillis = curMillis;
  }

  // Demonstrate non-blocking calls
  if (curMillis - trhMillis >= TRHSTEP) {      // Time for new measurements?
    measActive = true;
    measType = TEMP;
    sht.meas(TEMP, &rawData, NONBLOCK);        // Start temp measurement
    trhMillis = curMillis;
  }
  if (measActive && sht.measRdy()) {           // Check measurement status
    if (measType == TEMP) {                    // Process temp or humi?
      measType = HUMI;
      temp_c_indoor = sht.calcTempC(rawData);     // Convert raw sensor data
      temp_f_indoor = sht.calcTempF(temp_c_indoor);
      sht.meas(HUMI, &rawData, NONBLOCK);      // Start humi measurement
    } else {
      measActive = false;
      relative_humidity_indoor = sht.calcHumi(rawData, temp_c_indoor); // Convert raw sensor data
      dewpoint_c_indoor = sht.calcDewpointC(relative_humidity_indoor, temp_c_indoor);
      dewpoint_f_indoor = sht.calcDewpointF(relative_humidity_indoor, temp_f_indoor);
      logData();
    }
  } 
    //temp_bmp sensor end

  // include an if statement that allows ACM commands to overide these

  //vent actuators start
  float desiredIndoorHumidity = 40.0;
  float desiredIndoorTemp_f = 70.0;

  if (desiredIndoorHumidity != relative_humidity_indoor && desiredIndoorTemp_f != temp_f_indoor){
	  if (relative_humidity_indoor >= relative_humidity && temp_f_indoor <= temp_f){  //this accounts for warmer summer air
		  digitalWrite(ventAct1, LOW);		// maintains current conditions
		  digitalWrite(ventAct2, LOW);
	  }
	  else if (relative_humidity_indoor > relative_humidity && temp_f_indoor > temp_f){
		  analogWrite(ventAct1, 100);	// full purge
		  analogWrite(ventAct2, 100);
 //an intelligent behavior here would be to cross check the actual change to see if desiredIndoorHumidity was met
 //if not, PWM rate could be altered
	  }
	  else if (relative_humidity_indoor > relative_humidity && temp_f_indoor < temp_f){
		  analogWrite(ventAct1, 100);  // opens UPPER vent to allow humid air to escape while maintaining temp
		  analogWrite(ventAct2, 0);
	  }
	  else if (relative_humidity_indoor < relative_humidity && temp_f_indoor > temp_f){
		  analogWrite(ventAct1, 100);  // opens UPPER vent to allow WARM air to escape (CAN MESS WITH HUMIDITY THOUGH...)
		  analogWrite(ventAct2, 25);    // partially opens lower to control loss of humidity (maybe this would be a good way to employ a desicant)
	  }									  // a fan may also be usefull here
	  else if (relative_humidity_indoor > relative_humidity && temp_f_indoor > temp_f){
		  analogWrite(ventAct1, 100);  // opens UPPER AND LOWER vents to allow WARM air and HUMIDITY to escape 
		  analogWrite(ventAct2, 100);    
	  }
  }
  else {
	  digitalWrite(ventAct1, LOW);
	  digitalWrite(ventAct2, LOW);
  }
  //vent actuators end

  delay(5000);
}

void logData() {
  Serial.print("Temperature Celcius Indoors = ");   Serial.print(temp_c_indoor);
  Serial.print("Temperature Fahrenheit Indoors = ");   Serial.print(temp_f_indoor);
  Serial.print(" C, Humidity Indoors = ");  Serial.print(relative_humidity_indoor);
  Serial.print(" %, Dewpoint in Celcius Indoors = ");  Serial.print(dewpoint_c_indoor);
  Serial.print(" %, Dewpoint in Fahrenheit Indoors = ");  Serial.print(dewpoint_f_indoor);
  Serial.println(" F");
}

#endif  // End of non-error-checking example

The Processing code enables the desired functions of the Agent Computing Mechanism.

The program ,dbQuery_XMLFeed, houses the primary program functions in void setup() and void loop() and referrences the SQL libraries and XML functions.  In its current configuration it sets varying delays and calls the following functions:

  • doQuery() – queries the IITUIM DB for current weather data
  • writeXML_DB – stores weather and facade data to DB
  • getXML – retrives XML data from web-base source
  • readArduino – captures facade component data
  • writeArduino – sends current weather observations and adaptive commands to micro-controller

Figure 4. UML diagram of dbQuery_XMLFeed functions and relationships

At start-up, and for every 10 minutes (600,000 milliseconds) after the program returns parsed XML and DB data.  And for every 30 seconds it writes to and reads from the Arduino.

Example XML and DB data return:
number of rows in table weather: 1
chicago

station_location: U.S. Cellular Field, Chicago, IL
observation_time: Last Updated on May 4, 3:25 PM CDT

temp_f: 58.7
temp_c: 14.8
relative_humidity: 75
wind_dir: NE
wind_degrees: 45
wind_mph: 7.0
wind_gust_mph: 12.0
dewpoint_f: 50.8
dewpoint_c: 10.4

Much of this software has not been written or needs further development prior to deployment.  Not included in this discussion were the learning functions, which will be developed after the more basic functions are working.