Skocz do zawartości

Matthew11

Użytkownicy
  • Zawartość

    103
  • Rejestracja

  • Ostatnio

  • Wygrane dni

    3

Matthew11 wygrał w ostatnim dniu 18 maja

Matthew11 ma najbardziej lubianą zawartość!

Reputacja

106 Mistrz

O Matthew11

  • Ranga
    5/10

Informacje

  • Płeć
    Mężczyzna
  • Lokalizacja
    Kraków
  • Języki programowania
    C, C++

Ostatnio na profilu byli

Blok z ostatnio odwiedzającymi jest wyłączony i nie jest wyświetlany innym użytkownikom.

  1. @lubniewicz @olimek Super! Teraz musicie nam powiedzieć co było przyczyną i jak rozwiązaliście problem
  2. Cześć. Kilka pytań, których celem jest znalezienie potencjalnego problemu a niekoniecznie uzyskanie na nie odpowiedzi: Czy obecna konstrukcja jest przemyślana (średnica kół, położenie IMU, dystrybucja masy), czytałeś ten wpis - https://forbot.pl/blog/budowa-robota-balansujacego-praktyczne-porady-id9178? Czy częstotliwość (output data rate) z jaką wyrzuca dane jest taka sama lub większa od częstotliwości Twojej pętli sterowania? Czy dobierałeś parametry PID w jakiś konkretny sposób? Czy próbowałeś wysterować układ stosując inny filtr zamiast filtru Kalmana np. komplementarny Jak dobierałeś parametry filtru Kalmana? Może Twój Kalman wprowadza jakieś znaczące opóźnienie? Po obejrzeniu filmu, szukałbym problemu najpierw w samej konstrukcji (podlinkowany artykuł) a potem w parametrach PID - wygląda jak przesterowany regulator - zacznij z jakimś małym P i wyzerowanymi członami I i D i stopniowo zwiększaj P aż do uzyskania jakiegoś stabilnego zachowania.
  3. Polecam Ci kupić płytkę z pełnym zestawem czyli np: ESP32 https://botland.com.pl/pl/moduly-wifi/8893-esp32-wifi-bt-42-platforma-z-modulem-esp-wroom-32-zgodny-z-esp32-devkit.html ESP8266 https://botland.com.pl/pl/moduly-wifi/10908-modul-wifi-esp8266-wemos-nodemcu-v3-32mb-11-gpio-adc-pwm.html ESP8266 https://botland.com.pl/pl/plytki-zgodne-z-arduino-adafruit/5294-feather-huzzah-esp8266-modul-wifi-gpio-adc-adafruit-2821.html Coś tego pokroju, która ma w sobie wszystko co potrzebne, która od razu jest gotowa do użycia. Jeżeli projektujesz swoją płytkę, wtedy lepiej kupić "goły" moduł ESP. Tak, w rozumieniu tego samego API. Na przykład, żeby zaświecić diodą możesz napisać pinMode(ledpin, OUTPUT); digitalWrite(ledpin, HIGH); i dla obu płytek efekt będzie ten sam - bo zaimplementowane jest to samo API dla obu tych płytek - pod warunkiem że wykorzystasz Arduino Core dla ESP - https://github.com/esp8266/Arduino (ESP8266 możesz też programować w C). Jako edytor polecam Ci wykorzystać VS Code a jako menedżer płytek rozszerzenie PlatformIO (rozszerzenie do VS Code). Oczywiście możesz też klepać kod w Arduino IDE.
  4. Oczywiście wszystko zależy od wymagań, ale chyba zdecydowanie łatwiej zastosować coś co ma na jednej płytce wszystko co potrzebujesz niż bawić się z dodatkowymi shieldami czy komunikować dwa układy. Zakładam, że Twój projekt to coś więcej niż tylko sterowanie pinem, ale jeśli np. ESP8266 zrobi to samo co może zrobić zwykła ATmega i dodatkowo może załatwić również sprawy sieciowe to chyba jest to wygodniejszym rozwiązaniem. Oba (Uno lub ESP) będą tak samo proste w programowaniu jeśli będziesz pisał w frameworku Arduino. Natomiast wybierając ESP masz wszystko w jednym układzie na jednej płytce - podłączasz do PC i możesz działać.
  5. Faktycznie available() nie zadziała tak jak chcesz, ale już accept() wydaje się być rozwiązaniem Twojego problemu - https://www.arduino.cc/en/Reference/EthernetServerAccept
  6. @jacabe To po dwukropku to tzw. lista inicjalizacyjna konstruktora - https://pl.wikipedia.org/wiki/Lista_inicjalizacyjna_konstruktora. Czyli piszesz tam jakich członków klasy chcesz zainicjalizować jaki argumentami/wartościami. Tutaj DS(oneWire) - przypisz referencji DS referencje do obiektu oneWire przekazanego jako argument konstruktora.
  7. Podaj przykład Jasne, C++ Core Guidelines, Autosar C++14 punkt 6.16.7. Natomiast, mój argument - "bo nie wszystkie kompilatory implementują" - nie był za bardzo trafny...
  8. @jacabe ciekawe, że kompiluje Ci się ten kod, nawet gdy nie masz zainicjalizowanej referencji w konstruktorze (o czym zaraz): private: OneWire &DS; Tutaj: https://godbolt.org/z/cK2DBS minimalny kod, który pokazuje to co chcesz osiągnąć - sprawdź ostrzeżenia (które na razie wiele nie mówią). No bo właśnie tutaj jest kolejny mega poważny problem - próbujesz do DS przypisać referencję do obiektu tymczasowego, przykład z wyjaśnieniem: https://godbolt.org/z/dHwdFs (sprawdź okno po prawej na dole - wydrukowane informacje do konsoli z programu). Pewnie dlatego po za komentowaniu reszta kodu zaczyna działać. A dlaczego tak nie można robić to znajdziesz tutaj: https://www.quora.com/Why-cant-you-return-a-local-variable-by-reference To co nieświadomie chciałeś zrobić - to tak implementuje się wstrzykiwanie zależności, tylko wtedy tą referencję DS trzeba przypisać w konstruktorze np. tak: https://godbolt.org/z/pqHKEU. A jak chcesz stworzyć obiekt bezpośrednio w klasie, tak jak wskazywał @ethanak to robisz tak: https://godbolt.org/z/uy-D2t @ethanak Sporo best practices własnie nie poleca #pragma once, bo nie wszystkie kompilatory implementują, #ifndef #define #endif akurat zrozumie każdy. Dlatego jak piszemy biblioteki (jak w tym temacie) to raczej powinniśmy używać tego drugiego
  9. Odpowiedź na to pytanie jak zwykle brzmi - to zależy. Ale możemy posłużyć się wspomnianym projektem PW-Sat2 OBC i dokonując jego analizy za pomocą narzędzia cloc. Co prawda używają innego rodzaju mikrokontoler, ale... gdy uruchomimy go dla głównego katalogu dostaniemy: $ cloc . ------------------------------------------------------------------------------- Language files blank comment code ------------------------------------------------------------------------------- C/C++ Header 585 19137 50052 148464 C++ 452 10969 2407 47387 C 81 8236 18267 34174 Python 250 3854 715 10390 CMake 163 824 17 3235 Markdown 14 194 0 478 Assembly 1 28 102 208 Mustache 1 1 0 63 ------------------------------------------------------------------------------- SUM: 1547 43243 71560 244399 ------------------------------------------------------------------------------- Prawie ćwierć miliona linii, jak pokopiemy dalej możemy stwierdzić, że istotne foldery to /src i /libs/drivers. Pomijamy katalog /libs/external czyli zewnętrze biblioteki używane w projekcie wtedy: $ cloc libs/drivers/ src/ ------------------------------------------------------------------------------- Language files blank comment code ------------------------------------------------------------------------------- C++ 91 1902 217 9428 C/C++ Header 81 1503 4259 4240 CMake 28 122 0 556 ------------------------------------------------------------------------------- SUM: 200 3527 4476 14224 ------------------------------------------------------------------------------- Prawie 14 tysięcy linii głównego kodu. Jak policzymy ile linii przypada średnio na jeden plik to mamy ~80 linii kodu na plik pomijając puste linie i komentarze. Łatwiej przejrzeć 80 linii kodu niż 4000 w jednym pliku. Natomiast liczba linii kodu to żadna miara jakości projektu, jedne są duże drugie małe, jednak istotne jest to, żeby taki projekt dało się łatwo czytać i łatwo modyfikować niezależnie ile linii kodu na niego się składa. O ile te zmienne globalne nie są współdzielone między wątkami to jeszcze nie ma tragedii. Gdy wchodzą wątki i jednoczesny dostęp do danych gdzie jeden z nich jest operacją zapisu wtedy niestety mamy do czynienia z undefined behaviour. Kopiąc dalej, gdy mamy do czynienia z procesorami, które posiadają pamieć cache np. seria K66 NXP - użycie zmiennych globalnych w niektórych przypadkach może zwolnić nasz system - polecam obejrzeć prezentację Scott Meyers: Cpu Caches and Why You Care. W zasadzie to sam sobie odpowiedziałeś, możesz do tego jeszcze zaprzęgnąć DMA. Generalnie w takich przypadkach musisz zrobić testy i dobrać rozmiary buforów do konkretnych warunków pracy. Nie ma jednego generycznego rozwiązania, które będzie działać w każdym przypadku. Dwie najprostsze opcje to jak już zostało wspomniane - preprocesor i flagi. W C++17 masz if constexpr - ma zalety obu opcji i brak ich wad. Jeszcze innych podejściem może być przeniesienie testowania na PC i mockowanie modułów, które chcesz testować. Mockowanie peryferiów MCU może być trudne, ale z reguły znasz wartości wejściowe/wyjściowe, więc możesz stworzyć testy i wspomniany pomiar temperatury i jej filtrowanie - czyli Twój kod - przetestować bez użycia sprzętu. I właśnie tutaj wychodzi czy Twój kod jest modułowy. Takie podejście wymaga oczywiście dużo więcej pracy, ale gdy coś nie działa, a Ty wiesz, że Twój kod do obsługi czegoś działa w warunkach testowych, to wtedy wiesz że szukasz problemu w konfiguracji sprzętu, jego działaniu itp. Innymi słowy zmniejszasz sobie zakres poszukiwań. Tak użycie GPIO, to dość częsta praktyka i jej narzut jest znikomy, a na pewno policzalny. Niektóry procesory mają rejestry zliczające cykle zegara - więc jest to jakaś alternatywa. Lepiej jest odpowiedzieć na pytanie "na co ZWRACAĆ uwagę?" - tutaj z pomocą przychodzą wszystkie dobre praktyki dla danego języka, nie wiem ile jest materiałów dotyczących języka C, ale za to C++ ma masę różnych list np: Core Guidelines, są też różnego rodzaju standardy jak np. MISRA C/C++ gdzie oprócz konkretnej reguły znajdziesz też wyjaśnienia z przykładami i to jest bardzo konkretna i rzetelna wiedza. Z dostępnych publicznie (dla C++) to np. JSF czy AUTOSAR. Znając dobre praktyki jednocześnie wiesz czego masz unikać.
  10. Nawiązując do interfejsów, o których wspomniał @GAndaLF. W C++ nie mamy zapewnionego przez standard konceptu interfejsu jak w innych językach (np. Java), ale możemy taki stworzyć stosując klasy abstrakcyjne (te, których obiektów nie możemy stworzyć), nawiązując do przykładu sterowania silnikiem przedstawionego przez @GAndaLF możemy napisać coś takiego: struct IMotorDriver { virtual ~IMotorDriver() = default; virtual void moveForward(int speed) = 0; virtual void stop() = 0; }; Taki interfejs reprezentuje naszą abstrakcję jaką jest nasz sterownik silnika (bez szczegółów implementacji). Nasz docelowy sterownik musi ten interfejs zaimplementować w jakiś sposób (to już zawiera pełno szczegółów implementacyjnych) nadpisując metody interfejsu (override lub final). Więc robimy coś takiego: class MotorDriver : public IMotorDriver { // ... void stop() final { speed = 0; setPwm(); } // ... }; Następnie w kodzie odwołujemy się do konkretnego obiektu naszego sterownika za pośrednictwem referencji lub wskaźnika na typ IMotorDriver, np. jeśli przekazujemy go gdzieś dalej do naszej logiki, np: void someLogic(IMotorDriver& _motorDriver) { _motorDriver.moveForward(100); _motorDriver.stop(); } Dzięki temu, jeśli założenia projektowe ulegną zmianie i konieczne będzie sterowanie silnikiem innego rodzaju lub cokolwiek innego, zmiany dokonujesz jedynie przez stworzenie nowej klasy np. MotorDriverA. A pozostała część Twojego programu nie będzie wymagała żadnych modyfikacji z Twojej strony, poza miejscem, w którym tworzysz obiektu sterownika: // MotorDriver motorDriver; MotorDriverA motorDriver; Pełny przykład z wykorzystaniem interfejsów: https://godbolt.org/z/dhGTd4 Natomiast należy pamiętać, że takie rozwiązanie niesie za sobą pewny narzut związany z koniecznością utworzenia vtable - ale możliwe jest zastosowanie optymalizacji.
  11. Od wersji Qt 5.14 lub wyższej mamy nieco inaczej zorganizowaną obsługę różnych ABI dla naszych urządzeń. Wcześniej mieliśmy kilka kitów dla każdego ABI, teraz zostało to zunifikowane i dzięki temu można budować program jednocześnie na kilka architektur. Jednak pracując z konkretnym urządzeniem musimy jakoś wybrać docelowe ABI. Wcześniej wybieraliśmy docelową architekturę za pomocą przycisku: Teraz musimy wejść w ustawienia projektu: Następnie pod nagłówkiem Build Steps, wybrać interesujące nas ABI: Mogę posłużyć się pomocą QtCreator'a, który po kliknięciu przycisku Deploy pokaże mi jakie ABI obsługuje moje urządzenie: W moim przypadku: x86, armeabi-v7a armeabi I takie wybrałem w ustawieniach projektu. Jeśli nie ustawisz odpowiedniego ABI, Twoja aplikacja nie uruchomi się na Twoim urządzeniu. Więcej informacji https://www.kdab.com/qt-for-android-better-than-ever-before/ https://www.qt.io/blog/qt-5.14-android-multi-abi-and-cmake
  12. Z literatury to najbardziej uniwersalnym klasykiem jest chyba Czysty kod i Czysta architektura Roberta C. Martina. Każdy znajdzie tam coś ciekawego dla domeny, w której się porusza. Wiele problemów, które spotyka się w codziennej pracy, ktoś już wcześniej rozwiązał więc czemu by z nich nie korzystać. Wiele ciekawych informacji (nt. wielu spraw o które pytasz) znajdziesz na blogu @GAndaLF np. https://ucgosu.pl/2019/01/podstawy-architektury-embedded-warstwy-i-moduly/ Pewnie (niestety) tak. Lepiej byłoby zapytać "Czy monolit jest dobrym podejściem?" I tak pewnie patrząc, na kod z którym musisz pracować, zapewne odpowiesz, że nie. 4000 linii kodu to nie jest wcale dużo, a jak sam piszesz "bardzo ciężko się w nim odnaleźć, odłączać konkretne peryferia, modyfikować etc. ze względu na ogromne spaghetti, które się wytworzyło." Wprowadzenie modułów wymaga nieco więcej zastanowienia się nad tym co i jak się pisze, ale daje więcej możliwości i jest bardziej podatne na wprowadzanie zmian w końcowym rozwiązaniu. Moduły możesz testować jednostkowo, monolit raczej będzie ciężko. Ale to też zależy co robisz, jak pracujesz nad szybkim projektem, to zależy na Ci na jak najszybszym wykonaniu zadania a nie estetyce. Jak pracujesz nad projektem, który będzie utrzymywany kilka lat, to tam warto poświęcić więcej czasu na dobre zaprojektowanie architektury podatnej na zmiany. Pewnie każdy na swojej drodze nauki programowania spotkał się ze stwierdzeniem że użycie zmiennych globalnych jest złe, kilka linków: https://betterembsw.blogspot.com/2012/12/global-variables-are-evil-sample-chapter.html https://electronics.stackexchange.com/questions/97241/use-of-global-variables-in-embedded-systems Użycie flagi wiąże się z narzutem, który wynika z konieczności jej sprawdzenia, wykorzystanie dyrektyw preprocesora skutecznie wytnie Ci z użycia dany fragment kodu, ale może doprowadzić do sytuacji, że wyłączony kod z użycia przestanie być aktualizowany równolegle z "główną" gałęzią i tak przy odblokowaniu jakiegoś fragmentu kodu możemy się nagle natknąć na błędy kompilacji, np spowodowane zmianą nazwy jakiejś zmiennej. Oczywiście wszystko zależy od zastosowania, natomiast użycie dyrektyw jest bardzo częstą praktyką. W C++17 mamy konstrukcję if constexpr, która może być ciekawą alternatywą. Przy podstawowych typach dobrą praktyką jest przekazywanie danych przez wartość, przy złożonych zalecane jest przekazywanie wskaźników lub referencji (to już w C++). Nikomu nie są potrzebne tymczasowe kopie przekazywanych argumentów. Ale to oczywiście też zależy od kontekstu. Naturalnie spodziewamy się, że funkcja ma jakieś wejście i jakieś wyjście. Jeśli dobrze rozumiem, to zastosowanie o które pytasz - np. konfiguracja jakiejś struktury w jakieś funkcji, najczęściej będzie zrealizowana właśnie przez przekazanie wskaźnika (lub referencji) na obiekt tej struktury. I takie podejście jest stosowanie np. w StdPeriph czy HAL od STM32. Tyle ode mnie, mam nadzieję, że zachęci to innych do wypowiedzi
  13. Nowa wersja QtCreatora - 4.12 - niesie sporo usprawnień w przypadku Androida: Sporo problemów z konfiguracją, które zostały tutaj opisane może zostało już rozwiązanych wraz z nową aktualizacją.
  14. Nigdy takich nie robiłem, ale na tyle na ile znam możliwości Qt to myślę że z pewnością da się takie stworzyć. Natomiast do takich rzeczy potrzebujesz już pomocy dobrego grafika, który stworzy dla Ciebie wszystkie takie ładnie futurystyczne assety. Mając je gotowe to już raczej rzemieślnicza robota. Biedniejszą wersję takiego interfejsu możesz znaleźć w tym przykładzie: https://doc.qt.io/archives/qt-5.10/qtquickcontrols2-automotive-example.html - jak spojrzysz w pliki projektu to samego kodu interfejsu nie jest jakoś bardzo dużo, natomiast trzon całej aplikacji stanowi masa grafik. Nie wiem jak w przypadku innych modeli ale np. dla modelu 3B+ (ten testowałem), można zbudować Qt ze źródeł które wykorzysta sprzętowe wsparcie dla OpenGL z EGLFS. Ze sprzętowym wsparciem raczej nie powinno być większych problemów z generowaniem takich interfejsów. Budując Qt ze źródeł mamy możliwość wykorzystania najnowszej wersji biblioteki oraz dodatkowo zapewnić sobie np. wspomniane powyżej wsparcie sprzętowe, czego np. może nie mieć wersja dostarczona przez dystrybucję.
  15. @kulfi27 Okej w takim razie, jeśli problemem było użycie metody QObject::connect() z Qt4, która używa makr SIGNAL i SLOT: connect(this->device, SIGNAL(readyRead()), this, SLOT(readFromPort())); na connect z Qt5, connect(this->device, &QSerialPort::readyRead, this, &MainWindow::readFromPort); To sugeruje, że Qt nie mogło rozwiązać tego połączenia w czasie wykonywania programu. connect() z Qt4 jest rozwiązywany w czasie programu, natomiast connect() z Qt5 w czasie kompilacji. Ciężko powiedzieć dlaczego nie działa starsza wersja, ale to tylko znak, że należy używać tej z Qt5. Niestety wtedy kiedy pisałem kurs używałem connect() z Qt4 @kulfi27 Z ciekawości czy przy connect() z Qt4 po uruchomieniu programu w konsoli nie były drukowane jakieś ostrzeżenia? Jak się connect() z Qt4 nie powiedzie to jest drukowana informacja. Program do tego while'a wchodzi wtedy kiedy odczytany ciąg znaków zawiera znak końca linii, z przykładu z dokumentacji np: taką może mieć implementację metoda canReadLine(): bool CustomDevice::canReadLine() const { return buffer.contains('\n') || QIODevice::canReadLine(); } Czy dane, które wysyłałeś z uC zawierały znak końca linii? Jeśli nie to masz odpowiedź dlaczego nie wchodziło do while'a. Za to odpowiada mechanizm Sygnałów i Slotów Qt. To nie connect() sprawdza czy w buforze odbiorczym są jakieś dane, connect() się wykonuje i zwraca sterowanie od razu - więc jak by mógł to robić? To co robi connect() to rejestruje dane połączenie w mechanizmie sygnałów i slotów. Wtedy w tej pętli zdarzeń: int main(int argc, char *argv[]) { //... return a.exec(); // <- która startuje tutaj } Jeśli device wyemituje sygnał readyRead() to mechanizm sygnałów i slotów (nie connect()) wywoła w efekcie metodę readFromPort() klasy MainWindow. @erulission akurat mnie ubiegł A jak to działa dokładnie, możesz sprawdzić tutaj.
×
×
  • Utwórz nowe...