Skip to content

Commit

Permalink
first commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Ryan Zotti authored and Ryan Zotti committed Jul 3, 2016
0 parents commit e9fd810
Show file tree
Hide file tree
Showing 8 changed files with 585 additions and 0 deletions.
36 changes: 36 additions & 0 deletions DataSet.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import numpy as np

class DataSet(object):

def __init__(self,
images,
labels):

assert images.shape[0] == labels.shape[0], (
'images.shape: %s labels.shape: %s' % (images.shape, labels.shape))
self._num_examples = images.shape[0]

self._images = images
self._labels = labels
self._epochs_completed = 0
self._index_in_epoch = 0

def next_batch(self, batch_size):
"""Return the next `batch_size` examples from this data set."""

start = self._index_in_epoch
self._index_in_epoch += batch_size
if self._index_in_epoch > self._num_examples:
# Finished epoch
self._epochs_completed += 1
# Shuffle the data
perm = np.arange(self._num_examples)
np.random.shuffle(perm)
self._images = self._images[perm]
self._labels = self._labels[perm]
# Start next epoch
start = 0
self._index_in_epoch = batch_size
assert batch_size <= self._num_examples
end = self._index_in_epoch
return self._images[start:end], self._labels[start:end]
121 changes: 121 additions & 0 deletions dataprep.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import numpy as np
import cv2
import re
import os
from datetime import datetime


def process_session(session_path):

cap = cv2.VideoCapture(session_path + "/output.mov")
video_timestamps = []
with open(session_path + '/video_timestamps.txt') as video_timestamps_reader:
for line in video_timestamps_reader:
line = line.replace("\n", "")
ts = datetime.strptime(line, '%Y-%m-%d %H:%M:%S.%f')
video_timestamps.append(ts)

commands = []
with open(session_path + '/clean_session.txt') as clean_session_reader:
for line in clean_session_reader:
line = line.replace("\n", "")
match = re.match(r"^.*\['(.*)'\].*$", line)
if match is not None:
command = match.group(1)
else:
command = 'no command'
raw_ts = line[line.index(" ") + 1:]
ts = datetime.strptime(raw_ts, '%Y-%m-%d %H:%M:%S.%f')
commands.append([command, ts])

# time after which no other data is relevant because driving session has ended
end_time = commands[len(commands) - 1][1]

# cleanup to track only command transitions
compact_commands = []
prev_command = None
for item in commands:
command, ts = item[0], item[1]
if command != prev_command and command != 'no command':
compact_commands.append([command, ts])
prev_command = command
commands = compact_commands

# time before which no other data is relevant because driving session just started
start_time = commands[0][1]

current_command = commands[0][0]
command_counter = 1
future_command = commands[command_counter][0]
future_command_ts = commands[command_counter][1]

predictors = []
targets = []

frame_counter = -1
while (cap.isOpened()):
frame_counter = frame_counter + 1
ret, frame = cap.read()
if cv2.waitKey(1) & 0xFF == ord('q'): # don't remove this if statement or video feed will die
break
video_timestamp = video_timestamps[frame_counter]
if video_timestamp > start_time:
if video_timestamp < end_time:
if video_timestamp > future_command_ts:
current_command = future_command
command_counter = command_counter + 1
if command_counter < len(commands):
future_command = commands[command_counter][0]
future_command_ts = commands[command_counter][1]
else:
future_command = "END"
future_command_ts = end_time
print(current_command)
cv2.imshow('frame', frame)
predictors.append(frame)
target = [0, 0, 0] # in order: left, up, right
if current_command == 'left':
target[0] = 1
elif current_command == 'up':
target[1] = 1
elif current_command == 'right':
target[2] = 1
targets.append(target)
else:
cap.release()
cv2.destroyAllWindows()

return predictors, targets


def data_prep(data_path):

data_folders = os.listdir(data_path)
train_folder_size = int(len(data_folders) * 0.8)

train_predictors = []
train_targets = []
for folder in data_folders[:train_folder_size]:
predictors, targets = process_session(data_path+'/'+folder)
train_predictors.append(predictors)
train_targets.append(targets)
train_predictors_np = np.array(predictors)
train_targets_np = np.array(targets)

validation_predictors = []
validation_targets = []
for folder in data_folders[train_folder_size:]:
predictors, targets = process_session(data_path + '/' + folder)
validation_predictors.append(predictors)
validation_targets.append(targets)
validation_predictors_np = np.array(predictors)
validation_targets_np = np.array(targets)

np.savez(data_path+'/final_processed_data', train_predictors=train_predictors_np,
train_targets=train_targets_np,validation_predictors = validation_predictors_np,
validation_targets = validation_targets_np)

if __name__ == '__main__':
data_path = str(os.path.dirname(os.path.realpath(__file__))) + "/data"
data_prep(data_path)
print("Finished.")
222 changes: 222 additions & 0 deletions drive_api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
import tornado.ioloop
import tornado.web
from datetime import datetime
import os
from operator import itemgetter
import RPi.GPIO as GPIO
import requests
from time import sleep

class PostHandler(tornado.web.RequestHandler):

def post(self):
timestamp = datetime.now()
data_json = tornado.escape.json_decode(self.request.body)
allowed_commands = set(['37','38','39','40'])
command = data_json['command']
command = list(command.keys())
command = set(command)
command = allowed_commands & command
file_path = str(os.path.dirname(os.path.realpath(__file__)))+"/session.txt"
log_entry = str(command)+" "+str(timestamp)
log_entries.append((command,timestamp))
with open(file_path,"a") as writer:
writer.write(log_entry+"\n")
print(log_entry)
command_duration = 0.1

if '37' in command:
motor.forward_left(100)
elif '38' in command:
motor.forward(100)
elif '39' in command:
motor.forward_right(100)
elif '40' in command:
motor.backward(100)
else:
motor.stop()

# This only works on data from the same live python process. It doesn't
# read from the session.txt file. It only sorts data from the active
# python process. This is required because it reads from a list instead
# of a file on data from the same live python process. It doesn't
# read from the session.txt file. It only sorts data from the active
# log_entries python list
class StoreLogEntriesHandler(tornado.web.RequestHandler):
def get(self):
file_path = str(os.path.dirname(os.path.realpath(__file__)))+"/clean_session.txt"
sorted_log_entries = sorted(log_entries,key=itemgetter(1))
prev_command = set()
allowed_commands = set(['38','37','39','40'])
for log_entry in sorted_log_entries:
command = log_entry[0]
timestamp = log_entry[1]
if len(command ^ prev_command) > 0:
prev_command = command
with open(file_path,"a") as writer:
readable_command = []
for element in list(command):
if element == '37':
readable_command.append("left")
if element == '38':
readable_command.append("up")
if element == '39':
readable_command.append("right")
if element == '40':
readable_command.append("down")
log_entry = str(list(readable_command))+" "+str(timestamp)
writer.write(log_entry+"\n")
print(log_entry)
self.write("Finished")

class MultipleKeysHandler(tornado.web.RequestHandler):

def get(self):
print("HelloWorld")
self.write('''
<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.2/jquery.min.js"></script>
<script>
var keys = {};
$(document).keydown(function (e) {
keys[e.which] = true;
var json_upload = JSON.stringify({command:keys});
var xmlhttp = new XMLHttpRequest();
xmlhttp.open("POST", "/post");
xmlhttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xmlhttp.send(json_upload);
printKeys();
});
$(document).keyup(function (e) {
delete keys[e.which];
var json_upload = JSON.stringify({command:keys});
var xmlhttp = new XMLHttpRequest();
xmlhttp.open("POST", "/post");
xmlhttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xmlhttp.send(json_upload);
printKeys();
});
function printKeys() {
var html = '';
for (var i in keys) {
if (!keys.hasOwnProperty(i)) continue;
html += '<p>' + i + '</p>';
}
$('#out').html(html);
}
</script>
</head>
<body>
Click in this frame, then try holding down some keys
<div id="out"></div>
</body>
</html>
''')


class Motor:

def __init__(self, pinForward, pinBackward, pinControlStraight,pinLeft, pinRight, pinControlSteering):
""" Initialize the motor with its control pins and start pulse-width
modulation """

self.pinForward = pinForward
self.pinBackward = pinBackward
self.pinControlStraight = pinControlStraight
self.pinLeft = pinLeft
self.pinRight = pinRight
self.pinControlSteering = pinControlSteering
GPIO.setup(self.pinForward, GPIO.OUT)
GPIO.setup(self.pinBackward, GPIO.OUT)
GPIO.setup(self.pinControlStraight, GPIO.OUT)

GPIO.setup(self.pinLeft, GPIO.OUT)
GPIO.setup(self.pinRight, GPIO.OUT)
GPIO.setup(self.pinControlSteering, GPIO.OUT)

self.pwm_forward = GPIO.PWM(self.pinForward, 100)
self.pwm_backward = GPIO.PWM(self.pinBackward, 100)
self.pwm_forward.start(0)
self.pwm_backward.start(0)

self.pwm_left = GPIO.PWM(self.pinLeft, 100)
self.pwm_right = GPIO.PWM(self.pinRight, 100)
self.pwm_left.start(0)
self.pwm_right.start(0)

GPIO.output(self.pinControlStraight,GPIO.HIGH)
GPIO.output(self.pinControlSteering,GPIO.HIGH)

def forward(self, speed):
""" pinForward is the forward Pin, so we change its duty
cycle according to speed. """
self.pwm_backward.ChangeDutyCycle(0)
self.pwm_forward.ChangeDutyCycle(speed)

def forward_left(self, speed):
""" pinForward is the forward Pin, so we change its duty
cycle according to speed. """
self.pwm_backward.ChangeDutyCycle(0)
self.pwm_forward.ChangeDutyCycle(speed)
self.pwm_right.ChangeDutyCycle(0)
self.pwm_left.ChangeDutyCycle(100)

def forward_right(self, speed):
""" pinForward is the forward Pin, so we change its duty
cycle according to speed. """
self.pwm_backward.ChangeDutyCycle(0)
self.pwm_forward.ChangeDutyCycle(speed)
self.pwm_left.ChangeDutyCycle(0)
self.pwm_right.ChangeDutyCycle(100)

def backward(self, speed):
""" pinBackward is the forward Pin, so we change its duty
cycle according to speed. """

self.pwm_forward.ChangeDutyCycle(0)
self.pwm_backward.ChangeDutyCycle(speed)

def left(self, speed):
""" pinForward is the forward Pin, so we change its duty
cycle according to speed. """
self.pwm_right.ChangeDutyCycle(0)
self.pwm_left.ChangeDutyCycle(speed)

def right(self, speed):
""" pinForward is the forward Pin, so we change its duty
cycle according to speed. """
self.pwm_left.ChangeDutyCycle(0)
self.pwm_right.ChangeDutyCycle(speed)

def stop(self):
""" Set the duty cycle of both control pins to zero to stop the motor. """

self.pwm_forward.ChangeDutyCycle(0)
self.pwm_backward.ChangeDutyCycle(0)
self.pwm_left.ChangeDutyCycle(0)
self.pwm_right.ChangeDutyCycle(0)

def make_app():
return tornado.web.Application([
(r"/drive",MultipleKeysHandler),(r"/post", PostHandler),
(r"/StoreLogEntries",StoreLogEntriesHandler)
])

if __name__ == "__main__":
GPIO.setmode(GPIO.BOARD)
command_duration = 0.1
motor = Motor(16, 18, 22, 19, 21, 23)
log_entries = []
app = make_app()
app.listen(81)
tornado.ioloop.IOLoop.current().start()
Loading

0 comments on commit e9fd810

Please sign in to comment.