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:

Saturday, December 17, 2016

Breadboard and perfboard diagrams of an MCP73831 based Li-Ion, Li-Polymer battery charger

One of my ongoing projects is a very low-power electronics device that supposedly runs on a single-cell Li-Ion or Li-Polymer battery for a very long time, hopefully many-many months. In my idea, the battery actually could be a rechargeable one, e.g. LIR2032 or LIR2450 button cell, and I might also be able to recharge them during these months from a small solar panel, so ideally it runs "forever" with a single battery.

I looked for charger circuits and found out there are many incredibly cheap single-chip solutions available. For some reason I do not remember any more, I picked the MCP73831 chip, which is a very small, single-cell Li-Ion, Li-Polymer battery charger IC.

To be able to experiment with the chip, I just took the typical application schematics from the specification and created breadboard and perfboard diagrams. In this post I just want to share these diagrams as they maybe useful for others as well.


Please note that this circuit has no low voltage protection. On one hand, it is very practical as you can use it to restore over-discharged batteries. On the other hand, you should be careful with such batteries. Please read this blog post about recharging over-discharged LiPo batteries.


This chip is coming in a SOT23-5 package, so it is actually too small for prototyping; first of all, it must be converted to DIP format using a SOT23 to DIP adapter board. I used a DIP10 one, because that was at home, but there are other, smaller variants (less legs) as well.


The diagrams are slightly modified. First of all, I replaced the 2K resistor with a 10K one, so it has 100mA output instead of 500mA. I also added one more LED indicating incoming power. So, the green LED is always on, the red LED indicates the chargring status.

  • Blinking: no battery / battery fault
  • On: battery is being charged
  • Off: battery is fully charged 

I created Fritzing diagrams, and also a DIY Layour Creator one for the perfboard diagram as I find it nicer than the Fritzing one:

MCP73831 LiPo charger Fritzing diagram

MCP73831 LiPo charger perfboard diagram


And finally, the realization. Two remarks: 1. I added some more connectors to the perfboard to make it more useful 2. A single-sided perfboard would be sufficient, and would look nicer, but I had only the double-sided at home

MCP73831  LiPo charger breadboard

MCP73831  LiPo charger perfboard


Share:

Thursday, November 3, 2016

Markdown rendering issues on Hackage

Just a quick post on some Markdown rendering issues I recently ran into on Hackage. They were very annoying as the markup was properly rendered on github, and I could not spot any obvious problems.

1. First header is rendered as the original markup


You get

          ## synopsis

instead of

    Synopsis


Solution: there is an invisible Unicode byte order mark in the beginning of the file. Seemingly Hackage does not like Unicode.


2. Subsequent lists are merged


You have a markup like this:

* list1_item1
* list1_item2

Something in between

* list2_item1
* list2_item2
 

Something to the end
But you get something like this:

  • list1_item1
  • list1_item2  Something in between
  • list2_item1
  • list2_item2  Something to the end

Solution: you have Windows line endings. Obviously Hackage does not like Windows line endings.


Share:

Wednesday, October 19, 2016

On the approximation of Bezier curves by circular arcs

Introduction

 

Approximating bezier curves by circular arcs, in spite of how useless it sounds regarding modern drawing APIs, has (at least) one raison d'etre. The G-code language used by most CNC machines, and also adopted by most 3D printers, can deal with linear interpolation (lines) and circular interpolation (circular arcs) only. 

I have been working recently on a simple Haskell SVG to G-Code converter, and I realized, that in spite of the simplicity of the algorithm at the end, how confusing the subject is (partly because of some not very well written research paper(s) delivered by google search on the subject). 

The algorithm explained in this post is implemented in C# and can be found at my github repository. The C# code is only for illustrating the algorithm, later on it will be integrated with my Haskell SVG to G-Code project.

The article assumes that you understand basic algebra and geometry, complex numbers, etc... 

Bezier curves

 

If you do not know what a bezier curve is, please contact to your favorite information source first. Here I want to introduce only the notations used in this post. We restrict ourselves to cubic bezier curves (it is not a real limitation, any quadratic bezier curve can be straightforwardly converted to a cubic bezier curve). P1 denotes the start point, P2 the end point, and C1, C2 the control points of the start and end points, respectively.  Because it is important for the algorithm, please remember that the line denoted by P1 and C1 is the tangent at P1, and, similarly, the line denoted by P2 and C2 is the tangent at P2.

Biarcs

 

A biarc is a pair of circular arcs (= two arcs) which have the same tangent at the connection point they meet. We will use one biarc to approximate a bezier segment which has no inflection point. A traditional biarc approximation task has four parameters: a start point, an end point, and the tangents at these points. Using these four parameters, however, is not enough to uniquely identify a biarc, we need one more parameter: this can be e.g. the connection point of the arcs or the tangent at the connection point. 

Before we start

 

This algorithm works on bezier curves without any inflection points. Thus, as a preliminary step, the inflection points must be found and the curve must be split (the parts will be approximated one by one). It is a simple task though. We just have to find the points where the second derivative of the parametric equation of the bezier curve becomes zero. It is a quadratic equation, so that can happen at no more than two points. Obviously, we will be interested in the real (not complex) solutions only and only which are in the [0,1] interval. For the details please contact with the implementation.

The biarc fitting

 

We have a simple cubic bezier curve at this point, and we want to approximate it with a biarc. For that we need one more parameter. Some research suggests[1] that in the case of bezier curves, the connection point of the arcs of the biarc should be the incenter point of the triangle denoted by the points P1, P2, and V, where V is the intersection point of the tangents at P1 and P2. Let's call this incenter point G.

For illustration, see the image at the bottom of the article. Black color is related to the original bezier curve, green color is related to the incenter point, red color is related to the approximation biarc, and yellow color is related to the arc computations as it is explained in the following paragraph.

The next step is to find the two circles on which the arcs lie. We have three clues per circle. Circle 1: its two points P1 and G, and the tangent at P1. Circle 2: its two points G and P2, and the tangent at P2

To be done, we need to find C1 and C2, the center points of these circles.

It is simple. We know the tangent at P1. C1 lies on the line which is perpendicular to this tangent and goes through P1, let's denote it by P1C. If we take the section between P1 and G, its perpendicular bisector (EC1) intersects with P1C at C1. The same method can be used to find C2.

Estimate the error and go recursive


Obviously, as you can also see on the illustration, most of the time the approximation is not close enough. Thus, after approximation, the error must be estimated, and if it is out of the tolerance range the bezier curve must be split, then the two new bezier curves must be approximated recursively until you reach an acceptable deviation. In my implementation I just simply check the distance at a certain number of points along the curve and split if the maximum deviation is not acceptable (the bezier curve is split where the deviation is the maximum as suggested by [2]). This is certainly not fast, but acceptable for my purposes.


Biarc fitting

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