Logging
The logging system is the most important analysis tool we have.
This allows us to export any variable data we want out to an external text file. This way we can use any data processor to parse the data. (Excel, MATLAB, R, etc.)
Creating and Using the Log Files
We first need to initialize the log files and prepare them before each test.
- 1. Create empty File Pointers (code in 'eris_vrpn.cpp')
FILE * outHand = NULL; FILE * out1 = NULL; FILE * out2 = NULL; FILE * out3 = NULL; FILE * out4 = NULL;
- 2. Make sure Logging is Enabled (and Basic Logging if desired)
#define USE_VRPN 1 #define USE_KEYBOARD 1 #define USE_PRINTOUT 0 #define USE_LOGGING 1 <---//Enables Full Logging #define USE_BASIC_LOGGING 1 <---//Only logs the necessities (FULL LOGGING MUST BE ENABLED FOR THIS TO WORK) #define USE_PR_YAW_CORRECT 0 #define USE_HAND 1
- 3. Create Log File Header (# = Log File Title, % = List of Variable Names, & = List of Variable Units)
- Note 1: This example is using BASIC_LOGGING 1,
- Note 2: Variables and units are separated by a double tab '\t\t' (Does not have to be '\t\t' any delimiter works)
const char * logHeaderHand = "#Hand Log File\n\ %Time\t\tPitch\t\tRoll\t\tYaw\t\tX\t\tY\t\tZ\t\tLoopTime\t\tLoopTimeDelta\n\ &sec\t\tdegrees\t\tdegrees\t\tdegrees\t\tmeters\t\tmeters\t\tmeters\t\tseconds\t\tseconds\n"; const char * logHeader1 = "#Crazyflie1 Log File\n\ %Time\t\tPitch\t\tRoll\t\tYaw\t\tX\t\tY\t\tZ\t\tCamYaw\t\tCorrectedYaw\t\tLoopTimeTotal\t\tLoopTimeTotalDelta\t\tLoopTime\t\tLoopTimeDelta\t\tvrpnPacketDelta\t\tvrpnPacketTime\t\tvrpnPacketBackup\t\tRadioRSSI\n\ &sec\t\tdegrees\t\tdegrees\t\tdegrees\t\tmeters\t\tmeters\t\tmeters\t\tdegrees\t\tdegrees\t\tseconds\t\tseconds\t\tseconds\t\tseconds\t\tseconds\t\tseconds\t\tpackets\t\tstrength\n"; const char * logHeader2 = "#Crazyflie2 Log File\n\ %Time\t\tPitch\t\tRoll\t\tYaw\t\tX\t\tY\t\tZ\t\tCamYaw\t\tCorrectedYaw\t\tLoopTimeTotal\t\tLoopTimeTotalDelta\t\tLoopTime\t\tLoopTimeDelta\t\tvrpnPacketDelta\t\tvrpnPacketTime\t\tvrpnPacketBackup\n\ &sec\t\tdegrees\t\tdegrees\t\tdegrees\t\tmeters\t\tmeters\t\tmeters\t\tdegrees\t\tdegrees\t\tseconds\t\tseconds\t\tseconds\t\tseconds\t\tseconds\t\tseconds\t\tpackets\n"; const char * logHeader3 = "#Crazyflie3 Log File\n\ %Time\t\tPitch\t\tRoll\t\tYaw\t\tX\t\tY\t\tZ\t\tCamYaw\t\tCorrectedYaw\t\tLoopTimeTotal\t\tLoopTimeTotalDelta\t\tLoopTime\t\tLoopTimeDelta\t\tvrpnPacketDelta\t\tvrpnPacketTime\t\tvrpnPacketBackup\n\ &sec\t\tdegrees\t\tdegrees\t\tdegrees\t\tmeters\t\tmeters\t\tmeters\t\tdegrees\t\tdegrees\t\tseconds\t\tseconds\t\tseconds\t\tseconds\t\tseconds\t\tseconds\t\tpackets\n"; const char * logHeader4 = "#Crazyflie4 Log File\n\ %Time\t\tPitch\t\tRoll\t\tYaw\t\tX\t\tY\t\tZ\t\tCamYaw\t\tCorrectedYaw\t\tLoopTimeTotal\t\tLoopTimeTotalDelta\t\tLoopTime\t\tLoopTimeDelta\t\tvrpnPacketDelta\t\tvrpnPacketTime\t\tvrpnPacketBackup\n\ &sec\t\tdegrees\t\tdegrees\t\tdegrees\t\tmeters\t\tmeters\t\tmeters\t\tdegrees\t\tdegrees\t\tseconds\t\tseconds\t\tseconds\t\tseconds\t\tseconds\t\tseconds\t\tpackets\n";
- 4. Open External .txt file and Print Header into file
#if USE_HAND outHand = fopen("hand.txt", "w"); //Opens text file with write permission if(outHand == NULL) { printf("Could not open hand.log: errno %d\n", errno); exit(-1); } fprintf(outHand, "%s\n", logHeaderHand); //Prints log header into corresponding file #endif if(cflieCopter1) //Checks to see if cflieCopter pointer has been initialized before opening file { out1 = fopen("cflie1.txt", "w"); if(out1 == NULL) { printf("Could not open cflie1.log: errno %d\n", errno); exit(-1); } fprintf(out1, "%s\n", logHeader1); } if(cflieCopter2) { out2 = fopen("cflie2.txt", "w"); if(out2 == NULL) { printf("Could not open cflie2.log:errno %d\n", errno); exit(-1); } fprintf(out2, "%s\n", logHeader2); } if(cflieCopter3) { out3 = fopen("cflie3.txt", "w"); if(out3 == NULL) { printf("Could not open cflie3.log:errno %d\n", errno); exit(-1); } fprintf(out3, "%s\n", logHeader3); } if(cflieCopter4) { out4 = fopen("cflie4.txt", "w"); if(out4 == NULL) { printf("Could not open cflie4.log:errno %d\n", errno); exit(-1); } fprintf(out4, "%s\n", logHeader4); }
- 5. Init has Completed (Start Program Loop) Prints Variable values to file...
- Note 1: This code is run at the end of the Callback for each Crazyflie initialized (Executes once per new data packet)
- Note 2: This example is using BASIC_LOGGING, and is only showing Crazyflie1's code.
fprintf(out1, "%.6f\t\t", cflieCopter1->currentTime() - initTime1 ); fprintf(out1, "%.3f\t\t%.3f\t\t%.3f\t\t%.3f\t\t%.3f\t\t%.3f\t\t%.3f\t\t%.3f\t\t%.6f\t\t%.6f\t\t%.6f\t\t%.6f\t\t%.6f\t\t%.6f\t\t%d\t\t%d", cflieCopter1->roll(), cflieCopter1->pitch(), quadYaw1, t.pos[0], t.pos[1], -t.pos[2], camYawDeg1, correctedYaw1, loopTimeTotal, loopTimeTotalDelta, loopTime1, loopTime1Delta, vrpnPacketDelta1, vrpnPacketTime1, vrpnPacketBackup1, cflieCopter1->radioRSSI()); fprintf(out1, "\n");
- 6. Close External .txt files before ending program
#if USE_LOGGING #if USE_HAND fclose(outHand); #endif #if NUM_QUADS >= 1 fclose(out1); #endif #if NUM_QUADS >= 2 fclose(out2); #endif #if NUM_QUADS >= 3 fclose(out3); #endif #if NUM_QUADS >= 4 fclose(out4); #endif #endif
MATLAB Parser (TODO)
We use a Matlab script to parse the log files.
New Client Log Blocks
In the Swarm Client there is a sub-system designed for creating and maintaining Log Blocks. Once you have created a new log block in the Firmware that you want to utilize we can do the following:
- 1. Create an Enable function for the New Log Block (code in CCrazyflie.cpp)
void CCrazyflie::enableMagnetometerLogging() { m_tocLogs->registerLoggingBlock("magnetometer", 1000); m_tocLogs->startLogging("mag.x", "magnetometer"); m_tocLogs->startLogging("mag.y", "magnetometer"); m_tocLogs->startLogging("mag.z", "magnetometer"); }
- This will then register a log block called magnetometer on the Client, which will be updated every 1000 μs. It will then update the values listed in the block (X, Y, Z of the magnetometer).
- Note 1: The "mag.x" term must match the name defined in the Firmware log block (see firmware example 1)
- Note 2: The 2nd input to the startLogging function needs to match the name of the Client Log block we registered (in this case "magnetometer")
- 2. Add the enableLogBlock function we just made to the list of Log blocks we want at startup (code in CCrazyflie.cpp):
bool CCrazyflie::startLogging() { // Register the desired sensor readings this->enableStabilizerLogging(); // this->enableGyroscopeLogging(); // this->enableAccelerometerLogging(); this->enableBatteryLogging(); this->enableMagnetometerLogging(); <-----//This is the one we just made // this->enableAltimeterLogging(); // this->enablePIDOutputLogging(); // this->enableRadioRSSILogging(); // this->enableMotorLogging(); return true; }
- This startLogging function runs during Crazyflie startup and will start all the log blocks listed. As you can see we have some log blocks commented out currently so that we do NOT start those blocks.
- 3. Create Functions to Read the variable values during runtime (code in CCrazyflie.cpp):
float CCrazyflie::magX() { return this->sensorDoubleValue("mag.x"); } float CCrazyflie::magY() { return this->sensorDoubleValue("mag.y"); } float CCrazyflie::magZ() { return this->sensorDoubleValue("mag.z"); }
- So now we can use these sensor values, from the Firmware, in the Client code with
magX1 = cflieCopter1->magX(). This will store the sensor value in the Client-side variable magX1.
- So now we can use these sensor values, from the Firmware, in the Client code with
- 4. (OPTIONAL) We can also create a function to stop logging an individual log block in the Client (code in CCrazyflie.cpp)
void CCrazyflie::disableMagnetometerLogging() { m_tocLogs->unregisterLoggingBlock("magnetometer"); }
- This is straightforward, it just tells the client to stop logging this block.
- 5. (OPTIONAL) And we can stop ALL log blocks in the client as well (code in CCrazyflie.cpp):
bool CCrazyflie::stopLogging() { this->disableStabilizerLogging(); this->disableGyroscopeLogging(); this->disableAccelerometerLogging(); this->disableBatteryLogging(); // this->disableMagnetometerLogging(); // this->disableAltimeterLogging(); // this->disablePIDOutputLogging(); this->disableRadioRSSILogging(); this->disableMotorLogging(); return true; }