Add base files

This commit is contained in:
Andrew Johnson
2025-02-22 18:46:13 +01:00
parent c7b686d849
commit 81a42fde3c
24 changed files with 1032 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
venv/
+5
View File
@@ -0,0 +1,5 @@
{
"python.analysis.extraPaths": [
"./venv/lib/python3.10/site-packages"
]
}
+56
View File
@@ -0,0 +1,56 @@
from tello_drone import DroneSimulator, TelloSimulator
from ursina import *
app = Ursina()
window.color = color.rgb(135, 206, 235)
window.fullscreen = True
window.borderless = False
window.fps_counter.enabled = False
window.render_mode = 'default'
Sky(texture='sky_sunset')
tello_sim = TelloSimulator()
drone_sim = DroneSimulator(tello_sim)
def input(key):
if key == 'h':
drone_sim.help_text.visible = not drone_sim.help_text.visible
if key == 'c':
drone_sim.toggle_camera_view()
def update():
moving = False
rolling = False
if held_keys['w']:
drone_sim.move("forward")
moving = True
if held_keys['s']:
drone_sim.move("backward")
moving = True
if held_keys['a']:
drone_sim.move("left")
rolling = True
if held_keys['d']:
drone_sim.move("right")
rolling = True
if held_keys['j']:
drone_sim.rotate(-drone_sim.rotation_speed)
if held_keys['l']:
drone_sim.rotate(drone_sim.rotation_speed)
if held_keys['shift']:
drone_sim.change_altitude("up")
if held_keys['control']:
drone_sim.change_altitude("down")
if not moving:
drone_sim.pitch_angle = 0 # Reset pitch when not moving
if not rolling:
drone_sim.roll_angle = 0 # Reset roll when not rolling
drone_sim.update_movement()
drone_sim.update_pitch_roll()
app.run()
+1
View File
@@ -0,0 +1 @@
ursina
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
+511
View File
@@ -0,0 +1,511 @@
from ursina import *
from time import time
def lerp_color(start_color, end_color, factor):
"""Custom color interpolation function"""
return Color(
start_color.r + (end_color.r - start_color.r) * factor,
start_color.g + (end_color.g - start_color.g) * factor,
start_color.b + (end_color.b - start_color.b) * factor,
1 # Alpha channel
)
class TelloSimulator:
def __init__(self):
self.battery_level = 100
self.altitude = 3
self.speed = 0
self.rotation_angle = 0
self.is_flying = False
self.start_time = time()
def connect(self):
print("Tello Simulator: Drone connected")
return True
def get_battery(self):
elapsed_time = time() - self.start_time
self.battery_level = max(100 - int((elapsed_time / 3600) * 100), 0) # Reduce battery over 60 min
return self.battery_level
def takeoff(self):
if not self.is_flying:
print("Tello Simulator: Drone taking off")
self.is_flying = True
return "Taking off"
return "Already in air"
def land(self):
if self.is_flying:
print("Tello Simulator: Drone landing")
self.is_flying = False
return "Landing"
return "Already on ground"
def move(self, direction, distance=1):
print(f"Tello Simulator: Moving {direction} by {distance} meters")
def rotate(self, angle):
self.rotation_angle += angle
print(f"Tello Simulator: Rotating {angle} degrees")
#def start_video_stream(self):
#print("Tello Simulator: Starting video stream...")
# def get_video_frame(self):
#return "Simulated video frame"
class DroneSimulator(Entity):
def __init__(self, tello_api, **kwargs):
super().__init__()
self.help_text = Text(
text="Controls:\nW - Forward\nS - Backward\nA - Left\nD - Right\nShift - Launch/Up\nCtrl - Down\nJ - Rotate Left\nL - Rotate Right\nC - FPV\nH - Toggle Help",
position=(-0.85, 0.43), # Top-left position
scale=1.2,
color=color.white,
visible=True
)
self.tello = tello_api
self.drone = Entity(
model='tello.glb',
scale=0.06,
position=(-15.4, 3, 5),
collider='box',
cast_shadow=True
)
self.car = Entity(
model='dirty_car.glb',
scale=0.085,
position=(10, 2.45, 155),
rotation=(0, 0, 0),
collider='box',
cast_shadow=True
)
self.truck = Entity(
model='road_roller.glb',
scale=4.0,
position=(-150, 2.45, 155),
rotation=(0, -90, 0),
collider='box',
cast_shadow=True
)
self.road_closed = Entity(
model='road_closed.glb',
scale=7.0,
position=(-15, 2, 315),
rotation=(0, 90, 0),
collider='box',
cast_shadow=True
)
self.business_man = Entity(
model='business_man.glb',
scale=7.3,
position=(23, 12, 155),
rotation=(0, 55, 0),
collider='box',
cast_shadow=True
)
self.man = Entity(
model='bos_standing.glb',
scale=10.3,
position=(-83, 2.8, 165),
rotation=(0, 120, 0),
collider='box',
cast_shadow=True
)
self.patch = Entity(
model='pipeline_construction_site.glb',
scale=(15, 15, 12),
position=(-123, 0.0, 260),
rotation=(0, 0, 0),
collider='none',
cast_shadow=True
)
self.police_man = Entity(
model='pig.glb',
scale=10.0,
position=(-35, 1.7, 230),
rotation=(0, -70, 0),
collider='box',
cast_shadow=True
)
self.light1 = Entity(
model='street_light.glb',
scale=(4, 6.5, 5),
position=(-55, 2.5, 260),
rotation=(0, -90, 0),
collider='none',
cast_shadow=True
)
self.light2 = Entity(
model='street_light.glb',
scale=(4, 6.5, 5),
position=(25, 2.5, 95),
rotation=(0, 90, 0),
collider='none',
cast_shadow=True
)
self.light3 = Entity(
model='street_light.glb',
scale=(4, 6.5, 5),
position=(-55, 2.5, -70),
rotation=(0, -90, 0),
collider='none',
cast_shadow=True
)
for i in range(3):
Entity(
model='cobblestone.glb',
scale=(5, 10, 20),
position=(30, 0, i * 158.5),
)
for i in range(3):
Entity(
model='cobblestone.glb',
scale=(5, 10, 20),
position=(-58, 0, i * 158.5),
)
self.tunnel_road = Entity(
model='tunnel_3.glb',
scale=(63, 45, 45),
position=(-199, 3.0, 380),
rotation=(0, 0, 0),
collider='None',
cast_shadow=True
)
self.highway_road = Entity(
model='highway.glb',
scale=(20, 20, 5),
position=(-14, 2.5, 120),
rotation=(0, 90, 0),
collider='box',
cast_shadow=True
)
self.barrier1 = Entity(
model='construction_barrier.glb',
scale=(3, 3, 3),
position=(24, 2.5, 315),
rotation=(0, 0, 0),
collider='box',
cast_shadow=True
)
self.barrier2 = Entity(
model='construction_barrier.glb',
scale=(3, 3, 3),
position=(-54, 2.5, 315),
rotation=(0, 0, 0),
collider='box',
cast_shadow=True
)
self.station = Entity(
model='gas_station_-_gta_v.glb',
scale=(12.7, 10, 10),
position=(-210, 2.5, 77),
rotation=(0, -90, 0),
)
Entity(
model='dirty_leaking_concrete_wall.glb',
scale=(25, 20, 30),
position=(34.2, 2.5, 25),
rotation=(0, 90.5, 0),
collider='box',
cast_shadow=True
)
Entity(
model='dirty_leaking_concrete_wall.glb',
scale=(25, 20, 30),
position=(34, 2.5, 227),
rotation=(0, 91, 0),
collider='box',
cast_shadow=True
)
self.first_person_view = False
# Create a separate entity to hold the camera
self.camera_holder = Entity(position=self.drone.position)
self.drone_camera = EditorCamera()
self.drone_camera.parent = self.camera_holder
self.third_person_position = (0, 5, -15)
self.third_person_rotation = (10, 0, 0)
self.first_person_position = (0, 0.5, 22)
self.drone_camera.position = self.third_person_position
self.drone_camera.rotation = self.third_person_rotation
self.is_flying = False
self.velocity = Vec3(0, 0, 0)
self.acceleration = Vec3(0, 0, 0)
self.drag = 0.93
self.rotation_speed = 5
self.max_speed = 1.8
self.accel_force = 0.65
self.pitch_angle = 0
self.roll_angle = 0
self.max_pitch = 20
self.max_roll = 20
self.tilt_smoothness = 0.05
self.create_meters()
def create_meters(self):
# Main battery container
self.battery_container = Entity(
parent=camera.ui,
model=Quad(radius=0.01),
color=color.gray,
scale=(0.12, 0.04),
position=(0.74, 0.41),
z=-1
)
# Battery cap
self.battery_cap = Entity(
parent=self.battery_container,
model=Quad(radius=0.004),
color=color.gray,
position=(0.52, 0),
scale=(0.05, 0.3),
rotation_z=0
)
# Battery fill
self.battery_fill = Entity(
parent=self.battery_container,
model=Quad(radius=0.01),
color=color.green,
scale=(0.9, 0.7),
position=(-0.46, 0),
origin=(-0.5, 0),
z=-0.1
)
# Altitude meter
self.altitude_meter = Text(
text=f"Altitude: {self.tello.altitude}m",
position=(0.67, 0.38),
scale=0.94,
color=color.white
)
# Warning text
self.warning_text = Text(
text="",
position=(-0.25, 0),
scale=3,
color=color.red
)
def update_meters(self):
"""Update telemetry meters"""
battery = self.tello.get_battery()
# Update battery fill width with padding
fill_width = 0.92 * (battery / 100)
self.battery_fill.scale_x = fill_width
# color transitions (green → yellow → orange → red)
if battery > 60:
factor = (battery - 60) / 40 # 100-60%: green to yellow
col = lerp_color(color.yellow, color.green, factor)
elif battery > 30:
factor = (battery - 30) / 30 # 60-30%: yellow to orange
col = lerp_color(color.orange, color.yellow, factor)
else:
factor = battery / 30 # 30-0%: orange to red
col = lerp_color(color.red, color.orange, factor)
self.battery_fill.color = col
# Update altitude
self.altitude_meter.text = f"Altitude: {((self.drone.y) / 10 - 3/10):.1f}m"
# Battery warning
current_time = time() % 1
if battery <= 10 and battery > 0:
if current_time < 0.5:
self.warning_text.text = "Battery Low!"
else:
self.warning_text.text = ""
print("\n========== Battery Low! ==========\n")
elif battery == 0:
self.warning_text.text = "Battery Depleted!"
print("\n========== Battery Depleted! ==========\n")
else:
self.warning_text.text = ""
def update_movement(self):
self.velocity += self.acceleration
if self.velocity.length() > self.max_speed:
self.velocity = self.velocity.normalized() * self.max_speed
self.velocity *= self.drag
new_position = self.drone.position + self.velocity
hit_info = raycast(self.drone.position, self.velocity.normalized(), distance=self.velocity.length(), ignore=(self.drone,))
if not hit_info.hit:
self.drone.position = new_position
if self.drone.y < 3:
self.drone.y = 3
self.acceleration = Vec3(0, 0, 0)
# Apply pitch and roll to the drone
self.drone.rotation_x = lerp(self.drone.rotation_x, self.pitch_angle, self.tilt_smoothness)
self.drone.rotation_z = lerp(self.drone.rotation_z, self.roll_angle, self.tilt_smoothness)
if self.first_person_view:
self.camera_holder.position = self.drone.position
self.camera_holder.rotation_x = 0 # Keep horizon level
self.camera_holder.rotation_z = 0 # Prevent roll tilting
self.camera_holder.rotation_y = self.drone.rotation_y # yaw only
else:
# Third-person view
self.camera_holder.position = lerp(self.camera_holder.position, self.drone.position, 0.1)
self.camera_holder.rotation_y = self.drone.rotation_y # yaw only
self.drone_camera.rotation_x = 0 # Prevent pitch tilting
self.drone_camera.rotation_z = 0 # Prevent roll tilting
self.update_meters()
def move(self, direction):
self.tello.move(direction, 1)
if direction == "forward":
self.acceleration += self.drone.forward * self.accel_force
self.pitch_angle = self.max_pitch
elif direction == "backward":
self.acceleration -= self.drone.forward * self.accel_force
self.pitch_angle = -self.max_pitch
elif direction == "left":
self.acceleration -= self.drone.right * self.accel_force
self.roll_angle = -self.max_roll
elif direction == "right":
self.acceleration += self.drone.right * self.accel_force
self.roll_angle = self.max_roll
def toggle_camera_view(self):
self.first_person_view = not self.first_person_view
if self.first_person_view:
# First-person view
self.drone_camera.position = self.first_person_position
self.drone_camera.rotation = (0, 0, 0)
else:
# Third-person view
self.drone_camera.position = self.third_person_position
self.drone_camera.rotation = self.third_person_rotation
def change_altitude(self, direction):
if direction == "up":
self.drone.y += 0.27
self.tello.altitude += 0.27
elif direction == "down" and self.drone.y > 3:
self.drone.y -= 0.20
self.tello.altitude -= 0.20
def rotate(self, angle):
self.tello.rotate(angle)
self.drone.rotation_y = lerp(self.drone.rotation_y, self.drone.rotation_y + angle, 0.2)
def update_pitch_roll(self):
self.drone.rotation_x = lerp(self.drone.rotation_x, self.pitch_angle, self.tilt_smoothness)
self.drone.rotation_z = lerp(self.drone.rotation_z, self.roll_angle, self.tilt_smoothness)
#def land_drone(self):
# """ Land the drone when battery reaches zero """
# while self.drone.y > 0:
# self.drone.y -= 0.1
# self.update_meters()
# self.drone.y = 0
# self.warning_text.text = "Landed."
app = Ursina()
window.color = color.rgb(135, 206, 235)
window.fullscreen = True
window.borderless = False
window.fps_counter.enabled = False
window.render_mode = 'default'
Sky(texture='sky_sunset')
tello_sim = TelloSimulator()
drone_sim = DroneSimulator(tello_sim)
def input(key):
if key == 'h':
drone_sim.help_text.visible = not drone_sim.help_text.visible
if key == 'c':
drone_sim.toggle_camera_view()
def update():
moving = False
rolling = False
if held_keys['w']:
drone_sim.move("forward")
moving = True
if held_keys['s']:
drone_sim.move("backward")
moving = True
if held_keys['a']:
drone_sim.move("left")
rolling = True
if held_keys['d']:
drone_sim.move("right")
rolling = True
if held_keys['j']:
drone_sim.rotate(-drone_sim.rotation_speed)
if held_keys['l']:
drone_sim.rotate(drone_sim.rotation_speed)
if held_keys['shift']:
drone_sim.change_altitude("up")
if held_keys['control']:
drone_sim.change_altitude("down")
if not moving:
drone_sim.pitch_angle = 0 # Reset pitch when not moving
if not rolling:
drone_sim.roll_angle = 0 # Reset roll when not rolling
drone_sim.update_movement()
drone_sim.update_pitch_roll()
app.run()
BIN
View File
Binary file not shown.
+458
View File
@@ -0,0 +1,458 @@
from ursina import *
from time import time
def lerp_color(start_color, end_color, factor):
"""Custom color interpolation function"""
return Color(
start_color.r + (end_color.r - start_color.r) * factor,
start_color.g + (end_color.g - start_color.g) * factor,
start_color.b + (end_color.b - start_color.b) * factor,
1 # Alpha channel
)
class TelloSimulator:
def __init__(self):
self.battery_level = 100
self.altitude = 3
self.speed = 0
self.rotation_angle = 0
self.is_flying = False
self.start_time = time()
def connect(self):
print("Tello Simulator: Drone connected")
return True
def get_battery(self):
elapsed_time = time() - self.start_time
self.battery_level = max(100 - int((elapsed_time / 3600) * 100), 0) # Reduce battery over 60 min
return self.battery_level
def takeoff(self):
if not self.is_flying:
print("Tello Simulator: Drone taking off")
self.is_flying = True
return "Taking off"
return "Already in air"
def land(self):
if self.is_flying:
print("Tello Simulator: Drone landing")
self.is_flying = False
return "Landing"
return "Already on ground"
def move(self, direction, distance=1):
print(f"Tello Simulator: Moving {direction} by {distance} meters")
def rotate(self, angle):
self.rotation_angle += angle
print(f"Tello Simulator: Rotating {angle} degrees")
#def start_video_stream(self):
#print("Tello Simulator: Starting video stream...")
# def get_video_frame(self):
#return "Simulated video frame"
class DroneSimulator(Entity):
def __init__(self, tello_api, **kwargs):
super().__init__()
self.help_text = Text(
text="Controls:\nW - Forward\nS - Backward\nA - Left\nD - Right\nShift - Launch/Up\nCtrl - Down\nJ - Rotate Left\nL - Rotate Right\nC - FPV\nH - Toggle Help",
position=(-0.85, 0.43), # Top-left position
scale=1.2,
color=color.white,
visible=True
)
self.tello = tello_api
self.drone = Entity(
model='tello.glb',
scale=0.06,
position=(-15.4, 3, 5),
collider='box',
cast_shadow=True
)
self.car = Entity(
model='dirty_car.glb',
scale=0.085,
position=(10, 2.45, 155),
rotation=(0, 0, 0),
collider='box',
cast_shadow=True
)
self.truck = Entity(
model='road_roller.glb',
scale=4.0,
position=(-150, 2.45, 155),
rotation=(0, -90, 0),
collider='box',
cast_shadow=True
)
self.road_closed = Entity(
model='road_closed.glb',
scale=7.0,
position=(-15, 2, 315),
rotation=(0, 90, 0),
collider='box',
cast_shadow=True
)
self.business_man = Entity(
model='business_man.glb',
scale=7.3,
position=(23, 12, 155),
rotation=(0, 55, 0),
collider='box',
cast_shadow=True
)
self.man = Entity(
model='bos_standing.glb',
scale=10.3,
position=(-83, 2.8, 165),
rotation=(0, 120, 0),
collider='box',
cast_shadow=True
)
self.patch = Entity(
model='pipeline_construction_site.glb',
scale=(15, 15, 12),
position=(-123, 0.0, 260),
rotation=(0, 0, 0),
collider='none',
cast_shadow=True
)
self.police_man = Entity(
model='pig.glb',
scale=10.0,
position=(-35, 1.7, 230),
rotation=(0, -70, 0),
collider='box',
cast_shadow=True
)
self.light1 = Entity(
model='street_light.glb',
scale=(4, 6.5, 5),
position=(-55, 2.5, 260),
rotation=(0, -90, 0),
collider='none',
cast_shadow=True
)
self.light2 = Entity(
model='street_light.glb',
scale=(4, 6.5, 5),
position=(25, 2.5, 95),
rotation=(0, 90, 0),
collider='none',
cast_shadow=True
)
self.light3 = Entity(
model='street_light.glb',
scale=(4, 6.5, 5),
position=(-55, 2.5, -70),
rotation=(0, -90, 0),
collider='none',
cast_shadow=True
)
for i in range(3):
Entity(
model='cobblestone.glb',
scale=(5, 10, 20),
position=(30, 0, i * 158.5),
)
for i in range(3):
Entity(
model='cobblestone.glb',
scale=(5, 10, 20),
position=(-58, 0, i * 158.5),
)
self.tunnel_road = Entity(
model='tunnel_3.glb',
scale=(63, 45, 45),
position=(-199, 3.0, 380),
rotation=(0, 0, 0),
collider='None',
cast_shadow=True
)
self.highway_road = Entity(
model='highway.glb',
scale=(20, 20, 5),
position=(-14, 2.5, 120),
rotation=(0, 90, 0),
collider='box',
cast_shadow=True
)
self.barrier1 = Entity(
model='construction_barrier.glb',
scale=(3, 3, 3),
position=(24, 2.5, 315),
rotation=(0, 0, 0),
collider='box',
cast_shadow=True
)
self.barrier2 = Entity(
model='construction_barrier.glb',
scale=(3, 3, 3),
position=(-54, 2.5, 315),
rotation=(0, 0, 0),
collider='box',
cast_shadow=True
)
self.station = Entity(
model='gas_station_-_gta_v.glb',
scale=(12.7, 10, 10),
position=(-210, 2.5, 77),
rotation=(0, -90, 0),
)
Entity(
model='dirty_leaking_concrete_wall.glb',
scale=(25, 20, 30),
position=(34.2, 2.5, 25),
rotation=(0, 90.5, 0),
collider='box',
cast_shadow=True
)
Entity(
model='dirty_leaking_concrete_wall.glb',
scale=(25, 20, 30),
position=(34, 2.5, 227),
rotation=(0, 91, 0),
collider='box',
cast_shadow=True
)
self.first_person_view = False
# Create a separate entity to hold the camera
self.camera_holder = Entity(position=self.drone.position)
self.drone_camera = EditorCamera()
self.drone_camera.parent = self.camera_holder
self.third_person_position = (0, 5, -15)
self.third_person_rotation = (10, 0, 0)
self.first_person_position = (0, 0.5, 22)
self.drone_camera.position = self.third_person_position
self.drone_camera.rotation = self.third_person_rotation
self.is_flying = False
self.velocity = Vec3(0, 0, 0)
self.acceleration = Vec3(0, 0, 0)
self.drag = 0.93
self.rotation_speed = 5
self.max_speed = 1.8
self.accel_force = 0.65
self.pitch_angle = 0
self.roll_angle = 0
self.max_pitch = 20
self.max_roll = 20
self.tilt_smoothness = 0.05
self.create_meters()
def create_meters(self):
# Main battery container
self.battery_container = Entity(
parent=camera.ui,
model=Quad(radius=0.01),
color=color.gray,
scale=(0.12, 0.04),
position=(0.74, 0.41),
z=-1
)
# Battery cap
self.battery_cap = Entity(
parent=self.battery_container,
model=Quad(radius=0.004),
color=color.gray,
position=(0.52, 0),
scale=(0.05, 0.3),
rotation_z=0
)
# Battery fill
self.battery_fill = Entity(
parent=self.battery_container,
model=Quad(radius=0.01),
color=color.green,
scale=(0.9, 0.7),
position=(-0.46, 0),
origin=(-0.5, 0),
z=-0.1
)
# Altitude meter
self.altitude_meter = Text(
text=f"Altitude: {self.tello.altitude}m",
position=(0.67, 0.38),
scale=0.94,
color=color.white
)
# Warning text
self.warning_text = Text(
text="",
position=(-0.25, 0),
scale=3,
color=color.red
)
def update_meters(self):
"""Update telemetry meters"""
battery = self.tello.get_battery()
# Update battery fill width with padding
fill_width = 0.92 * (battery / 100)
self.battery_fill.scale_x = fill_width
# color transitions (green → yellow → orange → red)
if battery > 60:
factor = (battery - 60) / 40 # 100-60%: green to yellow
col = lerp_color(color.yellow, color.green, factor)
elif battery > 30:
factor = (battery - 30) / 30 # 60-30%: yellow to orange
col = lerp_color(color.orange, color.yellow, factor)
else:
factor = battery / 30 # 30-0%: orange to red
col = lerp_color(color.red, color.orange, factor)
self.battery_fill.color = col
# Update altitude
self.altitude_meter.text = f"Altitude: {int(self.drone.y - 3)}m"
# Battery warning
current_time = time() % 1
if battery <= 10 and battery > 0:
if current_time < 0.5:
self.warning_text.text = "Battery Low!"
else:
self.warning_text.text = ""
print("\n========== Battery Low! ==========\n")
elif battery == 0:
self.warning_text.text = "Battery Depleted!"
print("\n========== Battery Depleted! ==========\n")
else:
self.warning_text.text = ""
def update_movement(self):
self.velocity += self.acceleration
if self.velocity.length() > self.max_speed:
self.velocity = self.velocity.normalized() * self.max_speed
self.velocity *= self.drag
new_position = self.drone.position + self.velocity
hit_info = raycast(self.drone.position, self.velocity.normalized(), distance=self.velocity.length(), ignore=(self.drone,))
if not hit_info.hit:
self.drone.position = new_position
if self.drone.y < 3:
self.drone.y = 3
self.acceleration = Vec3(0, 0, 0)
# Apply pitch and roll to the drone
self.drone.rotation_x = lerp(self.drone.rotation_x, self.pitch_angle, self.tilt_smoothness)
self.drone.rotation_z = lerp(self.drone.rotation_z, self.roll_angle, self.tilt_smoothness)
if self.first_person_view:
self.camera_holder.position = self.drone.position
self.camera_holder.rotation_x = 0 # Keep horizon level
self.camera_holder.rotation_z = 0 # Prevent roll tilting
self.camera_holder.rotation_y = self.drone.rotation_y # yaw only
else:
# Third-person view
self.camera_holder.position = lerp(self.camera_holder.position, self.drone.position, 0.1)
self.camera_holder.rotation_y = self.drone.rotation_y # yaw only
self.drone_camera.rotation_x = 0 # Prevent pitch tilting
self.drone_camera.rotation_z = 0 # Prevent roll tilting
self.update_meters()
def move(self, direction):
self.tello.move(direction, 1)
if direction == "forward":
self.acceleration += self.drone.forward * self.accel_force
self.pitch_angle = self.max_pitch
elif direction == "backward":
self.acceleration -= self.drone.forward * self.accel_force
self.pitch_angle = -self.max_pitch
elif direction == "left":
self.acceleration -= self.drone.right * self.accel_force
self.roll_angle = -self.max_roll
elif direction == "right":
self.acceleration += self.drone.right * self.accel_force
self.roll_angle = self.max_roll
def toggle_camera_view(self):
self.first_person_view = not self.first_person_view
if self.first_person_view:
# First-person view
self.drone_camera.position = self.first_person_position
self.drone_camera.rotation = (0, 0, 0)
else:
# Third-person view
self.drone_camera.position = self.third_person_position
self.drone_camera.rotation = self.third_person_rotation
def change_altitude(self, direction):
if direction == "up":
self.drone.y += 0.27
self.tello.altitude += 0.27
elif direction == "down" and self.drone.y > 3:
self.drone.y -= 0.20
self.tello.altitude -= 0.20
def rotate(self, angle):
self.tello.rotate(angle)
self.drone.rotation_y = lerp(self.drone.rotation_y, self.drone.rotation_y + angle, 0.2)
def update_pitch_roll(self):
self.drone.rotation_x = lerp(self.drone.rotation_x, self.pitch_angle, self.tilt_smoothness)
self.drone.rotation_z = lerp(self.drone.rotation_z, self.roll_angle, self.tilt_smoothness)
#def land_drone(self):
# """ Land the drone when battery reaches zero """
# while self.drone.y > 0:
# self.drone.y -= 0.1
# self.update_meters()
# self.drone.y = 0
# self.warning_text.text = "Landed."