Heliostat Offset Mirror Algorithm

by robmilne.ca in Workshop > Solar

882 Views, 6 Favorites, 0 Comments

Heliostat Offset Mirror Algorithm

heliostat.jpg
offset_ref_1.jpg

When light reflects off of a mirror we intuitively know that the normal of the mirror plane bisects the incident and reflected light paths. Heliostats use this principle to direct solar energy to a fixed target location. My heliostat uses two servos taken from a scrap SCARA bot in an alt-azimuth arrangement whereby the altitude servo is perpendicular to, and on the centre of, the axis of the azimuth servo. The mirror attached to the altitude servo is offset from the centre of the altitude axis. The calculation that bisects the altitude angle between the incident and reflected angle is inaccurate for this arrangement, especially if the target is close to the mirror. 

The first drawing shows the sun reflected to a target located along the zenith angle. When the centre of the mirror is on the centre of rotation the mirror angle is simple to calculate. It is plain to see how the same mirror angle placed at offset R from the origin will not reflect to the target.

How should the altitude angle be adjusted for this offset?

offset_ref_0.jpg

Time to get out the compass, ruler and secondary school mathematics.


The second drawing uses basic geometric rules to describe a general formula for determining the reflection angle of a reflecting circle. The drawing shows the solar vector reflecting from point A to T with a reflection angle of 2 * Θ.

Since the solar vectors are parallel, the angle at point B between A and C is:

180° - 2Θ.

The angle at A between points T and C is:

180° - Θ

The sum of triangle interior angles is 180° and therefore the triangle formed by TBC is:

α + φ + (180° - 2Θ) = 180°

Which reduces to:

α = 2Θ - φ

Applying the Law of Sines to the triangle formed by points TAC:

r / sin(α) = d / sin(180 - Θ)

Substituting α:

r / sin(2Θ - φ) = d / sin(180° - Θ)

Which simplifies to the following since the sine of 180° is zero.

r / sin(2Θ - φ) = d / sin(-Θ)

Since d / r is always a positive value, the final form of the equation is the absolute value of the division of the two sine angles.

d / r = | sin(Θ) / sin(2Θ - φ) |

If the mirror radius, target location (angle and distance) and solar angle are known, then the angle of rotation at the origin (φ) and distance ratio can be calculated. The only unknown variable is Θ however the equation is not easily solvable. I devised a method of deriving the value of Θ by using successive approximation and in the process discovered 2 solutions to the formula.

The most notable feature about the equation is that the denominator is zero when Θ equals φ/2. This corresponds to the standard half reflection angle when the mirror plane is centred on the axis of rotation. The divide by zero implies that d/r is infinitely large (or rather, r is infinitely small relative to d). The φ/2 angle is the limit of the iterative process.

I started by selecting a test Θ value that is halfway between the φ/2 angle and either the incident light vector or the reflected light vector. If the generated test value of d/r is less than the actual d/r then we do the next halfway iteration toward the φ/2 angle. If the test value of d/r is more than the actual d/r, then the next halfway iteration must be away from the φ/2 angle. This method of halfway approximations will converge to a value with an arbitrary precision. 


Example:

solar angle (altitude): 45°

reflected angle: 22.5°

φ = 22.5°

Ratio of target distance to mirror radius: 5

| sin(Θ) / sin(2Θ - 22.5°) | = 5


Let Θ equal (1/2)φ + ((1/2)φ)/2 = (3/4)φ

   | sin((3/4)φ) / sin((3/2)φ - φ) | =  | sin((3/4)φ) / sin((1/2)φ) | =  1.487950167156238

The test d/r value is less than 5 so the next test angle must be closer to φ/2. We split the angle halfway between (1/2)φ and (3/4)φ which is (5/8)φ

   | sin((5/8)φ) / sin(5/4)φ - φ) | =  | sin((5/8)φ) / sin((1/4)φ) | =  2.478956018164758

The test d/r value is less than 5 so the next test angle must be closer to φ/2. We split the angle halfway between (1/2)φ and (5/8)φ which is (9/16)φ

   | sin((9/16)φ) / sin(9/8)φ - φ) | =  | sin((9/16)φ) / sin((1/8)φ) | =  4.465286834155914

The test d/r value is less than 5 so the next test angle must be closer to φ/2. We split the angle halfway between (1/2)φ and (9/16)φ which is (17/32)φ

   | sin((17/32)φ) / sin(17/16)φ - φ) | =  | sin((17/32)φ) / sin((1/16)φ) | =  8.439323891175796

The test d/r value is greater than 5 so the next test angle must be further from φ/2. We split the angle halfway between (17/32)φ and (9/16)φ which is (35/64)φ

   | sin((35/64)φ) / sin(35/32)φ - φ) | =  | sin((35/64)φ) / sin((3/32)φ) | =  5.789904885125893

The test d/r value is greater than 5 so the next test angle must be further from φ/2. We split the angle halfway between (35/64)φ and (9/16)φ which is (71/128)φ

   | sin((71/128)φ) / sin(71/64)φ - φ) | =  | sin((71/128)φ) / sin((7/64)φ) | =  5.032966228779621

This process continues until an arbitrarily small deviation from the value of 5 is achieved. In the case where a precision of .0001° is desired this process will repeat 17 times to generate a d/r value of 4.9999449189572935 and a Θ of 12.48948097°


Surprisingly, if we start the iteration process on the opposite side of the φ/2 angle we get a different answer:


Let Θ equal (1/2)φ - ((1/2)φ)/2 = (1/4)φ

   | sin((1/4)φ) / sin((1/2)φ - φ) | =  | sin((1/4)φ) / sin(-(1/2)φ) | =  0.5024192861881558

The test d/r value is less than 5 so the next test angle must be closer to φ/2. We split the angle halfway between (1/2)φ and (1/4)φ which is (3/8)φ

   | sin((3/8)φ) / sin((3/8)φ - φ) | =  | sin((3/8)φ) / sin(-(1/4)φ) | =  1.4969879141751483

The test d/r value is less than 5 so the next test angle must be closer to φ/2. We split the angle halfway between (1/2)φ and (3/8)φ which is (7/16)φ

   | sin((7/16)φ) / sin((7/8)φ - φ) | =  | sin((7/16)φ) / sin(-(1/8)φ) | =  3.484206070569177

The test d/r value is less than 5 so the next test angle must be closer to φ/2. We split the angle halfway between (1/2)φ and (7/16)φ which is (15/32)φ

   | sin((15/32)φ) / sin((15/16)φ - φ) | =  | sin((15/32)φ) / sin(-(1/16)φ) | =  7.458464753883468

The test d/r value is greater than 5 so the next test angle must be further from φ/2. We split the angle halfway between (15/32)φ and (7/16)φ which is (29/64)φ

   | sin((29/64)φ) / sin((29/32)φ - φ) | =  | sin((29/64)φ) / sin(-(3/32)φ) | =  4.8089534136859875

This process continues until an arbitrarily small deviation from the value of 5 is achieved. In the case where a precision of .0001° is desired this process will repeat 18 times to generate a d/r value of 4.999950581815801 and a Θ of 10.23200512°.


Which value of Θ to use? It is plain from the drawing that the value greater than φ/2 is the correct one. What is the smaller value?

angle_offset.png

CAD is useful for geometric validation. The tan coloured lines in this screen image from my trusty old NURBS modeller shows how the 10.232005° value of Θ matches the reflection from the backside of the inner surface of a circular mirror.

The algorithm used by the hand iterations has the following pattern...

d/r = | sin((x / (2^n+2)) φ) / sin(((x - 2^n+1) / 2^n+1) φ) |

...where n is the iteration count starting at 0.

The value of x is determined by the following rules:

x = 1 where n = 0 for inner circle reflection

x = 3 where n = 0 for outer circle reflection

x = (2 * x_p) + z where n > 0 and x_p is the value of x from the previous iteration. z is either 1 or -1 as determined by the previous iteration based on whether the computed d/r value is greater or less than the actual d/r value. The plus/minus decision corresponds to the movement away from or toward φ/2 in the hand calculations.


Attached is the Python class I developed for by my Raspberry Pi Zero W controlled heliostat. The test.py file calculates both outer and inner offset reflection angles for a origin reflection angle of 22.5° to a target that is placed at a distance that is 5 times the radius of mirror rotation. The final output values are in accordance with the CAD model.

The debug output of test.py:

i:0 x:3 angle:16.875 temp_ratio:1.487950167156238

i:1 x:5 angle:14.0625 temp_ratio:2.478956018164758

i:2 x:9 angle:12.65625 temp_ratio:4.465286834155914

i:3 x:17 angle:11.953125 temp_ratio:8.439323891175796

i:4 x:35 angle:12.3046875 temp_ratio:5.789904885125893

i:5 x:71 angle:12.48046875 temp_ratio:5.032966228779621

i:6 x:143 angle:12.568359375 temp_ratio:4.730200489592899

i:7 x:285 angle:12.5244140625 temp_ratio:4.8763623970819365

i:8 x:569 angle:12.50244140625 temp_ratio:4.953290377438017

i:9 x:1137 angle:12.491455078125 temp_ratio:4.992775700434724

i:10 x:2273 angle:12.4859619140625 temp_ratio:5.012781638626532

i:11 x:4547 angle:12.48870849609375 temp_ratio:5.00275648657824

i:12 x:9095 angle:12.490081787109375 temp_ratio:4.997760566192296

i:13 x:18189 angle:12.489395141601562 temp_ratio:5.0002571422601125

i:14 x:36379 angle:12.489738464355469 temp_ratio:4.9990085084823885

i:15 x:72757 angle:12.489566802978516 temp_ratio:4.999632738899387

i:16 x:145513 angle:12.489480972290039 temp_ratio:4.9999449189572935

Outer Angle: 12.489481° (0.217983 radians)

 17 iterations


i:0 x:1 angle:5.625 temp_ratio:0.5024192861881558

i:1 x:3 angle:8.4375 temp_ratio:1.4969879141751483

i:2 x:7 angle:9.84375 temp_ratio:3.484206070569177

i:3 x:15 angle:10.546875 temp_ratio:7.458464753883468

i:4 x:29 angle:10.1953125 temp_ratio:4.8089534136859875

i:5 x:59 angle:10.37109375 temp_ratio:5.868754898574653

i:6 x:117 angle:10.283203125 temp_ratio:5.290680825352222

i:7 x:233 angle:10.2392578125 temp_ratio:5.039344675133663

i:8 x:465 angle:10.21728515625 temp_ratio:4.9216980490912

i:9 x:931 angle:10.228271484375 temp_ratio:4.9798888469004545

i:10 x:1863 angle:10.2337646484375 temp_ratio:5.009456067926628

i:11 x:3725 angle:10.23101806640625 temp_ratio:4.9946326089967545

i:12 x:7451 angle:10.232391357421875 temp_ratio:5.002034336024518

i:13 x:14901 angle:10.231704711914062 temp_ratio:4.998330976959963

i:14 x:29803 angle:10.232048034667969 temp_ratio:5.000182031973311

i:15 x:59605 angle:10.231876373291016 temp_ratio:4.999256348415878

i:16 x:119211 angle:10.231962203979492 temp_ratio:4.999719151172038

i:17 x:238423 angle:10.23200511932373 temp_ratio:4.999950581815801

Inner Angle: 10.232005° (0.178582 radians)

 18 iterations

angle: (12.489480972290039, 10.23200511932373)


One final note is that this example has simplified the heliostat mirror angle to an altitude adjustment only. In the general case, where the target is not located along the azimuth axis, the algorithm is applied to the vector plane created by the incident and reflected light vectors.