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

458
tello_drone.py Normal file
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."