Single-axis tracking and backtracking, part 1

The tracking algorithm used in most commercial PV tracking systems is fairly sophisticated. It has to be able to know where the sun is and how to orient the array to best take advantage of the available sunlight. Let's figure out how they work!

At the basic level, the goal of tracking is to point the panels towards the sun as directly as possible. This maximizes module exposure to beam irradiance, which makes up the majority of incoming sunlight. Tracking can be implemented any number of ways. One of the more exotic methods involved black balloons filled with a volatile liquid. The balloons were placed under each side of the modules so that when the modules faced away from the sun, the exposed balloon heats up and expands, pushing the modules until the light is blocked. I'll leave the downsides to this approach as an exercise for the reader.

Another "empirical" approach is to detect solar position electronically and rotate appropriately with a motor. The internet is full of toy examples of this approach (for instance) because photodiodes are cheap and because this approach dynamically detects solar position, you don't have to go to any trouble aligning the tracker mount or worrying about what time of year it is or where in the world you are. However, relying on instrumentation to determine the optimal angle is a little risky -- the angle calculation would get thrown off by stray reflections, small obstructions (grasshoppers!), snow accumulation, sensor drift...

The most common approach is to use a mathematical model to calculate solar position based on array location and time of day and calculate the optimal tracker position with a little trigonometry.

Solar Position

One of the most popular methods of determining the position of astronomical bodies is to use pre-made ephemeris tables (e.g. JPL Horizons 1). Another method (and the default in pvlib-python 2) is the NREL Solar Position Algorithm (SPA) 3. The SPA is fairly complex (the current pvlib implementation is over 1000 lines of code), but highly accurate, capable of calculating

solar zenith and azimuth angles in the period from the year -2000 to 6000, with uncertainties of \(\pm0.0003^\circ\)

So suffice to say it'll be fine for our purposes. Given a latitude, longitude, and datetime, we can calculate solar position to a much higher precision than any tracker could take advantage of. Technically the SPA also requires pressure, temperature, and atmospheric refraction but these have small effect and pvlib supplies suitable default values.

Tracker Geometry

Look at this excellent diagram:

/images/solar_position_3d.jpg

Credit: Submerged and Floating Photovoltaic Systems: Modelling, Design and Case Studies. Marco Rosa-Clot and Giuseppe Marco Tina, 2018.

Unfortunately it's for a south-facing system and we're talking about N-S axis systems that rotate E-W.

Using the labeling in that diagram, the SPA gives us solar zenith \(\theta_z\) and solar azimuth \(\gamma_s\). Because our trackers can only rotate in a single axis, we can collapse the N-S axis without losing any necessary information.

Look at this crappy diagram:

/images/solar_position_2d.png

This is looking along the N-S tracker axis and \(x\) is along the E-W axis. \(\phi\) is both the solar zenith angle adjusted for the N-S collapse and the tracker angle that aligns the row as directly as possible towards the sun.

Once solar position is determined, calculating the tracker angle that minimizes angle of incidence is pretty straightforward:

\begin{equation*} x = \sin \theta_z \sin \gamma_s \end{equation*}
\begin{equation*} z = \cos \theta_z \end{equation*}
\begin{equation*} \tan \phi = \frac{x}{z} \end{equation*}

Very easy!

import pandas as pd
import numpy as np
import pvlib

times = pd.date_range('2019-03-01', '2019-03-02', freq='5min', tz='US/Eastern')
location = pvlib.location.Location(40, -80, tz='US/Eastern')
solpos = location.get_solarposition(times)
solpos = np.radians(solpos)

x = np.sin(solpos.zenith) * np.sin(solpos.azimuth)
z = np.cos(solpos.zenith)

phi = np.degrees(np.arctan2(x, z))
phi[z < 0] = 0

phi.plot()
/images/truetracking_angle.png

It has all the properties you'd expect -- facing straight east in the morning, rotates to flat at midday, and faces straight west in evening. This is the "basic" tracking pattern and is called true-tracking.

References

1

JPL HORIZONS emphemerides https://ssd.jpl.nasa.gov/?ephemerides

2

pvlib.solarposition https://pvlib-python.readthedocs.io/en/stable/api.html#solar-position

3

I. Reda and A. Andreas, Solar position algorithm for solar radiation applications. Solar Energy, vol. 76, no. 5, pp. 577-589, 2004. https://rredc.nrel.gov/solar/codesandalgorithms/spa/