Robot na bazie Raspberry Pi #4

View this thread on: d.buzz | hive.blog | peakd.com | ecency.com
·@flocki·
0.000 HBD
Robot na bazie Raspberry Pi #4
![_IGP2434.jpg](https://cdn.steemitimages.com/DQmYUzMUmWwT4cFhk63wzjy9wrtsYQndF32Pw3vozyvqs5e/_IGP2434.jpg)

W poprzednich częściach cyklu, opisałem sposób realizacji mojego robota od strony elektroniki, oraz programu sterującego silnikami urządzenia. Tym razem zgodnie z deklaracją opiszę realizację aplikacji umożliwiającej wysyłanie do robota komend sterujących jego poruszaniem się.  Jak już powiedziałem w poprzednich odcinkach, robot posiada zaimplementowany serwer UDP napisany w Pythonie.Nasłuchuje on na porcie 80, przy czym póki co komunikacja bazuje na adresie rozgłoszeniowym(broadcast) w sieci w jakiej jest on hostem. Nie muszę dzięki temu martwić się jaki adres został dla Raspberry Pi robota przydzielony przez serwer DHCP domowej sieci. W przypadku większej ilości podobnych urządzeń, można będzie dodać ich identyfikację przez dodanie unikalnego ID w pakiecie UDP przesyłanej komendy. Minusem operowania broadcastem jest bez wątpienia ryzyko generowania zbyt dużego ruchu w sieci LAN, jednak w tym wypadku chodzi o raptem kilka bajtów danych rozkazu, więc nie powinno to w niczym przeszkadzać. 

Aplikacja sterująca robotem powstała w C# przy użyciu Windows Forms. Posiada czytelny, prosty interfejs użytkownika.

![ui.jpg](https://cdn.steemitimages.com/DQmZo4mar9Pi8PysZzqJHyzxoA7YCVpdXqRtjZPktCdUvPb/ui.jpg)


Po prawej stronie okienka umieściłem przyciski zmiany kierunku robota. Z lewej przewiduję okno na stream video z kamery(której póki co robot jeszcze nie posiada). Przycisk "Zakończ" zamyka aplikację. Całość umożliwi sterowanie urządzeniem bez konieczności bycia w domu, za pośrednictwem sieci. W dzisiejszym artykule skupię się jednak na samym sterowaniu.

Oto kod głównego okna aplikacji sterującej urządzeniem
```
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace RobotPi
{
	public partial class Form1 : Form
	{
		UdpSender udpSender;
		public Form1()
		{
			InitializeComponent();
			//tu tworzymy obiekt umożliwiający wysyłanie datagramów udp do robota
			//jako argumenty w tym wypadku podaję adres rozgłoszeniowy sieci
			//w jakiej pracuje robot, oraz port na jakim robot nasluchuje rozkazow
			udpSender = new UdpSender(StaticHelper.BroadcastIp,StaticHelper.Port);
		}


		private void ButtonClose_Click(object sender, EventArgs e)
		{
			Close();
		}

		private void ButtonForward_MouseDown(object sender, MouseEventArgs e)
		{
			Console.WriteLine("jazda do przodu po wcisnieciu");
			udpSender.SendData("forward");

		}

		private void ButtonForward_MouseUp(object sender, MouseEventArgs e)
		{
			Console.WriteLine("zatrzymanie po puszczeniu");
			udpSender.SendData("stop");
		}


		private void StopCommand(object sender, MouseEventArgs e)
		{
			Console.WriteLine("zatrzymanie po puszczeniu");
			udpSender.SendData("stop");
		}

		private void ButtonRight_MouseDown(object sender, MouseEventArgs e)
		{
			Console.WriteLine("jazda w prawo - blokada lewego kola robota");
			udpSender.SendData("right");

		}

		private void ButtonLeft_MouseDown(object sender, MouseEventArgs e)
		{
			Console.WriteLine("jazda w lewo - blokada prawego kola robota");
			udpSender.SendData("left");
		}

		private void ButtonBack_MouseDown(object sender, MouseEventArgs e)
		{

			Console.WriteLine("jazda do tylu - oba silniki wstecz");
			udpSender.SendData("back");
		}

		private void ButtonStop_Click(object sender, EventArgs e)
		{
			//ręczne zatrzymanie robota.Przyda się po zaimplementowaniu trybu autonomicznego
			udpSender.SendData("stop");
		}
	}
}

```


W konstruktorze formy tworzony jest obiekt klasy UdpSender. Jest to klasa umożliwiająca wysyłanie komend w postaci datagramów UDP na zadany adres IP po odpowiednim porcie. W naszym wypadku jest to wspomniany wyżej adres rozgłoszeniowy sieci lokalnej. Klasa UdpSender posiada tylko jedną metodę o nazwie SendData, która jako argument przyjmuje komendę typu string, jaka ma zostać wysłana do urządzenia w celu wywołania oczekiwanej akcji. Zostały wykorzystane eventy mouse_up i mouse_down przycisków, dzięki czemu wciśnięcie przycisku skutkuje wysłaniem odpowiedniego rozkazu UDP do robota a jego puszczenie wysyła komendę "stop". Przewidziałem mimo wszystko odrębny przycisk "Stop" aby móc wyłączyć robota bez względu na tryb pracy w jakim w danym momencie będzie pracował(np autonomiczny, bazujący na czujnikach jaki mam zamiar w przyszłości dodać)

Rozkazy interpretowane przez robota:
- forward
- back
- left
- right
- stop

Wysłanie komend innych niż powyższe nie będzie miało wpływu na urządzenie. Łańcuch znaków podany jako argument do metody SendData, jest enkodowany do postaci bajtowej i wysyłany na adres docelowy, definiowany w konstruktorze klasy UdpSender. Poniżej kod klasy UdpSender:

```
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.IO;
using System.Net;

namespace RobotPi
{
	class UdpSender
	{
		private int _port;
		private string _ip;
		private System.Net.Sockets.UdpClient _client;

		public UdpSender(string ip, int port = 80)
		{
			_port = port;
			_ip = ip;
			_client = new System.Net.Sockets.UdpClient(ip, port);
		}

		private byte[] Encode(string txt)
		{
			return Encoding.ASCII.GetBytes(txt);
		}

		public void SendData(string dat)
		{
			byte[] data = Encode(dat);
			_client.Send(data, data.Length);
		}

	}
}

```
W konstruktorze klasy tworzymy obiekt klasy UDPClient. Utworzenie obiektu klasy UdpSender powoduje utworzenie gniazda o adresie IP i porcie zdefiniowanym w argumentach przekazanych do konstruktora klasy. Domniemanym portem jest port 80, dlatego jeśli go nie zmieniamy wystarczy podać sam adres IP. Ja dla czytelności podaję oba argumenty tworząc obiekt klasy UdpSender. Dodatkowo adres IP oraz port wyciągnąłem do statycznej klasy StaticHelper.
Do klasy tej będę mógł dodawać wszelkie dane konfiguracyjne odczytywane np z pliku. Będę mógł w wygodny sposób dokonywać zmian parametrów bez konieczności grzebania w całym kodzie. Zmienne tu zdefiniowane będą dostępne we wszystkich klasach mojego projektu.

Klasa StaticHelper:
```
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace RobotPi
{
	public static class StaticHelper
	{
		public static string BroadcastIp = "192.168.255.255";
		public static int Port = 80;
	}
}

```


Na dzisiaj tyle. Przedstawione rozwiązanie pozwala w sposób podstawowy sterować robotem po sieci LAN. Kolejnym krokiem jaki mam w planach będzie dodanie do Robota akumulatora aby uwolnić go od kabla, a następnie dodanie kamery. Ponadto prędzej czy później przyda się tu prosty serwer UDP, który pozwoli na odbieranie danych od robota. Gdy tylko coś w tych kierunkach zrobię, podzielę się tym z Wami. Wszelkie sugestie mile widziane, zachęcam do dyskusji. Pozdrawiam wszystkich! :)
👍 , , , , , , , , , , , , , , ,