Wie kompiliert man MicroPython vom Quellcode selbst?

Python auf Einplatinencomputer wie Raspberry Pi, Banana Pi / Python für Micro-Controller
Benutzeravatar
Dennis89
User
Beiträge: 1156
Registriert: Freitag 11. Dezember 2020, 15:13

Guten Abend,

die Woche lief leider so überhaupt nicht wie geplant. Ich hatte leider nicht wirklich viel Zeit für das Projekt, deswegen melde ich mich erst heute wieder.

Die Werte der open Loop fahrt habe ich aufgezeichnet, die Position in Schritten des Encoders und die die Zeit in Mikrosekunden

Jetzt habe ich leider den Code dazu nicht mit hier her genommen. Auf jeden Fall habe ich pwm.duty auf 100 gesetzt und dann in einer Schleife die Position des Encoders und die Zeit in Mikrosekunden aufgenommen und das ganze im 0,01 Sekunden Takt. Also wie der Soll-Ist-Vergleich auch.

Nun habe ich mal die Beschleunigung ausgerechnet und die zwei Werte "T" und "L". Bin aber gerade erst fertig geworden und konnte es noch nicht testen.
Aber mein Gefühl sagt mir dass die Werte so nicht hinhauen, ich zeig euch trotzdem mal was ich gerechnet habe. Die Konstanten sind die verlinkten Werte als Listen.
Von der Programmierung her geht das ein oder andere sicherlich noch eleganter, aber darauf habe ich gerade keinen Wert gelegt, weil ich mich erst mal auf die Rechnungen konzentrieren wollte. Die Beschleunigung habe ich nur interessehalber berechnet.

Mal noch etwas zu meinen Gedanken dabei. Mit den Positionen und den dazugehörigen Zeitabschnitten kann ich den Weg wieder grafisch darstellen. Die Zeit in der beschleunig wird, wird parabellförmig aussehen und bei voller Drehzahl wird eine Gerade mit konstanter Steigung entstehen (wohl gemerkt, in einer Welt ohne Reibung etc.). Da ich keine mathematische Funktion habe, die den Weg darstellt, musste ich mir was anderes überlegen um den Wendepunkt und damit den Anfang der Gerade zu bestimmen. Also dachte ich mir, ich berechne für jede Position die Steigung und vergleiche sie mit der nächsten und wenn beide gleich sind, dann habe ich den Anfang meiner Geraden gefunden. Naja das hat dann erst funktioniert, nachdem ich das Runden auf 5 Stellen in den Vergleich einbezogen habe.
Für meine Werte "T" und "L" muss ich die Gerade verlängern, bis sie die x-Achse schneidet (das tut sie bei y=0) und einmal benötige ich den Wert der x-Achse wenn die Gerade die Endposition schneidet (y=Endposition). Da mir die Werte komisch vorkommen, habe ich das mir mal grafisch ausgeben lassen und man sieht, dass es keine konstante Steigung gibt, was zu erwarten war und man auch in früheren Aufzeichnungen schon gesehen hat.

Naja jetzt hier mal der Code:

Code: Alles auswählen

from matplotlib import pyplot as plt

def draw_graphics(straight_line):
    plt.plot(TIME_STAMP, POSITIONS, color='r')
    plt.plot(TIME_STAMP, straight_line, color='g')
    plt.xlabel("Zeit")
    plt.ylabel("Step")
    plt.show()


def calculate_pitch():
    pitches = [(POSITIONS[index + 1] - position) / (TIME_STAMP[index + 1] - TIME_STAMP[index]) for index, position in
               enumerate(POSITIONS) if index + 1 < len(POSITIONS)]
    for index in range(len(pitches)):
        if index + 1 < len(pitches):
            if round(pitches[index], 5) == round(pitches[index + 1], 5):
                if not index == 0:
                    return pitches[index], index


def calculate_acceleration(end_acceleration):
    # 10**6 -> microsecond in second
    return 2 * POSITIONS[end_acceleration] / (TIME_STAMP[end_acceleration] * 10**-6)**2


def calculate_y_axis_section(end_acceleration, pitch):
    # equation of a straight line: y(x) = pitch * x + y_axis_section
    return POSITIONS[end_acceleration] - (pitch * TIME_STAMP[end_acceleration])


def calculate_constants(pitch, y_axis_section):
    # equation of a straight line: y(x) = pitch * x + y_axis_section
    # y(x) = 0
    delay_time = -y_axis_section / pitch
    # y(x) = Endposition
    constant_time = (POSITIONS[-1] - y_axis_section) / pitch
    return delay_time, constant_time


def calculate_straight_line(pitch, y_axis_section, end_acceleration):
    return [(pitch * x) + y_axis_section if x > TIME_STAMP[end_acceleration] else None for x in TIME_STAMP]


def main():
    pitch, end_acceleration = calculate_pitch()
    print(f'Pitch: {pitch}')
    print(f'Acceleration: {calculate_acceleration(end_acceleration):0.2f} 1/s²')
    y_axis_section = calculate_y_axis_section(end_acceleration, pitch)
    delay_time, constant_time = calculate_constants(pitch, y_axis_section)
    print(f'L: {delay_time}')
    print(f'T: {constant_time}')
    straight_line = calculate_straight_line(pitch, y_axis_section, end_acceleration)
    draw_graphics(straight_line)


if __name__ == '__main__':
    main()
Soviel zu meinem aktuellen Stand. Vielleicht beurteilst du/ihr die Werte auch anders, aber für mich sieht es falsch aus. (Trotzdem wollte ich den Stand mal vorstellen, allein das aufschreiben hier, hat einen anderen Fehler zum Vorschein gebracht. Dafür hat es schon geholfen)

Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
__deets__
User
Beiträge: 14542
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ich bin im Harz und habe nur das Telefon, darum kann ich das nicht gut beurteilen. Aber wie sieht dann die Beschleunigungskurve aus?

Was die Analyse dann angeht: das kannst du doch von Hand machen, auch wenn eine automatische natürlich “cool” ist.
Benutzeravatar
Dennis89
User
Beiträge: 1156
Registriert: Freitag 11. Dezember 2020, 15:13

Das eilt auch nicht, vor morgen Mittag komme ich eh nicht in die Werkstatt. Bitte keine Umstände wegen mir.

So sieht die Kurve aus

Wie meinst du das von Hand? Schon rechnerisch oder ausgedruckt mit Stift und Papier?

Danke und Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
__deets__
User
Beiträge: 14542
Registriert: Mittwoch 14. Oktober 2015, 14:29

Zur Kurve: die sollte schon in die Sättigung der Drehzahl gehen. Das ist dann ja der Punkt ab dem die Antwort auf 100% erreicht ist.

Und zur Analyse: ich habe das Verfahren gerade aus genannten Gründen nicht vor den Augen. Aber die Gerade muss doch angelegt werden an eben den ja gut sichtbaren Geraden Teil. Und händisch würde dann doch reichen, dass du den Startzeitpunkt und Endzeitpunkt der Geraden via zweier Konstanten einfach Angibst. Der Positionswert kommt dann einfach aus der Aufzeichnung, und zwischen diesen beiden Punkten ziehst du die Gerade.
__deets__
User
Beiträge: 14542
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ich habe einen Denkfehler gemacht: die Kurve der Position sieht natürlich nicht s-förmig aus, wenn man einfach nur Gas gibt. Das ist nur der Fall beim Auftrag von Geschwindigkeit auf Zeit! Nimm doch mal deine Daten & berechne pro Schritt die Geschwindigkeit, indem du die Differenz zweier aufeinanderfolgender Schritte durch deren time Delta teilst. Wie sieht das dann aus?
Benutzeravatar
Dennis89
User
Beiträge: 1156
Registriert: Freitag 11. Dezember 2020, 15:13

Guten Morgen,

und danke für deine Antwort.

Du meinst sowas(?):

Code: Alles auswählen

def calculate_velocity():
    return [(POSITIONS[index + 1] - position) / (TIME_STAMP[index + 1] - TIME_STAMP[index]) for index, position in
               enumerate(POSITIONS) if index + 1 < len(POSITIONS)]

Das würde dann so aussehen


Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
__deets__
User
Beiträge: 14542
Registriert: Mittwoch 14. Oktober 2015, 14:29

Hm. Das sieht nicht so aus wie ich mir das gedacht hätte. Ich muss mal die Daten anschauen. Gegebenenfalls ist die Regelung durch die Schrittauflösung beschränkt. Falls dem so ist, müsste ggf nochmal untersetzen.
Benutzeravatar
Dennis89
User
Beiträge: 1156
Registriert: Freitag 11. Dezember 2020, 15:13

Die Achse, die das Rohr dreht, wird über einen Riemen angetrieben, also eine Untersetzung wäre da kein Problem.

Wie hast du dir denn gedacht, wie das Diagramm aussehen sollte? Hätte ich einen Encoder mit höherer Auflösung benötigt?

Danke und Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
__deets__
User
Beiträge: 14542
Registriert: Mittwoch 14. Oktober 2015, 14:29

Na wie in den Ziegler-Nichols Artikeln. Auch eine S-Kurve, die hier ja aber natürlich ist: es wird statische Reibung überwunden, dann linear beschleunigt, und dann asymptotisch gegen V max gegangen.

Und ja, encoder Auflösung ist kritisch. Da würde ich immer eher höher als niedriger gehen, im Rahmen des Budgets natürlich.
Benutzeravatar
Dennis89
User
Beiträge: 1156
Registriert: Freitag 11. Dezember 2020, 15:13

Achso so war das gemein.

Siehst du denoch eine Chance, unabhängig von der Methode, den Regler sauber einzustellen oder wäre das jetzt vergebene Mühe und ich mache erst weiter, wenn der Motor an der Maschine angebaut ist und ich das Encoder-Signal an der Untersetzung abnehme?

Ich/wir haben uns für die 1000er Auflösung entschieden, weil der eigentlich nur dazu dienen sollte, die Endposition abzufrgaen. Das ich da einen Regler brauche, war mir nicht klar. Aber selbst wenn, hätte ich mangels Erfahung vielleicht trotzdem den falschen gewählt.

Vielen Dank und Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
__deets__
User
Beiträge: 14542
Registriert: Mittwoch 14. Oktober 2015, 14:29

Also ich denke erstmal schon, dass da noch was geht. Soooo viel haben wir da ja noch nicht probiert. Du hast auch noch gar nicht den Code gezeigt, mit dem du die Daten für die Step Response aufzeichnest. Vielleicht ist da was schief.
Benutzeravatar
Dennis89
User
Beiträge: 1156
Registriert: Freitag 11. Dezember 2020, 15:13

Stimmt, ich war gerade in der Werkstatt und habe den Code mal vom ESP kopiert.

In der 'boot.py' wird der Wlan-Access-Point ereitgestellt, nach diesem Beispiel (das habe ich vergessen zu kopieren).

Dann läuft auf dem ESP folgender Code:

Code: Alles auswählen

from machine import Pin, ADC
from esp32 import MCPWM, PCNT
from time import sleep, ticks_us, ticks_diff
from LargeCounter import LargeCounter
from PathPlanner import PathPlanner
from PID import PID

try:
    import usocket as socket
except:
    import socket
import json

PWM_PINS = [[16, 17], [5, 23]]
ENCODER_PINS = [15, 2]
KP = 0.03
KI = 0
KD = 0


class WeldingControl:
    def __init__(self):
        encoder = PCNT(0, Pin(ENCODER_PINS[1]), Pin(ENCODER_PINS[0]))
        encoder.filter(100)
        self.count_angle = LargeCounter(encoder)
        self.welding_axis = MCPWM(0)
        self.welding_axis.bind(Pin(PWM_PINS[0][0]), Pin(PWM_PINS[0][1]))
        self.welding_axis.duty(0)
        self.path = PathPlanner(45, 180, 45, 0.1)
        self.pid = PID(KP, KI, -KD)
        self.actually_pos = []
        self.should_pos = []
        self.time_stamp = []
        self.record_motor_conduct()
        # self.open_loop()

    def open_loop(self):
        start = ticks_us()
        while True:
            sleep(0.01)
            time_ = ticks_diff(ticks_us(), start)
            actually_position = self.count_angle.counter()
            self.welding_axis.duty(100)
            if actually_position >= 500:
                self.welding_axis.duty(0)
                break
            self.actually_pos.append(actually_position)
            self.time_stamp.append(time_)

    def record_motor_conduct(self):
        start = ticks_us()
        while True:
            sleep(0.01)
            time_ = ticks_diff(ticks_us(), start)
            should_be_position = self.path(time_ / 1000)
            actually_position = self.translate_into_angle(self.count_angle.counter())
            new_pwm = self.pid(should_be_position - actually_position, time_)
            self.welding_axis.duty(new_pwm)
            if actually_position >= 270:
                self.welding_axis.duty(0)
                break
            self.should_pos.append(int(should_be_position))
            self.actually_pos.append(actually_position)
            self.time_stamp.append(time_)

    def translate_into_angle(self, value):
        return 360 * value // 1000


def listen_and_connect(pid_values):
    while True:
        cl, addr = s.accept()
        print("client connected from", addr)
        cl_file = cl.makefile("rwb", 0)
        while True:
            line = cl_file.readline()
            if not line or line == b"\r\n":
                break
        response = pid_values
        cl.send("HTTP/1.0 200 OK\r\nContent-type: text/html\r\n\r\n")
        cl.send(response)
        cl.close()


def make_json(welding_control):
    pid_values = [
        welding_control.time_stamp,
        welding_control.should_pos,
        welding_control.actually_pos,
    ]
    return json.dumps(pid_values)


def main():
    welding_control = WeldingControl()
    pid_values = make_json(welding_control)
    listen_and_connect(pid_values)


if __name__ == "__main__":
    main()
Auf meinem Laptop läuft dann dieser Code:

Code: Alles auswählen

#!/usr/bin/env python3
import requests
import matplotlib.pyplot as plt


def draw_graphics(time_stamp, should_be_position, actually_position):
    plt.plot(time_stamp, actually_position, color="r", label="Ist-Wert")
    plt.plot(time_stamp, should_be_position, color='g', label='Soll-Wert')
    plt.xlabel("Zeit")
    plt.ylabel("Winkel")
    plt.title("Soll-Ist-Vergleich")
    plt.legend()
    plt.show()


def translate_into_list(content):
    return content[0], content[1], content[2]


def main():
    content = requests.get("http://192.168.4.1").json()
    time_stamp, should_be_position, actually_position = translate_into_list(content)
    draw_graphics(time_stamp, should_be_position, actually_position)


if __name__ == "__main__":
    main()
Also mit diesen zwei Dateien entstehen meine hier gezeigten Diagramme.

Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
__deets__
User
Beiträge: 14542
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ich hoffe mal den Test hast du auch im open loop gemacht? Im code oben ist das ja auskommentiert. Allerdings sehen die Daten “gut” bzw passend dazu aus. Zu dem Code: da lässt sich nichts pathologisches erkennen. Aber durchaus stilistisch verbessern. Zb andauern die PWM auf 100 statt einmal zu setzen. Und die Code dopplung der Zeiterfassung in beiden Ansteuerungen, statt das einmal zu machen un im Loop zb via callback eine der beiden Methoden zu nutzen. Aber natürlich nur kosmetisch das ganze. Und bei den Daten Links habe ich zweimal die Positionen gefunden, statt einmal Positionen und einmal Zeit.

Letztlich aber habe ich leider schlechte Nachrichten: wenn das die Encoder Positionen bei Vollgas sind, dann haben wir da ~6 Schritte pro Zeitschritt. Und mit 100 (eigentlich weniger, weil deine Zeitberechnung nicht optimal ist) schritten pro Sekunde ist das ein viel zu geringer wert aus meiner Sicht. Denn da ist ja ein Schritt gleichbedeutend mit 16% Geschwindigkeitsdifferenz. Wenn ich dir die Aufgabe gebe, Tempo 50 zu halten, und der Tacho schwankt zwischen 40 & 56 wild hin und her - da machst du auch nix.

Da kannst du aus meiner Sicht nur Vollgas fahren. Und das Gesamtsystem darauf auslegen. Oder wahlweise die Motorgeschwindigkeit oder Encoderauflösung deutlich erhöhen. Aber leider denke ich zu deutlich. Bzw was natürlich auch ginge: den Encoder an den Motor *vor* dem Getriebe zu hängen, wenn der das zulässt.
Benutzeravatar
Dennis89
User
Beiträge: 1156
Registriert: Freitag 11. Dezember 2020, 15:13

Guten Abend,

danke für deine ausführliche Antwort und für die Analyse meiner Positionsdaten.
Erst mal sorry, das ich zwei mal den gleichen Link gepostet habe. Die Positionen und die Zeiten

Ja das wurde im open loop gefahren. Auskommentiert wurde das nur, weil ich mit dem Regler nochmal etwas gespielt hatte.

Deine Anmerkungen zum Code werde ich mal einpflegen. Hoffentlich wird die kommende Woche etwas entspannter.

Das sind natürlich wirklich schlechte Nachrichten. Den Encoder kann ich so nicht direkt an den Motor anbauen. Das ist alles mehr oder weniger eine Einheit. Ich schau mir das noch mal an, aber so wie ich es in Erinnerung habe, sehe ich da schwarz.

Dann muss ich jetzt wohl warten bis die Maschine steht und das einfach mal laufen lassen und sehen wie sich das System verhält.

Jetzt ist es schon etwas spät, ich muss morgen noch mal über deine Antwort mit der Geschwindigkeiterhöung des Motors nachdenken. Spontan hätte ich gesagt, wenn meine Auflösung des Encoders zu gering ist, dann muss der Motor langsamer laufen um alles ordentlich aufzeichnen zu können. Das muss ich mir morgen in Ruhe noch mal durch den Kopf gehen lassen. Habe ehrlich gesagt deine Nachricht gerade erst gesehen und ich antworte jetzt noch, damit die richtigen Links zu den Messdaten vorhanden sind.


Grüße und gute Nacht
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
__deets__
User
Beiträge: 14542
Registriert: Mittwoch 14. Oktober 2015, 14:29

Wenn der Motor bei Vollgas 6 Schritte pro Zeitschritt macht, dann macht er bei langsamerer Fahrt noch weniger pro Zeitschritt. Das hilft nicht.

Es gibt zwei Stellschrauben, wenn alles sonst bleibt: Erhöhung der Encoder Geschwindigkeit. Oder Vergrößerung des Zeitintervalls. Ersteres geht nur mechanisch. Wenn das Ding nicht an den Motor angeflanscht werden kann, dann ginge ggf auch eine Übersetzung via Riemenscheiben. Um also die Reduktion der Geschwindigkeit zu kompensieren. Das ist normalerweise ungünstig, weil es die Genauigkeit verringert. Da du aber nur eine Fahrt in eine Richtung hast, und einen Endschalter, ist das wahrscheinlich ok.

Die Alternative mit dem verlängerten Zeitintervall hat natürlich potentiell katastrophale Nachteile: wenn du zb auf 1/25 gehst, also das interval vervierfachst, dann kann das System uU einfach nicht mehr schnell genug reagieren.

Zu guter letzt: du musst die geregelte Fahrt so ansetzen, dass die bei vielleicht 80% der maximalen Geschwindigkeit liegt. Sonst hast du ja gar keinen Spielraum. Das sind dann konkret schon wieder nur 5 statt 6 Schritten pro Zeitschritt. Das heißt bei Abweichung vom nur einem Schritt ist man wieder bei Vollgas. Das geht in meinen Augen nicht.

Probier mal ein doppelt so langes interval. Und dann ein vierfaches. Und auch dafür sollte übrigens diese Wartezeit eine Kosntante sein, statt die einfach im Code stehen zu haben.
Benutzeravatar
Dennis89
User
Beiträge: 1156
Registriert: Freitag 11. Dezember 2020, 15:13

So guten Morgen,

der folgende Text ist entstanden, bevor du deinen letzten Post geschrieben hast. Ich lasse den mal unverändert, dann siehst du, wieso ich meinte den Motor langsamer laufen zu lassen.
__deets__ hat geschrieben: Sonntag 20. März 2022, 20:28 Aber durchaus stilistisch verbessern. Zb andauern die PWM auf 100 statt einmal zu setzen. Und die Code dopplung der Zeiterfassung in beiden Ansteuerungen, statt das einmal zu machen [/quote ]
Dass der PWM-Wert in der Schleife steckt, ist mir durch die Lappen gegangen. Für den Rest den du erwähnt hast, habe ich keine plausible Ausrede. :oops:
__deets__ hat geschrieben: Sonntag 20. März 2022, 20:28 dann haben wir da ~6 Schritte pro Zeitschritt.
Okay ich verstehe, wir haben das Problem, dass wir nicht wissen was "innerhalb" der 6 Schritte passiert. Das sehe ich ein, dass ist schlecht bis scheisse.
__deets__ hat geschrieben: Sonntag 20. März 2022, 20:28 Oder wahlweise die Motorgeschwindigkeit oder Encoderauflösung deutlich erhöhen.
Ich habe pro Umdrehung 1000 Messpunkte. Wenn ich Vollgas gebe und wie im Code alle 0,01 Sekunden messe, dann wird während der Zeit von 0,01 Sekunden 6 Messpunkte überfahren. Dann hätte ich rein theoretisch und rechnerisch zwei Möglichkeiten um jeden Messpunkt zu erfassen. Entweder ich muss öfters messen, in dem Fall alle 0,00167 Sekunden oder ich muss den Encoder so langsam drehen, damit er nur alle 0,01 Sekunden an einem Messpunkt "vorbeikommt". Sind die Gedanken falsch bzw. übersehe ich etwas? Mal abgesehen von der dritten Möglichkeit, die höhere Encoderauflösung.

__deets__ hat geschrieben: Sonntag 20. März 2022, 20:28 weil deine Zeitberechnung nicht optimal ist

Der Punkt würde mich auch noch interessieren. Ich hatte 'ticks_diff' so verstanden, dass das dafür gut ist. Die Probleme mit dem Überlaufen und die Probleme mit dem zu langen warten zwischen den Messungen, die in der Doku erwähnt sind, treffen bei mir meiner Meinung nach nicht zu.


Aber hier beziehe ich mich auf deinen neusten Post.
Leider kann ich dazu gar nicht viel schreiben, weil ich gerade nicht erkennen kann, was ich davon habe, wenn ich pro Zeitinterval mehrere Schritte "überfahre". Kannst du mir bitte sagen, wo ich mich da wieder verrannt habe?

Vielen Dank und Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
__deets__
User
Beiträge: 14542
Registriert: Mittwoch 14. Oktober 2015, 14:29

Du hast da eine komische Vorstellung. Es geht nicht darum, jeden Schritt einzeln zu erfassen. Das der Encoder mit dieser PCNT-Klasse behandelt wird, die Ja Spezial-Hardware im ESP anspricht, die einen eben genau davon befreit, dass man per CPU jeden einzelnen Flankenwechsel erfassen muss.

Jede Regelung, die ich kenne, arbeitet immer in einem festen interval. Die Geschwindigkeit, die man fährt, sind die Anzahl der gezählten Schritte, geteilt durch die Zeit. Wenn du im Auto sitzt, schaust du doch auch auf den Tacho alle paar Sekunden, statt zu versuchen, jede Radumdrehung mitzubekommen.

Und ich verstehe nicht, wieso du denkst, das würde irgendwie helfen. Dein Ziel ist eine bestimmte Geschwindigkeit. Nicht, so langsam zu fahren, dass der Zähler auch garantiert immer einen Schritt gemacht hat. Und die Geschwindigkeit variiert doch auch, mindestens & geplant in den Rampenphasen, aber faktisch immer. Woher nimmst du denn dann eine Adaption deines Messintervals her? Du kennst das doch gar nicht, weil wenn du es kennen würdest, wüsstest du schon alles über das System. Und musst gar nix mehr messen.

Nun ist der hier vielleicht unterliegende Gedanke, der dich da verwirrt, nicht komplett falsch: man kann natürlich statt Schritten / Zeit auch Zeit / Schritt bestimmen, und aus dem Kehrwert die Geschwindigkeit berechnen. Und da reicht uns dann eigentlich auch ein geringerer aufgelöster Encoder, weil es ja letztlich egal ist, wie man die Geschwindigkeit ermittelt. Das dreht dann aber die Regelung auf den Kopf: du müsstest dazu die Pulse des encoders in einem Interrupt erfassen, und in dem Moment deine Regelung durchrechnen. Das ist aber Unsinn. Denn deine Regelung kostet Zeit. Und das immer gleich viel. Wenn man die jetzt also plötzlich von der Flanke triggers lässt, dann hat man diverse Probleme erzeugt:

1) die CPU Last steigt proportional mit der Geschwindigkeit an.
2) die maximale Geschwindigkeit ist dann beschränkt durch die Zeit, die deine Regelung dauert.
3) wenn dein Motor steht, läuft per Definition die Regelung nicht. Es muss also einen zweiten Mechanismus geben, der den anwirft, wenn du losfahren willst.

Summa summarum ist das also Quatsch. Theoretisch kann man einen hybriden Ansatz fahren: die Regelung läuft im interval, und die Geschwindigkeit wird im IRQ bestimmt. Aber das erlaubt immer noch nicht die Verwendung des PCNT (der befreit uns ja gerade davon!). Der ist aber kein Selbstzweck, nur bin ich skeptisch, dass man in micropython einen IRQ programmiert, der schnell & genau genug ist, um das zu schaffen. Das müsste dann schon C sein. Und wie gesagt: es ist ein Würgaround.

Zur Frage nach der Zeit: ticks_diff ist super. Das problem liegt woanders: du willst 100Hz erreichen. Aber was du machst ist

1) 0.01 Sekunden schlafen
2) wenn es gut geht, genau danach geweckt werden. Aber das OS garantiert das nicht, du hast also 0.01 + x geschlafen.
3) du tust Dinge, die auch Zeit kosten, y viel.

Summa summarum ist deine Frequenz also 1 /(0.01 + x + y). Also irgendwas kleiner als 100Hz.

Das ist ein Klassiker. Der Weg daraus besteht darin, die Wartezeit adaptiv zu machen: du berechnest einen zukünftigen Moment ausgehend vom Startzeitpunkt, und wartest so lange, bis der erreicht ist. Dann kommt zwar pro Schritt immer noch das x als Jitter dazu, aber x + y werden rausgerechnet.

Code: Alles auswählen

t = now()
while True:
    tuwas() # y
    t += interval
    sleep(t - now()) # hier entsteht x, aber korrigiert sich ja wieder im nächsten sleep. 
__deets__
User
Beiträge: 14542
Registriert: Mittwoch 14. Oktober 2015, 14:29

Und noch ein Nachtrag: es ist nicht schlimm, nicht zu wissen, was in den 6 Schritten passiert. Das problem ist, dass es zu große Abweichung bedeutet, wenn ein schritt so viel Gewicht hat durch die geringe Auflösung. Wenn du 50% der maximalen Geschwindigkeit erreichen willst, dann sind das 3 Schritte/Interval. Beim nächsten Mal wirst du aber dann wahlweise 2, 3, oder 4 Schritte bekommen. Sprich: 34% oder 50% oder 66% der Geschwindigkeit. Keine Regelung der Welt kann vernünftig eingreifen, wenn eine Abweichung zwangsweise immer so groß ist. Entweder ist die so lax eingestellt, dass sie nicht hinterherkommt, wenn last aufkommt. Und beschleunigt auch nicht richtig. Oder so hart, das sie auf dem Gaspedal rumtanzt.
Benutzeravatar
Dennis89
User
Beiträge: 1156
Registriert: Freitag 11. Dezember 2020, 15:13

Vielen Dank für die sehr ausführliche Erklärung.

Erst mal was positives: Das mit 'sleep' habe ich verstanden.

Meine komische Vorstellung kam daher, da ich nicht begriffen habe, auf was du mit den 6 Schritten raus wolltest. Da habe ich mir dann etwas zusammengestrickt und eine Theorie überlegt, was du damit sagen hast wollen. Gut war Quatsch, aber immerhin habe ich es jetzt verstanden.
Deine ganze Erklärung plus folgender Satz:
Das problem ist, dass es zu große Abweichung bedeutet, wenn ein schritt so viel Gewicht hat durch die geringe Auflösung
hat mein Rätsel im Kopf gelöst.

Das ich 80% als maximale Geschwindigkeit ansehen soll, leuchtet mir auch ein.

Dann versuche ich das mit dem größeren Zeitinterval mal aus.

Hast du im Allgemeinen Erfahrungswerte was die Auflösung des Encoders angeht? Wie geht man bei der Auswahl vor? Was würdest du als maximale Gewichtung der einzelnen Schritte ansetzen?
Ich weis ja nicht wie sich das nachher im zusammengebauten Zustand verhält und wenn es gar nicht geht, dann wird man ja nicht drum herum kommen passende Hardware zu kaufen.

Vielen Dank und Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
__deets__
User
Beiträge: 14542
Registriert: Mittwoch 14. Oktober 2015, 14:29

. Ich hätte das alles durchgerechnet & dann das so abgeschätzt, dass ich im Rahmen einer von mir gesteckten Toleranz wäre, aber auch aus dem Bauch. Also etwa 1%. Sprich: eher 15000 Schritte. Deinen Encoder gibt’s ja auch mit 5000. Da sind 3% Schwankung, die in der Regelstrecke minimal auflaufen können. Wäre mir etwas zu hoch, aber vielleicht reicht es auch. Ich hatte mal einen Roboter aus einem Kunstprojekt zur Reparatur. Der war von Phillips Ingenieuren gebaut. Und hatte etwa 30K Schritte pro Umdrehung. Also ein halbes % auf deinen Fall umgerechnet. Das ging gut.
Antworten