Programmatic focus control and absolute/relative focus position

Started by sl0w0rm, March 30, 2013, 07:35:14 PM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.


@BBA -- Your English is fine. Those documentation generation errors when compiling ML is normal--much of the documentation has been missing in the code for a long time. The SourceTree app makes it very easy to switch between branches but it is also possible to do it on the command line as you mentioned with the "hg update <branch>" command. Here's some Mercurial documentation on that.

@DeafEyeJedi -- It looks like a1ex was trying to debug a problem with the cropmarks and framing moving around but his instructions were similar to what he was having me do with the lens properties. Note the comments from 2016-09-05 on the Focus backend updates - precise focus position and other tweaks pull request.

The way I was able to display something on the screen is by adding a "bmp_printf(FONT_MED, 50, 50, "%x %x", len, len/2);" to src/lens.c like this:

    const struct prop_lv_lens * const lv_lens = (void*) buf;
    lens_info.focal_len     = bswap16( lv_lens->focal_len );
    lens_info.focus_dist    = bswap16( lv_lens->focus_dist );
//    lens_info.focus_pos     = (int16_t) bswap16( lv_lens->focus_pos );

bmp_printf(FONT_MED, 50, 50, "%x %x", len, len/2);
    if (lens_info.focal_len > 1000) // bogus values
        lens_info.focal_len = 0;

@BBA -- Note that in order to skip sections of C code you use "//" instead of "#".

I'm not sure if that's the right way to do it but that's where we left off. (Results circled in red in this image.)


@dfort -- Many thanks for your help.
I have used something like you:

bmp_printf( FONT_MED, 10, 2, "%d   -    %d      ", should_af, AF_DISABLE);

but the message falls on the 1st line and gets wiped out very quickly.
To diagnose camera.shoot(64,false), you need to find out whether the focus argument arrives as AF_DISABLE in lens_take_picture.
Nevertheless, I could be able to see that the 2d argument in lens_take_picture has a value of 1 which corresponds to AF_enable ( AF_DISABLE has a 0 value in lens.h ).
I could use a "dirty trick" to force the value (for the time being it would be sufficient in my tests) but I would loose more important things on why the sources are incorrect.

@dfort & @DeafEyeJedi
dfort's solution is much more efficient than what I suggested to avoid many debug messages.

// is used to "comment out" what follows on the same line in C
# is used to give instructions to the (pre)compiler like #define #ifdef,...
I can also share with you the wiki page for debugging ML:


Quote from: BBA on October 22, 2016, 06:50:28 PM
...the message falls on the 1st line and gets wiped out very quickly...

Try changing your font settings to what I used: FONT_MED, 50, 50,

(That seems to set the position on the LCD where it is displayed.)

Thanks for those other tips!


Before going further in testing, those first test results with EF 24mmf/1.4 L II USM/5Dm3 @f/1.4 near target for short DOF.

5 shots in sequence (1 step of step-size=1 (=7 sub-steps) apart from each other) :
1st shot =7401 = focused (max contrast) on target;
2d shot = 7402 = one step behind;
3d = 7403 = one step to the front (should be focused again on target) ;
4th = 7404 = one step to the front;
5th = 7405 = one step behind (should be focused again on target too)

The log file gives the value of lens.focus_pos before/after moving and shooting : for that lens at least, the counter is incremented and decremented correctly even if the focus distance gets incorrect.
Seems the move from 7403 to 7404 did not move correctly. The error propagates to 7405.
This confirms A1ex thinking.

The following is speculative:
At least with step-size=1 and with this lens, the counter should not allow to close the focus loop. In the lens scans I had made with step-size=1, the counter returned results too good (all steps were 7 sub-steps) to be true.

Below are some results on scanning lenses with different step-sizes.
I wanted to share them but I was waiting till I had more experimental results on the actual motor move.
Take them with a grain of salt:

In sub-steps                         Stepsize=1                       Stepsize=2                        Stepsize=3                           Total range

EF 24mm f/1.4 L                   Constant                           Constant                        Quasi constant
USM (ring)                       7 (630 steps) (1)              30 (147 steps) (2) (4)         From 208 to 207 (2)                    4 413

EF 35mm f/2 IS                    Constant                       Small variation                      Small variation
USM (ring)                       10 (824 steps) (1)             From 42 to 39 (2)               From 286 to 276 (2)                    8 243

EF 50mm f/1.8 II                  Variation                           unavailable                       unavailable                            Problem
(not ring)                            From 2 to 6                                                                                                        with limits (3)

EF 100mm                         (4416 steps) (4)               (396 steps) 

limit = lens.focus( numsteps, stepsize)
- For lenses with ring ultrasonic motors (ring USM), the length of the steps in domain 1 (of step-size=1) seems to be pretty constant.
- This is even true with large values of « numsteps », even though when a move of numsteps>>1 is actually commanded, it is executed one step at a time (one can hear the friction surfaces separation and coming back in contact).
- I wonder whether the domain 1 is not simply done with a « count » of the number of sub-steps. Maybe the move is very/too small to be easily detected ?
- The EF 50mm f/1.8 II doesn't have a ring USM (lens intended to be cheaper): in that case, the length in domain 1 is very changing. Nevertheless, the targeted focusing precision of such a lens could be achieved, even in such a case, if the lens has a higher number of (maybe varying) steps.

- There may be a small variation which seems at first sight to be « random ». See      for the EF 35mm.
- IMHO, there seems to be a trend which is more evident with step-size 3 : the mean step is longer at both ends.
- For the EF 24mm it is more easy to try to go to the exact target position in sub-steps as the step length in domain 2 (30 sub-steps) is not a multiple of the length in domain 1 (7 sub-steps).
- For EF 35mm the length of one step in domain 2 (near 40) is near a multiple of domain 1 (10).
- In theory, the mean error when using domain 1 steps should be a fraction of a step. This can be very good when there are many small steps.
- This raises a question about the usefulness of fiddling with sub-steps in regard to the precision already achievable with one lens in domain 1.

- With the EF 50mm lens I used, the lens.focus command did not return false at the infinity end. My script kept sending move commands (infinite loop).

The EF 100mm macro has a very high number of steps : it takes a very long time to scan as one has to wait enough (around 500ms) at each step for correct data to be available. The total duration is above 30 min and Lv can switch off. The scans were done before the lens_pos counter was available.

Walter Schulz

Thanks for your contribution!

There are 3 lenses matching "100mm macro":
1.) EF 100mm f/2.8 Macro (1990)
2.) EF 100mm f/2.8 Macro USM (2000)
3.) EF 100mm f/2.8L Macro IS USM (2009)


@Walter Schulz
You are right : it is the EF 100mm f/2.8 Macro IS USM.
There are still some errors but I have to leave now. I can add some graphs of the step variation too.
Don't hesitate to ask/criticize as we are in a community.


I would like to have your opinions on the following.

We will have to deal with a possible difference, hopefully kept to a small value, between the counter position (software view) and the actual hardware position.

One aim is to detect any difference as soon as possible.

At first sight, there are two opportunities at both ends to "home" = to resync software and hardware.
To do that, there must be some physical mean (electrical contact or optical detector) that is switched and allows to raise the soft limit error.

So, for this first reason, it would be better to know, as soon as possible, that the hardware end has been reached especially if the software counter does not report it.

But on the other end, is this hardware mean precise enough to be useful ? Can it be trusted ?
I am speculating, but I don't think it is precise enough at the sub-step level (the precision level of the counter).

Even at step-size 1 level, I am not sure because it is not necessary ...If it is only a hardware protection, it doesn't need to be that precise.

What do you think  :-[ ?

With that in mind, should we keep to delay the focus error when the lens doesn't move after 2 retries  :-\  ?


Here I can share with you the results of the moves back and forth of 6 steps (of step-size=2) apart from each other.
The camera is nearer to the target (almost the nearest focus distance).

Next time, I hope I'll be ready for moves from random starting points back to the same target using step-sizes= 3 for approaching, then 2 and 1 for precision.
I have simulated the algo which gives spot on results (error=0).
But the reality will be different for sure !


I think there should be something interesting here.

There seems to be a strong correlation between the position of the changes in the length of the step (expressed in sub-steps) and the position of the changes of the value of the focal distance. I mean they seem to change at the same position when scanning the lens from macro end to infinity end.

The values of the focus distance (as displayed on the liveview screen or given by lens.focus_dist in Lua) come from a data structure: they are read somewhere if I remember correctly. I would not be surprised if the values of the step length could be found there too, or nearby in the same data structure.

Being at a given position, one could then know what is the length (in sub-steps) of the step he can do and be able to calculate where he will get if he chooses to move with a given step-size. Companion files (with those step lengths data for each lens) would be unnecessary.

For those who are not afraid of spreadsheets, I put here under, the LibreOffice sheet I started to study the sub-steps scales of the EF 100mm f/2.8L Macro IS USM : with a little switch on the side of the lens it is possible to choose between a macro scale (30-50cm) a longer scale (50cm to infinity) and a full scale. The sheet « compar 30-50 and 50-infty scales » shows a beautiful correspondance : the scales can be superposed with an offset of 588 sub-steps.

What do you think ?


( just here to post a google drive (viewable online) link of your spreadsheet for those who doesn't have any *Office at disposal )


Thank you for your initiative !

I wonder if what I have written is understandable...(sub-steps, step-size,...).
If you have any question, don't hesitate to ask!

I was asking myself why the step-lengths were changing and how one would have to do to store the values to use them in a Lua script (companion file).
When the step-length is constant on the whole range, it is rather simple...but when it changes from time to time, what data structure should I conceptually set up to store where a new step-length has to be taken into account. With some lenses like the EF 50mm f/1.8 II USM EF 35mm f/2 IS USM there are (little) changes on top of that. What are the real changes and the (possible) random changes (sort of hiccup) ?

The simultaneous variation of the lens.focus_distance and the step-length gives a clue to make a distinction between real changes (coupled with a change in the focus_distance value) and the random ones (when there is no simultaneous change of the focus_distance).
This simplifies  to a limited number of intervals on which the step-length should remain the same.

This simultaneous variation gives better consistance to the step values as such : they acquire sort of real existence : they are specific to a given lens and have been decided when the lens was designed. With the EF 100mm f/2.8L Macro IS USM, the corresponding match between the different scales and the same length of the steps is really astonishing. It is difficult to speak of a coincidence.

There is still a conceptual problem as where the step-length changes, one would have to store two values : the value when going to macro end and the other value when moving to infinity end.

I will have to dig into lens.c and lens.h to try to find in the prop_lv data structure if there is some integer equal to the step-length I have seen.

struct prop_lv_lens
        uint32_t                lens_rotation; // float in little-endian actually
        uint32_t                lens_step; // float in little-endian actually
        uint32_t                off_0x08;
        uint32_t                off_0x0c;
        uint32_t                off_0x10;
        uint32_t                off_0x14;
        uint32_t                off_0x18;
        uint32_t                off_0x1c;
        int16_t                 focus_pos;  /* see lens_info.focus_pos */
        uint16_t                off_0x22;
        uint32_t                off_0x24;
        uint32_t                off_0x28;
        uint16_t                focal_len;      // off_0x2c;
        uint16_t                focus_dist;     // off_0x2e;
        uint32_t                off_0x30;
        uint32_t                off_0x34;
        uint16_t                off_0x38;
} __attribute__((packed));




In my previous post, the mentioned EF50mm F/1.8 II USM should be better replaced by the EF 35mm f/2 IS USM : I already have a graph of the small variations of the step length to show you the problem of what variation should or should not be taken into account:

I think you can be interested by the following picture.
I can share the following picture  :), but take it, as always, with a grain of salt  :( .

It shows the results of scans of the EF24mm f/1.4L II USM lens with step-sizes 1, 2 and 3.
Each point represents the value of the lens.focus_distance in function to the position of the lens achieved with lens.focus() moves of step-size 1 (blue) 2(red) 3(yellow) beginning at macro end.
For red and yellow colors, the points are connected by straight line segments. For the yellow points it is like a linear interpolation. The graph is limited to an interval between positions 2000 to 4000.

What seems interesting (To be checked) to me is the yellow curve which goes from point to point as if it gave the positions for the pictures of a focus stack (with a big grain of salt as IMHO it does not change with the aperture value) . As you used step-size 2 in your optimized HFD script (for which I thank you for sharing : I will have too check how you found the best aperture), maybe you can go one step further ?



I think I understand you (tell me if I am wrong) : with the "lenses equation", it is possible to do the transform from the object to the image field. Here is what I have tried in this direction:

I thought the "infinity end" of the step function seemed "not linear enough" (if I can say that).
I am not sure there is a non linearity in the helicoidal barrel (in cylindrical coordinates, z = k*theta , transforming the rotation of the ring motor (theta) into a linear move (z) ) ?
I don't think so...
At least, it should give a more easy way to handle the infinity end.

I would like to ask you if you have any info on the C data structure above?
I wonder what is called "lens_rotation" and "Lens_step" ?



Great investigative work :-)

Whilst I can see the potential benefits for macro shooters, I wonder that for landscape shooters, using wider lenses, such 'accuracy/precision' will be 'wasted'?

Of course, having said that, I can't wait to access such functionality in Lua  ;)





You put the finger on a good question.
You are a passionate to a certain level like many of us. I have seen an expression somewhere "for the theory of it". I think this is part of the answer.

Give me some more time for a better answer, but you are right : it can not be possible to do much better things than what the lenses have been designed for.

IMHO, from the little I know of the many different lenses, I think it will be possible to interpolate to get better precision for stack focusing or lens positioning, maybe in the infinity end.
I think there is a group on bitbucket to enable more precision via interpolation : this is a good thing.

Step-size 1 is precise : an important thing is to know where to stop.
It is where the position counter will give you something :
- you will not be "limited" by the "step length" of each step of the step function as you can be now... (still to be proved for landscape, as you say).
- you will know if it is better to move the lens one step further, or if it is better to stop where you are : this already reduces the error to a fraction of a step-size 1 step.
The hardware will never allow us to go to the sub-step precision level.

I have tried to figure out on a 45° target with the EF24mm f/1.4 L II USM lens at full aperture as close as possible to reduce the DOF.
The software gives a 0 error but on the hardware side, on the pictures taken of the target, it is very difficult to see.
That's why I turned to a macro lens.
I will share the results here with pictures.


Quote from: BBA on November 16, 2016, 06:22:07 PM
I wonder what is called "lens_rotation" and "Lens_step" ?

I didn't really look into them. On 24/2.8 STM, the first one stays at 0, while the second one changes from -6.3867 (macro) to -6.9453 (infinity) in sync with focus distance (same increments). So, at least on this lens, they don't give any new information.

BTW, just found a bug where the lens could still get stuck at one of the ends, even when giving the opposite focus command.

Seems to work fine now. Test script (the one used to create this figure):

-- Focus position experiment


test_log = logger("FOCUS.LOG")

function printf(s,...)

-- focus to the macro end quickly
while lens.focus(1,3,true) do end

-- focus with tiny increments towards the tele end and back,
-- logging focus position counter and focus distance to a file.
for i,dir in pairs{-1,1} do
    while lens.focus(dir,1,true,100) do
        printf("%d %d %d\n", dir, lens.focus_pos, lens.focus_distance)

Can you check whether this script runs properly (performing a scan in both directions) on different lenses after this fix?

You may post the logs as well, as I'm curious about the hysteresis from the figure linked above.

I'm thinking to figure out somehow the relationship between focus position counter and focus distance from these logs, maybe store it for each lens, and use the focus distance transitions to figure out where we are (the offset for focus_pos, which we don't know at camera startup, and may be lost during manual focus).

We'll have some precision issues if the lens moves fast, as two subsequent reports will differ by a large number of focus_pos increments. Kalman filter may help (e.g. we could combine the info from more transitions and give higher weight to those transitions performed during slow movement).

Finding out the above relationship dynamically (as the user focuses the lens) may also be possible (some sort of machine learning). Need to think about it.


I'm back.
I will do that.

Here are the lenses I can use (some L and some primes/zooms) :
EF 24mm f/1.4 L II USM
EF 35mm f/2 IS USM
EF 100mm f/2.8 L Macro IS USM
EF 50mm f/1.8  II USM
EF 17-40 f/4 L USM
Which ones is it better to choose ? (the step lengths seem more constant with L)
How many times do you think I should repeat the same experiment (to see...) ?

If needed, I can share some results of experiments:
Homing strategies
I have tried some "homing" strategies to each end (go to the mechanical end and sync hardware and software : absolute position = relative position + offset - hw_error ), but as far as I have seen (EF24L step-size=3), the precision was not so good, even for a L lens (I felt it was better not to do that, even less systematically, even when the target is near to one end ; actually, it is not needed in normal camera precision use (AF) but only as a protection ). My last thinking about homing was to assume that there were sort of "reading errors" of a given level (dispersion) at both ends that we have to accept without any change to the offset I had taken at the beginning of the script. After a given number of moves (heuristic to be found), it could be interesting to home because the hw_error would be of a higher level than the acceptable reading errors...given that hw_error could also subtract themselves.
I am in sync with you. These are the nearest source of information we have; (from experiments), I have seen "metastability" (taking the value of focus_distance at successive moments, it changed and went back to it's first value, not very bad : the only problem could be it can take more time in the worst cases (for a few step-size=3 the lens can take a little while to stabilize: the question is to wait just when needed and detect); I have not tested it but the position counter changes could be checked in this case until they don't move anymore, even more if lens.focus is used with wait argument=false); systematic errors could be eliminated by minimizing the sum of the differences between the current readings of transition positions and previously saved positions; the remaining errors ? random?
I have not yet tried to position the transitions at a higher precision then the step-size=1 when scanning.


Quote from: BBA on November 17, 2016, 12:52:44 PM
Which ones is it better to choose ? (the step lengths seem more constant with L)
How many times do you think I should repeat the same experiment (to see...) ?

If you don't mind, I'd like logs from all of them. Some of them can be inferred from your tables, but it's a bit error-prone, so it's best if you could run the test script on all the lenses and save the logs.

To check the repeatability, I'd say running an experiment 3 times should give a fair idea of what we can get.

Quotesystematic errors could be eliminated by minimizing the sum of the differences between the current readings of transition positions and previously saved positions; the remaining errors ? random?

The main unknown here is the origin (offset) of the focus position counter (focus_pos): we lose it on reboot and (with some lenses) on manual focus. This is a scalar variable.

As we move the lens using the focus motor, we can get some idea about this offset when encountering the first transition. If the speed is lower, the estimation will be more accurate. There may be systematic errors depending on the direction and speed of movement - need to check and model them, if any.

As we encounter more transitions, the Kalman filter kicks in: if we consider each measurement corrupted by Gaussian noise with known variance, it will be weighted optimally by 1/var (see kalman.pdf), iteratively (at each iteration, we get the optimal solution with what we know so far). If equal variances are assumed, the result becomes identical to least squares estimation. We can estimate the variance from the difference between two focus_pos reports (the focus distance transition can be anywhere in this interval). One issue is that our offsets (errors) are not normally distributed, so the Kalman filter will not be optimal (but hopefully a very close approximation).

The above assumes a priori knowledge about the real focus distance vs position counter. We can do some educated guesses (such as considering the position of each reported focus distance as the middle of an interval covered by that value). Calibrating the infinity focus point experimentally may be useful.


Many thanks for your answer !

I just had time to run two first tests : at first sight, the delay could be too short.

EF100mm f/2.8L Macro IS USM :

EF24mm f/1.4L II USM :


Results (including my lens):

The 100/2.8 macro has some trouble, not sure why.

You can increase the delay if you think it will change the results.


Very nice find, the hysteresis !

Would have had problems for positioning the transition more precisely if moving back and forth. Would like to see with step-size=2 if the change of the step length at the transition position is still valid in the other direction.

For the 24mm, it should be somewhere near 45 fine steps ? (to "visually" make the curves best match each other) which is significant in relation to the step length of 7 fine steps.

Anyway, I can share the logs for the 2d and third test for the EF24mm f/1.4L II USM.

2d test: starting in-between (macro and infinity)

3d test : starting near infinity end (done after a rest time)

I will retry the test with the 100mm Macro (Fyi, it was in full range the first time)
You are right, I don't think the delay would be the cause of this sort of trouble.

Your script is very fast, but the position counter does follow and finally returns the good values even if they arrive later.


The hysteresis was present in my first test as well.

Repeatability (24/1.4):

Probably it's worth increasing the delay in my script.


The hysteresis was present in my first test as well.

Yes, you are right (of course).
I made a mistake at that time : I thought it was like the clipping at each end (which is less than a step in length)...

I made :
- a second test with the 100 mm L macro IS USM (switch on full range as for the first test, same delay conditions = extra-delay set to 100ms) :

- a first test with the 35mmf/2 IS USM (extra-delay=100ms too).

What should be the strategy to increase the delay ?
What do you think of a loop for testing the value of the position counter ?
Note : the tests I have made till now are not a reference as I use very large delays to avoid problems (500ms for scans and seconds for the tests I have made with the 24mm to reach a target from random starting positions) which is unusable in practice.