Sunday, June 4, 2017

V-plotter math: coordinate transformation with rotation compensation


The most important thing to deal with in v-plotter software is the coordinate transformation. Descartes coordinates must be converted to the coordinate system of the plotter. In the most simple case, when the strings meet at one point of the carriage, this is a trivial task, straightforward application of the Pythagoras theorem.

However, such a simple design comes with a price as the carriage becomes unstable. And this happens when one tries to stabilize it without adjusting the coordinate transformation:



The reason for the distorted image lies in the rotation of the carriage that must be compensated to get the proper drawings:


In the following I explain the method I came up with to compensate the rotation. This method is integrated with my v-plotter step generator, and there is also a prototype Java application available for educational purposes.

Let us suppose that we have a v-plotter and the pen is in the center of the mass of its carriage. The task is to answer, given the \(C(x,y)\) coordinate, one of these three identical questions:

  • the rotation angle \((\gamma)\) of the box
  • the coordinates of the \(A\) and \(B\) points
  • the lengths of the strings.

The idea is that for any given \(C(x,y)\) there must be exactly one \(\gamma\) that nature chooses, the angle where the sum of torques acting on the carriage is zero.

First, for a given \(C(x,y)\) and \(\gamma\), we need to calculate the torques exerted on the carriage by the two strings. The algorithm consist of the following steps:

  1. Given \(C(x,y)\) and the dimensions of the carriage, calculate the \(A\) and \(B\) points (considering that the rotation is zero)
  2. Rotate the carriage around \(C\) by \(\gamma\) to get \(A'\) and \(B'\) 
  3. Calculate the \(\alpha\) and \(\beta\) angles
  4. Calculate the \(T_1\) and \(T_2\) tensions in the strings using the following formulas:

    For explanation, read the details at the end of the post.

    \[
    T_1 = \frac{F_g}{\cos \alpha \tan \beta + \sin \alpha}
    \qquad
    T_2 = T_1 \frac{\cos \alpha}{\cos \beta}
    \]
    where

    \(F_g = mg\), the mass of the carriage multiplied by the gravity constant.
  5. Calculate the force vectors:
    \[
    \vec{F_1} = \frac{\vec{S_1A'}}{\lvert\vec{S_1A'}\rvert}  T_1
    \qquad
    \vec{F_2} = \frac{\vec{S_2B'}}{\lvert\vec{S_2B'}\rvert}  T_2
    \]
  6. Calculate torques. Torque is the cross product of the force vector and the level arm vector:
    \[
    \tau_1 = \vec{A'C} \times \vec{F_1}
    \qquad
    \tau_2 = \vec{B'C} \times \vec{F_2}
    \]

Finally, let's create a function that implements this algorithm and returns the sum of the torques:
\[\tau(x,y)( \gamma) = \tau_1 + \tau_2\]
As we need to find the angle where \(\tau_1 + \tau_2 = 0\), that is the carriage is in rotational equilibrium, the next step is to calculate the root of the \(\tau(x,y)\) function. This function is strictly monotone, thus Newton's iteration can be applied with an initial guess of \(0\) degrees.

A prototype implementation can be found here, and if you are interested in the details of the tension equations, read further (and watch the lecture about the super hot tension problem):

To find the tension in the strings, Newton's second law must be solved:
\[a = \frac{\sum{F}}{m}\]
Or rather these: acceleration is required to be zero and the equation is split, because it is easier to solve the vertical and horizontal projections one by one:
\[0 = \frac{\sum{F_x}}{m}\]
\[0 = \frac{\sum{F_y}}{m}\]
First, the \(x\) direction:
\[0 = \frac{T_{1x}-T_{2x}}{m}\]
that is
\[T_{1x} = T_{2x}\tag{1}\]
The \(y\) direction is only a tiny bit more complicated as the force of gravity is also must be taken into account:
\[0 = \frac{-F_g+T_{1y}+T_{2y}}{m}\]
which is
\[F_g = T_{1y}+T_{2y}\tag{2}\]
The vertical and horizontal projections of \(T_1\) and \(T_2\) are simple like this (at this point the \(\alpha\) and \(\beta\) angles are already calculated):
\[
T_{1x} = T_1 \cos \alpha \tag{3}
\]
\[
T_{2x} = T_2 \cos \beta \tag{4}
\]
\[
T_{1y} = T_1 \sin \alpha \tag{5}
\]
\[
T_{2y} = T_2 \sin \beta \tag{6}
\]
Now substitute \((3)\) and \((4)\) into \((1)\):
\[T_1 \cos \alpha = T_2 \cos \beta\]
From that:
\[T_1 = T_2 \frac{\cos \beta}{\cos \alpha}\tag{7}\]
Combining \((5)\) and  \((7)\), \(T_{1y}\) can be expressed now as:
\[T_{1y} = T_2 \cos \beta \tan \alpha\tag{8}\]
Substitute \((6)\) and \((8)\) into \((2)\):
\[F_g = T_{2}(\cos \beta \tan \alpha + \sin beta)\tag{9}\] 
The final equations can be derived directly from \((7)\) and \((9)\).



Share:

Sunday, March 5, 2017

On the viability of semi-indoor solar energy harvesting

Introduction


This is the log of my recent experiment on the viability of solar powering of an IoT device in a semi-indoor environment (indoor, but bright enough to enable a plant growing, e.g. by a window). I basically wanted to find out what is the absolutely minimum power that can be squeezed out from a tiny 53x30mm, 5V, 30mA solar panel. The measurement took place in the Netherlands between January 10-26, when the amount of sunshine is the lowest in the year.

The hardware


Because I did not have a real load on the solar panel, a just measured the short-circuit current and the no-load voltage as an indication of the generated energy. The current was measured by an INA219 device and was logged by an Intel Edison Arduino Breakout Kit, the voltage was simply measured by one of the analog pins of the Edison (the used software is available at github).

However,  the short-circuit current and the no-load voltage cannot be measured in the same time as voltage drops to nearly zero when the short-circuit current is measured. To overcome this, I used a RFP30N06LE TTL level N-Channel MOSFET to connect and disconnect the INA219 from the circuit.

The Edison board has a 10-bit analog to digital converter, it maps input voltages between 0 and 5 volts into integer values between 0 and 1023, having a resolution of 4.9 mV. The INA219 was used in a div8 mode to have a resolution of 0.1 mA.


The result


During the measurement, the solar panel never gave more than 4V or 0.5mA, and it generated approximately 10 mA all together in this two weeks period.




Conclusion


The result sounds absolutely terrible regarding that the panel could generate 30mA in ideal conditions. However, this is an absolutely worst case scenario, and it still would generate 20 mA a month, 240 mA a year (probably it gives this amount of power during a sunny summer day). In theory, this is certainly enough to charge e.g. a 150 mA LiPo battery in a year, which sounds ridiculous, but can be acceptable for many low power applications.

Reality can be quite different though. The solar panel gave really small amount of power, a way below 1 mA. The question is if it is enough to drive a LiPo charger chip, and how much energy the chip itself uses up... In a next post I try to find a solar harvesting chip with a very low quiescent current to see how much of the generated energy can be actually stored in a battery.


Share:

Sunday, January 8, 2017

Arduino capacitive proximity sensor with a square-wave signal generator

Introduction


In this post I explain a capacitive proximity / touch sensor for Arduino based on a square wave generator rather than charge time measurement as the popular CapSense library. This design results in very stable sensor readings and it also works from a battery. It is compatible with virtually every Arduino board and even with most ATtiny chips.



How it works

 

It uses the very same technique as the beautiful musical instrument, the theremin, only it operates on a much lower frequency (around ~60KHz instead of some MHz) and uses square-wave signals for the sake of easier counting. So let me paraphrase a simple explanation of its working principle from theremin.info:
By using an alternating current of suitable frequency, tones of varying pitch are easily obtainable. A small vertical rod is used as the antenna. When the instrument is in operation, electro-magnetic waves of very weak energy are generated around this rod. These waves are of a definite length and frequency. The approach of a hand, which is an electrical conductor, alters the conditions in the electro-magnetic field surrounding the antenna, changes its capacity and thus affects the frequency of the alternating current generated by the apparatus. In this manner, a kind of invisible touch is produced in the space surrounding the antenna, and, as in a cello, a finger pressing on a string produces a higher pitch as it approaches the bridge, in this case also, the pitch increases as the finger is brought nearer the antenna. 
In summary, stable, ~60KHz square-waves are generated by an RC oscillator. The capacity of an approaching hand causes a slight drop in this frequency what is detected by an Arduino based frequency counting library.

The square-wave generator

 

The sensor uses a Schmitt trigger based square-wave generator; this was the simplest suitable circuit I could find for this purpose, it consists only of three very cheap, commonly available parts: a capacitor, a resistor and a 7414 Schmitt trigger chip. The frequency of the output signal can be calculated by the following formula:  

freq = 1.2 / (R*C)

With some experiments, I found that 60Khz base frequency fits nicely my initial goal of starting to sense around ~15cm. I achieved this by choosing C = 100pF and R = 200KΩ.

To make the oscillator stable in the long term, it may make sense to use a ceramic capacitor with a low temperature drift, a so called NP0 type.

In summary, the part list and a lovely Fritzing diagram for the whole sensor:
  • C1 = 100 pF
  • R   = 200 KΩ
  • IC  = 74HC14 Hex inverting Schmitt trigger 
  • C2 = 100nF (optional, but recommended)

The software


The following code can be downloaded directly from https://github.com/domoszlai/arduino-proximity-sensor

In a perfect world, all one should do is to constantly measure the frequency of the oscillator and compare it to the expected (60KHz) frequency.  At proximity, the frequency drop is small (some hundreds Hz) and more or less linear with the distance. At touch, there is a huge, sudden, non-linear 
frequency drop. With some experimentation one can easily determine these frequencies to get an idea how to interpret an actual frequency measurement.   

However, we do not live in a perfect world:
  • The actual value of a resistor or capacitor is precise only to the extent of some tolerance (so you never get 60Khz, only if you buy high precision parts)
  • The actual value of a capacitor is dependent on the current temperature, voltage, ... (e.g. the frequency is drifting with time) 
  • The actual frequency is influenced by environmental noise (just put the antenna closer to your laptop to verify this)

To get proper, stable result, these problems must be handled. Probably there is no one, ultimate solution for these issues, rather, the solution vary from application to application. For my purposes, I found the following heuristic satisfying:

Instead of using the measured frequency directly, two lines of frequency measurements are maintained through an exponential filter. A baseline, with a huge weight (0.995), and current line with a smaller weight (0.75). Their values are initialized in the setup().

float current_line;
float baseline;

void setup()
{
  // https://github.com/domoszlai/arduino-frequency-counter
  current_line = baseline = count_frequency();
}

void loop()
{
  unsigned long f = count_frequency();

  current_line = current_line * CL_WEIGHT + (1 - CL_WEIGHT) * f;
  baseline = baseline * BL_WEIGHT + (1 - BL_WEIGHT) * f;

  ...
}

The baseline contains the base frequency, the frequency expected when proximity is not sensed. However, the baseline is not static: because of the exponential filter, it very slowly follows the changes of the current frequency measurement. This way, drifting of the base frequency is handled. It has, however, the side effect, that the system slowly adapts to proximity...

The current line also follows the changes of the current frequency measurement, but, because of the smaller weight, in a much faster pace. In this case, the exponential filter is applied only to smooth the frequency measurement.

A relative distance of the proximity can be computed easily now by proportionate the difference of the baseline and the current line to the maximal expected frequency drop:

// distance is reciprocal, bigger value indicates smaller distance
// and its range is 0-255 
int diff = max(0, base_line - current_line);
if(diff < MAX_EXPECTED_FREQ_NOISE) diff = 0;
int distance = diff * 255L / MAX_FREQUENCY_DROP;
distance = min(distance, 255);


Finally, putting everything together:


// https://github.com/domoszlai/arduino-frequency-counter
#include "frequency_counter_TC.h"

//#define DEBUG

#define MAX_FREQUENCY_DROP        500
#define MAX_EXPECTED_FREQ_NOISE   30
#define CL_WEIGHT                 0.75
#define BL_WEIGHT                 0.995

float current_line = 0;
float baseline = 0;

void setup()
{
#ifdef DEBUG
  Serial.begin(9600);
#endif

  current_line = baseline = count_frequency();
}

void loop()
{
  unsigned long f = count_frequency();

  current_line = current_line * CL_WEIGHT + (1 - CL_WEIGHT) * f;
  baseline = baseline * BL_WEIGHT + (1 - BL_WEIGHT) * f;

  // distance is reciprocal, bigger value indicates smaller distance
  // and its range is 0-255 
  int diff = max(0, baseline - current_line);
  if(diff < MAX_EXPECTED_FREQ_NOISE) diff = 0;
  int distance = diff * 255L / MAX_FREQUENCY_DROP;
  distance = min(distance, 255);

#ifdef DEBUG
  Serial.print(distance);
  Serial.print(" ");
  Serial.print(f);
  Serial.print(" ");
  Serial.print(baseline);
  Serial.print(" ");
  Serial.println(current_line);   
#endif  
}

Implementation notes

  • The oscillator is prone to the instability of the input voltage. A standard USB 5V can be quite noisy; in  my case, switching to regulated 3.3V reduced the frequency deviation by 50% the least. A voltage regulator is especially important, if it runs on a battery where voltage may drop as the load increases
  • Avoid breadboards. The parasitic capacitance of an average breadboard messes up the frequency spectrum so much, it is hard see the peaks
  • If ATtiny is used as frequency counter, it is essential to use an at least 8MHz external crystal. For a frequency counter, a stable timebase is essential; for this purpose, the internal oscillator of an ATtiny is just not good enough (this video is gold on the stability of oscillators)
  • If you want to increase sensitivity, try increase the base frequency (and the size of the antenna). In this case, you might want to try an LC oscillator instead of the RC one, as they tend to be more stable
  • It also make sense to decrease sensitivity, especially for a touch sensor. In this case 15-20Khz base frequency must be enough

 





Share:

Wednesday, January 4, 2017

Arduino frequency counter experiments

Introduction 

 

In this post, I would like to summarize my recent experiments with different frequency counting approaches using the Arduino platform. My original goal is to develop a small, capacitive proximity sensor, that is more reliable than the default charge time measurement based one (and just as importantly, works on a battery). The idea is, similarly to a theremin, to use an oscillator to generate square waves and detect small changes in the frequency caused by the proximity of a person. For this, I needed a reliable frequency counter first. My requirements were the following:
  • it should be as precise as possible for at least up to 100KHz
  • it should work on an ATtiny (even on an ATtiny13)
UPDATE: check out the proximity sensor, this library is developed for: http://dlacko.blogspot.com/2017/01/arduino-capacitive-proximity-sensor.html

 

Results


I ended up with a small library that is able to count frequency up to the MHz range on the more complex platforms (e.g. Uno, Mega), and I also got it working on most of the ATtiny ones (I tested it on a ATtiny85), but not on the ATtiny13 (technically it would be possible, but I could not fit the proximity sensor code into the 1K flash memory, so I decided not to waste more time on it). On an ATTiny, the inaccuracy of the internal oscillator may affect the measurement with a constant factor, but this is fine for my application (why would you count frequency with an ATTiny anyway?)

The library uses fixed 100ms gate time (the time period of counting pulses), which introduces some error (the pulse count is multiplied by 10 to get the frequency, thus, the last digit is always 0). Because of this, it is also worth to mention that this library does not work well for very low frequencies.

The frequency counter library is available at https://github.com/domoszlai/arduino-frequency-counter. It implements two different approaches as they both have pros and cons, and a separate one for ATTiny. Implementation details can be found in the source code and on the github page.

Approaches

 

I identified three basic frequency counting methods based on the number of  required hardware timers:

  • No timer

    This is a very naive approach, and it is not even entirely timer less as it implicitly uses Timer0 for counting time. Theses methods are usually based on one of the pulseIn(), millis()micros()functions to measure gate time or pulse width. I found them too imprecise for my purposes.
  • 1 timer
    This method uses one timer for gate time measurement and a pin-change interrupt (PCI) for counting the pulses. It is very generally usable as pin-change interrupts are available for many/most pins, but it may work well for lower frequencies only (it worked perfectly with ~60KHz in my tests). This is the method used by the ATTiny counter as the smallest ones, e.g. ATTiny85, has only one available timer (two but, Timer0 is used by the Arduino core).

    See: frequency_counter_PCI.cpp
  • 2 timers

    I found this the most reliable method that works for up to several MHz. It uses one timer for gate time measurement and a hardware timer/counter (TC) for counting the pulses. However, the hardware counter requires the usage of one specific pin, what can be very impractical in some situations. It must be the T1 pin (usually pin 5) for most boards, but T5 (pin 47) in the case of an Arduino Mega.

    See: frequency_counter_TC.cpp



Share:

Featured Post

V-plotter math: coordinate transformation with rotation compensation

The most important thing to deal with in v-plotter software is the coordinate transformation. Descartes coordinates must be converted ...

Popular Posts