diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..644c3f7 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,34 @@ +name: CI + +on: + push: + branches: [master] + pull_request: + branches: [master] + +jobs: + test: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python-version: ["3.10", "3.11", "3.12"] + + steps: + - uses: actions/checkout@v4 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + + - name: Install package with dev dependencies + run: pip install -e ".[dev]" + + - name: Lint with ruff + run: ruff check . + + - name: Test with pytest + run: pytest tests/ -v + env: + MPLBACKEND: Agg # headless — no display server in CI diff --git a/.gitignore b/.gitignore index aa807b4..4265008 100644 --- a/.gitignore +++ b/.gitignore @@ -46,3 +46,6 @@ MANIFEST # Jupyter .ipynb_checkpoints/ + +# ruff +.ruff_cache/ diff --git a/examples/01_imu_ekf_basic.py b/examples/01_imu_ekf_basic.py index cb83380..e44ee3e 100644 --- a/examples/01_imu_ekf_basic.py +++ b/examples/01_imu_ekf_basic.py @@ -5,15 +5,15 @@ and runs the 10-state EKF to fuse accel/gyro/mag data. """ -import numpy as np import matplotlib.pyplot as plt +import numpy as np -from drones_sim.trajectory import generate_hover_accel_cruise +from drones_sim.estimation import ExtendedKalmanFilter +from drones_sim.models import load_drone_urdf from drones_sim.sensors import IMUSimulator from drones_sim.sensors.imu import IMUConfig -from drones_sim.estimation import ExtendedKalmanFilter +from drones_sim.trajectory import generate_hover_accel_cruise from drones_sim.visualization.plots import plot_ekf_results -from drones_sim.models import load_drone_urdf def main(): @@ -21,7 +21,7 @@ def main(): urdf_model = load_drone_urdf() print(urdf_model) print(f" Total mass : {urdf_model.total_mass:.3f} kg") - print(f" Links : {[l.name for l in urdf_model.links]}") + print(f" Links : {[link.name for link in urdf_model.links]}") print(f" Joints : {[j.name for j in urdf_model.joints]}") # 1. Generate trajectory @@ -56,7 +56,7 @@ def main(): filt_quat[i] = state["quaternion"] # 4. Plot - fig = plot_ekf_results( + plot_ekf_results( traj.t, traj.position, traj.velocity, traj.orientation_quat, filt_pos, filt_vel, filt_quat, diff --git a/examples/02_ekf_adaptive.py b/examples/02_ekf_adaptive.py index 9139a48..fdc937f 100644 --- a/examples/02_ekf_adaptive.py +++ b/examples/02_ekf_adaptive.py @@ -5,15 +5,15 @@ sensor models. """ -import numpy as np import matplotlib.pyplot as plt +import numpy as np -from drones_sim.trajectory import generate_hover_accel_cruise +from drones_sim.estimation.ekf import AdaptiveEKF +from drones_sim.models import load_drone_urdf from drones_sim.sensors import IMUSimulator from drones_sim.sensors.imu import IMUConfig -from drones_sim.estimation.ekf import AdaptiveEKF +from drones_sim.trajectory import generate_hover_accel_cruise from drones_sim.visualization.plots import plot_ekf_results -from drones_sim.models import load_drone_urdf def main(): @@ -21,7 +21,7 @@ def main(): urdf_model = load_drone_urdf() print(urdf_model) print(f" Total mass : {urdf_model.total_mass:.3f} kg") - print(f" Links : {[l.name for l in urdf_model.links]}") + print(f" Links : {[link.name for link in urdf_model.links]}") print(f" Joints : {[j.name for j in urdf_model.joints]}") # 1. Trajectory @@ -62,7 +62,7 @@ def main(): filt_quat[i] = state["quaternion"] # 4. Plot - fig = plot_ekf_results( + plot_ekf_results( traj.t, traj.position, traj.velocity, traj.orientation_quat, filt_pos, filt_vel, filt_quat, diff --git a/examples/03_waypoint_nav.py b/examples/03_waypoint_nav.py index 9a2189a..92c05bf 100644 --- a/examples/03_waypoint_nav.py +++ b/examples/03_waypoint_nav.py @@ -6,14 +6,14 @@ acceleration, and jerk) rather than a step-function target jump. """ -import numpy as np import matplotlib.pyplot as plt +import numpy as np -from drones_sim.dynamics import QuadcopterDynamics from drones_sim.control import QuadcopterController +from drones_sim.dynamics import QuadcopterDynamics +from drones_sim.models import load_drone_urdf from drones_sim.trajectory import generate_minimum_snap from drones_sim.visualization.plots import plot_quadcopter_results -from drones_sim.models import load_drone_urdf def main(): @@ -21,7 +21,7 @@ def main(): urdf_model = load_drone_urdf() print(urdf_model) print(f" Total mass : {urdf_model.total_mass:.3f} kg") - print(f" Links : {[l.name for l in urdf_model.links]}") + print(f" Links : {[link.name for link in urdf_model.links]}") print(f" Joints : {[j.name for j in urdf_model.joints]}") quad = QuadcopterDynamics() diff --git a/examples/04_viser_viewer.py b/examples/04_viser_viewer.py index 179f4c1..6a44a7e 100644 --- a/examples/04_viser_viewer.py +++ b/examples/04_viser_viewer.py @@ -7,11 +7,11 @@ import numpy as np -from drones_sim.dynamics import QuadcopterDynamics from drones_sim.control import QuadcopterController +from drones_sim.dynamics import QuadcopterDynamics from drones_sim.math_utils import euler_to_rotation_matrix -from drones_sim.visualization.viewer import DroneViewer from drones_sim.models import load_drone_urdf +from drones_sim.visualization.viewer import DroneViewer def main(): diff --git a/examples/05_full_pipeline.py b/examples/05_full_pipeline.py index 3402c4d..cd7d5f2 100644 --- a/examples/05_full_pipeline.py +++ b/examples/05_full_pipeline.py @@ -10,21 +10,21 @@ """ import os -import numpy as np + import matplotlib.pyplot as plt +import numpy as np -from drones_sim.dynamics import QuadcopterDynamics from drones_sim.control.pid import PIDController -from drones_sim.sensors.models import SensorNoiseModel -from drones_sim.sensors import GPSSimulator, GPSConfig +from drones_sim.dynamics import QuadcopterDynamics from drones_sim.estimation import ExtendedKalmanFilter from drones_sim.math_utils import ( euler_to_rotation_matrix, - quat_from_euler, quat_to_rotation_matrix, ) -from drones_sim.visualization.viewer import DroneViewer from drones_sim.models import load_drone_urdf +from drones_sim.sensors import GPSConfig, GPSSimulator +from drones_sim.sensors.models import SensorNoiseModel +from drones_sim.visualization.viewer import DroneViewer def main(): @@ -107,7 +107,7 @@ def main(): # Accelerometer measures specific force = R^T * (a_linear - g_world) # which in hover (a_linear ≈ 0) is just R^T * g (the reaction to gravity). # We approximate linear accel from the previous dynamics step. - lin_accel_world = np.array([0.0, 0.0, quad.g]) - np.array( + np.array([0.0, 0.0, quad.g]) - np.array( [0.0, 0.0, quad.g] ) # Proper: rotate net force / mass back to body frame as specific force diff --git a/examples/06_trajectory_following.py b/examples/06_trajectory_following.py index 33094a1..52cd095 100644 --- a/examples/06_trajectory_following.py +++ b/examples/06_trajectory_following.py @@ -29,15 +29,15 @@ import numpy as np -from drones_sim.dynamics import QuadcopterDynamics from drones_sim.control import QuadcopterController -from drones_sim.sensors.models import SensorNoiseModel -from drones_sim.sensors import GPSSimulator, GPSConfig +from drones_sim.dynamics import QuadcopterDynamics from drones_sim.estimation import ExtendedKalmanFilter -from drones_sim.trajectory import generate_circular, generate_minimum_snap from drones_sim.math_utils import euler_to_rotation_matrix -from drones_sim.visualization.viewer import DroneViewer from drones_sim.models import load_drone_urdf +from drones_sim.sensors import GPSConfig, GPSSimulator +from drones_sim.sensors.models import SensorNoiseModel +from drones_sim.trajectory import generate_circular, generate_minimum_snap +from drones_sim.visualization.viewer import DroneViewer # --------------------------------------------------------------------------- # Module-level constants diff --git a/pyproject.toml b/pyproject.toml index b7f56d6..10da453 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,5 +20,20 @@ dev = ["pytest>=7.0", "ruff"] [tool.setuptools.packages.find] where = ["src"] +[tool.ruff] +line-length = 100 +target-version = "py310" +src = ["src", "tests", "examples"] +extend-exclude = [".venv"] + +[tool.ruff.lint] +select = ["E", "F", "W", "I", "UP"] +ignore = ["E501"] # long lines — not worth a mass reformat + +[tool.pytest.ini_options] +testpaths = ["tests"] +addopts = "-q" +markers = ["slow: marks tests as slow (deselect with '-m \"not slow\"')"] + [tool.setuptools.package-data] "drones_sim.models" = ["*.urdf"] diff --git a/src/drones_sim/control/__init__.py b/src/drones_sim/control/__init__.py index ac4dd85..d731a62 100644 --- a/src/drones_sim/control/__init__.py +++ b/src/drones_sim/control/__init__.py @@ -1,3 +1,3 @@ -from .pid import PIDController -from .cascaded import QuadcopterController -from .lqr import LQRController +from .cascaded import QuadcopterController # noqa: F401 +from .lqr import LQRController # noqa: F401 +from .pid import PIDController # noqa: F401 diff --git a/src/drones_sim/dynamics/__init__.py b/src/drones_sim/dynamics/__init__.py index 5dc14cc..67e9607 100644 --- a/src/drones_sim/dynamics/__init__.py +++ b/src/drones_sim/dynamics/__init__.py @@ -1 +1 @@ -from .quadcopter import QuadcopterDynamics +from .quadcopter import QuadcopterDynamics # noqa: F401 diff --git a/src/drones_sim/dynamics/quadcopter.py b/src/drones_sim/dynamics/quadcopter.py index 7dc6375..ea861c0 100644 --- a/src/drones_sim/dynamics/quadcopter.py +++ b/src/drones_sim/dynamics/quadcopter.py @@ -36,11 +36,11 @@ from numpy.typing import NDArray from ..math_utils import ( + quat_derivative, quat_from_euler, quat_normalize, quat_to_euler, quat_to_rotation_matrix, - quat_derivative, ) diff --git a/src/drones_sim/estimation/__init__.py b/src/drones_sim/estimation/__init__.py index 61accf6..9fc3040 100644 --- a/src/drones_sim/estimation/__init__.py +++ b/src/drones_sim/estimation/__init__.py @@ -1,2 +1,2 @@ -from .ekf import ExtendedKalmanFilter -from .ahrs import AHRS +from .ahrs import AHRS # noqa: F401 +from .ekf import ExtendedKalmanFilter # noqa: F401 diff --git a/src/drones_sim/estimation/ahrs.py b/src/drones_sim/estimation/ahrs.py index 5c1d3fc..24810f7 100644 --- a/src/drones_sim/estimation/ahrs.py +++ b/src/drones_sim/estimation/ahrs.py @@ -9,7 +9,7 @@ import numpy as np from numpy.typing import NDArray -from ..math_utils import quat_normalize, quat_multiply, quat_to_rotation_matrix +from ..math_utils import quat_normalize, quat_to_rotation_matrix class AHRS: diff --git a/src/drones_sim/estimation/ekf.py b/src/drones_sim/estimation/ekf.py index 9509789..685567c 100644 --- a/src/drones_sim/estimation/ekf.py +++ b/src/drones_sim/estimation/ekf.py @@ -19,14 +19,13 @@ from numpy.typing import NDArray from ..math_utils import ( - quat_derivative, quat_angular_velocity_jacobian, + quat_derivative, quat_normalize, quat_to_rotation_matrix, ) from .ahrs import AHRS - # --------------------------------------------------------------------------- # Full-state EKF (10-state) — from imu_ekf_simulation.py # --------------------------------------------------------------------------- @@ -482,8 +481,8 @@ def correct(self, accel: NDArray, adaptive_factor: float = 1.0) -> None: self.x += K @ innovation # Joseph form for numerical stability - I = np.eye(self.n) - IKH = I - K @ H + I_mat = np.eye(self.n) + IKH = I_mat - K @ H self.P = IKH @ self.P @ IKH.T + K @ self.R_accel @ K.T # -- state access ------------------------------------------------------ diff --git a/src/drones_sim/math_utils.py b/src/drones_sim/math_utils.py index 2ce7252..135f5cc 100644 --- a/src/drones_sim/math_utils.py +++ b/src/drones_sim/math_utils.py @@ -10,7 +10,6 @@ import numpy as np from numpy.typing import NDArray - # --------------------------------------------------------------------------- # Quaternion operations # --------------------------------------------------------------------------- diff --git a/src/drones_sim/models/urdf_loader.py b/src/drones_sim/models/urdf_loader.py index 23f2ace..9a193e4 100644 --- a/src/drones_sim/models/urdf_loader.py +++ b/src/drones_sim/models/urdf_loader.py @@ -15,7 +15,6 @@ import numpy as np from numpy.typing import NDArray - # --------------------------------------------------------------------------- # Data structures # --------------------------------------------------------------------------- @@ -83,7 +82,7 @@ class DroneURDFModel: total_inertia: NDArray = field(default_factory=lambda: np.zeros((3, 3))) def __repr__(self) -> str: - link_names = [l.name for l in self.links] + link_names = [link.name for link in self.links] return ( f"DroneURDFModel(name={self.name!r}, " f"links={link_names}, " @@ -275,8 +274,8 @@ def load_drone_urdf(urdf_path: str | Path | None = None) -> DroneURDFModel: model.joints.append(joint) # --- aggregates --- - model.total_mass = sum(l.mass for l in model.links) - model.total_inertia = sum(l.inertia for l in model.links) # type: ignore[assignment] + model.total_mass = sum(link.mass for link in model.links) + model.total_inertia = sum(link.inertia for link in model.links) # type: ignore[assignment] return model diff --git a/src/drones_sim/sensors/__init__.py b/src/drones_sim/sensors/__init__.py index 66eab5a..a8ee66d 100644 --- a/src/drones_sim/sensors/__init__.py +++ b/src/drones_sim/sensors/__init__.py @@ -1,3 +1,3 @@ -from .imu import IMUSimulator -from .models import SensorNoiseModel, TemperatureModel -from .gps import GPSSimulator, GPSConfig, GPSData +from .gps import GPSConfig, GPSData, GPSSimulator # noqa: F401 +from .imu import IMUSimulator # noqa: F401 +from .models import SensorNoiseModel, TemperatureModel # noqa: F401 diff --git a/src/drones_sim/sensors/gps.py b/src/drones_sim/sensors/gps.py index ed5d401..10d8a67 100644 --- a/src/drones_sim/sensors/gps.py +++ b/src/drones_sim/sensors/gps.py @@ -14,8 +14,7 @@ from __future__ import annotations -from dataclasses import dataclass, field -from typing import Optional +from dataclasses import dataclass import numpy as np from numpy.typing import NDArray diff --git a/src/drones_sim/sensors/imu.py b/src/drones_sim/sensors/imu.py index c32d3b9..1f287fe 100644 --- a/src/drones_sim/sensors/imu.py +++ b/src/drones_sim/sensors/imu.py @@ -7,7 +7,6 @@ from __future__ import annotations from dataclasses import dataclass, field -from typing import Optional import numpy as np from numpy.typing import NDArray @@ -46,7 +45,7 @@ class IMUData: accel: NDArray # (N, 3) gyro: NDArray # (N, 3) mag: NDArray # (N, 3) - temperature: Optional[NDArray] = None # (N,) if temperature model enabled + temperature: NDArray | None = None # (N,) if temperature model enabled # Ground-truth biases for validation accel_bias: NDArray = field(default_factory=lambda: np.zeros(3)) diff --git a/src/drones_sim/trajectory.py b/src/drones_sim/trajectory.py index a07e9c2..d48ecdf 100644 --- a/src/drones_sim/trajectory.py +++ b/src/drones_sim/trajectory.py @@ -6,13 +6,13 @@ from __future__ import annotations -from dataclasses import dataclass, field +from dataclasses import dataclass from math import factorial -from typing import Literal import numpy as np +from numpy.polynomial.polynomial import polyder +from numpy.polynomial.polynomial import polyval as polyval1d from numpy.typing import NDArray -from numpy.polynomial.polynomial import polyval as polyval1d, polyder from scipy.linalg import lstsq from scipy.spatial.transform import Rotation as R diff --git a/src/drones_sim/visualization/__init__.py b/src/drones_sim/visualization/__init__.py index 4ba01df..330e5bd 100644 --- a/src/drones_sim/visualization/__init__.py +++ b/src/drones_sim/visualization/__init__.py @@ -1,2 +1,2 @@ -from .plots import plot_ekf_results, plot_quadcopter_results -from .viewer import DroneViewer +from .plots import plot_ekf_results, plot_quadcopter_results # noqa: F401 +from .viewer import DroneViewer # noqa: F401 diff --git a/src/drones_sim/visualization/plots.py b/src/drones_sim/visualization/plots.py index 15a6ab7..392bffd 100644 --- a/src/drones_sim/visualization/plots.py +++ b/src/drones_sim/visualization/plots.py @@ -2,9 +2,9 @@ from __future__ import annotations +import matplotlib.pyplot as plt import numpy as np from numpy.typing import NDArray -import matplotlib.pyplot as plt def plot_ekf_results( @@ -32,8 +32,6 @@ def plot_ekf_results( labels = ["X", "Y", "Z"] colors = ["blue", "green", "red"] - styles_true = ["-", "-", "-"] - styles_filt = ["--", "--", "--"] # Position ax = axes[0, 0] @@ -133,8 +131,8 @@ def plot_quadcopter_results( # Position vs time ax2 = fig.add_subplot(2, 3, 2) - for j, (c, l) in enumerate(zip(["r", "g", "b"], ["X", "Y", "Z"])): - ax2.plot(t, position[:, j], f"{c}-", label=l) + for j, (c, label) in enumerate(zip(["r", "g", "b"], ["X", "Y", "Z"])): + ax2.plot(t, position[:, j], f"{c}-", label=label) ax2.plot(t, target_position[:, j], f"{c}--", alpha=0.5) ax2.set_xlabel("Time (s)") ax2.set_ylabel("Position (m)") @@ -144,8 +142,8 @@ def plot_quadcopter_results( # Attitude ax3 = fig.add_subplot(2, 3, 3) - for j, (c, l) in enumerate(zip(["r", "g", "b"], ["Roll", "Pitch", "Yaw"])): - ax3.plot(t, np.rad2deg(attitude[:, j]), f"{c}-", label=l) + for j, (c, label) in enumerate(zip(["r", "g", "b"], ["Roll", "Pitch", "Yaw"])): + ax3.plot(t, np.rad2deg(attitude[:, j]), f"{c}-", label=label) ax3.set_xlabel("Time (s)") ax3.set_ylabel("Angle (deg)") ax3.set_title("Attitude vs Time") @@ -165,8 +163,8 @@ def plot_quadcopter_results( # Position error ax5 = fig.add_subplot(2, 3, 5) err = target_position - position - for j, (c, l) in enumerate(zip(["r", "g", "b"], ["X", "Y", "Z"])): - ax5.plot(t, err[:, j], f"{c}-", label=f"{l} Error") + for j, (c, label) in enumerate(zip(["r", "g", "b"], ["X", "Y", "Z"])): + ax5.plot(t, err[:, j], f"{c}-", label=f"{label} Error") ax5.set_xlabel("Time (s)") ax5.set_ylabel("Error (m)") ax5.set_title("Position Error vs Time") diff --git a/src/drones_sim/visualization/viewer.py b/src/drones_sim/visualization/viewer.py index 83c0b16..32875ed 100644 --- a/src/drones_sim/visualization/viewer.py +++ b/src/drones_sim/visualization/viewer.py @@ -295,8 +295,9 @@ def playback( if reference_positions is not None: _errors = np.linalg.norm(positions - reference_positions, axis=1) import io - import matplotlib.figure + import matplotlib.backends.backend_agg as _agg + import matplotlib.figure def _render_error_plot(idx: int) -> NDArray: fig = matplotlib.figure.Figure(figsize=(4, 1.9)) diff --git a/tests/test_cascaded.py b/tests/test_cascaded.py index f0d9aa0..6674287 100644 --- a/tests/test_cascaded.py +++ b/tests/test_cascaded.py @@ -6,8 +6,8 @@ from drones_sim.control import QuadcopterController from drones_sim.dynamics import QuadcopterDynamics from drones_sim.estimation import ExtendedKalmanFilter -from drones_sim.sensors.models import SensorNoiseModel from drones_sim.math_utils import euler_to_rotation_matrix +from drones_sim.sensors.models import SensorNoiseModel def _simulate_targets( @@ -83,7 +83,7 @@ def test_ekf_position_error_bounded(seed): With GPS σ=0.5 m the theoretical floor is well below 2 m; a regression that re-introduces any of the six historical bugs will exceed this threshold. """ - rng = np.random.default_rng(seed) + np.random.default_rng(seed) quad = QuadcopterDynamics() dt = 0.01 gravity = np.array([0.0, 0.0, 9.81]) diff --git a/tests/test_dynamics.py b/tests/test_dynamics.py index 3aaf52d..2dbecdf 100644 --- a/tests/test_dynamics.py +++ b/tests/test_dynamics.py @@ -1,7 +1,6 @@ """Tests for quadcopter dynamics.""" import numpy as np -import pytest from drones_sim.dynamics import QuadcopterDynamics diff --git a/tests/test_dynamics_quaternion.py b/tests/test_dynamics_quaternion.py index 07d5bfe..5fe2ce0 100644 --- a/tests/test_dynamics_quaternion.py +++ b/tests/test_dynamics_quaternion.py @@ -6,17 +6,14 @@ """ import numpy as np -import pytest from drones_sim.dynamics import QuadcopterDynamics from drones_sim.math_utils import ( quat_from_euler, quat_normalize, - quat_to_euler, quat_to_rotation_matrix, ) - # --------------------------------------------------------------------------- # Numerical stability # --------------------------------------------------------------------------- diff --git a/tests/test_ekf.py b/tests/test_ekf.py index d7c07f4..942eaf1 100644 --- a/tests/test_ekf.py +++ b/tests/test_ekf.py @@ -4,8 +4,7 @@ import pytest from drones_sim.estimation.ekf import ExtendedKalmanFilter -from drones_sim.math_utils import quat_to_rotation_matrix, quat_normalize - +from drones_sim.math_utils import quat_normalize, quat_to_rotation_matrix # --------------------------------------------------------------------------- # Helpers diff --git a/tests/test_gps.py b/tests/test_gps.py index 9cf4543..53877a6 100644 --- a/tests/test_gps.py +++ b/tests/test_gps.py @@ -3,9 +3,9 @@ import numpy as np import pytest -from drones_sim.sensors.gps import GPSSimulator, GPSConfig, GPSData -from drones_sim.trajectory import generate_hover_accel_cruise from drones_sim.estimation import ExtendedKalmanFilter +from drones_sim.sensors.gps import GPSConfig, GPSSimulator +from drones_sim.trajectory import generate_hover_accel_cruise @pytest.fixture @@ -85,7 +85,7 @@ def test_ekf_gps_fusion_reduces_position_error(traj): dt = float(np.mean(np.diff(traj.t))) gps_dt = 1.0 / 5.0 - gps_steps = max(1, int(round(gps_dt / dt))) + max(1, int(round(gps_dt / dt))) # EKF without GPS ekf_no_gps = ExtendedKalmanFilter(dt=dt) diff --git a/tests/test_lqr.py b/tests/test_lqr.py index 3aac90f..a08867d 100644 --- a/tests/test_lqr.py +++ b/tests/test_lqr.py @@ -1,7 +1,6 @@ """Tests for the LQR controller.""" import numpy as np -import pytest from drones_sim.control import LQRController from drones_sim.dynamics import QuadcopterDynamics diff --git a/tests/test_math_utils.py b/tests/test_math_utils.py index 63c28ca..6b897b0 100644 --- a/tests/test_math_utils.py +++ b/tests/test_math_utils.py @@ -1,17 +1,16 @@ """Tests for math_utils quaternion operations.""" import numpy as np -import pytest from drones_sim.math_utils import ( - quat_normalize, - quat_multiply, + euler_to_rotation_matrix, quat_conjugate, - quat_to_rotation_matrix, + quat_derivative, quat_from_euler, + quat_multiply, + quat_normalize, quat_to_euler, - quat_derivative, - euler_to_rotation_matrix, + quat_to_rotation_matrix, ) diff --git a/tests/test_pid.py b/tests/test_pid.py index 957b4f4..8f23f46 100644 --- a/tests/test_pid.py +++ b/tests/test_pid.py @@ -1,7 +1,6 @@ """Tests for PID controller.""" import numpy as np -import pytest from drones_sim.control.pid import PIDController diff --git a/tests/test_sensor_models.py b/tests/test_sensor_models.py index b81b0f9..03ddddb 100644 --- a/tests/test_sensor_models.py +++ b/tests/test_sensor_models.py @@ -9,6 +9,7 @@ from drones_sim.dynamics import QuadcopterDynamics from drones_sim.math_utils import euler_to_rotation_matrix +from drones_sim.sensors.models import SensorNoiseModel def test_hover_specific_force_magnitude_equals_g(): @@ -62,8 +63,6 @@ def test_accel_simulation_includes_dynamics(): # Gauss-Markov bias random walk tests # --------------------------------------------------------------------------- -from drones_sim.sensors.models import SensorNoiseModel - def test_bias_constant_when_gauss_markov_disabled(): """Default infinite time constant means the bias never changes.""" diff --git a/tests/test_trajectory.py b/tests/test_trajectory.py index 94c8293..a007019 100644 --- a/tests/test_trajectory.py +++ b/tests/test_trajectory.py @@ -4,11 +4,10 @@ import pytest from drones_sim.trajectory import ( - generate_minimum_snap, TrajectoryData, + generate_minimum_snap, ) - # --------------------------------------------------------------------------- # Minimum-snap tests # ---------------------------------------------------------------------------