- Reformat your numeric data so that the decimals align, and suffix zeros until they're all the same width
- You're using lists and tuples at (seemingly) random. In this use case, apply tuples throughout since none of your data mutate
- The standard acceleration due to earth's gravity isn't 9.81 m/s^2 but rather 9.80665
- There's no point in returning a string from your check routine, even if it were used (which it isn't). "Throw-or-return-None" is a perfectly fine validation strategy. Perhaps you meant to print that string - which is fine if no exceptions are raised.
acc_root_of_sum_of_squares
is a verbose way of saying acc_norm
- Consider making some domain-specific exceptions, and raising them instead of a broad
Exception
- Not strictly necessary to log within the check routine itself. If you want you can just log at the outer level. Logging in general seems like overkill for this application but whatever, it's good practice. Also consider logging with
exc_info=True
.
- Factor out
G_FORCE
from your range comparison, subtracting it from the middle term.
- Consider adding some structure and types around your data. If you make your data sample a class,
validate
is a natural fit as a method.
- Drop the exclamation marks; no need to shout
- Consider applying
any
to simplify your zero checks
- Separate your "unreasonable" from "zero" check
Suggested (classes)
import logging
import math
import sys
from dataclasses import dataclass
from typing import Sequence
GYRO_TOLERANCE = 0.05
ACC_TOLERANCE = 2
G_FORCE = 9.80665
class GyroValueError(Exception):
pass
class AccelValueError(Exception):
pass
@dataclass
class IMUDataSample:
accelerometer: Sequence[float]
gyro: Sequence[float]
@property
def accel_norm(self) -> float:
return math.sqrt(sum(
a**2 for a in self.accelerometer
))
def validate(self) -> None:
if any(g == 0 for g in self.gyro):
raise GyroValueError('one of the gyro axes is zero')
if any(abs(g) > GYRO_TOLERANCE for g in self.gyro):
raise GyroValueError('one of the gyro axis values is out-of-range')
if any(acc == 0 for acc in self.accelerometer):
raise AccelValueError('one or more of the acc axes is 0')
if not -ACC_TOLERANCE < self.accel_norm - G_FORCE < ACC_TOLERANCE:
raise AccelValueError('acc value is out of range')
def make_logger():
log = logging.getLogger(__name__)
log.setLevel(logging.INFO)
formatter = logging.Formatter('%(filename)s - %(asctime)s - %(levelname)s - %(message)s')
handler = logging.StreamHandler(sys.stdout)
handler.setFormatter(formatter)
log.addHandler(handler)
return log
logger = make_logger()
def check_IMU_values() -> None:
samples = (
IMUDataSample(
(-1.412037800268452800, -10.2105936349597930000, 3.3613594121682193000),
(-0.002742788783767945, -0.0011996491815411065, 0.0032035080745434893),
),
IMUDataSample(
(-1.408874821800522000, -9.2160227659083520000, 3.3767004976616977000),
( 0.003897155186363737, -0.0077723962104497280, 0.0081856905883421360),
),
)
for sample in samples:
sample.validate()
if __name__ == '__main__':
try:
check_IMU_values()
print('The IMU is in order.')
except (GyroValueError, AccelValueError):
logger.error('Bad IMU value(s)', exc_info=True)
Suggested (vectorization)
You've given no indication as to the scale of your real data, but this kind of thing is what Numpy was made for. It even has a built-in Frobenius norm
function for you. Separate your acceleration and gyroscopic data into one array each, with shape of 3 * number-of-samples. This will likely scale better and in some ways is easier to implement.
import numpy as np
GYRO_TOLERANCE = 0.05
ACC_TOLERANCE = 2
G_FORCE = 9.80665
class GyroValueError(Exception):
pass
class AccelValueError(Exception):
pass
def accel_norm(accel: np.ndarray) -> np.ndarray:
return np.linalg.norm(accel, axis=1)
def validate_accel(accel: np.ndarray) -> None:
if np.any(accel == 0):
raise AccelValueError('one or more of the acc axes is zero')
norm = accel_norm(accel)
if np.any(np.abs(norm - G_FORCE) > ACC_TOLERANCE):
raise AccelValueError('acc value is out of range')
def validate_gyro(gyro: np.ndarray) -> None:
if np.any(gyro == 0):
raise GyroValueError('one of the gyro axes is zero')
if np.any(np.abs(gyro) > GYRO_TOLERANCE):
raise GyroValueError('one of the gyro axis values is out-of-range')
def check_IMU_values() -> None:
accel = np.array(
(
(-1.412037800268452800, -10.2105936349597930000, 3.3613594121682193000),
(-1.408874821800522000, -9.2160227659083520000, 3.3767004976616977000),
)
)
gyro = np.array(
(
(-0.002742788783767945, -0.0011996491815411065, 0.0032035080745434893),
( 0.003897155186363737, -0.0077723962104497280, 0.0081856905883421360),
)
)
validate_accel(accel)
validate_gyro(gyro)
if __name__ == '__main__':
try:
check_IMU_values()
print('The IMU is in order.')
except (GyroValueError, AccelValueError) as e:
print(f'Bad IMU value(s): {e}')