Jak rozbalit hodnoty enkodéru nebo úhly, které se zalamují na určitém numerickém bodě

Práce se zalamovanými veličinami (úhly, čítače, modulo toky) je běžná ve zpracování signálů. Tento krátký příspěvek ukazuje dva jednoduché, samostatné příklady: statickou funkci unwrap() a stavový OnlineUnwrapper (pro streaming/online použití). Oba očekávají 1D vstupy a zachovávají stejný počet vzorků.

Unwrap

Použití unwrap() — statické (batch) rozbalování

Nejlepší, když máte k dispozici celou sérii. Tip: nastavte threshold, pokud vaše hodnoty nebo šum vyžadují jinou citlivost detekce zalomení.

unwrap_example.py
# Simple example (self-contained)
import numpy as np
from UliEngineering.SignalProcessing.WrappedValues import unwrap

# Angles in degrees, wrapped at 360
wrapped = np.array([350, 355,   1,   3])   # jump 355 -> 1 (wrap)
un = unwrap(wrapped, wrap_value=360)

print(un)  # -> [350. 355. 361. 363.]  (continuous increasing angle)

Použití OnlineUnwrapper — online/streaming rozbalování

Použijte, když data přicházejí po jednom vzorku nebo v chunkách. Udržuje interní stav.

Poznámka: Obě metody zachovávají počty vzorků a jsou pouze 1D. Použijte unwrap() pro batch zpracování a OnlineUnwrapper pro zpracování real-time streamů nebo smíšených skalárních/chunk vstupů.

online_unwrapper_example.py
# Online scalar and chunk usage
import numpy as np
from UliEngineering.SignalProcessing.WrappedValues import OnlineUnwrapper

u = OnlineUnwrapper(wrap_value=100)   # counters wrapping at 100

# Feed scalars
print(u(10))   # -> 10
print(u(95))   # -> 95
print(u(2))    # -> 102  (wrapped forward handled online)

# Or feed a chunk (1D array)
chunk = np.array([98, 3, 5])  # continues from previous state
print(u(chunk))  # -> array([ 98., 103., 105.])

Kód použitý pro generování výše uvedeného grafu

Unwrap

plot_wrapped_unwrapped.py
#!/usr/bin/env python3
# SPDX-License-Identifier: CC0-1.0
"""Example: plot wrapped vs unwrapped phase signals.

License: CC0 1.0 Universal — public domain dedication (see examples/LICENSE-CC0-1.0.txt)

Run as a script:
    python examples/unwrap_plot.py        # shows the plot
    python examples/unwrap_plot.py --save out.png  # saves to file

This creates a synthetic continuous phase signal, wraps it to [0,2*pi),
then recovers it using the batch `unwrap()` function and the stateful
`OnlineUnwrapper` to demonstrate they agree.
"""
import argparse

import numpy as np
import matplotlib.pyplot as plt
plt.style.use("ggplot")

from UliEngineering.SignalProcessing.WrappedValues import unwrap, OnlineUnwrapper

def make_data(n=1000, wrap_value=2 * np.pi, seed=0):
    """Create a continuous phase signal that *reverses direction* in the middle.

    The first half has a positive angular velocity and the second half a
    negative angular velocity so the phase reverses. The returned `wrapped`
    series is in [0, wrap_value).
    """
    np.random.seed(seed)
    t = np.linspace(0.0, 10.0, n)
    mid = n // 2
    # angular velocities (rad per unit time)
    omega1 = 1.2 * 2 * np.pi
    omega2 = -0.8 * 2 * np.pi

    true_phase = np.empty(n, dtype=float)
    # first half: positive slope + small oscillation
    true_phase[:mid] = omega1 * t[:mid] + 0.8 * np.sin(2 * np.pi * 0.3 * t[:mid])
    # second half: start from the last value of first half to make it continuous
    # and then integrate the negative angular velocity
    start_phase = true_phase[mid - 1]
    true_phase[mid:] = (
        start_phase
        + omega2 * (t[mid:] - t[mid - 1])
        + 0.8 * np.sin(2 * np.pi * 0.3 * t[mid:])
    )

    # wrap to [0, wrap_value)
    wrapped = np.mod(true_phase, wrap_value)
    return t, true_phase, wrapped


def plot_example(save_path=None):
    wrap_value = 2 * np.pi
    t, true_phase, wrapped = make_data(n=1200, wrap_value=wrap_value)

    # Batch unwrapping
    static_unwrapped = unwrap(wrapped, wrap_value=wrap_value)

    # Online unwrapping (streaming style)
    u = OnlineUnwrapper(wrap_value=wrap_value)
    online_unwrapped = np.array([u(x) for x in wrapped])

    # Quick sanity check
    maxdiff = np.max(np.abs(static_unwrapped - online_unwrapped))
    print(f"max difference between unwrap() and OnlineUnwrapper: {maxdiff:.3e}")

    fig, axs = plt.subplots(2, 1, figsize=(10, 6), sharex=True)

    # mark the direction change in the plots
    mid_idx = len(t) // 2
    axs[0].axvline(t[mid_idx], color="#444444", ls=":", lw=0.8)
    axs[1].axvline(t[mid_idx], color="#444444", ls=":", lw=0.8, label="direction change")

    axs[0].plot(t, wrapped, color="#1f77b4", lw=1)
    axs[0].set_title("Wrapped signal (0 .. 2π)")
    axs[0].set_ylabel("phase (rad)")
    axs[0].grid(True)

    axs[1].plot(t, static_unwrapped, color="#2ca02c", lw=1, label="unwrap()")
    axs[1].plot(t, online_unwrapped, color="#ff7f0e", lw=1, ls="--", label="OnlineUnwrapper")
    axs[1].plot(t, true_phase, color="#7f7f7f", lw=0.8, ls=":", label="true phase")
    axs[1].set_title("Unwrapped signal (continuous phase)")
    axs[1].set_xlabel("time")
    axs[1].set_ylabel("phase (rad)")
    axs[1].legend()
    axs[1].grid(True)

    plt.tight_layout()
    if save_path:
        fig.savefig(save_path, dpi=150)
        print(f"Saved figure to {save_path}")
    else:
        plt.show()


if __name__ == "__main__":
    p = argparse.ArgumentParser(description="Plot wrapped and unwrapped phase signals.")
    p.add_argument("--save", dest="save", help="Save figure to this path instead of showing")
    args = p.parse_args()
    plot_example(save_path=args.save)

Podívejte se na podobné články podle kategorie: UliEngineering, Python