import cv2
import numpy as np
import time
import math
from djitellopy import Tello
# =========================
# ===== 설정 / 파라미터 =====
# =========================
FLOOR_MARKER_ID1_ALIGN = 1 # 시작 정렬 마커
FLOOR_MARKER_ID3_ALIGN = 3 # 이동 후 정렬 마커
RC_PERIOD = 0.05
EMA_ALPHA = 0.35
CENTER_BAND_RATIO_X = 0.10
CENTER_BAND_RATIO_Y = 0.10
MOVE_UP_CM = 130
MOVE_RIGHT_AFTER_ID1_CM = 270 # 2.7m = 270cm
MOVE_RIGHT_AFTER_ID3_CM = 360 # ✅ 3.6m = 360cm
HOVER_BEFORE_UP_S = 5.0
HOVER_AFTER_ID1_ALIGN_S = 5.0 # ID1 정렬 후 호버
HOVER_AFTER_ID3_ALIGN_S = 5.0 # ID3 정렬 후 호버
HOVER_AFTER_RIGHT360_S = 5.0 # ✅ 3.6m 이동 후 호버
SEARCH_TIMEOUT_ID1_S = 12.0
SEARCH_TIMEOUT_ID3_S = 12.0
SEARCH_STABLE_N = 1
# ===== 바닥 yaw 정렬(대각 135도 기준) =====
FLOOR_TARGET_EDGE_DEG = 90.0
FLOOR_TARGET_DIAG_DEG = FLOOR_TARGET_EDGE_DEG + 45.0 # 135deg
YAW_KP = 2.0
MAX_YAW_RC = 30
MIN_YAW_RC = 10
YAW_EXIT_DEG = 3.0
STABLE_N = 8
ALIGN_TIMEOUT_S = 30.0
LOST_TIMEOUT_S = 0.30
YAW_SLEW_STEP = 6
YAW_SIGN = 1
# FB로 X 맞추기 (forward -> marker 오른쪽)
FB_KP = 35.0
MAX_FB_RC = 12
MIN_FB_RC = 6
FB_SLEW_STEP = 3
# LR로 Y 맞추기 (드론 오른쪽 -> 마커 아래) => LR_SIGN_Y=1
LR_KP = 35.0
MAX_LR_RC = 12
MIN_LR_RC = 6
LR_SLEW_STEP = 3
LR_SIGN_Y = 1
MOVE_SPEED_CM_S = 30
MOVE_MARGIN_S = 0.5
AUTO_LAND_ON_EXIT = False
# ===== 카메라 파라미터 =====
cameraMatrix = np.array([
[920.0, 0.0, 480.0],
[ 0.0, 920.0, 360.0],
[ 0.0, 0.0, 1.0]
], dtype=np.float32)
distCoeffs = np.array([-0.18, 0.06, 0.000, 0.000, -0.01], dtype=np.float32).reshape(-1, 1)
# ===== ArUco =====
aruco_dict = cv2.aruco.getPredefinedDictionary(cv2.aruco.DICT_4X4_50)
params = cv2.aruco.DetectorParameters()
detector = cv2.aruco.ArucoDetector(aruco_dict, params)
# =========================
# ===== 유틸 =====
# =========================
def clamp_int(x, lo, hi):
return int(max(lo, min(hi, x)))
def ema(prev, x, a):
return x if prev is None else (a * x + (1.0 - a) * prev)
def slew(prev, target, step):
if prev is None:
return target
if target > prev + step:
return prev + step
if target < prev - step:
return prev - step
return target
def parallel_err_deg(angle_deg):
return ((angle_deg + 90.0) % 180.0) - 90.0
def undistort_pts(px_pts_4x2):
pts = px_pts_4x2.reshape(-1, 1, 2).astype(np.float32)
und = cv2.undistortPoints(pts, cameraMatrix, distCoeffs)
return und.reshape(-1, 2)
def safe_set_downvision(tello_obj, enable: bool):
try:
cmd = f"downvision {1 if enable else 0}"
resp = tello_obj.send_command_with_return(cmd)
print(cmd, "->", resp)
return resp
except Exception as e:
print("[WARN] downvision switch failed:", e)
return None
def start_sdk_move(tello_obj, move_func, dist_cm):
try:
tello_obj.send_rc_control(0, 0, 0, 0)
except Exception:
pass
time.sleep(0.1)
try:
tello_obj.set_speed(int(MOVE_SPEED_CM_S))
except Exception as e:
print("[WARN] set_speed failed:", e)
move_func(int(dist_cm))
return time.time() + MOVE_MARGIN_S
# =========================
# ===== 상태머신 =====
# =========================
MODE_IDLE = "IDLE"
MODE_HOVER_PRE_UP = "HOVER_PRE_UP"
MODE_MOVE_UP = "MOVE_UP_130"
MODE_SWITCH_DOWNCAM = "SWITCH_DOWNVISION"
MODE_SEARCH_ID1 = "SEARCH_ID1"
MODE_ALIGN_ID1 = "ALIGN_ID1 (YAW->FB->LR)"
MODE_HOVER_AFTER_ID1 = "HOVER_AFTER_ID1_5"
MODE_MOVE_RIGHT_270 = "MOVE_RIGHT_270"
MODE_SEARCH_ID3 = "SEARCH_ID3"
MODE_ALIGN_ID3 = "ALIGN_ID3 (YAW->FB->LR)"
MODE_HOVER_AFTER_ID3 = "HOVER_AFTER_ID3_5"
MODE_MOVE_RIGHT_360 = "MOVE_RIGHT_360"
MODE_HOVER_AFTER_360 = "HOVER_AFTER_360_5"
MODE_LAND = "LAND"
mode = MODE_IDLE
# =========================
# ===== Tello 연결 =====
# =========================
tello = Tello()
tello.connect()
print("Battery:", tello.get_battery(), "%")
tello.streamon()
frame_read = tello.get_frame_read()
is_flying = False
last_rc_sent = 0.0
# =========================
# ===== 런타임 상태 =====
# =========================
hover_start_t = None
move_end_t = None
search_start_t = None
search_seen_cnt = 0
search_target_id = FLOOR_MARKER_ID1_ALIGN
search_timeout_s = SEARCH_TIMEOUT_ID1_S
# 정렬 공용 상태
align_start_t = None
last_seen_t = 0.0
yaw_f = None
stable_cnt = 0
target_yaw_cmd = 0
target_fb_cmd = 0
target_lr_cmd = 0
prev_yaw = 0
prev_fb = 0
prev_lr = 0
# UI 디버그용
marker_cx = marker_cy = None
control_mode = ""
active_align_id = None
print("\n[KEYS] t=takeoff(start), q=land, ESC=quit")
print("[FLOW]")
print(" Takeoff -> HOVER 5s -> UP 130cm -> downvision ON")
print(" SEARCH ID1 -> ALIGN ID1 -> HOVER 5s -> RIGHT 270cm")
print(" SEARCH ID3 -> ALIGN ID3 -> HOVER 5s -> RIGHT 360cm -> HOVER 5s -> LAND\n")
def reset_align_state():
global align_start_t, last_seen_t, yaw_f, stable_cnt
global target_yaw_cmd, target_fb_cmd, target_lr_cmd
global prev_yaw, prev_fb, prev_lr
global marker_cx, marker_cy, control_mode
align_start_t = time.time()
last_seen_t = time.time()
yaw_f = None
stable_cnt = 0
target_yaw_cmd = 0
target_fb_cmd = 0
target_lr_cmd = 0
prev_yaw = prev_fb = prev_lr = 0
marker_cx = marker_cy = None
control_mode = ""
try:
while True:
frame = frame_read.frame
if frame is None:
continue
frame = cv2.cvtColor(frame, cv2.COLOR_RGB2BGR)
h, w = frame.shape[:2]
cx = w / 2.0
cy = h / 2.0
# 중앙 박스 밴드 경계
band_x = CENTER_BAND_RATIO_X * w
left_band = int(cx - band_x)
right_band = int(cx + band_x)
band_y = CENTER_BAND_RATIO_Y * h
top_band = int(cy - band_y)
bottom_band = int(cy + band_y)
# =========================
# ===== 마커 검출 =====
# =========================
need_detection = mode in [MODE_SEARCH_ID1, MODE_ALIGN_ID1, MODE_SEARCH_ID3, MODE_ALIGN_ID3]
corners = ids = None
id1_visible = False
id1_c4 = None
id3_visible = False
id3_c4 = None
if need_detection:
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
corners, ids, _ = detector.detectMarkers(gray)
if ids is not None and len(ids) > 0:
ids_list = [int(x) for x in ids.flatten().tolist()]
if FLOOR_MARKER_ID1_ALIGN in ids_list:
idx = ids_list.index(FLOOR_MARKER_ID1_ALIGN)
id1_visible = True
id1_c4 = corners[idx][0]
if FLOOR_MARKER_ID3_ALIGN in ids_list:
idx = ids_list.index(FLOOR_MARKER_ID3_ALIGN)
id3_visible = True
id3_c4 = corners[idx][0]
# SEARCH에서는 마커 박스 표시
if mode in [MODE_SEARCH_ID1, MODE_SEARCH_ID3] and ids is not None:
cv2.aruco.drawDetectedMarkers(frame, corners, ids)
# =========================
# ===== SEARCH (ID1/ID3) =====
# =========================
if mode in [MODE_SEARCH_ID1, MODE_SEARCH_ID3] and is_flying:
if search_start_t is None:
search_start_t = time.time()
found = id1_visible if mode == MODE_SEARCH_ID1 else id3_visible
search_seen_cnt = search_seen_cnt + 1 if found else 0
if search_seen_cnt >= SEARCH_STABLE_N:
if mode == MODE_SEARCH_ID1:
mode = MODE_ALIGN_ID1
active_align_id = FLOOR_MARKER_ID1_ALIGN
reset_align_state()
else:
mode = MODE_ALIGN_ID3
active_align_id = FLOOR_MARKER_ID3_ALIGN
reset_align_state()
search_start_t = None
search_seen_cnt = 0
if search_start_t is not None and (time.time() - search_start_t) >= search_timeout_s:
print(f"[INFO] ID{search_target_id} not found within {search_timeout_s}s -> LAND")
mode = MODE_LAND
search_start_t = None
search_seen_cnt = 0
# =========================
# ===== ALIGN 공통 (ID1 or ID3) =====
# =========================
if mode in [MODE_ALIGN_ID1, MODE_ALIGN_ID3] and is_flying:
if align_start_t is not None and (time.time() - align_start_t) >= ALIGN_TIMEOUT_S:
print(f"[INFO] ID{active_align_id} align timeout -> LAND")
mode = MODE_LAND
target_yaw_cmd = target_fb_cmd = target_lr_cmd = 0
else:
if mode == MODE_ALIGN_ID1:
visible = id1_visible
c4 = id1_c4
mid = FLOOR_MARKER_ID1_ALIGN
else:
visible = id3_visible
c4 = id3_c4
mid = FLOOR_MARKER_ID3_ALIGN
if visible and (c4 is not None):
last_seen_t = time.time()
marker_cx = float(np.mean(c4[:, 0]))
marker_cy = float(np.mean(c4[:, 1]))
centered_x = (left_band <= marker_cx <= right_band)
centered_y = (top_band <= marker_cy <= bottom_band)
und = undistort_pts(c4)
tl = und[0]
br = und[2]
v = br - tl
diag_angle = math.degrees(math.atan2(float(v[1]), float(v[0])))
err = parallel_err_deg(diag_angle - FLOOR_TARGET_DIAG_DEG)
yaw_f = ema(yaw_f, err, EMA_ALPHA)
# 우선순위: yaw -> FB(X) -> LR(Y) -> stable
if abs(yaw_f) > YAW_EXIT_DEG:
stable_cnt = 0
raw_yaw = YAW_SIGN * (YAW_KP * yaw_f)
cmd_yaw = clamp_int(raw_yaw, -MAX_YAW_RC, MAX_YAW_RC)
if cmd_yaw != 0 and abs(cmd_yaw) < MIN_YAW_RC:
cmd_yaw = int(math.copysign(MIN_YAW_RC, cmd_yaw))
target_yaw_cmd = cmd_yaw
target_fb_cmd = 0
target_lr_cmd = 0
control_mode = "YAW_ONLY"
elif not centered_x:
stable_cnt = 0
target_yaw_cmd = 0
target_lr_cmd = 0
x_err = (marker_cx - cx)
x_norm = x_err / cx
raw_fb = -FB_KP * x_norm # forward(+) -> marker 오른쪽
cmd_fb = clamp_int(raw_fb, -MAX_FB_RC, MAX_FB_RC)
if cmd_fb != 0 and abs(cmd_fb) < MIN_FB_RC:
cmd_fb = int(math.copysign(MIN_FB_RC, cmd_fb))
target_fb_cmd = cmd_fb
target_lr_cmd = 0
control_mode = "FB_ONLY(X)"
elif not centered_y:
stable_cnt = 0
target_yaw_cmd = 0
target_fb_cmd = 0
y_err = (marker_cy - cy) # +면 아래
y_norm = y_err / cy
raw_lr = LR_SIGN_Y * (-LR_KP * y_norm) # LR_SIGN_Y=1: 오른쪽 -> 마커 아래
cmd_lr = clamp_int(raw_lr, -MAX_LR_RC, MAX_LR_RC)
if cmd_lr != 0 and abs(cmd_lr) < MIN_LR_RC:
cmd_lr = int(math.copysign(MIN_LR_RC, cmd_lr))
target_lr_cmd = cmd_lr
control_mode = "LR_ONLY(Y)"
else:
target_yaw_cmd = 0
target_fb_cmd = 0
target_lr_cmd = 0
stable_cnt += 1
control_mode = "HOLD(STABLE)"
cv2.putText(
frame,
f"ID{mid} ALIGN [{control_mode}] diag={diag_angle:+.1f} yaw_err={yaw_f:+.1f} "
f"yaw={target_yaw_cmd:+d} fb={target_fb_cmd:+d} lr={target_lr_cmd:+d} "
f"cx={marker_cx:.0f} cy={marker_cy:.0f} Xok={int(centered_x)} Yok={int(centered_y)} "
f"stable={stable_cnt}/{STABLE_N}",
(10, 120),
cv2.FONT_HERSHEY_SIMPLEX, 0.60, (0, 255, 255), 2
)
if stable_cnt >= STABLE_N:
try:
tello.send_rc_control(0, 0, 0, 0)
except Exception:
pass
if mode == MODE_ALIGN_ID1:
print("[STATE] ID1 aligned -> HOVER 5s -> RIGHT 270cm -> SEARCH ID3")
mode = MODE_HOVER_AFTER_ID1
hover_start_t = time.time()
else:
print("[STATE] ID3 aligned -> HOVER 5s -> RIGHT 360cm -> HOVER 5s -> LAND")
mode = MODE_HOVER_AFTER_ID3
hover_start_t = time.time()
else:
if (time.time() - last_seen_t) > LOST_TIMEOUT_S:
target_yaw_cmd = target_fb_cmd = target_lr_cmd = 0
stable_cnt = 0
# =========================
# ===== TAKEOFF 시퀀스 =====
# =========================
if mode == MODE_HOVER_PRE_UP and is_flying:
if hover_start_t is None:
hover_start_t = time.time()
if (time.time() - hover_start_t) >= HOVER_BEFORE_UP_S:
mode = MODE_MOVE_UP
hover_start_t = None
move_end_t = None
if mode == MODE_MOVE_UP and is_flying:
if move_end_t is None:
try:
move_end_t = start_sdk_move(tello, tello.move_up, MOVE_UP_CM)
except Exception as e:
print("[WARN] move_up failed:", e)
mode = MODE_LAND
else:
if time.time() >= move_end_t:
move_end_t = None
mode = MODE_SWITCH_DOWNCAM
if mode == MODE_SWITCH_DOWNCAM and is_flying:
safe_set_downvision(tello, True)
time.sleep(0.3)
mode = MODE_SEARCH_ID1
search_target_id = FLOOR_MARKER_ID1_ALIGN
search_timeout_s = SEARCH_TIMEOUT_ID1_S
search_start_t = time.time()
search_seen_cnt = 0
# =========================
# ===== ID1 정렬 후 호버 -> RIGHT 270 -> SEARCH ID3 =====
# =========================
if mode == MODE_HOVER_AFTER_ID1 and is_flying:
if hover_start_t is None:
hover_start_t = time.time()
if (time.time() - hover_start_t) >= HOVER_AFTER_ID1_ALIGN_S:
mode = MODE_MOVE_RIGHT_270
hover_start_t = None
move_end_t = None
if mode == MODE_MOVE_RIGHT_270 and is_flying:
if move_end_t is None:
try:
move_end_t = start_sdk_move(tello, tello.move_right, MOVE_RIGHT_AFTER_ID1_CM)
except Exception as e:
print("[WARN] move_right(270) failed:", e)
mode = MODE_LAND
else:
if time.time() >= move_end_t:
move_end_t = None
mode = MODE_SEARCH_ID3
search_target_id = FLOOR_MARKER_ID3_ALIGN
search_timeout_s = SEARCH_TIMEOUT_ID3_S
search_start_t = time.time()
search_seen_cnt = 0
# =========================
# ===== ID3 정렬 후 호버 -> RIGHT 360 -> HOVER 5 -> LAND =====
# =========================
if mode == MODE_HOVER_AFTER_ID3 and is_flying:
if hover_start_t is None:
hover_start_t = time.time()
if (time.time() - hover_start_t) >= HOVER_AFTER_ID3_ALIGN_S:
mode = MODE_MOVE_RIGHT_360
hover_start_t = None
move_end_t = None
if mode == MODE_MOVE_RIGHT_360 and is_flying:
if move_end_t is None:
try:
move_end_t = start_sdk_move(tello, tello.move_right, MOVE_RIGHT_AFTER_ID3_CM)
except Exception as e:
print("[WARN] move_right(360) failed:", e)
mode = MODE_LAND
else:
if time.time() >= move_end_t:
move_end_t = None
mode = MODE_HOVER_AFTER_360
hover_start_t = time.time()
if mode == MODE_HOVER_AFTER_360 and is_flying:
if hover_start_t is None:
hover_start_t = time.time()
if (time.time() - hover_start_t) >= HOVER_AFTER_RIGHT360_S:
mode = MODE_LAND
hover_start_t = None
# =========================
# ===== LAND =====
# =========================
if mode == MODE_LAND and is_flying:
try:
tello.send_rc_control(0, 0, 0, 0)
except Exception:
pass
try:
tello.land()
except Exception:
pass
is_flying = False
mode = MODE_IDLE
safe_set_downvision(tello, False)
# =========================
# ===== RC 명령 전송 (ALIGN 중만) =====
# =========================
lr_cmd = fb_cmd = yaw_cmd = 0
if is_flying:
now = time.time()
move_in_progress = (
(mode == MODE_MOVE_UP and move_end_t is not None and now < move_end_t)
or (mode == MODE_MOVE_RIGHT_270 and move_end_t is not None and now < move_end_t)
or (mode == MODE_MOVE_RIGHT_360 and move_end_t is not None and now < move_end_t)
)
if not move_in_progress and (now - last_rc_sent) >= RC_PERIOD:
tgt_lr = tgt_fb = tgt_yaw = 0
if mode in [MODE_ALIGN_ID1, MODE_ALIGN_ID3]:
tgt_lr = clamp_int(target_lr_cmd, -MAX_LR_RC, MAX_LR_RC)
tgt_fb = clamp_int(target_fb_cmd, -MAX_FB_RC, MAX_FB_RC)
tgt_yaw = clamp_int(target_yaw_cmd, -MAX_YAW_RC, MAX_YAW_RC)
lr_cmd = int(slew(prev_lr, tgt_lr, LR_SLEW_STEP))
fb_cmd = int(slew(prev_fb, tgt_fb, FB_SLEW_STEP))
yaw_cmd = int(slew(prev_yaw, tgt_yaw, YAW_SLEW_STEP))
prev_lr, prev_fb, prev_yaw = lr_cmd, fb_cmd, yaw_cmd
tello.send_rc_control(lr_cmd, fb_cmd, 0, yaw_cmd)
last_rc_sent = now
# =========================
# ===== HUD / UI =====
# =========================
if not is_flying:
step_text = "STEP: LANDED (press 't' to takeoff)"
else:
if mode == MODE_HOVER_PRE_UP:
step_text = "STEP: HOVER 5s (before UP 130)"
elif mode == MODE_MOVE_UP:
step_text = "STEP: MOVE UP 130cm"
elif mode == MODE_SWITCH_DOWNCAM:
step_text = "STEP: SWITCH CAMERA -> DOWNVISION"
elif mode == MODE_SEARCH_ID1:
step_text = "STEP: SEARCH ID1 (DOWNVISION)"
elif mode == MODE_ALIGN_ID1:
step_text = f"STEP: ALIGN ID1 | stable={stable_cnt}/{STABLE_N}"
elif mode == MODE_HOVER_AFTER_ID1:
step_text = "STEP: HOVER 5s (after ID1) -> RIGHT 270"
elif mode == MODE_MOVE_RIGHT_270:
step_text = "STEP: MOVE RIGHT 270cm"
elif mode == MODE_SEARCH_ID3:
step_text = "STEP: SEARCH ID3 (DOWNVISION)"
elif mode == MODE_ALIGN_ID3:
step_text = f"STEP: ALIGN ID3 | stable={stable_cnt}/{STABLE_N}"
elif mode == MODE_HOVER_AFTER_ID3:
step_text = "STEP: HOVER 5s (after ID3) -> RIGHT 360"
elif mode == MODE_MOVE_RIGHT_360:
step_text = "STEP: MOVE RIGHT 360cm"
elif mode == MODE_HOVER_AFTER_360:
step_text = "STEP: HOVER 5s (after RIGHT 360) -> LAND"
elif mode == MODE_LAND:
step_text = "STEP: LANDING"
else:
step_text = f"STEP: {mode}"
cv2.putText(frame, step_text, (10, 170),
cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 200, 255), 3)
# 중앙 박스(세로 3줄 + 가로 3줄)
cv2.line(frame, (left_band, 0), (left_band, h), (255, 255, 255), 1)
cv2.line(frame, (right_band, 0), (right_band, h), (255, 255, 255), 1)
cv2.line(frame, (int(cx), 0), (int(cx), h), (255, 255, 255), 1)
cv2.line(frame, (0, top_band), (w, top_band), (255, 255, 255), 1)
cv2.line(frame, (0, bottom_band), (w, bottom_band), (255, 255, 255), 1)
cv2.line(frame, (0, int(cy)), (w, int(cy)), (255, 255, 255), 1)
# 마커 중심 표시(정렬 중)
if marker_cx is not None and marker_cy is not None and mode in [MODE_ALIGN_ID1, MODE_ALIGN_ID3]:
cv2.circle(frame, (int(marker_cx), int(marker_cy)), 6, (0, 255, 0), -1)
status = "FLYING" if is_flying else "LANDED"
cv2.putText(frame, f"Status:{status} | Mode:{mode}",
(10, h - 70), cv2.FONT_HERSHEY_SIMPLEX, 0.60, (255, 255, 255), 2)
cv2.putText(frame, f"Cmd: lr={lr_cmd:+d} fb={fb_cmd:+d} yaw={yaw_cmd:+d}",
(10, h - 30), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 255, 255), 2)
cv2.imshow("Tello ArUco - Align ID1 -> Right270 -> Align ID3 -> Right360 -> Land", frame)
# =========================
# ===== 키 입력 =====
# =========================
key = cv2.waitKey(1) & 0xFF
if key == 27: # ESC
print("[USER] ESC -> quit")
break
if key == ord('t'):
if not is_flying:
print("[USER] Takeoff requested")
tello.takeoff()
time.sleep(2)
is_flying = True
last_rc_sent = 0.0
prev_lr = prev_fb = prev_yaw = 0
safe_set_downvision(tello, False)
time.sleep(0.2)
mode = MODE_HOVER_PRE_UP
hover_start_t = time.time()
move_end_t = None
# reset search/align
search_start_t = None
search_seen_cnt = 0
search_target_id = FLOOR_MARKER_ID1_ALIGN
search_timeout_s = SEARCH_TIMEOUT_ID1_S
active_align_id = None
yaw_f = None
stable_cnt = 0
target_yaw_cmd = target_fb_cmd = target_lr_cmd = 0
else:
print("[INFO] Already flying")
if key == ord('q'):
if is_flying:
print("[USER] Land requested")
try:
tello.send_rc_control(0, 0, 0, 0)
except Exception:
pass
try:
tello.land()
except Exception:
pass
is_flying = False
mode = MODE_IDLE
safe_set_downvision(tello, False)
else:
print("[INFO] Already landed")
finally:
try:
cv2.destroyAllWindows()
except Exception:
pass
try:
tello.send_rc_control(0, 0, 0, 0)
except Exception:
pass
if AUTO_LAND_ON_EXIT and is_flying:
try:
tello.land()
except Exception:
pass
try:
safe_set_downvision(tello, False)
except Exception:
pass
try:
tello.streamoff()
except Exception:
pass
try:
tello.end()
except Exception:
pass