Building an automatic plant waterer (3/?): capacitive sensor try 2

Finally some progress!

  • Part 1: resistive sensing
  • Part 2: finding resistive sensing is bad and capacitive sensing is hard
  • Part 3: another crack at a capacitive sensor

Day V (weekend 5, has it really been going that long?)

OK, so I’m not really happy about the enameled wire design. It feels like the insulation is a bit fragile and I don’t really feel I know what’s going on well enough to rely on it. So, I’ll so something much better: smear some 5 minute epoxy over some stripboard and hit it with a heat gun…

 

 

 

 

Yes, this is not at all dubious. Well turns out it is. Who knew? Nonetheless it works decently well, though the insulation only goes a few cm up and I think it’s hovering at around 1GΩ. The capacitances are:

  •  Out: 13pF
  • Dryish soil: 20pF
  • Quite wet soil: 30pF
  • Very wet soil: 44pF

Substantially less sensitive than the previous one, but proves the principle. If I can actually sense that level of capacitance using the Arduino, then I can get a nice double sided one made with the good quality thin and robust coating you get on PCBs.

On to the Arduino!

So the Arduino environment doesn’t natively support the comparator. Fortunately its not hard. Just fiddly. As in spending 2 hours on a really silly mistake…

It’s fairly straightforward given the datasheet, specifically section 27 AC (analog comparator). The easiest way to get started is:

  • Not using the multiplexer, which means the negative input is AIN1
  • Using the bandgap (1.1V reference) for the positive input
  • Polling by reading ACSR.ACO (analog comparator status register.analog comparator output)
  • No low power stuff (it’s a high power application anyway)

It’s surprisingly easy. The Atmega328p has nice defaults for prototyping (everything starts on), there are not too many registers to swizzle up the pins and its happy to have two functions (GPIO and comparator) on one pin at the same time. The bit that I got hung up on for hours is that AIN1 is the Atmega328-P’s AIN1 pin, not the Arduino’s AIN1 pin. AIN1/PD7 (in Atmel-speak) is actually digital pin 7 in Arduino speak. N00bish mistake but really easy to make.

The basic code to control an LED looks like this:

const int led = 13;

void setup() {
  pinMode(led, OUTPUT);     

  // Set ACME bit to 0 to disable the multiplexer
  // This also sets some ADC related flags
  ADCSRB  = 0; 

  // Set the positive input to the bandgap reference.
  // This also sets disable to off, interrupts to off
  // and a bunch of other stuff to off.
  ACSR = bit(ACBG);
}

void loop() {

    bool result = ACSR & bit(ACO);
    digitalWrite(led, result);
}

It works. Yay. Only slightly dampened by the wild goose chase over pin numbers.

HONK!

A wild goose, for illustrative purposes. (CC by SA https://commons.wikimedia.org/wiki/File:Greylag_goose_(Anser_anser)_head.jpg)

Day 6 (Weekend 6)

Well, this is interesting. The epoxy based probe (above) is now reading a steady 10M even in dry soil. Looks like that isn’t a long term solution. The best wire based one is now faring a lot better. I’m resigned to either having a custom board made with proper soldermask or using conformal coating.

So back to the circuit. Now because for some reason I’m intent on absolutely minimizing cost, an important part is minimizing the pin usage. So the circuit is simply this:

20190317_121550

All the clever bits of the circuit are provided by the microcontroller.

The capacitor charges from the positive rail through a 1M resistor.  Internally, I’ve got the comparator connected to 1.1V. The equation for the voltage is:
V = V_0(1 - e^{-\frac{t}{RC}}).
Rearranging gives:
t = -\ln (1-\frac{V}{V_0})RC
Substituting in the test capacitor (47pF), the 1M resistor, the 5V supply and the 1.1V reference gives a rather marginal 12μs. For now to make life easier, I’m going to use this circuit:

20190317_121721

Making a 1.65V rail with resistors means that the 1.1V threshold is on a much flatter part of the charging curve, increasing the time a lot.

Making a potential divider off the 3.3V rail gives 1.65V for the supply, and a much more generous 50μs. Passives are cheap, and I can greatly extend the charging time pretty easily by dividing down the supply. But we need some code to drive it.  The code implements a basic relaxation oscillator: let the capacitor charge then when the voltage exceeds the threshold, short out the capacitor to restart the cycle, then let the capacitor charge…

void setup() {
  // Set ACME bit to 0 to disable the multiplexer
  // This also sets some ADC related flags
  ADCSRB  = 0; 

  // Set the positive input to the bandgap reference.
  // This also sets disable to off, interrupts to off
  // and a bunch of other stuff to off.
  ACSR = bit(ACBG);

  // set PD7 to either hi-Z or low (depending on DDR)
  PORTD &= ~bit(7);
}

void loop(){
    DDRD &= ~bit(7); // Set pin 7 to hi-z

    //Loop until the AC outputs 0 (i.e. when the capacitor
    //exceeds 1.1V)
    while(ACSR & bit(ACO)){}

    DDRD |= bit(7); // Pin7 to low, emptying the cap

    delayMicroseconds(50);
}

And here’s what the voltage looks like in operation:

 

 

In order to get nice graphs I either had to touch a grounded thing or switch off my fluorescent desk lamps since they seem to spew noise all over the place.

It’s not really very useful like this since all it’s doing is displaying a nice graph. The scope also disturbs the signal since it’s got a non-trivial capacitance and resistance. To do some further analysis, I wrote the following code:

void setup() {
  ADCSRB  = 0;
  ACSR = bit(ACBG);
  Serial.begin(9600);
}

float o=0, o2=0; //IIR filter state for count and count squared
int i=0;

void loop(){
    PORTD &= ~bit(7);
    DDRD |= bit(7);
    DDRD &= ~bit(7);

    uint16_t count=0;
    while(ACSR & bit(ACO)){
      count++;
    }
    DDRD |= bit(7);

    o = 0.999*o + 0.001 * count;
    o2 = 0.999*o2 + 0.001 * count * count;

    if(i++%256 == 0){
      Serial.print(o);
      Serial.print(" ");
      Serial.println(sqrt(o2 - o*o));
    }
}

This code counts during the charging part of the cycle, then does a moving average on both the count and count squared using a simple IIR filter and prints the running mean and standard deviation of the counts to the serial port. I happen to be a big fan of IIR filters, I think they’re fun, interesting and efficient. Even the simplest one is much better than a naïve moving average. The trick of filtering both the value and value squared is one I’ve used many times for getting a running standard deviation in addition to mean.

By default I get counts of about :

  • No touch: 88.2 (σ=4.1)
  • Light touch: 110 (σ=5.5)
  • No touch with scope: 139

I can even spot proximity, so its really pretty sensitive, and you can see how much the scope loads it down by. It also turns out I was really pessimistic earlier. Reverting back to the simpler circuit, the numbers I get are:

  • No touch: 19.9 (σ=1.3)
  • Light touch: 26 (σ=1.7)

I can still spot proximity, but only with the aid of the filter: it’s about a count of 0.2 or less. So for fun, I modified the code to turn on the LED when the count exceeds 20.2, and you can see just how sensitive it is:

 

I think now that having faster charging helps: while there’s more quantization noise in the signal, the comparator changes state on a steeper part of the curve which means that electrical noise has less effect on the time.

Today’s conclusions

  1. The capacitive sensor is really good, cheap and easy to make. I’m going to have to use that for other things too
  2. It’s definitely the way forward for this project
  3. Something worked easily!

 

Building an automatic plant waterer (2/?): resistive sensing

This was harder than I expected.

  • Part 1: resistive sensing
  • Part 2: finding resistive sensing is bad and capacitive sensing is hard
  • Part 3: another crack at a capacitive sensor

 

Day III (weekend 3)

Not really much time this weekend. I pulled out the electrode and (it had been sitting there unpowered) and saw this:

Snapchat-910764504

Copper salts deposited on the electrodes. It was really hard to get my phone to reproduce the colour.

It looks like corrosion has already started. There’s not much but it’s only been there a few weeks and has probably spent a few of hours powered by now. With my current plan (maybe waking up every 30 minutes), the amount of time ‘on’ would be about 16 minutes per day (10 seconds of higher current charge in each direction). That’s only a week or two before it reaches these levels of corrosion. So, that I think precludes 10 second measurements, or at least 10 seconds of direct charging without a resistor in the way.

Day IV (Weekend 4)

Wow really not getting as much time on this as I’d like. So, what about capacitive measurements? Having insulated electrodes should preclude any corrosion problems and I’ll bet that using soil as dielectric will increase the capacitance as the water content increases. First, an initial experiment:

capacitor-1

The sensor is a bit of electrical tape over some strip board.

This indicates we’re into the realms of possibility, though the number of picofarads is still small enough to be pretty irritating. It’s also a pretty useless soil sensor: soil/water get through the holes behind and the result is a measurable resistance between the electrodes (about 9M or so). And the insulator is pretty thick which is going to make the capacitance low and reduce the sensitivity.

I do have some enameled wire in various grades. The finest (0.14mm) has a very thin coating. I made a couple of different sensors using the wire, mostly wrapping it around lots to get a decent surface area:

Snapchat-605221828

A couple of attempts at a capacitive sensor. Left, interleaved wires, right the two plates are well separated.

One slight problem: it doesn’t measure open circuit; there’s about 15M between the two sides, rising as it dries off. The flat one measures about 28pF when in the air, and about 280 in damp soil, rising to about 2nF when just watered. Unfortunately I don’t know how much the resistance is affecting this, so I’m going to have to try again. I’m going to try the next thicker grade of wire I have (0.23mm) and incidentally it has a different color coating.

Having a nice large spacing between the two plates seemed to work well in that it was easy to clean and reset back to the dry state. So, on to version 2:

Snapchat-2045180743

Version two of the sensor with thicker wire and nicely soldered joints. 40 turns of wire in each section.

As a reminiscent aside, I remember soldering lacquered wire back in the olden days with my fixed temperature iron. I could never settle on fine sandpaper versus a flame to remove it. Those days I do not miss, now I just crank up the iron temperature.

Anyway this seems to be going better: the resistance is greater than 2G. I think I might have mentioned it before but my multimeter skipped leg day. It can measure up to 2GOhm, but only down to 20mA. Weird. On to the capacitance measurements. So they are:

  • BOGUS! that’s what they are, bogus!

Well shoot. It seemed to be working great, but after a bit of use the resistance is back to being about 10M. That’s disappointing. OK, try3! I’m going to wrap the wires longitudinally so that they never even cross:

WLPjJ2i

I forgot to take a picture of it! Look at the “fix” below with hot-melt to get the idea.

Anyway the wires are always separated by about 4mm. So the measurements are:

  • First use: 12pF
  • Finger lightly on one side: 28pF
  • Fingers pressed on both sides: 100pF
  • Damp soil: 185pf, 233pF, 202pF, 190pF
  • Slightly compacted damp soil: 323pF,
  • Same place during watering: 680pF
  • Resistance: 12M

orly

OK well, this is getting suspicious. The 20M range is maxed out. But the 2G range reads low (there’s nothing in between, that’s only a few % difference). Now the capacitance reads in the nF range as well. Hitting it with a heat gun seems to reset everything.

So putting a blob of water on in the middle doesn’t do anything. Butting a blob of water on the end where the wires are bent round quickly drops the resistance back town to 10M. I think the act of wrapping the wire breaks the insulation very slightly. Well, that’s irritating. Let’s see:

Fixed with hot melt

If you use hot melt and don’t have a reflow style hot air gun, you’re really missing out.

OK, so the new measurements:

  • Damp soil: 250pF, 360pF
  • During watering: 1nF
  • After watering: 600pF and dropping
  • Resistance: 𝟚𝟘𝕄

rage

Apparently there is something hot-melt can’t fix. Observing more, the resistance is climbing very slowly, up to 26 now, now 40. Well, it might not matter. If I keep my measurement resistors well under the 20M range (say 200k), then the small error incurred due to leakage won’t matter. Still, I’d prefer to have it work properly.

So where are we? The capacitance sensor definitely works after a fashion, but we need to measure it. It bottoms out at 30pF, and is well into useful readings at about 300pF or so. I think I could get away with a 1M resistor safely. For an RC circuit, that would give a time constant of about 30us, which is small, but that’s at the 0 end of the range. It’s just about measurable on an Attiny85 with the 16MHz clock.

Additionally, the Attiny85 has a built in comparator. So, my current mental design has a relaxation oscillator in mind: charge up the capacitor through a 1M resistor, then discharge through a GPIO pin once the voltage crosses a threshold.

Sounds like a plan.

Building an automatic plant waterer (1/?): resistive sensing

This turned into a saga. Naturally this is Part 1.

  • Part 1: resistive sensing
  • Part 2: finding resistive sensing is bad and capacitive sensing is hard
  • Part 3: another crack at a capacitive sensor

This is a blow-by-blow account rather than a neat design story, so you get to see the experiments I did to prove/disprove ideas and the dead ends that I went down. All the dead ends. So many…

Day 1

I bought a pitcher plant. Unfortunately it turns out that I am less good at remembering to water it than I fooled myself into believing. So, instead of watering it, I’m in my lab building a device to water it for me. I’m also engaging in the entertaining game of minimizing the BoM on the electronics side as much as possible. My current thought is an old 12V supply, an attiny of some sort, a MOSFET, a soil probe and a peristaltic pump.

Through the magic of the internet I have some supplies:

A peristaltic pump (12V), some T-adapters which were supposed just be couplers but I must have ordered the wrong ones and some slightly odd sized tube because the tube I ordered did not arrive.

The pumping hardware

I ordered a series of tubes

And because I have to take everything apart, here’s the pump:

There are no gears. There are only 12 parts (motor, 2 screws, mount, case top, case bottom, 3 rollers, roller holder and tube). Compare this to the older design of cheapie peristaltic pump:

snapchat-57267770

Old cheap peristaltic pump.

The old design is more complex. It also noisier and doesn’t run as smoothly, it’s harder to put the tube in and it has a real tendency to split tubes if they’re not precisely the right size. I like the new design.

Exploration

The next bit is to figure out the moisture sensor. I’m going to measure the resistance between two conductors (on stripboard). Firstly, splitting a chunk off by bending it over in a vice is less reliable than I thought it might be…

Unreliably split stripboard.

Yuck.

Pre-scoring it heavily, then filing after was tedious but ultimately gave a much cleaner cut:

snapchat-786624393

Now to try it on a victim plant. It’s a fuchsia which I’ve propagated from cuttings and I’ve been keeping indoors. I’ve not watered it in a while, so it’s very dry. I made a bunch of measurements with a spacing zero and 1 columns between the electrodes.

Interestingly it didn’t make all that much difference. Either way the resistance was between about 0.8 and 3.3MΩ. Now for the other end of the scale:

snapchat-2013458458

Of course I didn’t wait half an hour. I waited more like an hour. Either way the moisture looks like it’s thoroughly propagated around. Time to measure. First a spacing of one row:

snapchat-1311116043

Interestingly, the resistance takes ages to settle, on the order of minutes where it keeps changing. The direction depends on the measurement range, so I suspect there’s some sort of electrochemical effect going on. Brief pulsed measurements (as brief as I can get) on the 200K range give about 30K resistance, but it rapidly climbs. On the 200k range long term it gives, well,  it’s up to118K and climbing very slowly. On the 2M range long term it gives about 62K resistance.

Now I find it’s a dodgy battery

And guess what! It’s a cell apparently (my trusty old multimeter has a 2GΩ range but nothing below 20mA. I think it skipped leg day):

Interestingly, measurements “shortly” after shorting it (hee hee) are also around 30k. Maybe less. There’s actually something interesting going on here, and it’s too fast really to see on one of these multimeters. Plus I don’t know what their characteristics are in general. So, I’ve set up a 5V supply, a 22k resistor and the moisture sensor, and I’ve put a scope across the moisture sensor. I start by shorting across the plant, then releasing the short. And this is what it looks like on both short and long timescales:

The results are… interesting. I suspect electrolysis is occurring.  I’m going to have to try feeding it with AC to see what the results look like. Everything is always more complicated than I expect! And here’s the voltage recovery after stopping shorting it:

scr04

Trying to measure it

OK, so to the Arduino! I’m going to use one to generate AC. I use two GPIO pins as a very tiny H-bridge to generate square wave AC which is 5V pk-pk. The setup is the same before, I’ve got the sensor in series with a 22kΩ resistor. I’m measuring the voltage across the sensor. For much of the rest of this post, the measurements are going to be done in the same way, with the results shown on a scope.

For interest I’m going to always be showing the voltage in yellow and the  absolute value of the voltage in red. All things being equal, you’d expect it to be symmetric, so both halves of the trace will look the same. Hey, here’s a question: do you think it’ll be nice and simple?

john-cleese-no

So, here’s the first measurement…

scr06

Look how asymmetric the measurements are: despite the voltage reversing every cycle, all the yellow measurements are negative!

but after a while it looked like this:

scr07

Very symmetric measurements. The y axis has been doubled.

OK so what is going on here?

Day II

You know I suspect now that I was making measurements with the same polarity every single time and I made a very crude rechargeable battery. During the AC measurements it eventually discharged which is why it went from biased to unbiased.

OK, so what about a 4 point measurement? That should eliminate effects on the driven electrodes on the other hand suddenly the complexity will have spiraled rather high, from essentially microcontroller and MOSFET to a whole analogue front end. Unless I can essentially do two 3 point measurements and subtract them. Then it’s just wires…

But first the rechargeable battery hypothesis. I’m going to apply 5V for 30 seconds at whatever current it will take across the electrodes. Then I’ll measure the open circuit voltage and the short circuit current. Also, of course take those measurements before with cleaned electrodes in an undisturbed location. Before, we get 8mv and 0.3uA. I guess the electrodes weren’t perfectly clean or the soil is not perfectly isotropic…

After it’s about 0.5V rapidly decaying to about 0.3 then 0.2, but delivering at 0.2V about 15uA rapidly decaying, slowing down at about 4uA, but continuing to decay. To investigate further, I’m going to use a square wave again, but with a more interesting pattern. I’m going to drive through the 22k resistor, then discharge through the 22k resistor, then the same but with the opposite polarity:

scr08

Charge, discharge, reverse charge, discharge. Also measured with averaging for a cleaner signal. It’s already lost a bit of symmetry.

You can see that after applying a voltage, some residual charge remains. Clearly though 100ms isn’t anything like enough to reach any kind of steady state. So, here’s a longer timescale:

scr10

10 second pulse, discharge, reverse pulse, discharge. Moderately symmetric this time.

That’s looking somewhat better. Most of them seem to have reached steady state after about 10s.

Day III (weekend 2)

OK, so the soil is drier than it was. That means the resistance will have gone up and so I’d expect a higher voltage across the soil than the last time I did some measurements.I’m going to do very long, long and medium length measurements (100s, 10s and 1s). Mostly I picked that as my scope maxes out at 50s per division. Here’s how they look. Also wow, 50s per division takes aaaagessss.

hzcat

well, they are looking oddly asymmetric (again). I didn’t leave any time between the measurements.  It looks settled after 20 seconds.

I wonder though, can I speed this up? At the moment, the battery is charging through a 22k resistor. Perhaps what I could do is put another pin in parallel with the resistor, so I can charge directly, then measure with the resistor. Time to add another pin and some more code…

The cycle is going to be charge directly, then measure using the 22k resistor, then discharge directly. Then repeat the cycle but in reverse. The first result is this:scr18

That looks pretty promising. Those reads look pretty stable. But just to be sure, I’m going to go for some longer reads to see how they look. By the way the code for this is:


void setup() {
  pinMode(4, INPUT); // This connects to the top of the moisture sensor.
  pinMode(2, OUTPUT); //This connects to the top of the 22k resistor
  pinMode(3, OUTPUT); // This connects to the bottom of the moisture sensor
  //The bottom of the 22K resistor and the top of the moisture sensor
  //are connected to form a potential divider
}

// the loop routine runs over and over again forever:
void loop() {

  static const int32_t D1 = 1000;
  static const int32_t D2 = 1000;

  //Fast charge
  pinMode(4, OUTPUT);
  digitalWrite(4, HIGH);
  digitalWrite(2, HIGH);
  digitalWrite(3, LOW);
  delay(D1);

  //Slow charge/ measure
  pinMode(4, INPUT);
  delay(D2);

  //Discharge
  pinMode(4, OUTPUT);
  digitalWrite(4, LOW);
  digitalWrite(2, LOW);
  digitalWrite(3, LOW);
  delay(D1);

  //Fast charge
  pinMode(4, OUTPUT);
  digitalWrite(4, LOW);
  digitalWrite(2, LOW);
  digitalWrite(3, HIGH);
  delay(D1);

  //Slow charge/ measure
  pinMode(4, INPUT) ;
  delay(D2);

  //Discharge
  pinMode(4, OUTPUT);
  digitalWrite(4, LOW);
  digitalWrite(2, LOW);
  digitalWrite(3, LOW);
  delay(D1);

Note how I can change the fast charging versus the measuring. So using that I’m going to keep the fast charge at 1s and extend the measuring to 10s.

scr19

Uhmmmm what? I’m getting seriously confused here. It looks like the cell isn’t fully charged after the initial 1 second spike. And it looks like one direction holds more charge than the other (one flattens off, the other does not). I’m going to try bumping everything up to 10s.scr20

If anything that seems worse than the 1s measurements.

Conclusions so far

  • Water does indeed reduce the resistance of the soil.
  • Weird electrochemical effects are happening
  • Longer measurements are not definitively better than shorter ones
  • AC seems to be necessary to stop really unpleasant memory effects
  • Shorter measurements might be better, causing less corrosion and electrolysis of the electrodes.
  • It’s probably worth trying a higher resistor to capture more of the useful range.

Moving on

So, I decided to try watering it just to see what happens. Here’s what the measurement plots look like:hzcat2

I don’t remotely understand what’s going on. The cycle times are 10x apart and yet the curves look really really similar. Either way though it looks like adding water makes it vary more over time. Bleh. Or maybe it makes the charging happen faster? I’m now really quite unsure what’s going on. In fact look at this one:

scr23

It decays down as usual after the first quick charge, but then the line slopes up slightly. It’s almost like it continues to charge.

The plan

Either way though it looks like the scheme will work.  The plan is to wake up every half an hour or so, measure the resistivity and dispense some water if it’s too high. It’s probably worth taking long measurements so they can be read on a multimeter, so the level can be calibrated easily. 10s seems decent for that.

Oh yeah! I completely forgot about doing 4 point measurements. I should totally do that. Here’s how:

Snapchat-546775062

4 point measurement electrode. The voltage is applied to the outer two and measured on the inner two.

This should be good. Yes:

scr24

giphy

Yeah so I have even less idea what’s going on there. Time to abandon THAT line of inquiry.

The conclusion is that the resistive sensor is probably workable, and with nice simple 2 point measurements, which is nice.

(on to Part 2)

More constexpr in C++20

The C++ Kona meeting starts tomorrow or if you prefer on February the 18th, 2019, since you’re probably not reading this on the day I’m writing it. Apart from looking forward to C++20 in general, it matters to me because I’ve got two closely related papers under consideration.  TL;DR:

things

Except since they’re C++ committee papers, it’s actually more like:

things3

Since the C++ committee doesn’t accept memes as proposals, what we actually submitted is:

  1. P0533R4, a proposal to add constexpr to the elementary cmath functions. Elementary is in this case as on the same level as +,-,* and /, which we defined formally as closed over the rationals.

    Floating point numbers aren’t rationals of course, but arithmetic operations have the property that if you apply them to a rational number you get a rational number (unlike, say, sqrt) and importantly, there are no domain errors when fed with a finite number. Our property seemed to be the simplest one that fitted. Looking at the other functions, we found things like floor, ceil also fitted neatly.

    We though this paper would be straightforward since those functions really aren’t fundamentally different from arithmetic, so  the same rules should apply.

  2. P1383R0 a proposal to constexpr most of the rest of cmath. The LEWG pretty much liked P0533 and asked if we could submit a further paper to add constexpr to the non elementary functions.

    This paper adds most of the rest (sin, cos, exp, log, lgamma, etc). After some useful discussion, we excluded the cmath special functions, on the grounds that there are very few people in the world who know how to implement them so having them in could be a serious burden on compiler vendors.

If they get in then this post will finally be legal C++.

Neutrogena light mask part 4: annoying resets

This post won’t make much sense without…
Part 1
Part 2
Part 3

A very small update:

Setting it to 99 lives doesn’t work that well. It reliably seems to get to 84, then when that’s done it resets to zero. It’s definitely not a glitch, it’s happened twice now. I haven’t discovered any other points where it resets yet. Perhaps it’s a little logic bomb but in there to stop people doing precisely this?

I’ve reset it to 63 this time to see where it gets to. I should probably actually run it and see, but the answer will come out eventually this way. And it’d be really annoying to run it 100 times in a row.

Linear 3D triangulation

I came across this 3D linear triangular method in TheiaSFM:

bool TriangulateNView(const std::vector<Matrix3x4d>& poses,
                      const std::vector<Vector2d>& points,
Vector4d* triangulated_point)
  CHECK_EQ(poses.size(), points.size());

  Matrix4d design_matrix = Matrix4d::Zero();
  for (int i = 0; i < points.size(); i++) {
    const Vector3d norm_point = points[i].homogeneous().normalized();
    const Eigen::Matrix cost_term =
        poses[i].matrix() -
        norm_point * norm_point.transpose() * poses[i].matrix();
    design_matrix = design_matrix + cost_term.transpose() * cost_term;
  }
  Eigen::SelfAdjointEigenSolver eigen_solver(design_matrix);
  *triangulated_point = eigen_solver.eigenvectors().col(0);
  return eigen_solver.info() == Eigen::Success;
}

I was aware of the DLT (direct linear transform), but it didn't look like any formulation I've seen before. It's actually pretty neat. Let's say you're trying to find an unknown homogeneous point in 3D, \mathbf{X} = [X, Y, Z, 1]. What we have is N poses, P, represented as 3\times 4 matrices and the corresponding 2D coordinates represented as homogeneous points in \mathbb R^3. The 2D points are written as \mathbf{x} = [ x, y, 1].

Since we're triangulating the 3D point, and we have homogeneous coordinate (i.e. \alpha \mathbf{x} \equiv \mathbf{x}) then for all i we should have:
\alpha_i \mathbf{x}_i \approx P_i \mathbf X
given an scale factor \alpha.

Now let's pick apart the code above. Let's call design_matrix D and cost_term C. On line 12, we have:
\displaystyle D = \sum_{i=1}^{N} C_i^\top C_i
And line 15 we’re finding the eigenvector corresponding to the smallest eigenvalue of D (SelfAdjointSolver produces them in a sorted order), i.e.
\mathbf{X} \approx \displaystyle \underset{\mathbf{v}, |\mathbf{v}|=1}{\text{argmin}}\ \mathbf{v}^\top D \mathbf{v}

We can rewrite D = \mathbf{C}^\top\mathbf{C} where:
\mathbf{C} = \left[ \begin{matrix} C_1\\ C_2\\ \vdots\\ C_N\\ \end{matrix}\right], which substituting in above gives:
\mathbf{X} \approx \displaystyle \underset{\mathbf{v}, |\mathbf{v}|=1}{\text{argmin}}\ \|\mathbf{C v}\|_2^2,
which is of course the right singular vector corresponding to the smallest singular value of C. Using eigen decomposition is much more efficient the size is O(1), not O(N), but probably at the penalty of less numerical precision.

Either way we’re trying to find the approximate nullspace of \mathbf{C}, which means finding something that’s roughly in the null space of all the C_is. But why?

On lines 8–11, we have:
C_i = P_i - \mathbf{\hat{x}\hat{x}^\top}P_i,
and we’re claiming \mathbf{X} is about in the null space. Let’s see what happens when we multiply by it:
(P_i - \mathbf{\hat{x}\hat{x}^\top}P_i) \mathbf{X} = P_i \mathbf{X} -\mathbf{\hat{x}\hat{x}^\top}P_i \mathbf{X}\\
Now, substituring in the first equation we have all the way back at the top gives:
\approx \alpha \mathbf{x} - \alpha\mathbf{\hat{x}\hat{x}^\top x} = \alpha \mathbf{x} - \alpha\mathbf{\hat{x} |x|} = \alpha \mathbf{x} - \alpha\mathbf{x} = 0
taa daa!

So there you go! If there is no noise, \mathbf{X} is in the right null space of C_i and so the null space of \mathbf C and of course D. If there is noise, then it’s closest to being in the null space of all of C_i measured in a least squares sense.

Note though that it’s just an algebraic error, not a reprojection error so it will give slightly oddly scaled results which will give more weight to some points than others. It is however a rather elegant solution.

Boom! One day your plug goes boom!

Don’t give up in gloom…

20180507_095613

I was pushing the plug into a socket on an extension lead and it gave a colossal flash and bang. So here’s the weird things:

  1. What the hell.
  2. No, seriously, what the hell?
  3. The extension lead was fine and appears to still work.
  4. The wiring to the wall socket was checked a couple of years ago when the electrician replaced my fuse boxes (including the wooden fusebox with asbestos lined fuse folders!) with a modern consumer unit.
  5. For those of you not familiar with British foot stabbersplugs the big pin in the front with the chunk blown out of it is the Earth pin. The plug as you 20180507_101513can see is just a simple  figure-8 adapter (Apple used to be really obnoxious and made the nonstandard, but they’ve stopped doing that now). Naturally, being a 2 pin connector, it’s not grounded so that earth pin SHOULD NOT BE CONNECTED TO ANYTHING!
  6. The pin on the upper left of the main picture with the really big chunk blown out of it and the black stuff all over it is the neutral.
  7. So I say again: WHAT THE HELL?

The event tripped the breaker but not the RCD (and not the 13A fuse in the extension lead either). I guess it’s a race to see which goes first…?

If you look closely, the live pin is not entirely unscathed. There’s definitely a little arc damage on it, though I don’t know if that was from the event or just regular use. You see on the live and neutral, there’s little bits of arc damage on/near the top. This is where the pins contact, so that may be from regular use.

Which made me think: the arc happened when the plug was already substantially inserted (you can see the damage is well down from the top) which means that the wipers were making good contact.

So, in conclusion:29sdew