Strona główna ASTOR
Automatyka w praktyce

Jak wykorzystać bibliotekę C# do programowania robota Astorino

Kontakt w sprawie artykułu: Kamila Jaworowska - 2025-08-11

Z tego artykułu dowiesz się:

  • jak stworzyć projekt z wykorzystaniem języka C# i frameworka .NET,
  • jak stworzyć własną aplikację do sterowania robotem Astorino.

Robot edukacyjny Astorino posiada dedykowaną bibliotekę do programowania w języku C#. Umożliwia ona na stworzenie aplikacji desktopowej, która pozwala na poruszanie robotem (zarówno jego przegubami, jak i w układzie kartezjańskim), obsługę sygnałów wejść i wyjść oraz uczenie robota punktów i ruch do nich.

To nie wszystko, biblioteka oferuje więcej możliwości, takich jak np. monitorowanie prędkości TCP, komunikacja Modbus i wiele innych. Po szczegóły odsyłam do dokumentacji.

Artykuł ten został napisany z myślą o osobach, które znają podstawy programowania strukturalnego i obiektowego. Cały projekt jest dostępny do pobrania.

Kilka słów o C# i .NET

Język C# to powszechnie stosowany język programowania, stawiający duży nacisk na programowanie obiektowe. Wywodzi się bezpośrednio z rodziny języków C oraz C++. Język ten jest wydajny, czytelny i często stosowany przy tworzeniu aplikacji.

Blisko z nim związany jest .NET: stworzony przez Microsoft framework umożliwiający tworzenie aplikacji desktopowych. Przyjazny użytkownikowi interfejs pozwala na proste dodawanie kontrolek, takich jak przyciski czy pola tekstowe.

Niniejszy artykuł został opracowany z wykorzystaniem środowiska Visual Studio 2022.

Astorino API

Astorino API to biblioteka służąca do komunikacji z robotem Astorino. Komunikacja może odbywać się za pośrednictwem portu szeregowego (USB) lub Ethernet TCP/IP. API aktualnie działa na wersji .NET 6.0 oraz umożliwia ruch robotem tylko wtedy, kiedy ten jest w trybie REPEAT MODE.

W bibliotece znajdują się następujące pliki:

  • astorino_lib.dll – wszystkie funkcje komunikacyjne,
  • System.IO.Ports.dll – biblioteka COM,
  • System.Managment.dll – biblioteka COM, umożliwia lokalizacje Astorino w liście portów COM,
  • ZeroConf.dll – biblioteka mDNS (komunikacja sieciowa),
  • System.Reactive.dll – biblioteka wymagana przez ZeroConf,
  • System.CodeDom.dll – biblioteka wymagana przez ZeroConf,
  • Astorino.cs – kod źródłowy biblioteki.

Tworzenie nowego projektu

Aby stworzyć nowy projekt z wykorzystaniem Astorino API, należy wykonać następujące kroki:

1. Tworzymy nowy projekt z szablonu .NET 6.0 (alternatywnie można stworzyć program w konsoli, ten przypadek nie będzie omawiany w artykule).

2. Następnie należy dodać odwołanie/reference (kliknięcie prawym przyciskiem na Zależności/Dependencies).

3. Za pomocą Przeglądaj…/Browse… znaleźć lokalizację API i wybrać stamtąd:

  • astorino_lib.dll
  • System.IO.Ports.dll
  • System.Managment.dll

4. Po załadowaniu okno projektu powinno wyglądać tak:

W dokumentacji Astorino API znajduje się przykład zarówno aplikacji konsolowej, jak i w .NET. Polecam najpierw zapoznać się z tamtym kodem, jest on prosty i nie posiada zbyt wielu funkcjonalności, dlatego świetnie posłuży za wprowadzenie dla osób, które nie miały wcześniej styczności z .NET.

Założenia projektu

Dobrą praktyką jest, aby na początku określić, jakie funkcjonalności powinien spełniać projekt. Ułatwi to zaplanowanie pracy i późniejsze wdrażanie nowych funkcji.

Najważniejsze cechy naszego projektu:

1. Główne okno aplikacji umożliwia:

  • połączenie się z robotem za pomocą portu COM, oraz rozłączenie się,
  • włączenie i wyłączenie silników oraz podgląd ich stanu,
  • rozpoczęcie zerowania oraz podgląd jego stanu,
  • ruch do pozycji domowej po naciśnięciu przycisku,
  • zawiera przycisk Jogging otwierający nowe okno oraz pola tekstowe zapewniające podgląd wartości wszystkich przegubów.

2. Dodatkowe okno posiadające trzy zakładki:

  • Pierwsza zakładka – Jogging – umożliwia ruch przegubami oraz w układzie kartezjańskim, odczyt aktualnej pozycji robota oraz zadanie mu prędkości i kroku, z jakim ma wykonywać ruchy.
  • Zakładka Teaching posiada tabelę podglądu współrzędnych wszystkich punktówm z rozróżnieniem na punkty złączowe i kartezjańskie. Oprócz tego przycisk Teach umożliwia nauczenie robota aktualnej pozycji do wybranego punktu. Przycisk Execute pozwala na wykonanie ruchu do wybranego punktu z uwzględnieniem zadanego przez użytkownika punktu (JMOVE, LMOVE, LAPPRO). Ponadto podczas wykonywania automatycznego ruchu pojawia się blokujące okienko z możliwością wciśnięcia przycisku Emergency stop, który natychmiast zatrzyma robota.
  • Ostatnią zakładką jest I/O signals, pozwalająca na odczyt stanów sygnałów wejść i wyjść, jak również sterowanie wyjściami.

Okno główne – wygląd

Okno główne zostało zaprojektowane tak, jak na grafice poniżej.

Znajduje się w nim:

  • Sześć przycisków: Go home, Jogging, Connect, Disconnect, Motors Off, Zeroing. Są to obiekty typu Button.
  • Sześć obiektów typu TextBox, służących do wyświetlania aktualnych wartości pozycji przegubów.
  • Sześć etykiet z numerami przegubów. Są to obiekty typu Label.

Należy zwrócić uwagę na kilka szczegółów:

  • W przypadku wszystkich kontrolek warto ustawiać nazwy obiektu na takie, któe umożliwią potem zrozumiałe odwoływanie się w kodzie, np. Go_home_button zamiast button1.
  • Jeśli chcemy wybrać tekst wyświetlany na kontrolce, należy zmienić parametr Text.
  • Jeśli chcemy zmienić kolor kontrolki, można to zrobić za pomocą parametru BackColor.
  • Na grafikach poniżej przedstawiono pola, w których można zmienić te i inne parametry.

Okno główne – program

Framework .NET niezwykle ułatwia tworzenie aplikacji, ponieważ kiedy dodajemy nową kontrolkę w oknie edycji wyglądu naszej aplikacji, automatycznie odpowiednie deklaracje powstają w kodzie. Naszym zadaniem jest zaprogramować zdarzenia. Zdarzenie to najprościej mówiąc kod opisujący, co się stanie w reakcji na działania użytkownika. Przykładowo przycisk może mieć zdarzenie OnClick, które wykonuje się wtedy, kiedy użytkownik wciśnie przycisk.

Na początku jednak zajmijmy się zmiennymi, które będą potrzebne w naszym oknie. Aby w ogóle móc korzystać z Astorino API na początku programu należy zawrzeć linię:

Zapewni to dostęp do wszystkich funkcji i obiektów potrzebnych od komunikacji z Astorino. Wewnątrz klasy naszego okna tworzymy następujące zmienne:

Są to odpowiednio:

  • astorino (typ zmiennej) r (nazwa zmiennej) – stworzenie nowego obiektu będącego reprezentacją naszego robota. Większość metod zawartych w Astorino API odnosi się do tego obiektu i umożliwia np. pobranie danych o jego pozycji.
  • astorino.retVal to typ zmiennej z Astorino API. Wiele funkcji zwraca właśnie taki obiekt, który może w sobie zwierać składowe takie, jak wartości czy RetunCode, informujący o stanie robota.

Zmienne te posłużą do przechowywania informacji np. o stanie silników, wartościach złączy robota itd.

Aby zapewnić czytelność kodu, utworzone zostały różne funkcje, które zajmują się wykonywaniem pewnych powtarzalnych czynności. W przypadku tego okna jest to funkcja update_position().

Metoda JT() służy do pozyskania informacji o aktualnych wartościach złączy robota. Zwracany typ to astorino.retVal. Zapisujemy to do naszej zmiennej joint_pos. Odwołując się pola Values (które jest tablicą typu Double) stosujemy metodę ToString(), która konwertuje to na łańcuch znaków. Przypisujemy je kolejno do pól Text utworzonych TextBoxów, mających pokazywać pozycję złączową robota.

Zaimplementowano również obsługę wyjątku, np. gdy robot zostanie przypadkowo rozłączony i nie będzie mógł odczytać wartości zmiennych. Warto również ustawić parametr ReadOnly tych kontrolek na True, tak aby użytkownik nie mógł ich sam uzupełnić.

Przycisk Connect

Pierwszym zaprogramowanym zdarzeniem będzie wciśnięcie przycisku Connect. Aby dodać wydarzenie typu Click, wystarczy, że w ekranie edycji okna dwukrotnie wciśniemy naszą kontrolkę. Ogólna struktura kodu jest następująca:

Większość funkcji będzie korzystała z instrukcji try…catch, co umożliwia prostą obsługę wyjątków.

Na początku wykonana została metoda findUSB() na obiekcie robota r. Jeżeli po jej wykonaniu nie znaleziono dostępnych robotów, zwraca ona wyjątek. W przypadku gdy istnieje dostępny robot, funkcja łączy się z nim i pokazuje okienko MessageBox z informacją o pozytywnym połączeniu się.

Następnie metoda isMotorOn() wywołana na obiekcie robota zwraca stan silników, gdzie 1 oznacza włączone, a -1 wyłączone. W zależności od tego zmieniony zostaje kolor przycisku oraz jego tekst. W przypadku, gdyby wartość ta nie została odczytana, zwracany jest wyjątek.

Analogicznie działa fragment kodu sprawdzający zerowanie robota.

Na sam koniec funkcja update_position() wpisuje aktualne wartości złączy do kontrolek TextBox.

Obsługa wyjątków w każdym miejscu programu wygląda niemalże identycznie – różnią się tylko komunikaty błędów. Jest to utworzenie nowego okna z komunikatem oraz wyświetlenie go.

Przycisk Disconnect

Przycisk Disconnect będzie służył do zakończenia połączenia z robotem. Wykorzystuje metodę Disconnect().

Przycisk Motor Off/On

Przycisk ten ma za zadanie odczytać i zmienić aktualny stan silników. Realizowane jest to za pomocą metody isMotorOn() i w sytuacji, gdy silniki są wyłączone, wykonywana jest metoda setMotorOn(), zmieniany wygląd przycisku oraz nadpisywana zmienna motor_status.

Analogicznie dzieje się w sytuacji odwrotnej, a jeśli stan silników nie został odczytany, zwracany jest wyjątek. Warunek status == 0 sprawdza stan połączenia z robotem.

Przycisk Zeroing

Służy do wyświetlenia stanu zerowania, jak również przeprowadzeniu go. Zdarzenie to jest asynchroniczne, a więc nieblokujące, co umożliwia wyświetlenia dodatkowego okna z opcją Emergency stop. Opis samego okna jest przedstawiony w dalszej części artykułu. Sama funkcja jest bardzo podobna do tej sterującej silnikami.

Przycisk Go Home

Służy do przemieszczenia robota do pozycji domowej. Jest to również działanie asynchroniczne. Do samego ruchu robotem użyta została metoda HOMEAsync(90, 90, 90),której parametrami są prędkość, przyspieszenie i opóźnienie.

Przycisk Jogging

Przycisk ten służy do otworzenia nowego okna z panelem sterowania robotem, obsługą sygnałów oraz możliwością uczenia i odczytywania punktów. Najważniejsze jest to, aby przy tworzeniu okna Jogging przekazać jako argument obiekt naszego robota, dzięki temu wszystkie funkcje w nowym oknie będą dotyczyły tego samego robota.

Odświeżanie pozycji

Aby w na bieżąco odczytywać pozycję robota, potrzebna jest funkcja, która będzie w nieskończoność wywoływała update_position(). Wykorzystamy tu zdarzenie Load, wywoływane po załadowaniu się okna.

Okno Jogging panel – wygląd zakładki Jogging

Do stworzenia tego okna została wykorzystana kontrolka TabControl, która pozwala w ramach jednego okna na utworzenie kilku zakładek, w tym przypadku odpowiednio: Jogging, Teaching oraz I/O signals.

Pierwsza z nich posiada dwanaście przycisków pozwalających na ruch na plusie lub minusie danej osi. Domyślnie przyciski są podpisane od numerów przegubów, jednak wchodząc w tryb kartezjański zmieniają się na X+, Y+ itd. Oprócz tego dwanaście TextBoxów służy do wyświetlania aktualnej pozycji robota. Obok każdego z nich znajduje się adekwatny Label.

Dwa RadioButtony odpowiadają za przełączanie trybu ruchu. RadioButtony idealnie się do tego nadają, ponieważ jednocześnie może być zaznaczony tylko jeden z nich. Na samym dole znajdują się dwa suwaki (TrackBar), służące do zadawania prędkości ruchu robota oraz kroku, z jakim ma się poruszyć po pojedynczym wciśnięciu przycisku.

Okno Jogging panel – kod dla zakładki Jogging

Inicjalizacja

Ważną rzeczą jest to, aby nowe okno przyjmowało jako argument robota, na którym będzie wykonywało różne akcje. Należy również dodać na początku programu using astorino_lib;jak poprzednio.

Następnie należy stworzyć szereg zmiennych, posłużą one głównie od odczytywania pozycji robota, punktów z jego pamięci oraz wykonywania ruchów.

Oprócz tego stworzone zostały dwie klasy TransPoint i JointPoint, które służą do zapisywania współrzędnych konkretnych punktów.

Funkcje pomocnicze

W celu zmniejszenia ilości powtarzalnego kodu powstały następujące funkcje:

  • check_robot()
  • perform_joint_move(int axis, bool inc)
  • perform_lin_move(int axis, bool inc)
  • update_positions()
  • perform_rotation(int axis, bool inc)

Funkcja check_robot() służy do sprawdzenia kolejnych warunków takich jak: czy silniki są włączone, czy robot jest w trybie REPEAT itp. Za każdym razem zwraca ona użytkownikowi stosowny komunikat, jeśli któryś z warunków jest niespełniony.

Funkcja perform_joint_move(int axis, bool inc) służy do wykonania ruchu złączowego. Funkcja na początku pobiera aktualną pozycję robota i zapisuje jej wartości jako target. target jest sześcioelementową tablicą zmiennych double. W zależności od tego, czy zmienna inc jest ustawiona na true czy false, ruch może być wykonywany w dodatnią lub ujemną stronę obrotu przegubu.

W zależności od wybranego przegubu (zmienna axis) aktualna pozycja zostaje nadpisana o wartość step tylko względem wybranego przegubu. Na koniec robot wykonuje ruch złączowy do nowo wyliczonego punktu.

Funkcja perform_lin_move(int axis, bool inc) realizuje tę samą funkcjonalność, ale z wykonaniem ruchu liniowego, odczytując i nadpisując współrzędne kartezjańskie robota.

Funkcja update_positions() aktualizuje wartości w TextBoxach do aktualnych wartości pozycji robota. Domyślnie Pose() zwraca orientację w kątach Eulera, natomiast metoda toRPY()pozwala na przeliczenie ich do kątów roll, pitch, yaw.

Funkcja perform_rotation(int axis, bool inc) pozwala na wykonanie obrotu wokół wybranej osi w kątach RPY, a nie domyślnych OAT (kątach Eulera). Istotna jest świadomość, że zmiana wartości jednego z kątów RPY wpływa na wszystkie kąty OAT, stąd potrzeba napisania wszystkich trzech.

Zdarzenia na przyciskach

Głównym celem zakładki Jogging jest uzyskanie możliwości łatwego poruszania robotem. Do tego celu wykorzystane zostały przyciski od J1+ i J1- do J6+ i J6-. Ich podstawowym działaniem jest wykonanie ruchu danym przegubem lub wykonanie ruchu w przestrzeni kartezjańskiej, jeśli użytkownik wybierze takowy tryb. Do zmiany trybów posłużą RadioButtony. Wykorzystują one tylko dwa proste zdarzenia zmieniające tekst na przyciskach:

W przypadku każdego z przycisków wykonujących ruch przyjęto następujący kod:

Doskonale widać, czemu powtarzalny kod warto umieszczać w funkcjach, znacznie zmniejsza to objętość kodu oraz ułatwia jego analizę. Wcześniej zdefiniowana funkcja check_robot() sprawdza stan robota i jeśli ten jest gotowy do pracy, zwraca wartość true.

Następnie, w zależności od wybranego RadioButtona, wykonujemy ruch, a jako argument axis podany zostaje numer osi robota (numerując od 0 do 5) oraz true, jeśli ruch jest wykonywany w dodatnim kierunku, a false, jeśli w ujemnym. Na koniec update_positions() aktualizuje wartości w TextBoxach.

Ostatnimi zdarzeniami w zakładce Jogging są te związane z aktualizacją ScrollBarów:

Okno Jogging panel – wygląd zakładki Teaching

Aby dodać kolejną zakładkę, należy zmienić parametr Collection kontrolki TabControl. W ten sposób można dodać dodatkowe zakładki oraz spersonalizować ich wygląd.

W zakładce Teaching wykorzystane zostaną następujące kontrolki:

  • DataGridView – posłuży do wyświetlania tabeli punktów,
  • Dwa Buttony –  Teach i Exectute, służące do uczenia punktów i wykonywania poleceń ruchowych.
  • Trzy ComboBoxy – służące do wyboru typu ruchu, rodzaju punktu oraz numeru punktu.
  • TextBox – do podania wartości odsunięcia dla polecenia LAPPRO.
  • Label – do oznaczenia TextBoxa od LAPPRO.

Okno powinno mieć następujący wygląd:

Dobrym pomysłem jest wpisanie domyślnych wartości do ComboBoxów. Spowoduje to, że domyślnie po uruchomieniu okna od razu załaduje się tabela punktów, będzie to wygodniejsze dla użytkownika.

Okno Jogging panel – kod dla zakładki Teaching

Funkcje pomocnicze

Do obsługi funkcjonalności w tej części programu stworzono następujące funkcje pomocnicze:

  • update_point_list()
  • get_point_index()

W tym miejscu potrzebne będą wcześniej stworzone klasy TransPoint oraz JointPoint. Funkcja update_point_list() została napisana w następujący sposób:

Na początku funkcja sprawdza, jaki rodzaj punktów został wybrany w ComboBoxie. W zależności od tego czyta ten rodzaj punktów z pamięci robota i zapisuje go. Lista punktów w pamięci programu zostaje wyczyszczona.

Następnie funkcja w pętli for przypisuje wartości tych punktów do kolejnych pól listy. Na koniec dane w tabeli DataGridView zostają zaktualizowane poprzez przypisanie im nowego źródła danych.

Wnętrze pętli for wygląda w sposób następujący:

Analogicznie dla współrzędnych złączowych.

Funkcja get_point_index() służy do odczytania numeru punktu z ComboBoxa. Jako że tekst w ComboBoxie jest typu String, należy go przekonwertować na typ int. Funkcja zwraca wartość indeksu danego punktu.

Zdarzenia na przyciskach

W tej zakładce główną rolę odgrywają przyciski Teach i Execute. Przycisk Teach ma zapisać punkt w pamięci robota oraz odświeżyć dane w tabeli, tak aby użytkownik mógł zobaczyć, że punkt się zapisał.

Zasada działania zdarzenia na wciśnięcie Teach jest bardzo prosta, w zależności od wybranego typu punktów aktualna pozycja jest zapisywana z takim numerem, jaki jest wybrany w ComboBoxie dzięki funkcji get_point_index().

Zdarzenie odpowiedzialne za wykonanie ruchu jest nieco dłuższe. Wynika to z faktu, że każdy ruch może być wykonany przy pomocy trzech komend: JMOVE, LMOVE, LAPPRO, a każdy z nich może mieć punkt docelowy typu TRANS lub JOINT.

W zależności od wybranych ComboBoxów instrukcje warunkowe wybierają odpowiedni ruch. W przypadku wyboru LAPPRO jest potrzeba konwersji Stringa na int. Wykonywanie ruchów otwiera okno z przyciskiem do awaryjnego zatrzymania.

Każdy ruch został zrealizowany w podobny sposób, tak jak w kodzie poniżej.

Warto dodać, że pole tekstowe na dystans LAPPRO powinno przyjmować tylko wartości liczbowe. Nadal będzie to String ale ważne, żeby nie było tam liter, to uniemożliwiłoby konwersję. Realizuje to następująca funkcja.

Okno Jogging panel – wygląd zakładki I/O signals

Kolejną zakładkę tworzy się w taki sam sposób, jak poprzednią. Potrzebne jest w niej dwadzieścia przycisków, po jednym dla każdego sygnału, jednak tylko te od sygnałów wyjściowych będą miały zdarzenia po wciśnięciu. Oprócz tego dwa Labele do oznaczenia przycisków.

Okno Jogging panel – kod dla zakładki I/O signals

Funkcje pomocnicze

Do obsługi funkcjonalności w tej części programu stworzono następujące funkcje pomocnicze:

  • update_signals()
  • change_output()

Funkcja update_signals() służy do odświeżenia wartości sygnałów, a następnie zaktualizowania kolorów przycisków. Na początku listy sygnałów zostają wyczyszczone, później w dwóch podobnych pętlach wczytywane są wszystkie sygnały wyjścia i wejścia. Następnie obszerny i dość powtarzalny fragment kodu sprawdza stany sygnałów i zmienia kolory konkretnych przycisków tak, aby zasygnalizować ich stan.

Na koniec funkcja change_output(int signal_index) umożliwia zmianę stanu wyjść. Później aktualizuje stan list.

Zdarzenia na przyciskach

W tej zakładce jedyne zdarzenia to przyciski związane z sygnałami wyjściowymi, każdy z nich wywołuje funkcję change_output() dla swojego numeru.

Okno Movement_win – wygląd

To okno służące do blokowania aplikacji, kiedy robot wykonuje ruch. Jedyny interaktywny przycisk na nim to Emergency stop.

Okno posiada jeden Label oraz przycisk Button do wykonania awaryjnego zatrzymania.

Okno Movement_win – kod

Okno to przy tworzeniu przyjmuje obiekt robota jako argument oraz wykorzystuje zmienne do śledzenia, czy robot jest w ruchu.

Funkcja windo_status() służy do monitorowania ruchu robota i wyłączeniu okna, jeśli ten się nie rusza.

Jedyne zdarzenie w tym oknie to wykonanie zatrzymania awaryjnego i zamknięcie okna.

Podsumowanie

Astorino API umożliwia tworzenie bardzo kreatywnych aplikacji, pozwalających na sterowanie robotem. Dzięki wykorzystaniu frameworka .NET tworzenie interfejsu jest bardzo wygodne. Aplikacje można rozbudować o więcej funkcjonalności, takich jak obsługa sygnałów wewnętrznych, komunikacja Modbus TCP, łączenie się z robotem za pomocą Ethernetu, liczenie kinematyki prostej i odwrotnej czy monitorowanie prędkości robota.

Autor artykułu:


Franciszek Rześny

Praktykant ASTOR

Student III roku kierunku Automatyka i robotyka na Politechnice Poznańskiej

Newsletter Poradnika Automatyka

Czytaj trendy i inspiracje, podstawy automatyki, automatykę w praktyce

Please wait...

Dziękujemy za zapis do newslettera!

Czy ten artykuł był dla Ciebie przydatny?

Średnia ocena artykułu: 0 / 5. Ilość ocen: 0

Ten artykuł nie był jeszcze oceniony.

Zadaj pytanie

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *