diff --git a/Code/.gitignore b/Code/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..b42097e865c14e2dde215fccdd2e303d6c103b36
--- /dev/null
+++ b/Code/.gitignore
@@ -0,0 +1 @@
+**/__pycache__/
\ No newline at end of file
diff --git a/Code/Control/Laptop_Code/ball_detection/__pycache__/esp32cam_live_feed.cpython-38.pyc b/Code/Control/Laptop_Code/ball_detection/__pycache__/esp32cam_live_feed.cpython-38.pyc
deleted file mode 100644
index 640418a2f7bf60aad201d640037e710058866105..0000000000000000000000000000000000000000
Binary files a/Code/Control/Laptop_Code/ball_detection/__pycache__/esp32cam_live_feed.cpython-38.pyc and /dev/null differ
diff --git a/Code/Control/Laptop_Code/ball_detection/ball_detection.py b/Code/Control/Laptop_Code/ball_detection/ball_detection.py
new file mode 100644
index 0000000000000000000000000000000000000000..c231c8a90efadcd02b88323cdef709e5b043c1da
--- /dev/null
+++ b/Code/Control/Laptop_Code/ball_detection/ball_detection.py
@@ -0,0 +1,157 @@
+## Codebase - Live Training-based Ball Detection
+### Import Modules and Drive
+import os
+import cv2
+import glob
+import torch
+import numpy as np
+import torch.nn as nn
+import torch.quantization
+import matplotlib.pyplot as plt
+import matplotlib.patches as patches
+
+from urllib.request import urlopen, Request  #
+
+from base64 import b64decode
+from datetime import datetime
+from matplotlib import patches
+from detecto import core, utils
+from torchvision import transforms
+from matplotlib import pyplot as plt
+from IPython.display import Image, display, Javascript
+distanceDetect = __import__('ball_detection.distance-detection-torch.distance-detection-torch', fromlist = ['distanceDetect']).distanceDetect
+# print("torch.cuda.is_available() = ", torch.cuda.is_available())
+
+# change the IP address below according to the IP shown in the Serial monitor of Arduino code
+# url='http://192.168.1.107/cam-lo.jpg'
+url='http://192.168.4.1/cam-hi.jpg'
+# url='http://192.168.1.107/cam-mid.jpg'
+
+def modelName(type = 1):
+  if type == 1:
+    return 'fasterrcnn_resnet50_fpn'
+  elif type == 2:
+    return 'fasterrcnn_mobilenet_v3_large_fpn'
+  elif type == 3:
+    return 'fasterrcnn_mobilenet_v3_large_320_fpn'
+  else:
+    return ValueError('Unknown Pretrained Model')
+
+def modelTitle(modelLoc, device, type):
+  time = datetime.now().strftime("%Y%m%d_%H%M%S")
+  if device == 'cpu':
+    dev = 'cpu'
+  elif device[:4] == 'cuda':
+    dev = 'cuda'
+  return modelLoc + 'model_weights-' + str(type) + '-' + time + '-' + dev + '.pth'
+
+def returnModel(device, labelSet, modelLoc, modelFile):
+  model = core.Model(labelSet, modelname = modelName(int(modelFile[14])))
+  model._model.load_state_dict(torch.load(modelLoc + modelFile, map_location = torch.device(device)))
+  return model
+
+#### Live Detection
+def detectLive(model, minDetectionScore = 0.90, showSight = True):
+    gbx  = 0
+    gby  = 0
+    dist = 0
+
+    header = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36."}
+    req      = Request(url, headers = header)
+    img_resp = urlopen(req, timeout = 60)
+    imgnp    = np.array(bytearray(img_resp.read()), dtype = np.uint8)
+    frame    = cv2.imdecode(imgnp, -1)
+
+    labels, boxes, scores = model.predict(frame)
+    for i in range(boxes.shape[0]):
+      box = boxes[i]
+      x0, y0 = float(box[0]), float(box[1])
+      x1, y1 = float(box[2]), float(box[3])
+      w      = x1 - x0
+      h      = y1 - y0
+      dCrop  = [int(y0*cropFactor + y1*(1-cropFactor)), int(y1*cropFactor + y0*(1-cropFactor)),
+                int(x0*cropFactor + x1*(1-cropFactor)), int(x1*cropFactor + x0*(1-cropFactor))]
+      avgClRGB = cv2.mean(frame[dCrop[0]:dCrop[1], dCrop[2]:dCrop[3]])
+      avgClHue = rgbToHue(avgClRGB)
+
+      if scores[i] > minDetectionScore and avgClHue > colorLow and avgClHue < colorHigh:
+        cv2.rectangle(frame, (int(x0), int(y0)),
+                             (int(x1), int(y1)),
+                             (0, 255, 0), 2)
+        if labels:
+          dist = distanceDetect('load-testImg', distWtsFile, [[x0, x1, y0, y1], frame.shape[:2]])
+          gbx = int((x1 + x0) / 2)
+          gby = int((y1 + y0) / 2)
+          cv2.putText(frame, 'x:{}, y:{}'.format(gbx, gby),
+                             (int(box[0]), int(box[1]) - 10),
+                             cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 0, 0), 3)
+
+    if showSight:
+      cv2.imshow('Ball Detection', frame)
+      key = cv2.waitKey(1) & 0xFF
+    
+    return gbx, gby, dist
+
+### Supplementary Functions
+#### Display of Image with Bounding Boxes
+def displayBoxedImage(image, boxes = [], labels = None):
+  fig, ax = plt.subplots(1)
+  if isinstance(image, torch.Tensor):
+    image = utils.reverse_normalize(image)
+    image = transforms.ToPILImage()(image)
+  ax.imshow(image)
+  if labels is not None and not utils._is_iterable(labels):
+    labels = [labels]
+  for i in range(len(boxes)):
+    box = boxes[i]
+    width, height =  box[2] - box[0], box[3] - box[1]
+    initial_pos   = (box[0], box[1])
+    rect = patches.Rectangle(initial_pos, width, height, linewidth = 1, edgecolor = 'r', facecolor = 'none')
+    if labels:
+      ax.text(box[0] + 5, box[1] - 5, '{}'.format(labels[i]), color='red')
+    ax.add_patch(rect)
+  plt.show()
+
+#### Calculation of Hue from RGB (adopted from [code](https://www.geeksforgeeks.org/program-change-rgb-color-model-hsv-color-model/))
+def rgbToHue(RGB):
+  RGB = [val/255.0 for val in RGB]
+  cmax = max(RGB)
+  cmin = min(RGB)
+  diff = cmax - cmin
+  r, g, b = RGB[0:3]
+  if cmax == cmin:
+    return 0
+  if cmax == r:
+    return (60 * ((g - b) / diff) + 360) % 360
+  if cmax == g:
+    return (60 * ((b - r) / diff) + 120) % 360
+  if cmax == b:
+    return (60 * ((r - g) / diff) + 240) % 360
+
+#### Formula-based Depth Estimation (adopted from [mono_depth.py](https://github.com/agrawalparth10/FORAY-Perception/blob/master/mono_depth/mono_depth.py))
+def depth_estimate(figDims, boundingBox):
+  def x_hat(realLen, ip, i0, imageLen):
+    return (realLen * (ip-i0)) / imageLen
+  def y_hat(realLen, jp, j0, imageLen):
+    return (realLen * (jp-j0)) / imageLen
+  def z_hat(dist, pixelLen, imageLen):
+    return dist*(pixelLen/imageLen)
+  def cal_coordinates(dist, realLen, ip, jp, i0, j0, imageLen, pixelLen):
+    return (x_hat(realLen, ip, i0, imageLen), y_hat(realLen, jp, j0, imageLen), z_hat(dist, pixelLen, imageLen))
+  def measure(dist, realLen, ip, jp, i0, j0, imageLen, pixelLen):
+    x_cor = x_hat(realLen, ip, i0, imageLen)
+    z_cor = z_hat(dist, pixelLen, imageLen)
+    dist  = (x_cor ** 2 + z_cor ** 2) ** 0.5
+    return dist
+  imHeight, imWidth = figDims
+  center = (imWidth // 2, imHeight // 2)
+  x, y, w, h   = boundingBox[0:4]
+  relativeDist = measure        (dist = 0.51, realLen = 0.228, ip = x, jp = y, i0 = center[0], j0 = center[1], imageLen = w, pixelLen = 500)
+  coordinates  = cal_coordinates(dist = 0.51, realLen = 0.228, ip = x, jp = y, i0 = center[0], j0 = center[1], imageLen = w, pixelLen = 500)
+  return relativeDist, coordinates
+
+### Declare Varables and Constants
+colorLow    =  60
+colorHigh   = 180
+cropFactor  =   0.90
+distWtsFile = './ball_detection/distance-detection-torch/distance-detection-weights-3-2.0sd-20210808_212550.json'
\ No newline at end of file
diff --git a/Code/Control/Laptop_Code/ball_detection/esp32cam_live_feed.py b/Code/Control/Laptop_Code/ball_detection/ball_detection_full.py
similarity index 89%
rename from Code/Control/Laptop_Code/ball_detection/esp32cam_live_feed.py
rename to Code/Control/Laptop_Code/ball_detection/ball_detection_full.py
index 2d407591ff1ebc285fb26c5d13b40d5031823d1b..247ee0b07715263f5dd451ffce7bcefbe2c76dd7 100644
--- a/Code/Control/Laptop_Code/ball_detection/esp32cam_live_feed.py
+++ b/Code/Control/Laptop_Code/ball_detection/ball_detection_full.py
@@ -1,4 +1,4 @@
-## Codebase - Live Training-based Ball Detection (COMPLETE)
+## Codebase - Live Training-based Ball Detection
 ### Import Modules and Drive
 import os
 import cv2
@@ -22,14 +22,13 @@ from IPython.display import Image, display, Javascript
 distanceDetect = __import__('ball_detection.distance-detection-torch.distance-detection-torch', fromlist = ['distanceDetect']).distanceDetect
 print("torch.cuda.is_available() = ", torch.cuda.is_available())
 
+minDetectionScore    =   0.50
 
-#change the IP address below according to the
-#IP shown in the Serial monitor of Arduino code
+# change the IP address below according to the IP shown in the Serial monitor of Arduino code
 # url='http://192.168.1.107/cam-lo.jpg'
 url='http://192.168.4.1/cam-hi.jpg'
 # url='http://192.168.1.107/cam-mid.jpg'
 
-
 #### Modify Detecto Core to include possibility of other base models
 def modifyCore():
   cModLineNums = [221, 254]
@@ -38,9 +37,6 @@ def modifyCore():
   # REPLACABLE LINE FOR DIFFERENT LOCAL COMPUTER DEVICES
   coreFile     = 'C:\\Users\\uclal\\.conda\\envs\\FORAY\\Lib\\site-packages\\detecto\\core.py'
 
-
-  print('debug')
-
   cModLineVals = ["    def __init__(self, classes=None, device=None, pretrained=True, modelname=\'fasterrcnn_resnet50_fpn\'):\n",
                   "            self._model = torchvision.models.detection.fasterrcnn_resnet50_fpn(pretrained=pretrained)\n"]
   cAddLineVals = ["        if modelname == \'fasterrcnn_resnet50_fpn\':\n",
@@ -110,9 +106,6 @@ def objectDetection(loc):
     center = (x0 + (w/2), y0 + (h/2))
     dCrop  = [int(y0*cropFactor + y1*(1-cropFactor)), int(y1*cropFactor + y0*(1-cropFactor)),
               int(x0*cropFactor + x1*(1-cropFactor)), int(x1*cropFactor + x0*(1-cropFactor))]
-    # relativeDist, coordinates = depth_estimate((imHeight, imWidth), (x0, y0, w, h))
-    # relativeDist = float(relativeDist)
-    # coordinates  = tuple([float(coord) for coord in coordinates])
     distance     = distanceDetect('load-testImg', distWtsFile, [[x0, x1, y0, y1], [imWidth, imHeight]])
     avgClRGB     = cv2.mean(image[dCrop[0]:dCrop[1], dCrop[2]:dCrop[3]])
     avgClHue     = rgbToHue(avgClRGB)
@@ -124,7 +117,7 @@ def objectDetection(loc):
     # print('Relative Distance   : ', relativeDist)
     # print('Coordinates         : ', coordinates)
     print()
-    if tScores[count] > minScore and avgClHue > colorLow and avgClHue < colorHigh:
+    if tScores[count] > minDetectionScore and avgClHue > colorLow and avgClHue < colorHigh:
       scores.append(float(tScores[count]))
       labels.append(tLabels[count]+' '+str(round(float(tScores[count])*100, 2))+'%')
       boxes.append ([x0, y0, x1, y1])
@@ -132,18 +125,6 @@ def objectDetection(loc):
 
 #### Live Detection
 def detectLive(model, showSight = True):
-    # cv2.namedWindow('Ball Detection')
-    # try:
-    #   cap = cv2.VideoCapture(0)#, cv2.CAP_DSHOW)
-    # except:
-    #   print('No webcam available.')
-    #   return
-
-    # ret = True
-    # while ret:
-    # ret, frame = cap.read()
-    #print(ret)
-    #print(frame)
     gbx = 0
     gby = 0
     dist = 0
@@ -155,7 +136,6 @@ def detectLive(model, showSight = True):
     frame=cv2.imdecode(imgnp,-1)
 
     labels, boxes, scores = model.predict(frame)
-    # labels, boxes, scores = [np.array([]) for i in range(3)]
     for i in range(boxes.shape[0]):
       box = boxes[i]
       x0, y0 = float(box[0]), float(box[1])
@@ -167,15 +147,12 @@ def detectLive(model, showSight = True):
       avgClRGB = cv2.mean(frame[dCrop[0]:dCrop[1], dCrop[2]:dCrop[3]])
       avgClHue = rgbToHue(avgClRGB)
 
-      if scores[i] > minScore and avgClHue > colorLow and avgClHue < colorHigh:
+      if scores[i] > minDetectionScore and avgClHue > colorLow and avgClHue < colorHigh:
         cv2.rectangle(frame, (int(x0), int(y0)),
                              (int(x1), int(y1)),
                              (0, 255, 0), 2)
         if labels:
           dist = distanceDetect('load-testImg', distWtsFile, [[x0, x1, y0, y1], frame.shape[:2]])
-          # cv2.putText(frame, '{}: {}% at {} cm'.format(labels[i], round(scores[i].item()*100.0, 1), round(distance, 2)),
-          #                    (int(box[0]), int(box[1]) - 10),
-          #                    cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 0, 0), 3)
           gbx = int((x1 + x0) / 2)
           gby = int((y1 + y0) / 2)
           cv2.putText(frame, 'x:{}, y:{}'.format(gbx, gby),
@@ -185,11 +162,7 @@ def detectLive(model, showSight = True):
 
     cv2.imshow('Ball Detection', frame)
     key = cv2.waitKey(1) & 0xFF
-    # if key == ord('q') or key == 27:
-    #   break
     return gbx, gby,dist
-    #cv2.destroyAllWindows()
-    # cap.release()
 
 ### Supplementary Functions
 #### Display of Image with Bounding Boxes
@@ -255,7 +228,6 @@ imageLoc    = './ball_detection/images/train-test-4/'  #
 labelSet    = ['ball']
 modelLoc    = './ball_detection/model_weights/'  #
 trainLoc    = imageLoc + 'train/'
-minScore    =   0.50
 colorLow    =  60
 colorHigh   = 180
 modelFile   = 'model_weights-2-20210818_002355-cpu.pth'
@@ -280,4 +252,4 @@ modelAction = 'load' # 'load' to load previous model, 'new' to train new model (
 #
 # ## Evaluation (using Trained Images)
 # for file in sorted(glob.glob(imageLoc + '/train/*.jpg')):
-#   objectDetection(file)
+#   objectDetection(file)
\ No newline at end of file
diff --git a/Code/Control/Laptop_Code/ball_detection/model_weights/model_weights-1-20210728_215304-cuda.pth b/Code/Control/Laptop_Code/ball_detection/model_weights/model_weights-1-20210728_215304-cuda.pth
new file mode 100644
index 0000000000000000000000000000000000000000..95b2ffacb11e4a1333d0a62a64114527bfd88a1e
Binary files /dev/null and b/Code/Control/Laptop_Code/ball_detection/model_weights/model_weights-1-20210728_215304-cuda.pth differ
diff --git a/Code/Control/Laptop_Code/ball_detection/model_weights/model_weights-1-20210808_132627-cuda.pth b/Code/Control/Laptop_Code/ball_detection/model_weights/model_weights-1-20210808_132627-cuda.pth
new file mode 100644
index 0000000000000000000000000000000000000000..6508c9f62979fc38e00d2616c3e3d732c62ddfcf
Binary files /dev/null and b/Code/Control/Laptop_Code/ball_detection/model_weights/model_weights-1-20210808_132627-cuda.pth differ
diff --git a/Code/Control/Laptop_Code/ball_detection/model_weights/model_weights-1-20210809_162415-cuda.pth b/Code/Control/Laptop_Code/ball_detection/model_weights/model_weights-1-20210809_162415-cuda.pth
new file mode 100644
index 0000000000000000000000000000000000000000..7beaa71ccd7336654f454e025b970816dff5e5f4
Binary files /dev/null and b/Code/Control/Laptop_Code/ball_detection/model_weights/model_weights-1-20210809_162415-cuda.pth differ
diff --git a/Code/Control/Laptop_Code/ball_detection/model_weights/model_weights-1-20210811_114017-cpu.pth b/Code/Control/Laptop_Code/ball_detection/model_weights/model_weights-1-20210811_114017-cpu.pth
new file mode 100644
index 0000000000000000000000000000000000000000..686dcbfb4aa89abb00f21aee4c0c7c3e5c82d5aa
Binary files /dev/null and b/Code/Control/Laptop_Code/ball_detection/model_weights/model_weights-1-20210811_114017-cpu.pth differ
diff --git a/Code/Control/Laptop_Code/ball_detection/model_weights/model_weights-2-20210811_183605-cpu.pth b/Code/Control/Laptop_Code/ball_detection/model_weights/model_weights-2-20210811_183605-cpu.pth
new file mode 100644
index 0000000000000000000000000000000000000000..1da22a53df3248b417ec4bd8e2dc21f098e7cdff
Binary files /dev/null and b/Code/Control/Laptop_Code/ball_detection/model_weights/model_weights-2-20210811_183605-cpu.pth differ
diff --git a/Code/Control/Laptop_Code/ball_detection/model_weights/model_weights-2-20210812_081758-cpu.pth b/Code/Control/Laptop_Code/ball_detection/model_weights/model_weights-2-20210812_081758-cpu.pth
new file mode 100644
index 0000000000000000000000000000000000000000..3ebebd192ec041f31fa1002e9f9988407698f75f
Binary files /dev/null and b/Code/Control/Laptop_Code/ball_detection/model_weights/model_weights-2-20210812_081758-cpu.pth differ
diff --git a/Code/Control/Laptop_Code/ball_detection/model_weights/model_weights-2-20210818_002355-cpu.pth b/Code/Control/Laptop_Code/ball_detection/model_weights/model_weights-2-20210818_002355-cpu.pth
new file mode 100644
index 0000000000000000000000000000000000000000..904a595ce300523035519e413be12758b3372cf9
Binary files /dev/null and b/Code/Control/Laptop_Code/ball_detection/model_weights/model_weights-2-20210818_002355-cpu.pth differ
diff --git a/Code/Control/Laptop_Code/ball_detection/model_weights/model_weights-2-20210818_155713-cpu.pth b/Code/Control/Laptop_Code/ball_detection/model_weights/model_weights-2-20210818_155713-cpu.pth
new file mode 100644
index 0000000000000000000000000000000000000000..b4d5d2750da828123aa179c1c5734e5b96474912
Binary files /dev/null and b/Code/Control/Laptop_Code/ball_detection/model_weights/model_weights-2-20210818_155713-cpu.pth differ
diff --git a/Code/Control/Laptop_Code/inputs.py b/Code/Control/Laptop_Code/inputs.py
new file mode 100644
index 0000000000000000000000000000000000000000..e5b63a918108bc46a2b71d7777f0653a77a28f9a
--- /dev/null
+++ b/Code/Control/Laptop_Code/inputs.py
@@ -0,0 +1,662 @@
+import numpy
+
+minDetectionScore = 0.90
+
+
+def formatInput(inp):
+  tx, ty, tz, rx, ry, rz, d_lidar, debug = inp
+  t = [tx, ty, tz]
+  r = [rx, ry, rz]
+  return [t, r, d_lidar, debug]  
+  
+#include <esp_now.h>
+#include <WiFi.h>
+#include <SparkFun_VL53L1X.h>
+#include <Wire.h>
+#include <Adafruit_MotorShield.h>
+#include "LedPanel.h"
+#include <Arduino.h>
+#include <string>
+#include <vector>
+#include "Camera.h"
+#include <PID_v1.h>
+#include "utilities.h"
+
+// ============================== All variables ============================================
+//-------------------- lidar part -------------------- 
+SFEVL53L1X distanceSensor;
+int budgetIndex = 4;
+int budgetValue[7] = {15, 20, 33, 50, 100, 200, 500};
+int LED = LED_BUILTIN;
+// ---------------------------------------------------
+// -----------------------const variables----------------------------
+//Identify all the global constants that will be used by the robot
+const int BAUD_RATE = 115200;
+const int MAX_SPEED = 255;
+const double RESOLUTION_W = 320.0;
+const double RESOLUTION_H = 240.0;
+const int ENDDEMO_TAG = 0;
+const uint32_t THISNODE_ID = 88789821;
+double Setpointx, Inputx, Outputx;
+double Setpointy, Inputy, Outputy;
+
+
+// -------------------------------------------------------------------
+// ----------------------- variables ----------------------------
+uint8_t broadcastAddress[] = {0x40, 0xF5, 0x20, 0x44, 0xB6, 0x4C}; // #4
+String success;
+
+String sentDebugM = "";
+int count = 0;
+int count_var = 0;
+int print_count = 0;
+int change_count =0;
+//----------------------------------------------------------
+String strData = "";
+double valData = 0.0;
+String queData = "";
+double Kpx=2, Kix=0.1, Kdx=0.25;
+double Kpy=1, Kiy=0.1, Kdy=0.25;
+int8_t g1 = 0,g2=1,g3=2;
+int8_t goal_id[3] = {g1, g2, g3};
+// int8_t goal[3] = {0,1,2};
+// ------------- color part ------------------------- 
+      // LAB: L[min] - L[max], A[min] - A[max], B[min] - B[max]
+      //green old = (30, 100, -68, -13, 30, 127)
+      // green_new = (30, 100, -49, -22, 31, 127)
+      //(30,100,-68,2,6,127) - detect the yellow wall as well
+      // red (30, 100, 127, 41, 127, 13)      
+int8_t Lmin = 30,Lmax = 100, Amin = -49,Amax = -22,Bmin = 31,Bmax = 127;
+int8_t threshold[6] = {Lmin, Lmax, Amin, Amax, Bmin, Bmax};
+int base_speed = 70;
+int seeking_speed = 70;
+int LIDAR_Thres = 300; // mm 
+int gbx = 0, gby = 0,gbd = 0;
+double gbc = 0;
+int debug_ball_cap = 0;
+// -------------------------------------------------------------------
+
+//Identify all the variables that will be used by the robot
+int findcolor = 1; //based on the same code from openMV. Determine the color you want to find
+char newPTCL = '1';
+int pau = 0;
+int displayTracking = 0;
+// int8_t threshold[6] = {0, 0, 0, 0, 0, 0};
+// int BASE_SPEED = 70; //125;
+
+//The debug LEDs that we will be using. Description at:
+
+const int DEBUG_STATE = 31;
+const int DEBUG_KP = 30;
+const int DEBUG_KI = 29;
+const int DEBUG_KD = 28;
+const int DEBUG_BASESPEED = 27;
+const int DEBUG_THRESHOLD_MIN = 26;
+const int DEBUG_THRESHOLD_MAX = 25;
+const int DEBUG_VERTICALSPEED = 17;
+const int DEBUG_RSPEED = 16;
+const int DEBUG_LSPEED = 24;
+
+// =============================== All the setup ===========================================
+//Create the interface that will be used by the camera
+openmv::rpc_scratch_buffer<256> scratch_buffer; // All RPC objects share this buffer.
+openmv::rpc_i2c_master interface(0x12, 100000); //to make this more robust, consider making address and rate as constructor argument
+Camera cam(&interface);
+LedPanel panel(NUMPIXELS,PIN_PIXELS);
+// ========================== Motor part ====================================
+// Create the motor shield object with the default I2C address
+Adafruit_MotorShield AFMS = Adafruit_MotorShield();
+// changed
+Adafruit_DCMotor *motorVertical_L = AFMS.getMotor(1); 
+Adafruit_DCMotor *motorVertical_R = AFMS.getMotor(2);
+Adafruit_DCMotor *motorLeft = AFMS.getMotor(3);
+Adafruit_DCMotor *motorRight = AFMS.getMotor(4);
+// changed
+// ========================== PID part ====================================
+// Setup PID
+PID PID_x(&Inputx, &Outputx, &Setpointx, Kpx, Kix, Kdx, DIRECT);
+PID PID_y(&Inputy, &Outputy, &Setpointy, Kpy, Kiy, Kdy, DIRECT);
+
+//-------------------------------------------------------------------------------------
+typedef struct struct_message {
+  String StrD;
+  double ValD;
+  String DebugM;
+  String QueM;
+  String VarM;
+} struct_message;
+
+struct_message receivedData;
+//-------------------------------------------------------------------------------------
+// Callback when data is sent
+void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) {
+  print("\r\nLast Packet Send Status:\t");
+  print(status == ESP_NOW_SEND_SUCCESS ? "Delivery Success" : "Delivery Fail");
+  
+  if (status ==0){
+    success = "Delivery Success :)";
+  }
+  else{
+    success = "Delivery Fail :(";
+  }
+}
+
+
+// Callback when data is received
+void OnDataRecv(const uint8_t * mac, const uint8_t *incomingData, int len) {
+  memcpy(&receivedData, incomingData, sizeof(receivedData));
+  print("\ndata:");
+  print(receivedData.StrD);
+  print(receivedData.ValD);
+  print(receivedData.QueM);
+  
+  if (receivedData.QueM != "?"){
+    valData = receivedData.ValD;
+  }
+  
+  strData = receivedData.StrD;
+  queData = receivedData.QueM;
+  
+  count = 0;
+  count_var = 0;
+  print_count=0;
+  change_count = 0;
+  
+  print("queData:");
+  print(queData);
+}
+//======================================================================================
+
+void setup() {
+  Serial.begin(BAUD_RATE);
+  AFMS.begin();  // create with the default frequency 1.6KHz
+  interface.begin(); //communication between ESP and OpenMV
+  panel.beginPanel();
+  // -------------- PID part --------------------
+  //the values that the PID will try to reach
+  Setpointx = 400.0; 
+  Setpointy = 300.0; 
+  PID_y.SetOutputLimits(-255, 255); //up positive
+  PID_x.SetOutputLimits(-255, 255); //left positive
+  PID_x.SetMode(AUTOMATIC);
+  PID_y.SetMode(AUTOMATIC);
+  // -------------- lidar part --------------------
+  pinMode(LED, OUTPUT);
+  digitalWrite(LED, HIGH);
+  if (distanceSensor.begin() == 0)
+    print("Sensor online!");
+  distanceSensor.startRanging();
+  distanceSensor.setIntermeasurementPeriod(budgetValue[budgetIndex]);
+  digitalWrite(LED, LOW);
+
+  // ----------------esp now -----------------------
+  Wire.begin();
+  WiFi.mode(WIFI_STA);
+
+  // Init ESP-NOW
+  if (esp_now_init() != ESP_OK) {
+    print("Error initializing ESP-NOW");
+    return;
+  }
+
+  // Once ESPNow is successfully Init, we will register for Send CB to
+  // get the status of Trasnmitted packet
+  esp_now_register_send_cb(OnDataSent);
+  
+  // Register peer
+  esp_now_peer_info_t peerInfo;
+  memcpy(peerInfo.peer_addr, broadcastAddress, 6);
+  peerInfo.channel = 0;  
+  peerInfo.encrypt = False;
+  
+  // Add peer        
+  if (esp_now_add_peer(&peerInfo) != ESP_OK){
+    print("Failed to add peer");
+    return;
+  }
+  
+  // Register for a callback function that will be called when data is received
+  esp_now_register_recv_cb(OnDataRecv);
+
+  
+}
+
+// ============================== other functions  ====================================
+def ML_detect_gb(x, y):
+  if x == 0 and y == 0:
+    return False
+  else:
+    return True
+
+def QueryVariables():
+  if (strData == "kpx"){
+    receivedData.VarM = String(Kpx);
+  }else if(strData == "kix"){
+    receivedData.VarM = String(Kix);
+  }else if(strData == "kdx"){
+    receivedData.VarM = String(Kdx);
+  }else if(strData == "kpy"){
+    receivedData.VarM = String(Kpy);
+  }else if(strData == "kiy"){
+    receivedData.VarM = String(Kiy);
+  }else if(strData == "kdy"){
+    receivedData.VarM = String(Kdy);
+  }else if(strData == "tha"){
+    receivedData.VarM = String(Lmin);
+  }else if(strData == "thb"){
+    receivedData.VarM = String(Lmax);
+  }else if(strData == "thc"){
+    receivedData.VarM = String(Amin);
+  }else if(strData == "thd"){
+    receivedData.VarM = String(Amax);
+  }else if(strData == "the"){
+    receivedData.VarM = String(Bmin);
+  }else if(strData == "thf"){
+    receivedData.VarM = String(Bmin);
+  }else if(strData == "bsp"){
+    receivedData.VarM = String(base_speed);
+  }else if(strData == "ssp"){
+    receivedData.VarM = String(seeking_speed);
+  }else if(strData == "lth"){
+    receivedData.VarM = String(LIDAR_Thres);
+  }
+
+  send_message_var_once();
+  // queData = "";
+  receivedData.VarM = "";
+}
+
+
+void send_message_var_once(){
+  if(count_var==0){
+    send_message(); 
+    count_var+=1;
+  }
+}
+
+
+void send_message_once(){
+  if(count==0){
+    send_message(); 
+    count+=1;
+    receivedData.DebugM = "";
+  }
+}
+
+
+void send_message(){
+  esp_err_t result = esp_now_send(broadcastAddress, (uint8_t *) &receivedData, sizeof(receivedData));
+  // esp_now_send(RxMACaddress, (uint8_t *) &sentData, sizeof(sentData));
+  //-------------------------------------------------------------------------------------
+  if (result == ESP_OK) {
+    print("Sent with success");
+  }
+  else {
+    print("Error sending the data");
+  }
+  //-------------------------------------------------------------------------------------
+  delay(100);
+}
+
+
+void ChangeVariables(){ 
+  // all the variables in this function can be remotely changed
+  
+  //-------------------PID-----------------
+  Kpx = getDoubleVal(strData,"kpx",valData,Kpx);
+  Kix = getDoubleVal(strData,"kix",valData,Kix);
+  Kdx = getDoubleVal(strData,"kdx",valData,Kdx);
+  Kpy = getDoubleVal(strData,"kpy",valData,Kpy);
+  Kiy = getDoubleVal(strData,"kiy",valData,Kiy);
+  Kdy = getDoubleVal(strData,"kdy",valData,Kdy);
+  
+  //-------------------Goal id-----------------
+  g1 = getIntVal(strData,"gda",valData,goal_id[0]);
+  g2 = getIntVal(strData,"gdb",valData,goal_id[1]);
+  g3 = getIntVal(strData,"gdc",valData,goal_id[2]);
+  goal_id[0] = g1;
+  goal_id[1] = g2;
+  goal_id[2] = g3;
+  
+  //-------------------Color threshold-----------------
+  Lmin = getIntVal(strData,"tha",valData,threshold[0]);
+  Lmax = getIntVal(strData,"thb",valData,threshold[1]);
+  Amin = getIntVal(strData,"thc",valData,threshold[2]);
+  Amax = getIntVal(strData,"thd",valData,threshold[3]);
+  Bmin = getIntVal(strData,"the",valData,threshold[4]);
+  Bmax = getIntVal(strData,"thf",valData,threshold[5]);
+  threshold[0] = Lmin;
+  threshold[1] = Lmax;
+  threshold[2] = Amin;
+  threshold[3] = Amax;
+  threshold[4] = Bmin;
+  threshold[5] = Bmax;
+  
+  //-------base_speed,seeking_speed,LIDAR_Thres-----------------
+  base_speed = abs(getDoubleVal(strData,"bsp",valData,base_speed));
+  seeking_speed = abs(getDoubleVal(strData,"ssp",valData,seeking_speed));
+  LIDAR_Thres = getDoubleVal(strData,"lth",valData,LIDAR_Thres);
+
+  //------ green ball x, y----
+  gbc = getDoubleVal(strData,"gbc",valData,gbc);
+  gbx = gbc /1000000;
+  gby = fmod(gbc,1000000)/1000;//(gbc.toInt() % 1000000)/1000;
+  gbd = fmod(gbc,1000); // gbc.toInt() % 1000;
+
+  // 
+  Setpointx = getDoubleVal(strData,"spx",valData,Setpointx);
+  Setpointy = getDoubleVal(strData,"spy",valData,Setpointy);
+
+  debug_ball_cap = getDoubleVal(strData,"dbc",valData,debug_ball_cap);
+  
+  // gbx = getDoubleVal(strData,"gbx",valData,gbx);
+  // gby = getDoubleVal(strData,"gby",valData,gby);
+  
+}
+
+double getDoubleVal(String checkData,String Ans,double val,double ori_val){
+  if (checkData == Ans){
+    strData = "";
+    valData = 0.0;
+    return val;
+  }else {
+    return ori_val;
+  }
+}
+
+def getIntVal(checkData, Ans, val, ori_val):
+  if checkData == Ans:
+    strData = ""
+    valData = 0.0
+    return int(val)
+  else:
+    return ori_val
+
+def print_allvariables():
+  if print_count <= 1:
+    print("---------------")
+    print("base speed:")
+    print(base_speed)
+    print("|")
+    print("seeking speed:")
+    print(seeking_speed);
+    print("|");
+    print("lidar thres:");
+    print(LIDAR_Thres);
+    
+    print("threshold:");
+    print(threshold[0]);
+    print("|");
+    print(threshold[1]);
+    print("|");
+    print(threshold[2]);
+    print("|");
+    print(threshold[3]);
+    print("|");
+    print(threshold[4]); 
+    print("|");   
+    print(threshold[5]);  
+    
+    print("gid:");
+    print(goal_id[0]);
+    print(goal_id[1]);
+    print(goal_id[2]);
+    
+    print("Kdx:");
+    print(Kdx);
+    print("|"); 
+    print("Kix:");
+    print(Kix);
+    print("|");
+    print("Kpx:");
+    print(Kpx);
+    print("|");
+    print("Kdy:");
+    print(Kdy);
+    print("|");
+    print("Kiy:");
+    print(Kiy);
+    print("|");
+    print("Kpy:");
+    print(Kpy);
+    print("|");
+
+    print("gbc:");
+    print(gbc);
+    print("|");
+    print("gbx:");
+    print(gbx);
+    print("|");
+    print("gby:");
+    print(gby);
+    print("gbd:");
+    print(gbd);
+    
+    print("---------------------------\n");
+    print_count +=1 ;
+  }
+}
+
+void seeking(){
+    moveVertical(0);
+    moveHorizontal(seeking_speed, 0);
+}
+
+# vel value should be between -255 to 255 with positive values moving the blimp upward.
+def moveVertical(vel):
+  if vel > 0:  # up
+    panel.singleLED(DEBUG_VERTICALSPEED, abs(vel), 0, 0)
+    motorVertical_L->setSpeed(abs((int) vel))
+    motorVertical_R->setSpeed(abs((int) vel)) 
+    motorVertical_L->run(BACKWARD)
+    motorVertical_R->run(FORWARD)
+  elif vel < 0:  # down
+    panel.singleLED(DEBUG_VERTICALSPEED, 0, 0, abs(vel))
+    motorVertical_L->setSpeed(abs((int) vel))
+    motorVertical_R->setSpeed(abs((int) vel))
+    motorVertical_L->run(FORWARD)
+    motorVertical_R->run(BACKWARD)
+  else:
+      panel.singleLED(DEBUG_VERTICALSPEED, 0, 0, 0)
+      motorVertical_L->setSpeed(0)
+      motorVertical_R->setSpeed(0)
+
+def moveHorizontal(vel_rot, vel_trn):
+  lspeed = -1*vel_rot + vel_trn
+  rspeed =  1*vel_rot + vel_trn
+
+  if lspeed > 0:
+    motorLeft->run(BACKWARD)   # make it move forward 
+  else:
+    motorLeft->run(FORWARD)
+  if rspeed > 0:
+    motorRight-> run(FORWARD)
+  else:
+    motorRight-> run(BACKWARD)  # make it move backward
+  displaySpeed(lspeed, rspeed)
+  motorLeft->setSpeed(min(MAX_SPEED, abs(lspeed )))
+  motorRight->setSpeed(min(MAX_SPEED, abs(rspeed)))
+
+def displaySpeed(lspeed, rspeed):
+  if lspeed < 0:
+    panel.singleLED(DEBUG_LSPEED, 0, 0, abs(lspeed))
+  else:
+    panel.singleLED(DEBUG_LSPEED, abs(lspeed), 0, 0)
+  
+  if rspeed < 0:
+    panel.singleLED(DEBUG_RSPEED, 0, 0, abs(rspeed))
+  else:
+    panel.singleLED(DEBUG_RSPEED, abs(rspeed), 0, 0)
+
+//When using the camera to detect objects such as colored blobs or april tags. This function is
+//useful when only a single object is the target. The approximate position will be marked with an
+//led turning white in the 8*4 panel of the NeoPixel LED Panel. Therefore, this function can be 
+//called assuming that you have created the LED panel object, which in this code is named "panel".
+//If the LED panel is called something else, just edit the code here
+void displayTrackedObject(int cx, int cy, int w_res, int h_res){
+  int lednum = 0;
+  int vertshift = 0;
+  panel.resetPanel();
+  lednum = cx/(w_res/8); //because lednum is an int, it will handle flooring the value
+  vertshift = cy/(h_res/4);
+  lednum = lednum + 8*vertshift;
+  panel.singleLED(lednum, 10, 10 , 10);
+}
+
+//This function translate a string of message into the constants for a PID controller. Ignoring the 
+//first command character, user can input any one, two, or all three parameter to be changed by identifying
+//the new parameter with the capital letter of that specific parameter, each separated with a space. 
+//Some of the valid msg examples are:
+//  - "P0.02"
+//  - "D1.23"
+//  - "P0.14 D9.5"
+//  - "P0.1 I0.1 D0.1"
+//
+//NOTE:
+//  - the parameter doesn't have to be in order. You can do DI or IP in whichever order as long as it follows a valid double value
+//  - while the function works if you passed a negative double, the PID controller will not and will produce error.
+//  - suggested command character: 'T'[uning]
+def setPIDConstants(msg, &p_constant, &i_constant, &d_constant):
+  new_p = Kpx
+  new_i = Kix
+  new_d = Kdx
+  
+  msgLen = msg.length()
+  startpoint = 0
+  endpoint = 0
+
+  for i in range(0, msgLen):
+    if msg[i] == 'P':
+      startpoint = i + 1
+      for j in range(i + 1, msgLen):
+        if msg[j] == ' ':
+          endpoint = j
+          break
+        else:
+          endpoint = msgLen
+      if endpoint > startpoint  # check to see if it is a valid value
+        new_p = msg.substring(startpoint, endpoint).toDouble()
+
+    if msg[i] == 'I':
+      startpoint = i + 1
+      for j in range(i + 1, msgLen):
+        if msg[j] == ' ':
+          endpoint = j
+          break
+        else:
+          endpoint = msgLen
+      if endpoint > startpoint # check to see if it is a valid value
+        new_i = msg.substring(startpoint, endpoint).toDouble()
+
+    if msg[i] == 'D':
+      startpoint = i + 1
+      for j in range(i + 1, msgLen + 1):
+        if msg[j] == ' ' or msg[j] == '\0':
+          endpoint = j
+          break
+        else:
+          endpoint = msgLen
+      if endpoint > startpoint # check to see if it is a valid value
+        new_d = msg.substring(startpoint, endpoint).toDouble()
+
+  ## Debugging
+  unsigned long t = millis();
+  
+  debugPIDConstants(DEBUG_KP, p_constant, new_p)
+  debugPIDConstants(DEBUG_KI, i_constant, new_i)
+  debugPIDConstants(DEBUG_KD, d_constant, new_d)
+  
+  p_constant = new_p
+  i_constant = new_i
+  d_constant = new_d
+  
+  while(millis() < t+2000):   # debugging LED will light up for 2 seconds
+    pass
+  
+  panel.singleLED(DEBUG_KP, 0, 0, 0)
+  panel.singleLED(DEBUG_KI, 0, 0, 0)
+  panel.singleLED(DEBUG_KD, 0, 0, 0)
+}
+
+def debugPIDConstants(lednum, oldval, newval):
+  if newval > oldval:
+    panel.singleLED(lednum, 0, 10, 0)
+  elif newval < oldval:
+    panel.singleLED(lednum, 10, 0, 0)
+  else:
+    panel.singleLED(lednum, 10, 10, 10)
+
+def loop():
+  if change_count == 0:
+    if queData == "?":
+      QueryVariables()
+    else:
+      ChangeVariables()
+    change_count += 1
+  
+  panel.singleLED(DEBUG_BASESPEED, base_speed, base_speed, base_speed)
+  
+  ## Standby Mode
+  panel.singleLED(DEBUG_STATE, 10, 0, 0)
+  
+  ## LIDAR Detection
+  d_LIDAR = 0
+  ballCapture = 0
+  d_LIDAR = distanceSensor.getDistance()
+  print(d_LIDAR)
+  
+  if (d_LIDAR < LIDAR_Thres) and (d_LIDAR > 0):  # if we capture the ball 
+    ballCapture = 1
+    print("found ball")
+    receivedData.DebugM = String(d_LIDAR)
+  else:
+    ballCapture = 2
+    print("no ball")
+    receivedData.DebugM = String(d_LIDAR)
+
+  ## Goal Finder
+  int id = -1;
+  int tx = 0; int ty = 0; int tz = 0;
+  int rx = 0; int ry = 0; int rz = 0;
+  int x = 0;
+  int y = 0; 
+  
+  # ballDetect = cam.exe_color_detection_biggestblob(threshold[0], threshold[1], threshold[2], threshold[3], threshold[4], threshold[5], x, y)
+  ballDetect = ML_detect_gb(gbx, gby)
+  goalDetect = cam.exe_goalfinder(goal_id[0],goal_id[1],goal_id[2], id, tx, ty, tz, rx, ry, rz)
+  ballCapture = 
+  
+  if ballCapture == 1 or debug_ball_cap == 1:  # Ball captured
+    if goalDetect:  # Goal detected
+      panel.singleLED(DEBUG_STATE, 0, 10, 0);
+      Inputx = tx/1.00;
+      Inputy = ty/1.00;
+      PID_x.Compute();
+      PID_y.Compute();
+      receivedData.DebugM = "det g mv2g";
+      send_message_once(); 
+      moveVertical(Outputy);
+      moveHorizontal(Outputx, base_speed);
+    else:  # Goal not detected
+      seeking(); 
+      receivedData.DebugM = "seeking2";
+      send_message_once(); 
+  else:  # Ball not captured
+    if ballDetect:  # Ball detected
+      receivedData.DebugM = "catching b";
+      send_message_once(); 
+      Inputx = gbx/1.00
+      Inputy = gby/1.00
+      PID_x.Compute()
+      PID_y.Compute()
+      moveVertical(Outputy)
+      moveHorizontal(Outputx, base_speed)
+    else:  # Ball not detected
+      seeking()
+      receivedData.DebugM = "seeking1"
+      send_message_once()
+  print_allvariables()
+
+if __name__ == '__main__':
+  while(True):
+    loop()
\ No newline at end of file
diff --git a/Code/Control/Laptop_Code/main.py b/Code/Control/Laptop_Code/main.py
index 355087d0d7a510aadf85c3f6813dc4948d96ca5b..3805f1db98525b63fc8ff621e2b204e9cca96424 100644
--- a/Code/Control/Laptop_Code/main.py
+++ b/Code/Control/Laptop_Code/main.py
@@ -1,34 +1,31 @@
-import ball_detection.esp32cam_live_feed as live_feed
+import ball_detection.ball_detection as ball_detection
 
 import serial
 import time
-PORT = "COM6" # for Alianware
+PORT = "COM6" # for Alienware
 # serial_port = serial.Serial(PORT,115200)
 # serial_port.close()
 # serial_port.open()
 
-gbx = 0
-gby = 0
+gbc  = 0
+gbx  = 0
+gby  = 0
 dist = 0
-gbc = 0
 
 ### Declare Varables and Constants
-imageLoc    = './ball_detection/images/train-test-4/'  #
-device      = 'cuda'
-trainLoc    = imageLoc + 'train/'
-labelSet    = ['ball']
-modelLoc    = './ball_detection/model_weights/'  #
-modelFile   = 'model_weights-2-20210818_002355-cpu.pth'
-modelAction = 'load'
+device            = 'cuda'
+labelSet          = ['ball']
+modelLoc          = './ball_detection/model_weights/'  #
+modelFile         = 'model_weights-2-20210818_002355-cpu.pth'
+minDetectionScore = 0.90
 
-model = live_feed.returnModel(modelAction, device, trainLoc, labelSet, modelLoc, modelFile)
-
-waitTime = 0.1
+waitTime          = 0.10
 
+model = ball_detection.returnModel(device, labelSet, modelLoc, modelFile)
 
 while True:
     ## Testing (using Live Streaming)
-    gbx, gby, dist  = live_feed.detectLive(model, showSight = True)
+    gbx, gby, dist  = ball_detection.detectLive(model, minDetectionScore, showSight = True)
     intDist = int(dist) # cm
 
     '''