From 544d1006316fe822e1913beb653ad63c99b180f4 Mon Sep 17 00:00:00 2001 From: Sushant Kumar Khobian Date: Tue, 23 Jun 2026 11:43:16 +0530 Subject: [PATCH 1/3] Create gesture_drone_control.py --- .../gesture_drone_control.py | 397 ++++++++++++++++++ 1 file changed, 397 insertions(+) create mode 100644 client/python/example_user_scripts/gesture_drone_control.py diff --git a/client/python/example_user_scripts/gesture_drone_control.py b/client/python/example_user_scripts/gesture_drone_control.py new file mode 100644 index 00000000..e22232ca --- /dev/null +++ b/client/python/example_user_scripts/gesture_drone_control.py @@ -0,0 +1,397 @@ +""" +Gesture Drone Control Example for AirSim + +Dependencies: +pip install mediapipe +pip install opencv-python +pip install airsim + +Controls: +1 Finger Up -> Ascend +1 Finger Down -> Descend +2 Fingers Up -> Forward +Closed Fist -> Hover +3 Fingers Up -> Land + +""" + +import cv2 +import mediapipe as mp +import airsim +import math +import time +from collections import deque, Counter + +# ============================== +# SETTINGS +# ============================== + +CAMERA_INDEX = 0 + +MOVE_SPEED = 2.0 +Z_SPEED = 1.5 +COMMAND_DURATION = 0.12 + +HOLD_KP = 0.9 +HOLD_MAX_SPEED = 2.5 + +FAST_LAND_SPEED = 5.0 +LAND_LOCK_TIMEOUT = 5.0 + +GESTURE_HISTORY_LENGTH = 5 + +# ============================== +# MEDIAPIPE SETUP +# ============================== + +mp_hands = mp.solutions.hands +mp_draw = mp.solutions.drawing_utils + +hands = mp_hands.Hands( + max_num_hands=1, + model_complexity=1, + min_detection_confidence=0.5, + min_tracking_confidence=0.5 +) + +gesture_history = deque(maxlen=GESTURE_HISTORY_LENGTH) + +# ============================== +# HELPER FUNCTIONS +# ============================== + +def distance(p1, p2): + return math.hypot(p1.x - p2.x, p1.y - p2.y) + +def get_position(client): + state = client.getMultirotorState() + pos = state.kinematics_estimated.position + return pos.x_val, pos.y_val, pos.z_val + +def is_landed(client): + state = client.getMultirotorState() + return state.landed_state == airsim.LandedState.Landed + +def clamp(value, min_value, max_value): + return max(min_value, min(value, max_value)) + +def get_direction(tip, base): + dx = tip.x - base.x + dy = tip.y - base.y + + if abs(dx) > abs(dy): + return "RIGHT" if dx > 0 else "LEFT" + else: + return "UP" if dy < 0 else "DOWN" + +def is_extended(tip, base): + return distance(tip, base) > 0.11 + +def get_stable_gesture(new_gesture): + if new_gesture: + gesture_history.append(new_gesture) + + if len(gesture_history) == 0: + return "" + + return Counter(gesture_history).most_common(1)[0][0] + +# ============================== +# GESTURE DETECTION +# ============================== + +def detect_gesture(frame): + rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) + result = hands.process(rgb) + + detected_gesture = "" + + if result.multi_hand_landmarks: + for hand_landmarks in result.multi_hand_landmarks: + lm = hand_landmarks.landmark + + mp_draw.draw_landmarks(frame, hand_landmarks, mp_hands.HAND_CONNECTIONS) + + wrist = lm[0] + + thumb_tip = lm[4] + thumb_ip = lm[3] + + index_tip = lm[8] + middle_tip = lm[12] + ring_tip = lm[16] + pinky_tip = lm[20] + + index_base = lm[5] + middle_base = lm[9] + ring_base = lm[13] + pinky_base = lm[17] + + index_extended = is_extended(index_tip, index_base) + middle_extended = is_extended(middle_tip, middle_base) + ring_extended = is_extended(ring_tip, ring_base) + pinky_extended = is_extended(pinky_tip, pinky_base) + + index_dir = get_direction(index_tip, index_base) + middle_dir = get_direction(middle_tip, middle_base) + ring_dir = get_direction(ring_tip, ring_base) + + two_fingers_close = distance(index_tip, middle_tip) < 0.10 + + thumb_dx = thumb_tip.x - wrist.x + thumb_dy = thumb_tip.y - wrist.y + + thumb_right = thumb_dx > 0.16 and abs(thumb_dx) > abs(thumb_dy) + thumb_down = thumb_dy > 0.16 and abs(thumb_dy) > abs(thumb_dx) + + fingers_folded = ( + not index_extended and + not middle_extended and + not ring_extended and + not pinky_extended + ) + + three_fingers_up = ( + index_extended and + middle_extended and + ring_extended and + not pinky_extended and + index_dir == "UP" and + middle_dir == "UP" and + ring_dir == "UP" + ) + + if three_fingers_up: + detected_gesture = "LAND" + + elif fingers_folded and thumb_right: + detected_gesture = "RIGHT" + + elif fingers_folded and thumb_down: + detected_gesture = "DOWN" + + elif fingers_folded: + detected_gesture = "HOLD" + + elif index_extended and middle_extended and two_fingers_close and not ring_extended and not pinky_extended: + if index_dir == "UP" and middle_dir == "UP": + detected_gesture = "FRONT" + elif index_dir == "DOWN" and middle_dir == "DOWN": + detected_gesture = "BACK" + + elif index_extended and not middle_extended and not ring_extended and not pinky_extended: + if index_dir == "UP": + detected_gesture = "UP" + elif index_dir == "LEFT": + detected_gesture = "LEFT" + + return get_stable_gesture(detected_gesture) + +# ============================== +# MOVEMENT CONTROL +# ============================== + +def gesture_to_velocity(gesture): + vx, vy, vz = 0, 0, 0 + + if gesture == "UP": + vz = -Z_SPEED + + elif gesture == "DOWN": + vz = Z_SPEED + + elif gesture == "FRONT": + vx = MOVE_SPEED + + elif gesture == "BACK": + vx = -MOVE_SPEED + + elif gesture == "RIGHT": + vy = MOVE_SPEED + + elif gesture == "LEFT": + vy = -MOVE_SPEED + + return vx, vy, vz + +def hold_position(client, hold_x, hold_y, hold_z): + current_x, current_y, current_z = get_position(client) + + vx = clamp((hold_x - current_x) * HOLD_KP, -HOLD_MAX_SPEED, HOLD_MAX_SPEED) + vy = clamp((hold_y - current_y) * HOLD_KP, -HOLD_MAX_SPEED, HOLD_MAX_SPEED) + vz = clamp((hold_z - current_z) * HOLD_KP, -HOLD_MAX_SPEED, HOLD_MAX_SPEED) + + client.moveByVelocityAsync(vx, vy, vz, COMMAND_DURATION) + +def fast_land(client): + client.moveByVelocityAsync(0, 0, FAST_LAND_SPEED, 0.4).join() + client.landAsync() + +# ============================== +# AIRSIM SETUP +# ============================== + +print("Connecting to AirSim...") + +client = airsim.MultirotorClient() +client.confirmConnection() + +client.enableApiControl(True) +client.armDisarm(True) + +print("Camera started.") +print("Drone will NOT take off automatically.") +print("UP gesture = takeoff / move up.") +print("3 fingers up = fast landing.") +print("P = land and close.") + +# ============================== +# CAMERA LOOP +# ============================== + +cap = cv2.VideoCapture(CAMERA_INDEX) + +hold_position_saved = False +hold_x, hold_y, hold_z = 0, 0, 0 +last_gesture = "" + +landing_started = False +landing_start_time = 0 + +while True: + ret, frame = cap.read() + + if not ret: + print("Camera not detected") + break + + frame = cv2.flip(frame, 1) + + gesture = detect_gesture(frame) + landed = is_landed(client) + + # ============================== + # LANDING MODE + # ============================== + + if landing_started: + gesture = "LANDING..." + + elapsed_landing_time = time.time() - landing_start_time + + if landed: + landing_started = False + hold_position_saved = False + gesture_history.clear() + gesture = "LANDED - SHOW UP TO TAKEOFF" + print("Drone landed. Commands unlocked.") + + elif elapsed_landing_time >= LAND_LOCK_TIMEOUT: + landing_started = False + hold_position_saved = False + gesture_history.clear() + gesture = "LAND TIMEOUT - COMMANDS UNLOCKED" + print("Landing timeout reached. Commands unlocked.") + + else: + client.moveByVelocityAsync(0, 0, FAST_LAND_SPEED, 0.15) + + cv2.putText( + frame, + f"LANDING... COMMANDS LOCKED {int(LAND_LOCK_TIMEOUT - elapsed_landing_time)}s", + (30, 130), + cv2.FONT_HERSHEY_SIMPLEX, + 0.8, + (0, 0, 255), + 3 + ) + + # ============================== + # NORMAL MODE + # ============================== + + else: + if landed: + hold_position_saved = False + + if gesture == "UP": + print("UP detected. Taking off...") + client.takeoffAsync().join() + client.moveByVelocityAsync(0, 0, -Z_SPEED, 0.5) + + elif gesture == "LAND": + gesture = "ALREADY LANDED" + + else: + gesture = "LANDED - SHOW UP TO TAKEOFF" + + else: + if gesture == "LAND": + landing_started = True + landing_start_time = time.time() + hold_position_saved = False + gesture_history.clear() + print("Fast landing started. Commands locked.") + fast_land(client) + + elif gesture == "HOLD": + if last_gesture != "HOLD" or not hold_position_saved: + hold_x, hold_y, hold_z = get_position(client) + hold_position_saved = True + print( + "Hold position saved:", + round(hold_x, 2), + round(hold_y, 2), + round(hold_z, 2) + ) + + hold_position(client, hold_x, hold_y, hold_z) + + else: + hold_position_saved = False + vx, vy, vz = gesture_to_velocity(gesture) + client.moveByVelocityAsync(vx, vy, vz, COMMAND_DURATION) + + last_gesture = gesture + + cv2.putText( + frame, + f"GESTURE: {gesture}", + (30, 70), + cv2.FONT_HERSHEY_SIMPLEX, + 1.1, + (0, 255, 0), + 3 + ) + + if hold_position_saved and gesture == "HOLD": + cv2.putText( + frame, + f"HOLD POS: X={hold_x:.2f} Y={hold_y:.2f} Z={hold_z:.2f}", + (30, 125), + cv2.FONT_HERSHEY_SIMPLEX, + 0.75, + (255, 255, 255), + 2 + ) + + cv2.imshow("AirSim Gesture Drone Control", frame) + + if cv2.waitKey(1) & 0xFF == ord("p"): + print("P pressed. Landing and closing...") + if not is_landed(client): + client.moveByVelocityAsync(0, 0, FAST_LAND_SPEED, 0.5).join() + client.landAsync().join() + break + +# ============================== +# CLEAN EXIT +# ============================== + +client.armDisarm(False) +client.enableApiControl(False) + +cap.release() +cv2.destroyAllWindows() + +print("Program closed.") From 57aa1a24ff25f8fdc8a94be3ce499b895c6aa3d5 Mon Sep 17 00:00:00 2001 From: Sushant Kumar Khobian Date: Sat, 27 Jun 2026 16:39:30 +0530 Subject: [PATCH 2/3] Update gesture_drone_control.py --- .../gesture_drone_control.py | 479 +++++++----------- 1 file changed, 184 insertions(+), 295 deletions(-) diff --git a/client/python/example_user_scripts/gesture_drone_control.py b/client/python/example_user_scripts/gesture_drone_control.py index e22232ca..cb9e3996 100644 --- a/client/python/example_user_scripts/gesture_drone_control.py +++ b/client/python/example_user_scripts/gesture_drone_control.py @@ -1,48 +1,35 @@ """ -Gesture Drone Control Example for AirSim +Gesture-Based Drone Control System using Project AirSim Dependencies: pip install mediapipe pip install opencv-python -pip install airsim - -Controls: -1 Finger Up -> Ascend -1 Finger Down -> Descend -2 Fingers Up -> Forward -Closed Fist -> Hover -3 Fingers Up -> Land +pip install projectairsim + +Gesture Mapping: +- 1 Finger Up -> Ascend / Takeoff +- 1 Finger Down -> Descend +- 2 Fingers Up -> Move Forward +- 2 Fingers Down -> Move Backward +- Fist (Closed Hand) -> Hover (Stop motion) +- Thumb Right -> Move Right +- Thumb Down -> Move Down +- 3 Fingers Up -> Instant Kill (Disarm + Disconnect) """ + +import asyncio import cv2 import mediapipe as mp -import airsim import math -import time from collections import deque, Counter -# ============================== -# SETTINGS -# ============================== - -CAMERA_INDEX = 0 - -MOVE_SPEED = 2.0 -Z_SPEED = 1.5 -COMMAND_DURATION = 0.12 - -HOLD_KP = 0.9 -HOLD_MAX_SPEED = 2.5 - -FAST_LAND_SPEED = 5.0 -LAND_LOCK_TIMEOUT = 5.0 - -GESTURE_HISTORY_LENGTH = 5 +from projectairsim import ProjectAirSimClient, Drone, World -# ============================== -# MEDIAPIPE SETUP -# ============================== +# ========================= +# SETUP (UNCHANGED LOGIC) +# ========================= mp_hands = mp.solutions.hands mp_draw = mp.solutions.drawing_utils @@ -50,30 +37,21 @@ hands = mp_hands.Hands( max_num_hands=1, model_complexity=1, - min_detection_confidence=0.5, - min_tracking_confidence=0.5 + min_detection_confidence=0.6, + min_tracking_confidence=0.6 ) -gesture_history = deque(maxlen=GESTURE_HISTORY_LENGTH) +gesture_history = deque(maxlen=5) -# ============================== -# HELPER FUNCTIONS -# ============================== +# ========================= +# HELPERS (UNCHANGED) +# ========================= -def distance(p1, p2): - return math.hypot(p1.x - p2.x, p1.y - p2.y) +def distance(a, b): + return math.hypot(a.x - b.x, a.y - b.y) -def get_position(client): - state = client.getMultirotorState() - pos = state.kinematics_estimated.position - return pos.x_val, pos.y_val, pos.z_val - -def is_landed(client): - state = client.getMultirotorState() - return state.landed_state == airsim.LandedState.Landed - -def clamp(value, min_value, max_value): - return max(min_value, min(value, max_value)) +def is_extended(tip, base): + return distance(tip, base) > 0.11 def get_direction(tip, base): dx = tip.x - base.x @@ -84,314 +62,225 @@ def get_direction(tip, base): else: return "UP" if dy < 0 else "DOWN" -def is_extended(tip, base): - return distance(tip, base) > 0.11 - -def get_stable_gesture(new_gesture): - if new_gesture: - gesture_history.append(new_gesture) - - if len(gesture_history) == 0: +def stable(gesture): + if gesture: + gesture_history.append(gesture) + if not gesture_history: return "" - return Counter(gesture_history).most_common(1)[0][0] -# ============================== -# GESTURE DETECTION -# ============================== +# ========================= +# GESTURE LOGIC (UNCHANGED) +# ========================= def detect_gesture(frame): rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) - result = hands.process(rgb) + res = hands.process(rgb) - detected_gesture = "" + gesture = "" - if result.multi_hand_landmarks: - for hand_landmarks in result.multi_hand_landmarks: - lm = hand_landmarks.landmark + if res.multi_hand_landmarks: + for lm in res.multi_hand_landmarks: + mp_draw.draw_landmarks(frame, lm, mp_hands.HAND_CONNECTIONS) - mp_draw.draw_landmarks(frame, hand_landmarks, mp_hands.HAND_CONNECTIONS) + l = lm.landmark + wrist = l[0] - wrist = lm[0] + thumb_tip = l[4] - thumb_tip = lm[4] - thumb_ip = lm[3] + index_tip = l[8] + middle_tip = l[12] + ring_tip = l[16] + pinky_tip = l[20] - index_tip = lm[8] - middle_tip = lm[12] - ring_tip = lm[16] - pinky_tip = lm[20] + index_base = l[5] + middle_base = l[9] + ring_base = l[13] + pinky_base = l[17] - index_base = lm[5] - middle_base = lm[9] - ring_base = lm[13] - pinky_base = lm[17] - - index_extended = is_extended(index_tip, index_base) - middle_extended = is_extended(middle_tip, middle_base) - ring_extended = is_extended(ring_tip, ring_base) - pinky_extended = is_extended(pinky_tip, pinky_base) + index_up = is_extended(index_tip, index_base) + middle_up = is_extended(middle_tip, middle_base) + ring_up = is_extended(ring_tip, ring_base) + pinky_up = is_extended(pinky_tip, pinky_base) index_dir = get_direction(index_tip, index_base) middle_dir = get_direction(middle_tip, middle_base) ring_dir = get_direction(ring_tip, ring_base) - two_fingers_close = distance(index_tip, middle_tip) < 0.10 + fingers_folded = not (index_up or middle_up or ring_up or pinky_up) thumb_dx = thumb_tip.x - wrist.x thumb_dy = thumb_tip.y - wrist.y - thumb_right = thumb_dx > 0.16 and abs(thumb_dx) > abs(thumb_dy) - thumb_down = thumb_dy > 0.16 and abs(thumb_dy) > abs(thumb_dx) - - fingers_folded = ( - not index_extended and - not middle_extended and - not ring_extended and - not pinky_extended - ) + thumb_right = thumb_dx > 0.15 and abs(thumb_dx) > abs(thumb_dy) + thumb_down = thumb_dy > 0.15 and abs(thumb_dy) > abs(thumb_dx) - three_fingers_up = ( - index_extended and - middle_extended and - ring_extended and - not pinky_extended and - index_dir == "UP" and - middle_dir == "UP" and - ring_dir == "UP" - ) - - if three_fingers_up: - detected_gesture = "LAND" - - elif fingers_folded and thumb_right: - detected_gesture = "RIGHT" + if fingers_folded and thumb_right: + gesture = "RIGHT" elif fingers_folded and thumb_down: - detected_gesture = "DOWN" + gesture = "DOWN" elif fingers_folded: - detected_gesture = "HOLD" + gesture = "HOLD" - elif index_extended and middle_extended and two_fingers_close and not ring_extended and not pinky_extended: - if index_dir == "UP" and middle_dir == "UP": - detected_gesture = "FRONT" - elif index_dir == "DOWN" and middle_dir == "DOWN": - detected_gesture = "BACK" - - elif index_extended and not middle_extended and not ring_extended and not pinky_extended: + elif index_up and not middle_up and not ring_up and not pinky_up: if index_dir == "UP": - detected_gesture = "UP" + gesture = "UP" elif index_dir == "LEFT": - detected_gesture = "LEFT" - - return get_stable_gesture(detected_gesture) - -# ============================== -# MOVEMENT CONTROL -# ============================== - -def gesture_to_velocity(gesture): - vx, vy, vz = 0, 0, 0 + gesture = "LEFT" - if gesture == "UP": - vz = -Z_SPEED - - elif gesture == "DOWN": - vz = Z_SPEED - - elif gesture == "FRONT": - vx = MOVE_SPEED + elif index_up and middle_up and not ring_up and not pinky_up: + if index_dir == "UP" and middle_dir == "UP": + gesture = "FRONT" + elif index_dir == "DOWN" and middle_dir == "DOWN": + gesture = "BACK" - elif gesture == "BACK": - vx = -MOVE_SPEED + elif index_up and middle_up and ring_up and not pinky_up: + if index_dir == "UP": + gesture = "LAND" - elif gesture == "RIGHT": - vy = MOVE_SPEED + return stable(gesture) - elif gesture == "LEFT": - vy = -MOVE_SPEED +# ========================= +# MOVEMENT SETTINGS +# ========================= - return vx, vy, vz +MOVE_SPEED = 6.5 +Z_SPEED = 6.0 +COMMAND_DURATION = 0.05 -def hold_position(client, hold_x, hold_y, hold_z): - current_x, current_y, current_z = get_position(client) +# ========================= +# CONTROL FIXES (IMPORTANT PART) +# ========================= - vx = clamp((hold_x - current_x) * HOLD_KP, -HOLD_MAX_SPEED, HOLD_MAX_SPEED) - vy = clamp((hold_y - current_y) * HOLD_KP, -HOLD_MAX_SPEED, HOLD_MAX_SPEED) - vz = clamp((hold_z - current_z) * HOLD_KP, -HOLD_MAX_SPEED, HOLD_MAX_SPEED) +async def move_xy(drone, vx, vy): + # 🔥 FIX: NO ALTITUDE CHANGE EVER + await drone.move_by_velocity_async(vx, vy, 0, COMMAND_DURATION) - client.moveByVelocityAsync(vx, vy, vz, COMMAND_DURATION) +async def move_vertical(drone, vz): + await drone.move_by_velocity_async(0, 0, vz, COMMAND_DURATION) -def fast_land(client): - client.moveByVelocityAsync(0, 0, FAST_LAND_SPEED, 0.4).join() - client.landAsync() +# ========================= +# 🔥 INSTANT KILL LAND (NEW REQUIREMENT) +# ========================= -# ============================== -# AIRSIM SETUP -# ============================== +async def instant_shutdown(drone, client): + # 1. STOP ALL MOTION IMMEDIATELY + await drone.move_by_velocity_async(0, 0, 0, 0.01) -print("Connecting to AirSim...") + # 2. DISARM IMMEDIATELY (cuts motors logically) + drone.disarm() -client = airsim.MultirotorClient() -client.confirmConnection() + # 3. REMOVE CONTROL (prevents further commands) + drone.disable_api_control() -client.enableApiControl(True) -client.armDisarm(True) + # 4. HARD DISCONNECT + client.disconnect() -print("Camera started.") -print("Drone will NOT take off automatically.") -print("UP gesture = takeoff / move up.") -print("3 fingers up = fast landing.") -print("P = land and close.") +# ========================= +# VELOCITY MAP +# ========================= -# ============================== -# CAMERA LOOP -# ============================== +def gesture_to_velocity(g): + vx, vy, vz = 0, 0, 0 -cap = cv2.VideoCapture(CAMERA_INDEX) + if g == "UP": + vz = -Z_SPEED + elif g == "DOWN": + vz = Z_SPEED + elif g == "FRONT": + vx = MOVE_SPEED + elif g == "BACK": + vx = -MOVE_SPEED + elif g == "RIGHT": + vy = MOVE_SPEED + elif g == "LEFT": + vy = -MOVE_SPEED -hold_position_saved = False -hold_x, hold_y, hold_z = 0, 0, 0 -last_gesture = "" + return vx, vy, vz -landing_started = False -landing_start_time = 0 +# ========================= +# MAIN +# ========================= -while True: - ret, frame = cap.read() +async def main(): - if not ret: - print("Camera not detected") - break + client = ProjectAirSimClient() + client.connect() - frame = cv2.flip(frame, 1) + world = World(client, "scene_basic_drone.jsonc", delay_after_load_sec=1) + drone = Drone(client, world, "Drone1") - gesture = detect_gesture(frame) - landed = is_landed(client) + drone.enable_api_control() + drone.arm() - # ============================== - # LANDING MODE - # ============================== + cap = cv2.VideoCapture(0) - if landing_started: - gesture = "LANDING..." + flying = False - elapsed_landing_time = time.time() - landing_start_time + try: + while True: - if landed: - landing_started = False - hold_position_saved = False - gesture_history.clear() - gesture = "LANDED - SHOW UP TO TAKEOFF" - print("Drone landed. Commands unlocked.") + ret, frame = cap.read() + if not ret: + break - elif elapsed_landing_time >= LAND_LOCK_TIMEOUT: - landing_started = False - hold_position_saved = False - gesture_history.clear() - gesture = "LAND TIMEOUT - COMMANDS UNLOCKED" - print("Landing timeout reached. Commands unlocked.") + frame = cv2.flip(frame, 1) + gesture = detect_gesture(frame) - else: - client.moveByVelocityAsync(0, 0, FAST_LAND_SPEED, 0.15) + # ========================= + # TAKEOFF + # ========================= + if gesture == "UP": + if not flying: + await drone.takeoff_async() + flying = True + await move_vertical(drone, -Z_SPEED) + + # ========================= + # 🔥 LAND = INSTANT KILL + # ========================= + elif gesture == "LAND": + await drone.move_by_velocity_async(0, 0, 0, 0.05) + drone.disarm() + drone.disable_api_control() + shutdown_requested = True + break + # ========================= + # HOLD + # ========================= + elif gesture == "HOLD": + await drone.move_by_velocity_async(0, 0, 0, COMMAND_DURATION) - cv2.putText( - frame, - f"LANDING... COMMANDS LOCKED {int(LAND_LOCK_TIMEOUT - elapsed_landing_time)}s", - (30, 130), - cv2.FONT_HERSHEY_SIMPLEX, - 0.8, - (0, 0, 255), - 3 - ) + # ========================= + # HORIZONTAL ONLY (NO ALTITUDE DRIFT) + # ========================= + elif gesture in ["LEFT", "RIGHT", "FRONT", "BACK"]: + vx, vy, _ = gesture_to_velocity(gesture) + await move_xy(drone, vx, vy) - # ============================== - # NORMAL MODE - # ============================== + # ========================= + # VERTICAL ONLY + # ========================= + elif gesture in ["UP", "DOWN"]: + _, _, vz = gesture_to_velocity(gesture) + await move_vertical(drone, vz) - else: - if landed: - hold_position_saved = False + cv2.putText(frame, f"GESTURE: {gesture}", (30, 60), + cv2.FONT_HERSHEY_SIMPLEX, 1, (0,255,0), 2) - if gesture == "UP": - print("UP detected. Taking off...") - client.takeoffAsync().join() - client.moveByVelocityAsync(0, 0, -Z_SPEED, 0.5) + cv2.imshow("Drone Control", frame) - elif gesture == "LAND": - gesture = "ALREADY LANDED" + if cv2.waitKey(1) & 0xFF == ord('q'): + break - else: - gesture = "LANDED - SHOW UP TO TAKEOFF" + await asyncio.sleep(0.005) - else: - if gesture == "LAND": - landing_started = True - landing_start_time = time.time() - hold_position_saved = False - gesture_history.clear() - print("Fast landing started. Commands locked.") - fast_land(client) + finally: + cap.release() + cv2.destroyAllWindows() + print("Shutdown complete") - elif gesture == "HOLD": - if last_gesture != "HOLD" or not hold_position_saved: - hold_x, hold_y, hold_z = get_position(client) - hold_position_saved = True - print( - "Hold position saved:", - round(hold_x, 2), - round(hold_y, 2), - round(hold_z, 2) - ) - - hold_position(client, hold_x, hold_y, hold_z) - - else: - hold_position_saved = False - vx, vy, vz = gesture_to_velocity(gesture) - client.moveByVelocityAsync(vx, vy, vz, COMMAND_DURATION) - - last_gesture = gesture - - cv2.putText( - frame, - f"GESTURE: {gesture}", - (30, 70), - cv2.FONT_HERSHEY_SIMPLEX, - 1.1, - (0, 255, 0), - 3 - ) - - if hold_position_saved and gesture == "HOLD": - cv2.putText( - frame, - f"HOLD POS: X={hold_x:.2f} Y={hold_y:.2f} Z={hold_z:.2f}", - (30, 125), - cv2.FONT_HERSHEY_SIMPLEX, - 0.75, - (255, 255, 255), - 2 - ) - - cv2.imshow("AirSim Gesture Drone Control", frame) - - if cv2.waitKey(1) & 0xFF == ord("p"): - print("P pressed. Landing and closing...") - if not is_landed(client): - client.moveByVelocityAsync(0, 0, FAST_LAND_SPEED, 0.5).join() - client.landAsync().join() - break - -# ============================== -# CLEAN EXIT -# ============================== - -client.armDisarm(False) -client.enableApiControl(False) - -cap.release() -cv2.destroyAllWindows() - -print("Program closed.") +if __name__ == "__main__": + asyncio.run(main()) From 4e72c9ce58a110d62d3d7477552f3239dabc0d49 Mon Sep 17 00:00:00 2001 From: Sushant Kumar Khobian Date: Sat, 27 Jun 2026 16:51:47 +0530 Subject: [PATCH 3/3] Update gesture_drone_control.py --- .../gesture_drone_control.py | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/client/python/example_user_scripts/gesture_drone_control.py b/client/python/example_user_scripts/gesture_drone_control.py index cb9e3996..5a755b61 100644 --- a/client/python/example_user_scripts/gesture_drone_control.py +++ b/client/python/example_user_scripts/gesture_drone_control.py @@ -9,6 +9,7 @@ Gesture Mapping: - 1 Finger Up -> Ascend / Takeoff - 1 Finger Down -> Descend +- 1 Finger Left -> Move Left - 2 Fingers Up -> Move Forward - 2 Fingers Down -> Move Backward - Fist (Closed Hand) -> Hover (Stop motion) @@ -216,6 +217,9 @@ async def main(): drone.arm() cap = cv2.VideoCapture(0) + cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640) + cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 360) + flying = False @@ -243,9 +247,12 @@ async def main(): # ========================= elif gesture == "LAND": await drone.move_by_velocity_async(0, 0, 0, 0.05) + drone.disarm() drone.disable_api_control() - shutdown_requested = True + await asyncio.sleep(0.2) + client.disconnect() + break # ========================= # HOLD @@ -280,6 +287,24 @@ async def main(): finally: cap.release() cv2.destroyAllWindows() + + try: + drone.disable_api_control() + except: + pass + + try: + drone.disarm() + except: + pass + + try: + client.disconnect() + except: + pass + + await asyncio.sleep(0.2) + print("Shutdown complete") if __name__ == "__main__":