diff --git a/Code/Control/Laptop_Code/ESP32_AT/imageTread_AT.py b/Code/Control/Laptop_Code/ESP32_AT/imageTread_AT.py
index fd39118bde4a5c31f7d1d3f9c4117676820b4c0a..b2deee2c997ed34154930d592863395824f1a9c4 100644
--- a/Code/Control/Laptop_Code/ESP32_AT/imageTread_AT.py
+++ b/Code/Control/Laptop_Code/ESP32_AT/imageTread_AT.py
@@ -2,7 +2,7 @@ import cv2
 from urllib.request import urlopen, Request
 import numpy as np
 import time
-
+import math
 # import apriltag
 from pupil_apriltags import Detector
 def nothing(x):
@@ -11,16 +11,23 @@ def nothing(x):
 
 # detector = apriltag.Detector()
 
-detector = Detector()
+
 
 def get_AT_6DOF_info(url):
+    tid,tx,ty,tz= 0,0,0,0
+    rx = [0.0,0.0,0.0]
+    ry = [0.0, 0.0, 0.0]
+    rz = [0.0, 0.0, 0.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)
+
+    # ret,frame = cap.read()
     gray_image = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
+
     h, w, _ = frame.shape
     # put a dot in center of the frame
     cv2.circle(frame, (w // 2, h // 2), 7, (255, 0, 0), -1)
@@ -29,7 +36,8 @@ def get_AT_6DOF_info(url):
     fy = 600
     cx = 400
     cy = 300
-    results = detector.detect(gray_image, estimate_tag_pose=True, camera_params=[fx, fy, cx, cy], tag_size=0.16)
+    AT_size = 0.16
+    results = detector.detect(gray_image, estimate_tag_pose=True, camera_params=[fx, fy, cx, cy], tag_size=AT_size)
     debug_print = 0
     for r in results:
         # extract the bounding box (x, y)-coordinates for the AprilTag
@@ -41,6 +49,7 @@ def get_AT_6DOF_info(url):
         ptA = (int(ptA[0]), int(ptA[1]))
         tx, ty, tz = r.pose_t # in meters
         rx,ry,rz = r.pose_R
+        tid = r.tag_id
 
         # draw the bounding box of the AprilTag detection
         cv2.line(frame, ptA, ptB, (0, 255, 0), 5)
@@ -55,7 +64,8 @@ def get_AT_6DOF_info(url):
 
 
         tagFamily = r.tag_family.decode("utf-8")
-        tid = r.tag_id
+        
+        
         cv2.putText(frame, tagFamily, (ptA[0], ptA[1] - 15), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)
 
         cv2.putText(frame, "tx: {:.2f}  ty: {:.2f}  tz:{:.2f}".format(tx[0], ty[0], tz[0]), (ptA[0], ptA[1] + 30),
@@ -71,20 +81,35 @@ def get_AT_6DOF_info(url):
 
     return tid,tx,ty,tz,rx,ry,rz
 
+
+def rotationMatrixToEulerAngles(rx,ry,rz) :
+
+    sy = math.sqrt(rx[0] * rx[0] + ry[0] * ry[0])
+    roll = math.atan2(rz[1] , rz[2])
+    pitch = math.atan2(-rz[0], sy)
+    yaw = math.atan2(ry[0], rx[0])
+
+    return np.array([roll, pitch, yaw])
 if __name__ == "__main__":
     #change the IP address below according to the
     #IP shown in the Serial monitor of Arduino code
     # url='http://192.168.4.1/cam-hi.jpg'
     # url='http://192.168.1.107/cam-hi.jpg'
     url='http://192.168.4.1/cam-hi.jpg'
-    # url = 'http://192.168.1.118/cam-hi.jpg'
+    url = 'http://192.168.1.118/cam-hi.jpg'
 
 
     # cv2.namedWindow("live transmission", cv2.WINDOW_AUTOSIZE)
 
     # cv2.namedWindow("live transmission", cv2.WINDOW_NORMAL)
 
+    detector = Detector()
+    tid,tx,ty,tz,rx,ry,rz = 0,0,0,0,0,0,0
+    test_webcam = 0
+    if test_webcam == 1:
+        cap = cv2.VideoCapture(0)
     while True:
+        """
         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)
@@ -97,7 +122,7 @@ if __name__ == "__main__":
 
         # put a dot in center of the frame
         cv2.circle(frame, (w//2, h//2), 7, (255, 0, 0), -1)
-
+        """
         """
         If you also want to extract the tag pose, estimate_tag_pose should be set to True
         and camera_params ([fx, fy, cx, cy])
@@ -117,6 +142,7 @@ if __name__ == "__main__":
         fx, fy, cx, cy are given in Pixels in Computer Vision ( and openCV)
         but e.g. in Photogrammetry you often use mm
         """
+        """
         fx = 800
         fy = 600
         cx = 400
@@ -156,11 +182,24 @@ if __name__ == "__main__":
 
         # show the output image after AprilTag detection
         cv2.imshow("Image", frame)
+        """
+        tid,tx,ty,tz,rx,ry,rz = get_AT_6DOF_info(url)
 
+        print("testing new function")
+        print("-----------------------")
+        print("tid:{}".format(tid))
+        print("tx,ty,tz:{},{},{}".format(tx,ty,tz))
+        print("rx,ry,rz:{},{},{}".format(rx,ry,rz))
+
+        # R = np.array([rx,ry,rz])
+        roll,pitch, yaw = rotationMatrixToEulerAngles(rx,ry,rz)
+        roll = roll * 180 /math.pi
+        pitch = pitch * 180 / math.pi
+        yaw = yaw * 180 / math.pi
+        print("roll,pitch,yaw:{},{},{}".format(roll, pitch, yaw))
 
         key=cv2.waitKey(5)
         if key==ord('q'):
             break
 
-
     #cv2.destroyAllWindows()
diff --git a/Code/Control/Laptop_Code/Figs/fig1.png b/Code/Control/Laptop_Code/Figs/fig1.png
new file mode 100644
index 0000000000000000000000000000000000000000..238f4463386fc1ea8118aaa6a50bf04510c41bdc
Binary files /dev/null and b/Code/Control/Laptop_Code/Figs/fig1.png differ
diff --git a/Code/Control/Laptop_Code/README.md b/Code/Control/Laptop_Code/README.md
index a19c8a625086acc4cbcf3c094426f04b6ba1f620..16fa2cbb921756723a618b3451de1b1ebe719301 100644
--- a/Code/Control/Laptop_Code/README.md
+++ b/Code/Control/Laptop_Code/README.md
@@ -1,10 +1,43 @@
-[Progress] The overall logic is clear and tested! 
+# Laptop Code
 
-What to do next in python main:
+## 1 Main structure 
+
+## 2 Software Requirements
+
+### Step 1: 
+
+Download Anaconda or Pycharm
+
+### Step 2: 
+
+- If using Anaconda, then create a virtual environment first:
+
+```shell
+# crreate virtual environment
+conda create -n FORAYenv python=3.7
+# activate virtual environment
+conda activate FORAYenv
+# install environment installation tool pip (if it doesn't have one)
+conda install pip
+```
+
+- If using Pycharm, at the very beginning, create a virtual environment
+
+![](\Figs\fig1.png)
+
+### Step 3:
+
+Install dependency
+
+- If using Anaconda, install it from the terminal. And always remember to activate the conda environment fist: `conda activate FORAYenv`
+
+```shell
+# install pyserial
+pip install pyserial
+# install pygame 
+pip install pygame
+# install cv2
+pip install opencv-python
+# install 
+```
 
-- Have keyboard interruption in the python function
-  -  To tweak PID value while running the program
-  - To stop the balloon from being crazy 
-- The meter version April tag detection is still problematic so we need to tune that 
-- Use field test experiment to test PID
-- 
\ No newline at end of file
diff --git a/Code/Control/Laptop_Code/constants.py b/Code/Control/Laptop_Code/constants.py
index 7d4416bf3702150f02d81d81f1b34eb217c43408..336b1d4ad468da7c2d177d72374d8f8fb72d00cf 100644
--- a/Code/Control/Laptop_Code/constants.py
+++ b/Code/Control/Laptop_Code/constants.py
@@ -46,3 +46,7 @@ kpy_g,kiy_g,kdy_g = 1.2, 0.01, 0.5
 
 # difference between center of AT and center of goal
 AT_goal_Delta = 110 #cm
+
+AT_h1 = 50 # meters
+
+kph,kih,kdh = 1.2,0.01,0.5
\ No newline at end of file
diff --git a/Code/Control/Laptop_Code/main_keyboard.py b/Code/Control/Laptop_Code/main_keyboard.py
index 184a14f7cefcb2ca3ab35cf95d25c00ae608d75d..d82613177590319aeebe23d10a0037a787f11bde 100644
--- a/Code/Control/Laptop_Code/main_keyboard.py
+++ b/Code/Control/Laptop_Code/main_keyboard.py
@@ -1,13 +1,17 @@
 import time
 import serial
-import ball_detection.ball_detection as ball_detection
+
 import simple_pid.PID as PID
 import timeit
 import pygame
 from constants import *
+from ESP32_AT.imageTread_AT import get_AT_6DOF_info
 
 global ml
-ml = 1
+ml = 0
+if ml == 1:
+    import ball_detection.ball_detection as ball_detection
+
 # ========= Serial Port I/O ===========
 
 def serial_port_in(serial_port):
@@ -118,6 +122,12 @@ def ball_capture(LIDAR_dist):
     else:
         return False
 
+def AT_detect(tx,ty):
+    if tx == 0 and ty == 0:
+        return False
+    else:
+        return True
+
 def stop_all():
     pwm1, pwm2, pwm3, pwm4 = 0 , 0 , 0 , 0
     dir1, dir2, dir3, dir4 = '+', '-', '+', '-'
@@ -197,17 +207,65 @@ def seeking():
         dir1, dir2, dir3, dir4
     """
 
-    pwm1, pwm2, pwm3, pwm4, dir1, dir2, dir3, dir4 = rotate_one_direction()
+    delta_h = 2
+    url = 'http://192.168.1.118/cam-hi.jpg'
+    tid, tx, ty, tz, rx, ry, rz = get_AT_6DOF_info(url)
+
+    AT_detected = AT_detect(tx, ty)
+    count_h = 0
+
+    threshold_h = 0.3
+    if AT_detected:
+        current_h = get_altitude_from_AT(AT_h1,ty)
+        count_h += 1
+        if count_h == 1:
+            set_h = current_h + delta_h  # in meter
+        pwm1, pwm2, pwm3, pwm4, dir1, dir2, dir3, dir4 = vertical_control(current_h, set_h)
+        diff_h = abs(set_h - current_h)
+        if diff_h < threshold_h:
+            new_ssp = 200
+            pwm1, pwm2, pwm3, pwm4, dir1, dir2, dir3, dir4 = rotate_one_direction_ssp(new_ssp)
+        else:
+            pwm1, pwm2, pwm3, pwm4, dir1, dir2, dir3, dir4 = vertical_control(current_h, set_h)
+    else:
+        pwm1, pwm2, pwm3, pwm4, dir1, dir2, dir3, dir4 = rotate_one_direction()
 
 
     return int(pwm1), int(pwm2), int(pwm3), int(pwm4), dir1, dir2, dir3, dir4
 
+def vertical_control(current_h,set_h):
+
+    pid_h = PID(kph, kih, kdh, setpoint = set_h)
+    pid_h.auto_mode = True
+    pid_h.set_auto_mode(True, last_output = 8.0)
+    pid_h.output_limits = (-255,255)
+
+    input_h = current_h
+    output_h = pid_h(input_h)
+
+    pwm1 = abs(output_h)
+    pwm2 = abs(output_h)
+    pwm3 = 0
+    pwm4 = 0
+    dir1 = '+'
+    dir2 = '+'
+    dir3 = '+'
+    dir4 = '+'
+
+    return int(pwm1), int(pwm2), int(pwm3), int(pwm4), dir1, dir2, dir3, dir4
+
 def rotate_one_direction():
     pwm1, pwm2, pwm3, pwm4 = 0, 0, seeking_speed, seeking_speed
     dir1, dir2, dir3, dir4 = '+', '+', '+', '-'
 
     return int(pwm1), int(pwm2), int(pwm3), int(pwm4), dir1, dir2, dir3, dir4
 
+def rotate_one_direction_ssp(ssp):
+    pwm1, pwm2, pwm3, pwm4 = 0, 0, ssp, ssp
+    dir1, dir2, dir3, dir4 = '+', '+', '+', '-'
+
+    return int(pwm1), int(pwm2), int(pwm3), int(pwm4), dir1, dir2, dir3, dir4
+
 def move2ball(gbx, gby, gb_dist):
     """
     Description:
@@ -303,13 +361,13 @@ def main_control(gbx, gby, gb_dist, tx, ty, tz, rx, ry, rz, LIDAR_dist, debugM):
             pwm1, pwm2, pwm3, pwm4, dir1, dir2, dir3, dir4 = move2goal(tx, ty)
         else:  # Goal not detected
             # stop_all()  # Debug
-            pwm1, pwm2, pwm3, pwm4, dir1, dir2, dir3, dir4 = seeking()
+            pwm1, pwm2, pwm3, pwm4, dir1, dir2, dir3, dir4 = rotate_one_direction()
     else:  # Ball not captured
         if ballDetect:  # Ball detected
             pwm1, pwm2, pwm3, pwm4, dir1, dir2, dir3, dir4 = move2ball(gbx,gby,gb_dist)
         else:  # Ball not detected
             # stop_all()  # Debug
-            pwm1, pwm2, pwm3, pwm4, dir1, dir2, dir3, dir4 = seeking()
+            pwm1, pwm2, pwm3, pwm4, dir1, dir2, dir3, dir4 = rotate_one_direction()
 
     return pwm1, pwm2, pwm3, pwm4, dir1, dir2, dir3, dir4
 
@@ -480,6 +538,24 @@ def decode_ctl(Ctl_com):
     dir3 = Ctl_com[6]
     dir4 = Ctl_com[7]
     return pwm1, pwm2, pwm3, pwm4, dir1, dir2, dir3, dir4
+
+def get_altitude_from_AT(AT_h,ty):
+    # below AT center, ty > 0
+    # above AT center, ty < 0
+    altitude = AT_h - ty
+    return altitude
+
+
+def test_function():
+    url = 'http://192.168.1.118/cam-hi.jpg'
+    tid, tx, ty, tz, rx, ry, rz = get_AT_6DOF_info(url)
+    print("testing new function")
+    print("-----------------------")
+    print("tid:{}".format(tid))
+    print("tx,ty,tz:{},{},{}".format(tx,ty,tz))
+    print("rx,ry,rz:{},{},{}".format(rx,ry,rz))
+
+
 # ===== Main Function =====
 if __name__ == '__main__':
     # =========== SET UP ============
@@ -550,6 +626,12 @@ if __name__ == '__main__':
                 flag = 0
                 print_count = 1
 
+        elif get_key('t'):
+            flag = 4
+            while (flag == 4):
+                test_function()
+                flag, print_count = keyboard_stop(flag, print_count)
+
         elif get_key('k'):
             break