Logging

From Distributed Autonomous and Networked Control Lab
Jump to: navigation, search

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.)

Client-Side External 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


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.
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;
}

This will disable ALL the logging blocks included in the list.


MATLAB Parser

We use a Matlab script to parse the log files. You can use any program you want to parse the data if you are more comfortable with it, below you will find some details on how we parse the log-files.

Multi-Logfile Parser

The primary way we use to visualize the log files is via a very simple and messy MATLAB script. It consists of a bunch of sub-routines that were all thrown together into a single .m file.

Import Log Data


%% Crazyflie Logging Parser

[handLog, delimHand] = importdata('hand.txt','\t');    %Imports data from all 5 log files from the Swarm Client
[cflie1Log, delim1] = importdata('cflie1.txt','\t');
[cflie2Log, delim2] = importdata('cflie2.txt','\t');
[cflie3Log, delim3] = importdata('cflie3.txt','\t');
[cflie4Log, delim4] = importdata('cflie4.txt','\t');
%
handTime = handLog.data(:,1);
handRoll = handLog.data(:,3);
handPitch = handLog.data(:,5);
handYaw = handLog.data(:,7);
handX = handLog.data(:,9);                             %Assigns Hand Data to user-friendly variable names
handY = handLog.data(:,11);
handZ = handLog.data(:,13);
handLoopTime = handLog.data(:,15);
handLoopTimeDelta = handLog.data(:,17);
%
cflie1Time = cflie1Log.data(:,1);
cflie1Roll = cflie1Log.data(:,3);
cflie1Pitch = cflie1Log.data(:,5);                      %Assigns Crazyflie1 Data to user-friendly variable names
cflie1Yaw = cflie1Log.data(:,7);
cflie1X = cflie1Log.data(:,9);
cflie1Y = cflie1Log.data(:,11);
cflie1Z = cflie1Log.data(:,13);
cflie1CamYaw = cflie1Log.data(:,15);
cflie1CorrectedYaw = cflie1Log.data(:,17);
cflie1LoopTimeTotal = cflie1Log.data(:,19);
cflie1LoopTimeTotalDelta = cflie1Log.data(:,21);
cflie1LoopTime = cflie1Log.data(:,23);
cflie1LoopTimeDelta = cflie1Log.data(:,25);
cflie1VrpnPacketDelta = cflie1Log.data(:,27);
cflie1VrpnPacketTime = cflie1Log.data(:,29);
cflie1VrpnPacketBackup = cflie1Log.data(:,31);
% cflie1XSetpoint = cflie1Log.data(:,33);
% cflie1YSetpoint = cflie1Log.data(:,35);
% cflie1ZSetpoint = cflie1Log.data(:,37);
% cflie1YawSetpoint = cflie1Log.data(:,39);
% cflie1PitchDesired = cflie1Log.data(:,41);
% cflie1RollDesired = cflie1Log.data(:,43);
% cflie1YawDesired = cflie1Log.data(:,45);
% cflie1ThrustDesired = cflie1Log.data(:,47);
% cflie1RadioRSSI = cflie1Log.data(:,49);
% cflie1CamYawRad = cflie1Log.data(:,51);
% cflie1CamPitchRad = cflie1Log.data(:,53);
% cflie1CamRollRad = cflie1Log.data(:,55);
% cflie1Motor1 = cflie1Log.data(:,57);
% cflie1Motor2 = cflie1Log.data(:,59);
% cflie1Motor3 = cflie1Log.data(:,61);
% cflie1Motor4 = cflie1Log.data(:,63);
% cflie1RollRate = cflie1Log.data(:,65);
% cflie1PitchRate = cflie1Log.data(:,67);
% cflie1YawRate = cflie1Log.data(:,69);
...
...
...
(same for Crazyflie 2, 3, and 4..)
...
...

Note: This list needs to match the log file length that the Swarm Client creates (comment out as needed)


Plotting X and Y Position of Crazyflies

The plots look like this:
XPositionPlot.png YPositionPlot.png


%% Plot X Using ImportData

figure(8)    %Plotting X-Positions
hold off
plot(cflie1Time,cflie1X, '-b')
hold on
% plot(cflie1Time,cflie1XSetpoint, '-.b')
% 
plot(cflie2Time,cflie2X, '-g')
% plot(cflie2Time,cflie2XSetpoint, '-.g')
% 
plot(cflie3Time,cflie3X, '-k')
% plot(cflie3Time,cflie3XSetpoint, '-.k')
% 
plot(cflie4Time,cflie4X, '-y')
%
plot(handTime, handX, '-r')
% plot(cflie4Time,cflie4XSetpoint, '-.y')
title 'Crazyflie X-Positions'
legend('Crazyflie 1','Crazyflie 2','Crazyflie3', 'Crazyflie4', 'Hand')
xlabel 'Time (seconds)'
ylabel 'X-Position (m)'
grid on
% legend('Crazyflie 1','Setpoint', 'Crazyflie 2','Setpoint', 'Crazyflie3','Setpoint', 'Crazyflie4','Setpoint')
% 
% 

figure(9)      %Plotting Y-Positions
hold off
plot(cflie1Time,cflie1Y, '-b')
hold on
% plot(cflie1Time,cflie1YSetpoint, '-.b')
% 
plot(cflie2Time,cflie2Y, '-g')
% plot(cflie2Time,cflie2YSetpoint, '-.g')
% 
plot(cflie3Time,cflie3Y, '-k')
% plot(cflie3Time,cflie3YSetpoint, '-.k')
% 
plot(cflie4Time,cflie4Y, '-y')
% plot(cflie3Time,cflie3YSetpoint, '-.y')
%
plot(handTime, handY, '-r')
title 'Crazyflie Y-Positions'
legend('Crazyflie 1','Crazyflie 2','Crazyflie3', 'Crazyflie4', 'Hand')
% legend('Crazyflie 1', 'Setpoint','Crazyflie 2', 'Setpoint','Crazyflie3','Setpoint', 'Crazyflie4','Setpoint')
xlabel 'Time (seconds)'
ylabel 'Y-Position (m)'
grid on


Plotting Loop Timing

This sub-routine plots a 3-piece subplot:
  1. Z-Positions (Height) of all 4 Crazyflies
  2. Time it takes for each Crazyflie Callback to finish
  3. Time between subsequent Callback calls (Loop Deltas)
Resulting in this Plot
HandModeTimingFigure.JPG

figure(7)

subplot(3,1,1)
hold off
plot(cflie1Time,cflie1Z,'-b')
hold on
plot(cflie2Time,cflie2Z,'-g')
plot(cflie3Time,cflie3Z,'-k')
plot(cflie4Time,cflie4Z,'-y')
title 'Crazyflie Z (Height)'
legend('Crazyflie 1', 'Crazyflie2', 'Crazyflie3', 'Crazyflie4','Location','NorthWest')
ylabel 'Height (m)'
grid on

subplot(3,1,2)
hold off
plot(cflie1Time(4:end),cflie1LoopTime(4:end),'-b')
hold on
plot(cflie2Time(4:end),cflie2LoopTime(4:end),'-g')
plot(cflie3Time(4:end),cflie3LoopTime(4:end),'-k')
plot(cflie4Time(4:end),cflie4LoopTime(4:end),'-y')
title 'Loop Timing'
legend('Callback 1', 'Callback 2', 'Callback3', 'Callback4','Location','NorthWest')
ylabel 'Loop Time (seconds)'
grid on

subplot(3,1,3)
hold off
plot(cflie1Time(4:end),cflie1LoopTimeDelta(4:end),'-b')
hold on
plot(cflie2Time(4:end),cflie2LoopTimeDelta(4:end),'-g')
plot(cflie3Time(4:end),cflie3LoopTimeDelta(4:end),'-k')
plot(cflie4Time(4:end),cflie4LoopTimeDelta(4:end),'-y')
title 'Time Between Loop Calls'
legend('Callback 1', 'Callback 2', 'Callback3', 'Callback4','Location','NorthWest')
ylabel 'Loop Deltas (seconds)'
xlabel 'Time (seconds)'
grid on


Hand Mode Position Plot

This sub-routine plots a 4-piece subplot:
  1. X-Positions of all 4 Crazyflies and Hand
  2. Y-Positions of all 4 Crazyflies and Hand
  3. Z-Position of Hand (to show position based Gestures)
  4. Pitch Angle of Hand (to show rotation based Gestures)
Resulting in this Plot (w/o added annotations)
HandModePlot.jpg


figure(12)

subplot(4,1,1)
hold off
plot(cflie1Time,cflie1X,'-b')
hold on
plot(cflie2Time,cflie2X,'-g')
plot(cflie3Time,cflie3X,'-k')
plot(cflie4Time,cflie4X,'-y')
plot(handTime, handX, '-r')
title 'X Position'
legend('Crazyflie 1', 'Crazyflie2', 'Crazyflie3', 'Crazyflie4','Hand')
ylabel 'X Position (m)'
grid on

subplot(4,1,2)
hold off
plot(cflie1Time,cflie1Y,'-b')
hold on
plot(cflie2Time,cflie2Y,'-g')
plot(cflie3Time,cflie3Y,'-k')
plot(cflie4Time,cflie4Y,'-y')
plot(handTime, handY, '-r')
title 'Y Position'
legend('Crazyflie 1', 'Crazyflie2', 'Crazyflie3', 'Crazyflie4','Hand')
ylabel 'Y Position (m)'
grid on

subplot(4,1,3)
hold off
plot(handTime, handZ, '-r')
title 'Hand Z Position'
legend('Hand Height')
ylabel 'Height (m)'
grid on

subplot(4,1,4)
hold off
plot(handTime, handPitch, '-r')
title 'Hand Pitch'
legend('Hand Pitch')
ylabel 'Angle (rad)'
grid on

NOTE: There are many other sub-routines in the MATLAB script that we are not currently using, this is just a sample of some of the more useful data visualization methods we have in place already. You can see more in cflieLogParser.m (located in the Swarm Client directory).

Universal Lab Parser (Alternative)

Alternatively, the lab has a unified logging format that can be read by a universal log parser. More information about this parsing method can be found here

The Swarm Platform log files are compatible with this format and can be used just as any other lab log files. The problem is that this parser can only view a single text file at a time. This makes comparisons and plotting across all Crazyflies in the swarm very very difficult.



Main Directory
Crazyflie Swarm Home | PC Client Software | USB Radio | Firmware | FAQ
Modifications Directory
Controller | Logging | Keyboard Commands | Changing Radio Channel | Flight Modes | Callbacks | Adding a Crazyflie | Firmware