OMK Logo
OMK Title

Chimaera

Firmware Documentation

So we have a lot of sensors and a micro controller. How do we get to our event signals? That is what this page is all about. One component of our event signal for a given magnetic source is its distance from the sensor. We will start right there and firstly look at the relationship of magnetic field and distance.

Distance - magnetic field - relationship

The strength of the magnetic field of a permanent magnet decreases with increasing distance from the sensor. This decrease is non-linear and the equations involved are non-trivial, it depends on the material and form of the permanent magnet. As a general rule, a cubic relationship is assumed. Nowadays, magnetic fields can be approximated with finite-element calculations, this however is overkill and not at all possible in realtime on a microcontroller. To approximate the distance of a given magnet from the sensor based on the sensed magnetic field flux at the latter in realtime it is therefore best to do some non-linear curve fitting for the given pair of sensor and magnet before-hand. In realtime, the fitted distance-function is simply applied to the sensed magnetic field flux.

Instead of distance, we use the concept of vicinity (\(1 - distance\)) because in the end we are interested in how near we are to the sensors and not how far away from them. The following simple function to get normalized vicinity \(y_n\) from the normalized magnetic flux \(B_n\) has been shown to be adequate for the type of sensors and magnets (Neodymium) we use: $$\begin{eqnarray} y_n &=& c_0 \cdot \sqrt[3]{B_n} + c_1 \cdot \sqrt{B_n} + c_2 \cdot B_n \end{eqnarray}$$

fit
Measured and least-squares estimate between magnetic field at and vicinity to the sensor.

The relationship can vary considerably between different types of permanent magnets, the Chimaera therefore can be calibrated on-the-fly to different types of permanent magnets. This is done with a five point least squares fit and leads to an analytical solution with the help of some linear algebra.

$$\begin{eqnarray} B &=& \begin{pmatrix} 0 & 0 & 0 \\ \sqrt[3]{b_1} & \sqrt{b_1} & b_1 \\ \sqrt[3]{b_2} & \sqrt{b_2} & b_2 \\ \sqrt[3]{b_3} & \sqrt{b_3} & b_3 \\ 1 & 1 & 1 \end{pmatrix} \\ y &=& \begin{pmatrix} 0 \\ y_1 \\ y_2 \\ y_3 \\ 1 \end{pmatrix} \\ c &=& \begin{pmatrix} c_0 \\ c_1 \\ c_2 \end{pmatrix} = (B^T \cdot B)^{-1} \cdot B^T \cdot y \end{eqnarray}$$

Sensor calibration

Get familiar first with the hardware design of the Chimaera sensor unit, it will help a lot to understand all the following.

Although we now know the relationship between magnetic flux and vicinity we cannot yet approximate the distance of a magnet from its sensor. From the sensor unit we get a voltage which is linearly related to the magnetic flux the currently active sensor is in. However, the sensors do not all have the exactly same sensitivity and quiescent output, their values scatter over a given range. We have to take care of this firstly.

An ideal linear hall-effect sensor outputs a voltage \(V_s\) dependent on its sensitivity \(S\) and magnetic flux \(B\) relative to its quiescent voltage output \(V_q\). $$\begin{eqnarray} V_s &=& V_q + S \cdot B \end{eqnarray}$$

Both \(V_q\) and \(S\) vary between sensors. To make the voltage output of sensors comparable to each other (which is a prerequisite to interpolate between them later on), we need to calibrate them and apply the calibration data in a normalization step. But before we can do that we also have to consider the amplification of the voltage signal at the sensor unit.

The voltage output \(V_o\) after the amplification is dependent on the sensor voltage \(V_s\), the reference voltage \(V_r\) at the OpAmp, and the amplification factor \(A\) of the OpAmp. $$\begin{eqnarray} A &=& 1 + \frac{RV_1}{R_1} \\ V_o &=& V_r + (V_s - V_r) \cdot A \\ V_o &=& V_r + (V_q - V_r + S \cdot B) \cdot A \end{eqnarray}$$

\(V_o\) is what we measure with the analog-to-digital converters at the microcontroller. This is the only thing we can measure directly. We now have to apply some simple math to get to one value we want in the end: the relative vicinity normalized relative to the whole sensor array.

In the case of no magnetic field, we can measure the amplified quiescent voltage \(V_{oq}\) of each sensor from which we can solve for \(V_q\) and rewrite \(V_o\). $$\begin{eqnarray} B &=& 0 \\ V_s &=& V_q \\ V_{oq} &=& V_r + (V_q - V_r) \cdot A \\ V_q &=& \frac{V_{oq} - V_r}{A} + V_r \\ V_o &=& V_{oq} + A \cdot S \cdot B \end{eqnarray}$$

\(V_o\) we can measure, \(V_{oq}\) we can measure, too. \(B\) we want to know finally. The next therefore is to decipher \(A\) and \(S\). For this we define a new single parameter \(AS = A \cdot S\). With a known \(B\) we could already calculate the latter, but we want to calculate it over both polarization domains concurrently to be more robust. We therefore look at the output voltages for an arbitrary magnetic field strength, once south polarized \(B_1\) and once north polarized \(B_2\). $$\begin{eqnarray} B_2 &=& -B_1 \\ V_{o1} &=& V_{oq} + AS \cdot B_1 \\ V_{o2} &=& V_{oq} + AS \cdot B_2 \\ \frac{V_{o1}}{V_{o2}} &=& \frac{V_{oq} + AS \cdot B_1}{V_{oq} + AS \cdot B_2} \\ V_{o1} \cdot (V_{oq} + AS \cdot B_2) &=& V_{o2} \cdot (V_{oq} + AS \cdot B_1) \\ V_{o1} \cdot V_{oq} + V_{o1} \cdot AS \cdot B_2 &=& V_{o2} \cdot V_{oq} + V_{o2} \cdot AS \cdot B_1 \\ AS \cdot (V_{o1} \cdot B_2 - V_{o2} \cdot B_1) &=& V_{oq} \cdot (V_{o2} - V_{o1}) \\ AS &=& \frac{V_{oq} \cdot (V_{o2} - V_{o1})}{V_{o1} \cdot B_2 - V_{o2} \cdot B_1} \\ AS &=& \frac{V_{oq} \cdot (V_{o2} - V_{o1})}{|B| \cdot (-V_{o1} - V_{o2})} \\ AS &=& \frac{V_{oq} \cdot (V_{o1} - V_{o2})}{|B| \cdot (V_{o1} + V_{o2})} \end{eqnarray}$$

So, with measured \(V_{oq}\) at \(B=0\) and \(V_{o1}, V_{o2}\) at a arbitrary \(|B|\), we get to \(AS\). But how do we know \(|B|\)? It's quite simple, we can numerically approximate it with the inverse of the magnetic-field-distance relationship, because vicinity is something we can easily measure, too. By doing so, we also have to define what the normalized magnetic flux \(B_n\) actually is. $$\begin{eqnarray} y_n &=& c_0 \cdot \sqrt[3]{B_n} + c_1 \cdot \sqrt{B_n} + c_2 \cdot B_n \\ B_n &=& f^{-1}(y_n) \\ B_n &=& \frac{B - B_{min}}{B_{max} - B_{min}} \\ B &=& B_n \cdot (B_{max} - B_{min}) + B_{min} \end{eqnarray}$$

Let us get one step further. In the end we want vicinity to be mapped continuously from \([0-1]\). \(y=0\) will be at a threshold magnetic flux \(B_{min}\), \(y=1\) in turn at the maximal to expect magnetic flux \(B_{max}\). \(B_n\) will be the normalized measured magnetic flux \(B\) between the two extremes and depends on the three constants \(V_{oq}\), \(U\) and \(W\): $$\begin{eqnarray} B &=& \frac{V_o - V_{oq}}{AS} \\ B &=& (V_o - V_{oq}) \cdot AS^{-1} \\ N &=& B_{max} - B_{min} \\ B_n &=& (B - B_{min}) \cdot N^{-1} \\ B_n &=& ((V_o - V_{oq}) \cdot AS^{-1} - B_{min}) \cdot N^{-1} \\ B_n &=& (V_o - V_{oq}) \cdot AS^{-1} \cdot N^{-1} - B_{min} \cdot N^{-1} \\ U &=& AS^{-1} \cdot N^{-1} \\ W &=& B_{min} \cdot N^{-1} \\ B_n &=& (V_o - V_{oq}) \cdot U - W \end{eqnarray}$$

\(V_{oq}\) and \(U\) are different for each sensor (so they are actually two arrays of constants each), \(W\) is the same for the whole sensor array. By calibrating them once, we get from the sensor unit voltage output \(V_o\) directly to the normalized magnetic field strength \(B_n\) with only 3 mathematical operations (efficiency is important as we are on a microcontroller, e.g. we have to cope with limited computation power).

From the normalized magnetic flux \(B_n\) we then get to the vicinity \(y\). As those vicinities are comparable over the whole sensor array, we are now ready to interpolate the position \(x\) of the magnetic source along the sensor array axis.

Blob event handling

With a blob we refer to a recognized magnetic field source (e.g. permanent magnet) that is to be tracked over space and time. Blob event handling can be divided into several stages: decipher polarity, narrowing down the area of interest on the sensor array, actual blob detection, interpolation of blob position, group association and blob tracking.

Magnetic flux polarity

First thing that is to be done is to discriminate between the polarities of the potentially present magnetic field sources. For each sensor value, we therefore subtract the previously calibrated quiescent value, so we get the difference of the present raw sensor value from its quiescent value. The sign of this difference gives us the polarity of the magnetic source we are encountering here, e.g. south and north polarity. The absolute value of the difference gives us the strength of the magnetic field (which is to be normalized with previously calibrated factors in a later step).

Area of interest

After each scan of the whole sensor array, areas of interest are marked on the array. These are regions, where the sensor value exceeds the threshold magnetic flux \(B_{min}\). The rest of the sensors are not of interest for the further steps. We do not apply the distance-magnetic-field relationship over all sensors at this step, as it would be computationally too costly. During calibration, for each sensor we precalculated its raw value \(V_{o,min}\) corresponding to threshold magnetic flux \(B_{min}\). This then can be efficiently checked against for each sensor at each update step to find out whether the threshold has been exceeded or not.

Blob detection

In the next step, we search for peaks in the areas of interest. A peak will correspond to an individual magnetic field source and is simply defined as a sensor with an absolute value which is greater than the values of a given number of adjacent sensors in the same area of interest. We save all found peaks along the sensor array for the blob's positional interpolation in the next step.

Blob interpolation

As always more than one sensor is excited by an individual magnetic source, we can decipher its real position by interpolation around the peak sensor. We therefore fit curves \(y_n(x)\) over a varying number of sensor values around the peak and find the maximum \((X, Y)\) by finding the cross point of the curve's derivative. Only at this step we convert the sensor values into normalized vicinities based on calibration data. For efficiency reasons, we use a precalculated lookup table for the distance-magnetic-field relationship, because the cube root and square root functions would be too costly.

$$\begin{eqnarray} y_n(X) &=& Y \\ \frac{\partial y_n(X)}{\partial x} &=& 0 \end{eqnarray}$$

We can use varying numbers of adjacent sensors to the peak and fit linear, quadratic, cubic or spline functions through the values. The coordinates \((X, Y\)) of the maximum will be the current position of the blob on the sensor array.

$$\begin{eqnarray} y_n(x) &=& A \cdot x + B \\ y_n(x) &=& A \cdot x^2 + B \cdot x + C \\ y_n(x) &=& A \cdot x^3 + B \cdot x^2 + C \cdot x + D \end{eqnarray}$$
interpolation order 0
Vicinities of four adjacent sensors and no fit (order 0), e.g. the highest value is its own fit.
interpolation order 1
Vicinities of four adjacent sensors and a linear fit (order 1) between the two highest values.
interpolation order 2
Vicinities of four adjacent sensors and a quadratic, aka hyperbolic fit (order 2) betwwen the three highest values.
interpolation order 3
Vicinities of four adjacent sensors and a cubic fit with Catmull-Rom splines (order 3) between all four values. Only the spline segment between the highest values is shown.
interpolation order 3
Vicinities of four adjacent sensors and a fit with a Lagrange polynomial (order 3) between all four values.

Group association

For convenience, we have introduced a concept of groups. Each blob has not only a given position on the sensor array, but is also affiliated to a group. A group is defined by a range (\(x_{min}..x_{max}\)) on the sensor array and magnetic field flux polarity (south, north or both). Only blobs with a position in the range of - and a polarity corresponding to a given group will be associated with the latter.

Groups can overlap, e.g. there can be two groups for the same range with differing polarities. A blob can only be part of one group at a time though and will be affiliated to the first matching one.

Blob tracking

Up to now, everything we have done was based on the current state of the sensor array. However, to track blobs over time, there needs to be a means to associate identified blobs in the current iteration to blobs found in previous iterations to decipher whether new blobs have appeared or old ones have disappeared.

To link current blobs to previous blobs they are matched by position on the sensor array. Each new blob gets a unique ID, which will persist over time as long as the blob can be linked to itself in a previous iteration step, until the blob disappears. In the special case when a blob can be linked but has exited its previous group, it will be regarded as a new one, too.

Out of the blob tracking, we get a stream of event signals: When a new blob has appeared, an on event is triggered. When a blob could be linked to a previous iteration step, a set event is triggered to update the blobs position. When a blob has disappeared, an off event is triggered. All event signals will be triggered with complete set of current parameters: position \((X, Y)\), polarity (south or north) and group association.

Summary

So, what the firmware does in the end is: converting a raw array of sampled sensor values into a stream of blob event signals. The event signals now can be translated into a given transport protocol and sent over the network to drive other software or hardware. This is elaborated upon in a dedicated section, the usage scenarios.

WANTED:
Beta Testers

After a high interest at Maker Faire Rome, we are now running a beta-testing campaign to collect more comprehensive feedback of first-hand experiences of our final Chimaera prototype design from interested individuals. Get in contact with us. Now.

Please support free/libre software and hardware designs.

or

donate bitcoins

or

request bank credentials via encrypted mail for SEPA transfers.

Last update - 16 Sep 2017

GitHub Vimeo

Copyright © 2014-2017, Hanspeter Portner, Open Music Kontrollers, cc-by-sa 4.0. Uses libre javascript.