Deep Learning Basics for Drones
Deep learning enables drones to process raw sensor data—images, lidar point clouds, and time-series signals—without manual feature engineering. This tutorial covers the foundational architectures powering modern drone AI.
Neural Network Architecture
Neural networks learn hierarchical representations by stacking layers of neurons, each transforming inputs through weighted connections.
Real-world analogy: A neural network is like a factory assembly line. Raw materials (sensor data) enter, each station (layer) performs a specific transformation, and the final product (decision) emerges at the end.
Convolutional Neural Networks (CNNs)
CNNs are the backbone of drone vision—efficiently processing images by learning spatial hierarchies of features.
Real-world analogy: A CNN is like an art critic examining a painting. First, they notice individual brushstrokes (edges), then shapes (circles, squares), then objects (trees, buildings), and finally the overall scene (landscape portrait).
Recurrent Neural Networks (RNNs)
RNNs process sequential drone data—flight paths, time-series sensor readings, and temporal patterns.
Real-world analogy: RNNs are like a reader following a story. Each word's meaning depends on the words before it, and the reader maintains context throughout the narrative.
The Training Loop
Training a deep learning model follows a consistent pattern: forward pass, loss calculation, backpropagation, and weight update.
class SimpleNeuralNetwork:
"""A simple neural network for understanding backpropagation."""
def __init__(self, input_size, hidden_size, output_size, lr=0.01):
# Xavier initialization
self.W1 = np.random.randn(input_size, hidden_size) * np.sqrt(2.0 / input_size)
self.b1 = np.zeros((1, hidden_size))
self.W2 = np.random.randn(hidden_size, output_size) * np.sqrt(2.0 / hidden_size)
self.b2 = np.zeros((1, output_size))
self.lr = lr
def relu(self, z):
return np.maximum(0, z)
def relu_derivative(self, z):
return (z > 0).astype(float)
def softmax(self, z):
exp_z = np.exp(z - np.max(z, axis=1, keepdims=True))
return exp_z / np.sum(exp_z, axis=1, keepdims=True)
def forward(self, X):
"""Forward pass through the network."""
self.z1 = X @ self.W1 + self.b1
self.a1 = self.relu(self.z1)
self.z2 = self.a1 @ self.W2 + self.b2
self.a2 = self.softmax(self.z2)
return self.a2
def cross_entropy_loss(self, y_pred, y_true):
"""Calculate cross-entropy loss."""
m = y_true.shape[0]
log_likelihood = -np.log(y_pred[range(m), y_true])
return np.sum(log_likelihood) / m
def backward(self, X, y_true):
"""Backpropagation to compute gradients."""
m = X.shape[0]
# Output layer gradients
dz2 = self.a2.copy()
dz2[range(m), y_true] -= 1
dW2 = self.a1.T @ dz2 / m
db2 = np.sum(dz2, axis=0, keepdims=True) / m
# Hidden layer gradients
da1 = dz2 @ self.W2.T
dz1 = da1 * self.relu_derivative(self.z1)
dW1 = X.T @ dz1 / m
db1 = np.sum(dz1, axis=0, keepdims=True) / m
# Update weights
self.W2 -= self.lr * dW2
self.b2 -= self.lr * db2
self.W1 -= self.lr * dW1
self.b1 -= self.lr * db1
def train(self, X, y, epochs=100, batch_size=32):
"""Complete training loop with mini-batches."""
losses = []
n_samples = X.shape[0]
for epoch in range(epochs):
# Shuffle data
indices = np.random.permutation(n_samples)
X_shuffled = X[indices]
y_shuffled = y[indices]
epoch_loss = 0
n_batches = 0
for i in range(0, n_samples, batch_size):
X_batch = X_shuffled[i:i+batch_size]
y_batch = y_shuffled[i:i+batch_size]
# Forward pass
y_pred = self.forward(X_batch)
# Calculate loss
loss = self.cross_entropy_loss(y_pred, y_batch)
epoch_loss += loss
n_batches += 1
# Backward pass
self.backward(X_batch, y_batch)
avg_loss = epoch_loss / n_batches
losses.append(avg_loss)
if (epoch + 1) % 20 == 0:
accuracy = np.mean(np.argmax(y_pred, axis=1) == y_batch)
print(f"Epoch {epoch+1}/{epochs}, "
f"Loss: {avg_loss:.4f}, "
f"Batch Accuracy: {accuracy:.2%}")
return losses
# Example: Classify drone flight patterns
np.random.seed(42)
# Generate synthetic flight data (4 classes: hover, forward, turn, land)
n_samples = 500
X = np.vstack([
np.random.normal([0, 0, 100, 0.5], [0.1, 0.1, 5, 0.1], (n_samples, 4)), # hover
np.random.normal([5, 0, 100, 0.3], [1, 0.5, 5, 0.1], (n_samples, 4)), # forward
np.random.normal([0, 5, 100, 0.4], [0.5, 1, 5, 0.1], (n_samples, 4)), # turn
np.random.normal([0, 0, 50, 0.2], [0.1, 0.1, 10, 0.1], (n_samples, 4)), # land
])
y = np.array([0]*n_samples + [1]*n_samples + [2]*n_samples + [3]*n_samples)
# Normalize features
X = (X - X.mean(axis=0)) / X.std(axis=0)
# Train the network
nn = SimpleNeuralNetwork(input_size=4, hidden_size=16, output_size=4, lr=0.01)
losses = nn.train(X, y, epochs=100, batch_size=32)
print(f"\nFinal loss: {losses[-1]:.4f}")
Backpropagation Explained
Backpropagation computes gradients by applying the chain rule backward through the network.
Real-world analogy: Imagine tracing a mistake back through a factory assembly line. You start at the defective product and work backward station by station to find where each error was introduced.
CNN Implementation for Drone Imagery
class ConvLayer:
"""Simple 2D convolution layer."""
def __init__(self, in_channels, out_channels, kernel_size, stride=1, padding=0):
self.stride = stride
self.padding = padding
self.kernel_size = kernel_size
# He initialization
scale = np.sqrt(2.0 / (in_channels * kernel_size * kernel_size))
self.weights = np.random.randn(
out_channels, in_channels, kernel_size, kernel_size
) * scale
self.bias = np.zeros(out_channels)
def forward(self, X):
"""Forward pass with convolution."""
batch, in_ch, h, w = X.shape
out_ch = self.weights.shape[0]
k = self.kernel_size
# Padding
if self.padding > 0:
X_padded = np.pad(X, ((0,0), (0,0),
(self.padding, self.padding),
(self.padding, self.padding)))
else:
X_padded = X
# Output dimensions
h_out = (h + 2*self.padding - k) // self.stride + 1
w_out = (w + 2*self.padding - k) // self.stride + 1
output = np.zeros((batch, out_ch, h_out, w_out))
for b in range(batch):
for oc in range(out_ch):
for i in range(h_out):
for j in range(w_out):
h_start = i * self.stride
h_end = h_start + k
w_start = j * self.stride
w_end = w_start + k
region = X_padded[b, :, h_start:h_end, w_start:w_end]
output[b, oc, i, j] = np.sum(
region * self.weights[oc]
) + self.bias[oc]
return output
class MaxPoolLayer:
"""Max pooling layer."""
def __init__(self, pool_size=2, stride=2):
self.pool_size = pool_size
self.stride = stride
def forward(self, X):
batch, channels, h, w = X.shape
h_out = (h - self.pool_size) // self.stride + 1
w_out = (w - self.pool_size) // self.stride + 1
output = np.zeros((batch, channels, h_out, w_out))
for b in range(batch):
for c in range(channels):
for i in range(h_out):
for j in range(w_out):
h_start = i * self.stride
w_start = j * self.stride
region = X[b, c,
h_start:h_start+self.pool_size,
w_start:w_start+self.pool_size]
output[b, c, i, j] = np.max(region)
return output
class ReLU:
"""ReLU activation."""
def forward(self, X):
return np.maximum(0, X)
# Build a mini CNN for drone image classification
np.random.seed(42)
conv1 = ConvLayer(in_channels=3, out_channels=16, kernel_size=3, padding=1)
pool1 = MaxPoolLayer(pool_size=2)
relu = ReLU()
# Simulate batch of drone images (batch=2, channels=3, 32x32)
X = np.random.randn(2, 3, 32, 32)
# Forward pass
out1 = conv1.forward(X)
print(f"After Conv1: {out1.shape}")
out2 = relu.forward(out1)
print(f"After ReLU: {out2.shape}")
out3 = pool1.forward(out2)
print(f"After Pool1: {out3.shape}")
Training Optimizations
class AdamOptimizer:
"""Adam optimizer for training deep networks."""
def __init__(self, params, lr=0.001, beta1=0.9, beta2=0.999, eps=1e-8):
self.lr = lr
self.beta1 = beta1
self.beta2 = beta2
self.eps = eps
self.t = 0
self.m = {k: np.zeros_like(v) for k, v in params.items()}
self.v = {k: np.zeros_like(v) for k, v in params.items()}
def step(self, grads):
self.t += 1
updated_params = {}
for key in grads:
self.m[key] = self.beta1 * self.m[key] + (1 - self.beta1) * grads[key]
self.v[key] = self.beta2 * self.v[key] + (1 - self.beta2) * grads[key]**2
m_hat = self.m[key] / (1 - self.beta1**self.t)
v_hat = self.v[key] / (1 - self.beta2**self.t)
updated_params[key] = -self.lr * m_hat / (np.sqrt(v_hat) + self.eps)
return updated_params
print("Adam optimizer ready for training")
Hands-On Project: Flight Pattern Classifier
Build a CNN that classifies drone flight patterns from accelerometer data.
def generate_flight_patterns(n_samples=1000, seq_length=50):
"""Generate synthetic flight pattern data."""
patterns = {
'hover': lambda: np.random.normal([0, 0, 9.8], [0.1, 0.1, 0.05], (seq_length, 3)),
'forward': lambda: np.column_stack([
np.random.normal(2, 0.5, seq_length),
np.random.normal(0, 0.1, seq_length),
np.random.normal(9.8, 0.1, seq_length)
]),
'turn': lambda: np.column_stack([
np.random.normal(0, 0.2, seq_length),
np.random.normal(1.5, 0.3, seq_length),
np.random.normal(9.8, 0.2, seq_length)
]),
'ascend': lambda: np.column_stack([
np.random.normal(0, 0.1, seq_length),
np.random.normal(0, 0.1, seq_length),
np.random.normal(11, 0.5, seq_length)
]),
}
X = []
y = []
labels = list(patterns.keys())
for i in range(n_samples):
label_idx = i % len(labels)
data = patterns[labels[label_idx]]()
X.append(data)
y.append(label_idx)
return np.array(X), np.array(y), labels
class FlightPatternCNN:
"""1D CNN for flight pattern classification."""
def __init__(self, seq_length=50, n_features=3, n_classes=4):
self.seq_length = seq_length
# Simple 1D conv kernels
self.conv1_filters = np.random.randn(8, 3, 3) * 0.1
self.conv2_filters = np.random.randn(16, 8, 3) * 0.1
# Fully connected weights
self.fc_size = 16 * (seq_length // 4)
self.W_fc = np.random.randn(self.fc_size, n_classes) * 0.1
self.b_fc = np.zeros(n_classes)
def conv1d(self, x, filters, stride=1):
"""1D convolution."""
out_channels, in_channels, kernel_size = filters.shape
length = x.shape[1]
out_length = (length - kernel_size) // stride + 1
output = np.zeros((out_channels, out_length))
for oc in range(out_channels):
for i in range(out_length):
start = i * stride
output[oc, i] = np.sum(x[:, start:start+kernel_size] * filters[oc])
return output
def relu(self, x):
return np.maximum(0, x)
def global_avg_pool(self, x):
return np.mean(x, axis=1)
def forward(self, X):
batch_size = X.shape[0]
outputs = []
for i in range(batch_size):
x = X[i].T # (features, seq_length)
# Conv block 1
x = self.conv1d(x, self.conv1_filters, stride=1)
x = self.relu(x)
# Conv block 2
x = self.conv1d(x, self.conv2_filters, stride=1)
x = self.relu(x)
# Global average pooling
x = self.global_avg_pool(x)
# FC layer
logits = x @ self.W_fc + self.b_fc
probs = np.exp(logits) / np.sum(np.exp(logits))
outputs.append(probs)
return np.array(outputs)
# Generate data and train
X, y, labels = generate_flight_patterns(n_samples=400, seq_length=50)
# Normalize
X = (X - X.mean(axis=0)) / (X.std(axis=0) + 1e-8)
# Split
X_train, X_test = X[:320], X[320:]
y_train, y_test = y[:320], y[320:]
# Train
model = FlightPatternCNN()
preds = model.forward(X_test)
accuracy = np.mean(np.argmax(preds, axis=1) == y_test)
print(f"Flight Pattern Classification Accuracy: {accuracy:.1%}")
print(f"Classes: {labels}")
Key Takeaways
- Neural networks learn hierarchical representations from sensor data
- CNNs excel at spatial feature extraction from drone imagery
- RNNs capture temporal patterns in flight sequences
- Backpropagation efficiently computes gradients for weight updates
- Optimization techniques like Adam accelerate convergence
Next, we'll explore computer vision fundamentals specifically for drone applications.