Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move local_global_conversion from computer-vision #54

Merged
merged 2 commits into from
Oct 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
133 changes: 133 additions & 0 deletions kml/modules/drone_odometry_local.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
"""
Drone odometry in local space (origin at home location).
"""

from ...mavlink.modules import drone_odometry


class DronePositionLocal:
"""
Drone position in NED system.
"""

__create_key = object()

@classmethod
def create(
cls, north: float, east: float, down: float
) -> "tuple[bool, DronePositionLocal | None]":
"""
North, east, down in metres.
"""
return True, DronePositionLocal(cls.__create_key, north, east, down)

def __init__(
self, class_private_create_key: object, north: float, east: float, down: float
) -> None:
"""
Private constructor, use create() method.
"""
assert class_private_create_key is DronePositionLocal.__create_key, "Use create() method"

self.north = north
self.east = east
self.down = down

def __str__(self) -> str:
"""
To string.
"""
return f"DronePositionLocal (NED): {self.north}, {self.east}, {self.down}"


class DroneOrientationLocal:
"""
Wrapper for DroneOrientation as it is the same in both local and global space.
"""

__create_key = object()

@classmethod
def create_new(
cls, yaw: float, pitch: float, roll: float
) -> "tuple[bool, DroneOrientationLocal | None]":
"""
Yaw, pitch, roll in radians.
"""
result, orientation = drone_odometry.DroneOrientation.create(yaw, pitch, roll)
if not result:
return False, None

# Get Pylance to stop complaining
assert orientation is not None

return True, DroneOrientationLocal(cls.__create_key, orientation)

@classmethod
def create_wrap(
cls, orientation: drone_odometry.DroneOrientation
) -> "tuple[bool, DroneOrientationLocal | None]":
"""
Wrap existing orientation.
"""
return True, DroneOrientationLocal(cls.__create_key, orientation)

def __init__(
self, class_private_create_key: object, orientation: drone_odometry.DroneOrientation
) -> None:
"""
Private constructor, use create() method.
"""
assert class_private_create_key is DroneOrientationLocal.__create_key, "Use create() method"

self.orientation = orientation

def __str__(self) -> str:
"""
To string.
"""
# TODO: Update common
return f"DroneOrientationLocal (YPR rad): {self.orientation.yaw}, {self.orientation.pitch}, {self.orientation.roll}"


class DroneOdometryLocal:
"""
Wrapper for DronePositionLocal and DroneOrientationLocal.
"""

__create_key = object()

@classmethod
def create(
cls, position: DronePositionLocal, orientation: DroneOrientationLocal
) -> "tuple[bool, DroneOdometryLocal | None]":
"""
Position and orientation in one class.
"""
if position is None:
return False, None

if orientation is None:
return False, None

return True, DroneOdometryLocal(cls.__create_key, position, orientation)

def __init__(
self,
class_private_create_key: object,
position: DronePositionLocal,
orientation: DroneOrientationLocal,
) -> None:
"""
Private constructor, use create() method.
"""
assert class_private_create_key is DroneOdometryLocal.__create_key, "Use create() method"

self.position = position
self.orientation = orientation

def __str__(self) -> str:
"""
To string.
"""
return f"DroneOdometryLocal: {self.position}, {self.orientation}"
100 changes: 100 additions & 0 deletions kml/modules/local_global_conversion.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
"""
Conversion between local and global space.
"""

import pymap3d as pm

from . import drone_odometry_local
from ...mavlink.modules import drone_odometry


def drone_position_global_from_local(
home_location: drone_odometry.DronePosition,
drone_position_local: drone_odometry_local.DronePositionLocal,
) -> "tuple[bool, drone_odometry.DronePosition | None]":
"""
Local coordinates to global coordinates.
Return: Drone position in WGS 84.
"""
latitude, longitude, altitude = pm.ned2geodetic(
drone_position_local.north,
drone_position_local.east,
drone_position_local.down,
home_location.latitude,
home_location.longitude,
home_location.altitude,
)

result, drone_position = drone_odometry.DronePosition.create(
latitude,
longitude,
altitude,
)
if not result:
return False, None

# Get Pylance to stop complaining
assert drone_position is not None

return True, drone_position


def __drone_position_local_from_global(
home_location: drone_odometry.DronePosition, drone_position: drone_odometry.DronePosition
) -> "tuple[bool, drone_odometry_local.DronePositionLocal | None]":
"""
Global coordinates to local coordinates.
Return: Drone position relative to home location (NED system).
"""
north, east, down = pm.geodetic2ned(
drone_position.latitude,
drone_position.longitude,
drone_position.altitude,
home_location.latitude,
home_location.longitude,
home_location.altitude,
)

result, drone_position_local = drone_odometry_local.DronePositionLocal.create(
north,
east,
down,
)
if not result:
return False, None

# Get Pylance to stop complaining
assert drone_position_local is not None

return True, drone_position_local


def drone_odometry_local_from_global(
odometry: drone_odometry.DroneOdometry, home_location: drone_odometry.DronePosition
) -> "tuple[bool, drone_odometry_local.DroneOdometryLocal | None]":
"""
Converts global odometry to local.
"""
result, drone_position_local = __drone_position_local_from_global(
home_location,
odometry.position,
)
if not result:
return False, None

# Get Pylance to stop complaining
assert drone_position_local is not None

result, drone_orientation_local = drone_odometry_local.DroneOrientationLocal.create_wrap(
odometry.orientation,
)
if not result:
return False, None

# Get Pylance to stop complaining
assert drone_orientation_local is not None

return drone_odometry_local.DroneOdometryLocal.create(
drone_position_local,
drone_orientation_local,
)