Seeker#
- class c4dynamics.sensors.seeker.seeker(origin: rigidbody | None = None, isideal: bool = False, **kwargs)[source]#
Direction seeker.
The
seeker
class models sensors that measure the direction to a target in terms of azimuth and elevation. Such sensors typically use electro-optical or laser technologies, though other technologies may also be used.A seeker object can operate in an ideal mode, providing precise direction measurements. Alternatively, in a non-ideal mode, the measurements may be affected by errors such as scale factor, bias, and noise, as defined by the errors model. A random variable generation mechanism allows for Monte Carlo simulations.
- Parameters:
- Keyword Arguments:
bias_std (float) – The standard deviation of the bias error, [radians]. Defaults \(0.1°\).
scale_factor_std (float) – The standard deviation of the scale factor error, [dimensionless]. Defaults \(0.05 (= 5\%)\).
noise_std (float) – The standard deviation of the seeker angular noise, [radians]. Default value for non-ideal seeker: \(0.4°\).
dt (float) – The time-constant of the operational rate of the seeker (below which the seeker measures return None), [seconds]. Default value: \(dt = -1sec\) (no limit between calls to
measure
).
Functionality
At each sample the seeker returns measures based on the true geometry with the target.
Let the relative coordinates in an arbitrary frame of reference:
\[ \begin{align}\begin{aligned}dx = target.x - seeker.x\\dy = target.y - seeker.y\\dz = target.z - seeker.z\end{aligned}\end{align} \]The relative coordinates in the seeker body frame are given by:
\[x_b = [BR] \cdot [dx, dy, dz]^T\]where \([BR]\) is a Body from Reference DCM (Direction Cosine Matrix) formed by the seeker three Euler angles. See the rigidbody section below.
The azimuth and elevation measures are then the spatial angles:
\[ \begin{align}\begin{aligned}az = tan^{-1}{x_b[1] \over x_b[0]}\\el = tan^{-1}{x_b[2] \over \sqrt{x_b[0]^2 + x_b[1]^2}}\end{aligned}\end{align} \]Where:
\(az\) is the azimuth angle
\(el\) is the elevation angle
\(x_b\) is the target-radar position vector in radar body frame
Fig-1: Azimuth and elevation angles definition#
Errors Model
The azimuth and elevation angles are subject to errors: scale factor, bias, and noise.
Bias
: represents a constant offset or deviation from the true value in the seeker’s measurements. It is a systematic error that consistently affects the measured values. The bias of a seeker instance is a normally distributed variable with mean = 0 and std = bias_std, where bias_std is a parameter with default value of 0.1°.Scale Factor
: a multiplier applied to the true value of a measurement. It represents a scaling error in the measurements made by the seeker. The scale factor of a seeker instance is a normally distributed variable with mean = 0 and std = scale_factor_std, , where scale_factor_std is a parameter with default value of 0.05.Noise
: represents random variations or fluctuations in the measurements that are not systematic. The noise at each seeker sample (measure
) is a normally distributed variable with mean = 0 and std = noise_std, where noise_std is a parameter with default value of 0.4°.
The errors model generates random variables for each seeker instance, allowing for the simulation of different scenarios or variations in the seeker behavior in a technique known as Monte Carlo. Monte Carlo simulations leverage this randomness to statistically analyze the impact of these biases and scale factors over a large number of iterations, providing insights into potential outcomes and system reliability.
The errors model can be disabled by applying isideal = True at the seeker construction stage.
The erros model operates as follows: the particular parameters that form each error, for example the standard deviation of the bias error, may be determined at the stage of creating the sensor object:
seeker = c4d.sensors.seeker(bias_std = 0.1 * c4d.d2r)
, where c4d.d2r is a conversion from degrees to radians. Then, the errors model generates a normal random variable and establishes the seeker bias error. However, the user can override the generated bias by using thebias
property and determine the seeker bias error:seeker.bias = 1 * c4d.d2r
, to have a 1° bias error.rigidbody
The seeker class is a subclass of
rigidbody
, i.e. it suggests attributes of position and attitude and the manipulation of them.As a fundamental propety, the rigidbody’s state vector
X
sets the spatial coordinates of the seeker:\[X = [x, y, z, v_x, v_y, v_z, {\varphi}, {\theta}, {\psi}, p, q, r]^T\]The first six coordinates determine the translational position and velocity of the seeker while the last six determine its angular attitude in terms of Euler angles and the body rates.
Passing a rigidbody parameter as an origin sets the initial conditions of the seeker.
Construction
A seeker instance is created by making a direct call to the seeker constructor:
>>> skr = c4d.sensors.seeker()
Initialization of the instance does not require any mandatory arguments, but the seeker parameters can be determined using the **kwargs argument as detailed above.
Examples
Import required packages:
>>> import c4dynamics as c4d >>> from matplotlib import pyplot as plt >>> import numpy as np
Target
For the examples below let’s generate the trajectory of a target with constant velocity:
>>> tgt = c4d.datapoint(x = 1000, y = 0, vx = -80 * c4d.kmh2ms, vy = 10 * c4d.kmh2ms) >>> for t in np.arange(0, 60, 0.01): ... tgt.inteqm(np.zeros(3), .01) ... tgt.store(t)
The method
inteqm
of thedatapoint
class integrates the 3 degrees of freedom equations of motion with respect to the input force vector (np.zeros(3) here).Since the call to
measure
requires a target as a datapoint object we utilize a custom create function that returns a new datapoint object for a given X state vector in time.c4d.kmh2ms
converts kilometers per hour to meters per second.c4d.r2d
converts radians to degrees.c4d.d2r
converts degrees to radians.
Origin
Let’s also introduce a pedestal as an origin for the seeker. The pedestal is a rigidbody object with position and attitude:
>>> pedestal = c4d.rigidbody(z = 30, theta = -1 * c4d.d2r)
Ideal Seeker
Measure the target position with an ideal seeker:
>>> skr_ideal = c4d.sensors.seeker(origin = pedestal, isideal = True) >>> for x in tgt.data(): ... skr_ideal.measure(c4d.create(x[1:]), t = x[0], store = True)
Comparing the seeker measurements with the true target angles requires converting the relative position to the seeker body frame:
>>> dx = tgt.data('x')[1] - skr_ideal.x >>> dy = tgt.data('y')[1] - skr_ideal.y >>> dz = tgt.data('z')[1] - skr_ideal.z >>> Xb = np.array([skr_ideal.BR @ [X[1] - skr_ideal.x, X[2] - skr_ideal.y, X[3] - skr_ideal.z] for X in tgt.data()])
where
skr_ideal.BR
is a Body from Reference DCM (Direction Cosine Matrix) formed by the seeker three Euler anglesNow az_true is the true target azimuth angle with respect to the seeker, and el_true is the true elevation angle (atan2d is an aliasing of numpy’s arctan2 with a modification returning the angles in degrees):
>>> az_true = c4d.atan2d(Xb[:, 1], Xb[:, 0]) >>> el_true = c4d.atan2d(Xb[:, 2], c4d.sqrt(Xb[:, 0]**2 + Xb[:, 1]**2)) >>> # plot results >>> fig, axs = plt.subplots(2, 1) >>> axs[0].plot(tgt.data('t'), az_true, label = 'target') >>> axs[0].plot(*skr_ideal.data('az', scale = c4d.r2d), label = 'seeker') >>> axs[1].plot(tgt.data('t'), el_true) >>> axs[1].plot(*skr_ideal.data('el', scale = c4d.r2d))
Non-ideal Seeker
Measure the target position with a non-ideal seeker. The seeker’s errors model introduces bias, scale factor, and noise that corrupt the measurements.
To reproduce the result, let’s set the random generator seed (42 is arbitrary):
>>> np.random.seed(42)
>>> skr = c4d.sensors.seeker(origin = pedestal) >>> for x in tgt.data(): ... skr.measure(c4d.create(x[1:]), t = x[0], store = True)
Results with respect to an ideal seeker:
>>> fig, axs = plt.subplots(2, 1) >>> axs[0].plot(*skr_ideal.data('az', scale = c4d.r2d), label = 'ideal') >>> axs[0].plot(*skr.data('az', scale = c4d.r2d), label = 'non-ideal') >>> axs[1].plot(*skr_ideal.data('el', scale = c4d.r2d)) >>> axs[1].plot(*skr.data('el', scale = c4d.r2d))
The bias, scale factor, and noise that used to generate these measures can be examined by:
>>> skr.bias * c4d.r2d -0.01... >>> skr.scale_factor 1.02... >>> skr.noise_std * c4d.r2d 0.4
Points to consider:
The scale factor error increases with the angle, such that for a \(5%\) scale factor, the error of \(Azimuth = 100°\) is \(5°\), whereas the error for \(Elevation = -15°\) is only \(-0.75°\).
The standard deviation of the noise in the two channels is the same. However, as the Elevation values are confined to a smaller range, the effect appears more pronounced there.
Rotating Seeker
Measure the target position with a rotating seeker. The seeker origin is yawing (performed by the increment of \(\psi\)) in the direction of the target motion:
>>> skr = c4d.sensors.seeker(origin = pedestal) >>> for x in tgt.data(): ... skr.psi += .02 * c4d.d2r ... skr.measure(c4d.create(x[1:]), t = x[0], store = True) ... skr.store(x[0])
The seeker yaw angle:
And the target angles with respect to the yawing seeker are:
>>> fig, axs = plt.subplots(2, 1) >>> axs[0].plot(*skr_ideal.data('az', c4d.r2d), label = 'ideal static seeker') >>> axs[0].plot(*skr.data('az', c4d.r2d), label = 'non-ideal yawing seeker') >>> axs[1].plot(*skr_ideal.data('el', scale = c4d.r2d)) >>> axs[1].plot(*skr.data('el', scale = c4d.r2d))
The rotation of the seeker with the target direction keeps the azimuth angle limited, such that non-rotating seekers with limited FOV (field of view) would have lost the target.
Operation Time
By default, the seeker returns measurments for each call to
measure
. However, setting the parameter dt to a positive number makes measure return None for any t < last_t + dt, where t is the current measure time, last_t is the last measurement time, and dt is the seeker time-constant:>>> np.random.seed(770) >>> tgt = c4d.datapoint(x = 100, y = 100) >>> skr = c4d.sensors.seeker(dt = 0.01) >>> for t in np.arange(0, .025, .005): ... print(f'{t}: {skr.measure(tgt, t = t)}') 0.0: (0.73..., 0.007...) 0.005: (None, None) 0.01: (0.73..., 0.0006...) 0.015: (None, None) 0.02: (0.71..., 0.006...)
Random Distribution
The distribution of normally generated random variables across mutliple seeker instances is shown for biases of two groups of seekers:
One group generated with a default bias_std of 0.1°.
The second group with bias_std of 0.5°.
>>> from c4dynamics.sensors import seeker >>> seekers_type_A = [] >>> seekers_type_B = [] >>> B_std = 0.5 * c4d.d2r >>> for i in range(1000): ... seekers_type_A.append(seeker().bias * c4d.r2d) ... seekers_type_B.append(seeker(bias_std = B_std).bias * c4d.r2d)
The histogram highlights the broadening of the distribution as the standard deviation increases:
>>> ax = plt.subplot() >>> ax.hist(seekers_type_A, 30, label = 'Type A') >>> ax.hist(seekers_type_B, 30, label = 'Type B')
Properties
Gets and sets the object's bias.
Gets and sets the object's scale_factor.
Methods
seeker.measure
(target[, t, store])Measures azimuth and elevation between the seeker and a target.