Search code examples
pythontensorflowrecurrent-neural-networktflearn

TFLearn RNN output is always constant - New to TFLearn


GOAL:

I am trying to develop a NN model that is capable of learning some unknown non-linear quadcopter drone dynamics. The end purpose is for this model to be used inside of a Genetic Algorithm tool I wrote that will tune my drone's control system to achieve a desired response. The GA needs a "black box" model it can use to generate a prediction of the drone behavior given a set of inputs.

NN Design:

My idea for creating such a model is to utilize a RNN that takes a time history of ESC motor controller command values as inputs (4 inputs, 1 for each motor), and spits out the corresponding Euler angles (3 outputs, roll, pitch, yaw). I have a custom flight controller I designed/wrote that enables me to log any necessary data to an SD card, ie the motor outputs and Euler angles.

The code I am currently using to try and train this model unsuccessfully is shown below. This is also my first time using TFLearn or any major NN programming, so pointers for improvement would also be appreciated.

Also, if you want to run this yourself, everything you need (assuming you already have TFLearn and TensorFlow) can be found on my github repo HERE

# An attempt to use a Recurrent Neural Network to learn and predict drone dynamics. In this version, the
# network inputs are the four motor commands and the outputs are the roll, pitch, and yaw angles.

from __future__ import division, print_function, absolute_import

import tflearn
from tflearn.layers.normalization import batch_normalization
import numpy as np
import pandas as pd
import math
import matplotlib
matplotlib.use('Agg')   # use('Agg') for saving to file and use('TkAgg') for interactive plot
import matplotlib.pyplot as plt

# Configuration Variables
input_dim = 4
output_dim = 3
steps_of_history = 10
batch_len = 128
epoch_len = 3

rawDataPath = 'DroneData/timeSeriesData.csv'

# Generate the data used in training
timeSeries = pd.read_csv(rawDataPath)
x = [timeSeries['m1CMD'], timeSeries['m2CMD'], timeSeries['m3CMD'], timeSeries['m4CMD']]
y = [timeSeries['pitch'], timeSeries['roll'], timeSeries['yaw']]

# Convert row vectors into column vectors
x = np.array(x);     y = np.array(y)
x = np.transpose(x); y = np.transpose(y)

# Generate the input and target training data
input_seq = []
output_seq = []

for i in range(0, len(timeSeries['rtosTick']) - steps_of_history):
    input_seq.append(x[i:i+steps_of_history, :])    # Time history input
    output_seq.append(y[i+steps_of_history, :])     # Single output resulting from ^^^


trainX = np.reshape(input_seq, [-1, input_dim, steps_of_history])
trainY = np.reshape(output_seq, [-1, output_dim])


# Build the network model
input_layer = tflearn.input_data(shape=[None, input_dim, steps_of_history])

layer1 = tflearn.simple_rnn(input_layer, n_units=10, activation='softmax', return_seq=True, name='Layer1')
layer2 = tflearn.simple_rnn(layer1, n_units=10, activation='sigmoid', name='Layer2')
layer3 = tflearn.fully_connected(layer2, output_dim, activation='linear', name='Layer3')

output_layer = tflearn.regression(layer3, optimizer='adam', loss='mean_square', learning_rate=0.1)


# Training
model = tflearn.DNN(output_layer, clip_gradients=0.3, tensorboard_verbose=0)
model.fit(trainX, trainY, n_epoch=epoch_len, validation_set=0.1, batch_size=batch_len)


# Generate a model prediction as a very simple sanity check...
predictY = model.predict(trainX)


# Plot the results
plt.figure(figsize=(20, 4))
plt.suptitle('Pitch Predictions')
plt.plot(trainY[:, 0], 'r-', label='Actual')
plt.plot(predictY[:, 0], 'g-', label='Predicted')
plt.legend()
plt.savefig('pitch.png')

plt.figure(figsize=(20, 4))
plt.suptitle('Roll Predictions')
plt.plot(trainY[:, 1], 'r-', label='Actual')
plt.plot(predictY[:, 1], 'g-', label='Predicted')
plt.legend()
plt.savefig('roll.png')

plt.figure(figsize=(20, 4))
plt.suptitle('Yaw Predictions')
plt.plot(trainY[:, 2], 'r-', label='Actual')
plt.plot(predictY[:, 2], 'g-', label='Predicted')
plt.legend()
plt.savefig('yaw.png')

PROBLEM:

If you look at the "pitch.png", "roll.png", "yaw.png" graphs in the root folder of my linked repo, you'll see that the predicted value of my network is a constant value. I trained against a simple flight log data set of 7410 samples that included decent variations in pitch roll and yaw (+/-20 deg or so). See raw data HERE. I know this is probably not good enough for the final model, but it's something to start with.

I feel like I should at least be getting SOME variation in output, even if it doesn't fit well. Can someone help me pinpoint this problem? I'm not making any progress myself.


Solution

  • I ended up solving the issue. It turns out that the NN does not like being fed input data with magnitudes in the 1000+ range. The ESC data was recorded as the time a PWM command spent HI in uS, so the NN was getting values anywhere from 1060-1860.

    By preprocessing the data in Matlab to scale it down into the 0.0-5.0 range, the system behaved nicely and was able to learn the dynamics without too much trouble.

    I'll be continuously updating the code on the github link should anyone who comes across this post feel like using it.