Logging: Difference between revisions

From Distributed Autonomous and Networked Control Lab
Jump to navigation Jump to search
Jnoronha (talk | contribs)
→‎MATLAB Parser: added data import and x and y positions
Jnoronha (talk | contribs)
moved the matlab section to the bottom
Line 199: Line 199:
</code>
</code>
</blockquote>
</blockquote>
===[http://wikis.ece.iastate.edu/robotic-agriculture-data-acquisition/index.php/Data_Analysis_Tool 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 [http://wikis.ece.iastate.edu/robotic-agriculture-data-acquisition/index.php/Data_Analysis_Tool 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.
==New Client Log Blocks==
In the Swarm Client there is a sub-system designed for creating and maintaining Log Blocks. Once you have [[:Firmware#Log Blocks|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'')
<blockquote>
<code>
<pre>
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");
}
</pre>
</code>
</blockquote>
::This will then register a log block called ''magnetometer'' on the Client, which will be updated every 1000 &mu;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#Log Blocks|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''):
<blockquote>
<code>
<pre>
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;
}
</pre>
</code>
</blockquote>
::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''):
<blockquote>
<code>
<pre>
float CCrazyflie::magX() {
return this->sensorDoubleValue("mag.x");
}
float CCrazyflie::magY() {
return this->sensorDoubleValue("mag.y");
}
float CCrazyflie::magZ() {
return this->sensorDoubleValue("mag.z");
}
</pre>
</code>
</blockquote>
::So now we can use these sensor values, from the Firmware, in the Client code with <code>magX1 = cflieCopter1->magX()</code>. 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'')
<blockquote>
<code>
<pre>
void CCrazyflie::disableMagnetometerLogging() {
m_tocLogs->unregisterLoggingBlock("magnetometer");
}
</pre>
</code>
</blockquote>
::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''):
<blockquote>
<code>
<pre>
bool CCrazyflie::stopLogging() {
this->disableStabilizerLogging();
this->disableGyroscopeLogging();
this->disableAccelerometerLogging();
this->disableBatteryLogging();
// this->disableMagnetometerLogging();
// this->disableAltimeterLogging();
// this->disablePIDOutputLogging();
this->disableRadioRSSILogging();
this->disableMotorLogging();
return true;
}
</pre>
</code>
</blockquote>
::This will disable ALL the logging blocks included in the list.


==MATLAB Parser==
==MATLAB Parser==
Line 337: Line 459:
</code>
</code>
</blockquote>
</blockquote>
===[http://wikis.ece.iastate.edu/robotic-agriculture-data-acquisition/index.php/Data_Analysis_Tool 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 [http://wikis.ece.iastate.edu/robotic-agriculture-data-acquisition/index.php/Data_Analysis_Tool 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.
==New Client Log Blocks==
In the Swarm Client there is a sub-system designed for creating and maintaining Log Blocks. Once you have [[:Firmware#Log Blocks|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'')
<blockquote>
<code>
<pre>
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");
}
</pre>
</code>
</blockquote>
::This will then register a log block called ''magnetometer'' on the Client, which will be updated every 1000 &mu;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#Log Blocks|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''):
<blockquote>
<code>
<pre>
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;
}
</pre>
</code>
</blockquote>
::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''):
<blockquote>
<code>
<pre>
float CCrazyflie::magX() {
return this->sensorDoubleValue("mag.x");
}
float CCrazyflie::magY() {
return this->sensorDoubleValue("mag.y");
}
float CCrazyflie::magZ() {
return this->sensorDoubleValue("mag.z");
}
</pre>
</code>
</blockquote>
::So now we can use these sensor values, from the Firmware, in the Client code with <code>magX1 = cflieCopter1->magX()</code>. 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'')
<blockquote>
<code>
<pre>
void CCrazyflie::disableMagnetometerLogging() {
m_tocLogs->unregisterLoggingBlock("magnetometer");
}
</pre>
</code>
</blockquote>
::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''):
<blockquote>
<code>
<pre>
bool CCrazyflie::stopLogging() {
this->disableStabilizerLogging();
this->disableGyroscopeLogging();
this->disableAccelerometerLogging();
this->disableBatteryLogging();
// this->disableMagnetometerLogging();
// this->disableAltimeterLogging();
// this->disablePIDOutputLogging();
this->disableRadioRSSILogging();
this->disableMotorLogging();
return true;
}
</pre>
</code>
</blockquote>
::This will disable ALL the logging blocks included in the list.


----
----

Revision as of 19:00, 2 August 2016

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


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.

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

(**TODO**) We use a Matlab script to 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


%% 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


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