Ir al contenido

← Algoritmo de seguidor solar con PyEphem en un Raspberry PLC

Seguidores solares de dos ejesRaspberry PLC 21CANopenControl

Algoritmo de seguidor solar con PyEphem en un Raspberry PLC — ejemplo completo

Calcula elevación y azimut solar con PyEphem y gobierna un seguidor de dos ejes desde un Raspberry PLC 21. Código Python con posición de reposo nocturna.

Programa completo y ejecutable para el Raspberry PLC 21 (solar-tracking-pyephem.py): incluye cabecera de conexionado, requisitos y notas de integración.

Descarga el pack completo del proyecto — gratisEste ejemplo + los relacionados + lista de materiales

Vista de solo lectura.

#!/usr/bin/env python3
# -----------------------------------------------------------------------------
# COMPLETE EXAMPLE — Astronomical solar tracking algorithm with PyEphem
#
# Device:    Raspberry PLC 21 (Industrial Shields)
# Based on:  dual-axis solar tracker project
#
# Requirements:
#   pip3 install ephem
#
# Logic:
#   A PyEphem observer configured with lat/long/altitude computes the sun's
#   elevation and azimuth at any instant. The main loop updates the setpoint
#   of both axes every 300 s. When the sun drops below the minimum elevation
#   threshold, the tracker goes to the night rest position and waits; at
#   sunrise it re-arms automatically and resumes tracking.
#
#   The move_*_axis() functions are stubs: on the real tracker they drive the
#   motor via the CANopen VFD until the encoder confirms the target position
#   (see lovato-vlb3-vfd-canopen-sdo.py and canopen-encoder-inclinometer-reading.py).
# -----------------------------------------------------------------------------

import math
import time
from datetime import datetime, timezone

import ephem

# --- Installation site (adjust per plant) -------------------------------------
LATITUDE   = '41.6167'    # decimal degrees, as a string for ephem
LONGITUDE  = '1.8333'
ALTITUDE_M = 280          # meters above sea level

# --- Tracking parameters ------------------------------------------------------
PERIOD_S          = 300   # setpoint update every 300 s
MIN_ELEV_DEG      = 5.0   # below this elevation -> night rest position
REST_POS_ELEV     = 0.0   # degrees, table horizontal
REST_POS_AZ       = 90.0  # degrees, facing east (ready for sunrise)

# --- Mechanical axis limits (software limit switches) --------------------------
ELEV_MIN, ELEV_MAX = 0.0, 75.0
AZ_MIN,   AZ_MAX   = 60.0, 300.0

# --- Astronomical observer -----------------------------------------------------
obs = ephem.Observer()
obs.lat       = LATITUDE
obs.lon       = LONGITUDE
obs.elevation = ALTITUDE_M
sun = ephem.Sun()


def get_sun_angle(t):
    """Sun elevation (degrees) at instant t (UTC)."""
    obs.date = t.strftime('%Y/%m/%d %H:%M:%S')
    sun.compute(obs)
    return math.degrees(float(sun.alt))


def get_azimuth_angle(t):
    """Sun azimuth (degrees, 0=N, 90=E) at instant t (UTC)."""
    obs.date = t.strftime('%Y/%m/%d %H:%M:%S')
    sun.compute(obs)
    return math.degrees(float(sun.az))


def clamp(value, vmin, vmax):
    """Clips the setpoint to the mechanical limits of the axis."""
    return max(vmin, min(vmax, value))


def move_elevation_axis(degrees):
    """Stub: move the elevation axis to 'degrees' (VFD + encoder)."""
    print(f"  -> ELEVATION axis to {degrees:6.2f} deg")


def move_azimuth_axis(degrees):
    """Stub: move the azimuth axis to 'degrees' (VFD + encoder)."""
    print(f"  -> AZIMUTH   axis to {degrees:6.2f} deg")


def go_to_rest():
    """Night position: flat table facing east."""
    print("Sun below the horizon: night rest position")
    move_elevation_axis(REST_POS_ELEV)
    move_azimuth_axis(REST_POS_AZ)


def tracking_loop():
    at_rest = False
    while True:
        now = datetime.now(timezone.utc)
        elev = get_sun_angle(now)
        az   = get_azimuth_angle(now)
        print(f"{now:%H:%M:%S} UTC  sun: elev={elev:6.2f}  az={az:6.2f}")

        if elev < MIN_ELEV_DEG:
            # Night or twilight: a single rest maneuver, then wait.
            if not at_rest:
                go_to_rest()
                at_rest = True
        else:
            # Daytime: automatic re-arm at sunrise and normal tracking.
            if at_rest:
                print("Sunrise detected: re-arming tracking")
                at_rest = False
            move_elevation_axis(clamp(elev, ELEV_MIN, ELEV_MAX))
            move_azimuth_axis(clamp(az, AZ_MIN, AZ_MAX))

        time.sleep(PERIOD_S)


if __name__ == '__main__':
    try:
        tracking_loop()
    except KeyboardInterrupt:
        print("\nManual stop: the tracker stays at its last position")
Descarga el pack completo del proyecto — gratisEste ejemplo + los relacionados + lista de materiales