Communication Port Série avec Arduino.

Pour les scripts écrits en C#
Règles du forum
Merci de respecter la NOMENCLATURE suivante pour vos TITRES de messages :

Commencez par le niveau de vos scripts
DB = Débutant
MY = Moyen
CF = Confirmé

Puis le domaine d'application
-RS = Réseau
-AL = Algorithmie

Exemple :

[DB-RS] Mouvement perso multijoueur
Proautobot
Messages : 20
Inscription : 25 Mars 2018 01:04

Communication Port Série avec Arduino.

Message par Proautobot » 19 Mai 2018 23:22

Bonjour,

J'essaye de piloter des servos-moteurs avec unity, le portSerie et Arduino . J'y suis parvenus au bout de plusieurs tentatives. Cependant le résultat est loin d'être propre (Je ne parle pas de l'esthétisme).

Pour l'instant je fonctionne en boucle ouverte unity envoie -> Arduino lit et exécute. J'ai une chaîne de caractère A et B de format (000), respectivement pour le servo A et B. Celles-ci dépendent de la valeur du slider qui leur est attribué. Avant d'envoyer une info, je concatène la chaîne A et B dans mystring qui prend donc un format (000000). Par exemple, si j'envoie 020175, ma carte Arduino traite cette info et positionne le servo A à 20° et le servo B à 175°. Seulement je peux envoyer cette info que 1 fois par secondes

(j'ai mis du temps avant de m'en apercevoir. J'était entrain de jouer avec des boutons et je me suis aperçus que je devais attendre pour que ma carte Arduino reçoive l'info).

J'ai augmenter les bauds et la ca ne marche pas du tout, même à 1 sec.

Une info par secondes c'est trop lent. J'aimerais que mes servo se déplace quasiment aussi vite que mon interface graphique. Un servo c'est 60°/s). J'aimerais donc pouvoir envoyer 60 informations par seconde ou du moins 25/s pour tromper l’œil . Vous allez voir que mon code C# utilise la vitesse de mon ordinateur pour déplacer les servos virtuel. C'est bourrin mais pour l'instant le rendu est sympa.

Je découvre le code ASCII et les rouage de la communication. Je pense que tout est inventé et qu'il y a moyen que le rendu virtuel resemble au rendu réel... Je me corrige : que le réel resemble au virtuel.

Voici mon code C# Vous remarquerez mon niveau :gene: :

Code : Tout sélectionner

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using System.IO.Ports;

public class control : MonoBehaviour {

	//communication
	SerialPort serial;
	public string myString;
	float temps = 0.0f;
	float delay = 0.0f;
	public float comRapidity = 2.0f;

	//Servo 1 
	public int servoDegre1; //Degré value
	public Transform servo1;
	public Slider sliderServo1;
	public Button buttonS1plus;
	public Button buttonS1moins;
	public Text textS1;
	string A;

	// Servo 2
	public int servoDegre2; //Degré value
	public Transform servo2;
	public Slider sliderServo2;
	public Button buttonS2plus;
	public Button buttonS2moins;
	public Text textS2;
	string B;

	// Use this for initialization
	void Start () {
		//communication
		serial = new SerialPort();

		//Button S1
		buttonS1plus.onClick.AddListener(TaskOnClickS1plus);
		buttonS1moins.onClick.AddListener(TaskOnClickS1moins);

		//Button S2
		buttonS2plus.onClick.AddListener(TaskOnClickS2plus);
		buttonS2moins.onClick.AddListener(TaskOnClickS2moins);
	}
	
	// Update is called once per frame
	void Update () {

		//Servo 1 
		if (servoDegre1 != (sliderServo1.value)) 
		{
			if (servoDegre1 < (sliderServo1.value) ) 
			{
				servoDegre1 = servoDegre1 + 1 ;		
			} 
			if (servoDegre1 > (sliderServo1.value) )
			{
				servoDegre1 = servoDegre1 - 1;
			}
		}
		servo1.localRotation = Quaternion.AngleAxis (-servoDegre1, Vector3.up);
		textS1.text = servoDegre1.ToString() + "°";

		//Servo 2 
		if (servoDegre2 != (sliderServo2.value)) 
		{
			if (servoDegre2 < (sliderServo2.value)) 
			{
					servoDegre2 = servoDegre2 + 1;
			} 

			if (servoDegre2 > (sliderServo2.value))
			{
				servoDegre2 = servoDegre2 - 1;
			} 
		}
		servo2.localRotation = Quaternion.AngleAxis (-servoDegre2, Vector3.up);
		textS2.text = servoDegre2.ToString() + "°";
	
		//communication

		A = sliderServo1.value.ToString("000");
		B = sliderServo2.value.ToString("000");
		myString=string.Concat(A,B);

		temps = Time.time;
		if ((delay + comRapidity) < temps)
		{
			Envoyer ();
			delay = temps;
		}
			
	}

	// button S1
	void TaskOnClickS1plus ()
	{
		(sliderServo1.value) = (sliderServo1.value) + 1;
	}

	void TaskOnClickS1moins ()
	{
		(sliderServo1.value) = (sliderServo1.value) - 1;
	}
	// button S2
	void TaskOnClickS2plus ()
	{
		(sliderServo2.value) = (sliderServo2.value) + 1;
	}

	void TaskOnClickS2moins ()
	{
		(sliderServo2.value) = (sliderServo2.value) - 1;
	}

	//communication
	public void Envoyer ()
	{
		serial.PortName = "COM5";
		serial.Parity = Parity.None;
		serial.BaudRate = 9600;
		serial.DataBits = 8;
		serial.StopBits = StopBits.One;
		serial.Open ();
		serial.Write (myString);
		serial.Close ();
	}
		
}
et mon code Arduino au passage :

Code : Tout sélectionner

#include <Servo.h>

Servo myServoA;
Servo myServoB;

const int pinServo1 = 9;
const int pinServo2 = 10;

String incomming;
String maChaine1;
String maChaine2;

int servoPos1;
int servoPos2;
int oldServoPos1 = 0;
int oldServoPos2 = 0;

void setup()
{
  Serial.begin(9600);

  myServoA.attach(pinServo1);
  myServoB.attach(pinServo2);

  myServoA.write(0);
  myServoB.write(0);
}

void loop()
{
  if (Serial.available() > 0)
  {
    incomming = Serial.readString();

    maChaine1 = incomming.substring(0, 3);
    maChaine2 = incomming.substring(3, 6);

    servoPos1 = maChaine1.toInt();
    servoPos2 = maChaine2.toInt();

    oldServoPos1 = servoPos1;
    oldServoPos2 = servoPos2;

    servoPos1 = limitePosServo(servoPos1); //vérifier l'intervalle [0 ; 175]
    servoPos2 = limitePosServo(servoPos2);

    /*  Serial.print ("servoPos1: ");// Serial.print reset la carte quand arduino essaye d'envoyer une info vers unity.
      Serial.println (servoPos1);
      Serial.print ("servoPos2: ");
      Serial.println (servoPos2);
      Serial.println ("");*/


  }
  myServoA.write(servoPos1);
  myServoB.write(servoPos2);
}//end loop


int limitePosServo(int valeur)
{
  if (valeur < 0)
  {
    valeur = 0;
  }

  if (valeur > 175)
  {
    valeur = 175;
  }
  return valeur;

}
un imprime-écran pour visualiser l'interface:


J'ai bien une petite idée j'envoie un string d'un forma plus grand et Arduino devras traiter plusieurs positions les une après les autres. Exemple 001002003004005006007008009010001002003004005006007008009010 8| ouille les yeux. Les dix première valeur de format 000 pour le servo A les dix autres pour le servo B. Seulement la taille d'une chaîne est certainement limité ? Et puis quelqu'un a certainement un meilleur conseil. :merci:

Ah oui, petite remarque vous avez vue la tail du canevas ?
Et aussi vous avez remarqué que j'envoie une valeur supérieur à celle affiché sur l'interface. Je doit avoir un problème de conversion de float vers int mais interne au code des sliders. En effet si je joue avec les slider des fois j'envois le même chiffre ou +- 1 . J'ai modifier les > en >= dans mon code et j'ai toujours un petit décalage. Mais c'est qu'un détail pour l'instant.

Bien à vous.
Pièces jointes
Deux servos et communication.png
Deux servos et communication.png (168.38 Kio) Consulté 6775 fois

Moi 1971
Messages : 727
Inscription : 29 Sep 2015 13:38

Re: Communication Port Série avec Arduino.

Message par Moi 1971 » 20 Mai 2018 09:03

Bonjour,
tu poses ta question un week-end, il y a moins de monde pour répondre. Il va falloir attendre mardi pour avoir un avis certain.

Pour ma part, je ne comprends pas tout. Quel est ton problème?
Si ta question est d'envoyer 60 fois par seconde les données alors j'ai des pistes, mais pas forcément les réponses.
1/=> "public float comRapidity = 2.0f;" => Je crois que tu défini un délais de 2 secondes.
"public float comRapidity = 1.0f;" => 1 seconde
"public float comRapidity = 0.5f;" => 0.5 seconde
"public float comRapidity = 0.25f;" => 0.25 seconde
etc...
Si tu veux faire autrement un exemple à adapter :
https://docs.unity3d.com/ScriptReferenc ... nvoke.html

2/ Ton histoire de convertir les float en int... Classique
Flaot => Int
1 =>1
1.1 => 1
1.2 =>1
1.3 =>1
1.4 =>1
1.5 =>1
1.6 =>2
1.7 =>2
1.8 =>2
1.9 =>2
2 =>2
etc..

Une fois que ton code fonctionnera, il y aura de l'optimisation à faire.
Essayes mes pistes et reviens nous tenir au courant.

Ps : Et dans la réalité, cela va servir à quoi tes robots?

Proautobot
Messages : 20
Inscription : 25 Mars 2018 01:04

Re: Communication Port Série avec Arduino.

Message par Proautobot » 20 Mai 2018 15:38

Bonjour Toi 1971,

Mon projet est perso, j'ai tout mon temps. Aussi je ne cherche pas une aide dans l’immédiat. Je voulais juste poster à un moment qui était le moment auquel j'ai posté :lol: .

La première piste que tu me donne :
1/=> "public float comRapidity = 2.0f;" => Je crois que tu défini un délais de 2 secondes.
"public float comRapidity = 1.0f;" => 1 seconde
"public float comRapidity = 0.5f;" => 0.5 seconde
"public float comRapidity = 0.25f;" => 0.25 seconde
Tu remarqueras que comRapidity c'est un "public" float, je l'ai modifié dans mes essais de transmission. ma carte ne réagit pas si comRapidity n'est pas un nombre entier positif (Je vais enlever le "= 2.0f " la prochaine fois, ça porte à confusion).
Exemple 1.2f ou 0.9 ou 3.5 ne fonctionnent pas. 1 ; 2 ou 3 fonctionnent. L'idée c'est que le lag entre ce que je vois sur mon ordi et dans la réalité ne se vois pas trop.

Faudrait que je contrôle les temps d'intervals de position de mes mouvements. Aussi j'ai essayé une co-routine mais je peux juste ralentir le mouvement et c'est moche si le temps est trop long. Je vais assayer un Time.deltaTime plutôt qu'un "= + 1." Mais je suis jamais certain de la position final.

Pour ton exemple:
Ma fonction n'est pas comme la fonction Invoke. il faudrait que je créer un paramètre float dans ma fonction qui à la suite indique les temps d'interval entre chaque transmission. Mais comme tu dis c'est de l'optimisation.

Ps : Et dans la réalité, cela va servir à quoi tes robots?
Tu n'est pas le premier à me poser cette question. Avant tout chose cela m'apprend à programmer tout en ayant un objectif. Puis j'aime bien les robots :P . C'est vrais le robot que je confectionne est digne d'un budget limité, c'est une expérimentation. Il ne pourra pas soulever une bière et encore moins me l’ouvrir "dommage". Mais si j'abouti a quelque choses de convenable la versions suivante pourras remplir mon lave-vaisselle :hehe: . On peut dire que c'est une passion.

Proautobot
Messages : 20
Inscription : 25 Mars 2018 01:04

Re: Communication Port Série avec Arduino.

Message par Proautobot » 01 Juin 2018 20:32

Salut, je passe pour faire un up

Quelqu'un sais comment trouver la doc sur Serial.xxx () dans le manuel d'unity ?

Merci

Avatar de l’utilisateur
ZJP
Messages : 5745
Inscription : 15 Déc 2009 06:00

Re: Communication Port Série avec Arduino.

Message par ZJP » 02 Juin 2018 03:17

Proautobot a écrit :
01 Juin 2018 20:32
Salut, je passe pour faire un up

Quelqu'un sais comment trouver la doc sur Serial.xxx () dans le manuel d'unity ?

Merci
Ce n'est pas de "l'Unity" : C'est un classe standard de C#.

Dans ton code, ta fonction ouvre, envoie, et referme le port à chaque envoi ?!!!. :shock: :shock:

Code : Tout sélectionner


	//communication
	public void Envoyer ()
	{
		serial.PortName = "COM5";
		serial.Parity = Parity.None;
		serial.BaudRate = 9600;
		serial.DataBits = 8;
		serial.StopBits = StopBits.One;
		serial.Open ();
		serial.Write (myString);
		serial.Close ();
	}
L'ouverture/fermeture du port prend du temps !!!.





Un extrait d'un de mes projets : communication entre MBlock/ mBOT(Scratch) et Unity.

Code : Tout sélectionner

// **************************************************//
// *               mBOT/mBLOCK to UNITY  (c)                *//
// **************************************************//
using UnityEngine;
using System.IO.Ports;
using System.Threading;
using System;

public class mBot_COMM : MonoBehaviour {

	private SerialPort ComPort;
	public string comNum  = "COM5";
	public byte bToSend   = 100; // test
	public float fToSend  = 1000.50f; // test
	public string sToSend = "hello"; // test

	public bool isComOk = false;

	public bool mBot_start   = false;
	public int CommandMBLOCK = 0;
	public int indexReplay   = 0;
	public int readWRITE     = 0;

	public int[] btr;
	private byte[] ackno = new byte[] {0xff, 0x55, 0x0d, 0x0a}; // 255, 85, 13, 10 // acknoledge
	private byte[] bufferResponse;
	private byte[] dataToByte = new byte[4]; // utilisé pour la convertion float to byte ou int to byte

	Thread readThread;

	void Start(){
		btr = new int[30];
		bufferResponse = new byte[100]; // buffer de 100 pour les responses
		clear_Buffer();

		Application.runInBackground = true;
		ComPort	= new SerialPort(); 

		ComPort.PortName     = comNum;
		ComPort.BaudRate     = 115200;
		ComPort.DataBits     = 8;
		ComPort.StopBits     = System.IO.Ports.StopBits.One;   // One, OnePointFive, Two
		ComPort.Handshake    = System.IO.Ports.Handshake.None; // None, XOnXOff, RequestToSend, RequestToSendXOnXOff;
		ComPort.Parity       = System.IO.Ports.Parity.None;    // None, Even, Mark, Odd, Space 
		ComPort.RtsEnable    = false;
		ComPort.DtrEnable    = false;
		ComPort.ReadTimeout  = 5;
		ComPort.WriteTimeout = 5;

		isComOk = true;

		try{
			ComPort.Open();
		}
		catch (System.IO.IOException e) 
		{
			Debug.Log("Port déjà ouvert !!!!!");
			isComOk = false;
		}

		if(isComOk){
			ComPort.DiscardInBuffer();  // vide le buffer ?!
			ComPort.DiscardOutBuffer(); // vide le buffer ?!
			//Debug.Log (ComPort.IsOpen);
			readThread = new Thread(ReadComData);
			readThread.IsBackground = true;
			readThread.Priority = System.Threading.ThreadPriority.Lowest; // Lowest, BelowNormal, Normal, AboveNormal, Highest
			readThread.Start();
		}
	}

	void Update (){
	}

	void clear_Buffer(){
		for (int i = 0; i < 30; i++){
			btr[i] = 999999;
		}
	}
...

     //*******************************************************************//
     //                     mBlock - Envoi d'un Acknowledge               //
     //*******************************************************************//
	void mBot_Ack() {
		ComPort.Write(ackno,0,4); // {0xff, 0x55, 0x0d, 0x0a}
		Thread.Sleep(1);
	}
....

     //*******************************************************************//
     //                  Thread de réception des datas de mBlock          //
     //*******************************************************************//
 	void ReadComData(){

		while (true){
			int okRead = 999999;
			try{
				okRead = ComPort.ReadByte();
			}
			catch (TimeoutException) { }

			if (okRead == 255){ // debut protocol
				clear_Buffer();
				btr[0] = 255;
	
...

Le port est configuré dans Start(), usage d'un tableau de bytes qui permet de réduire le nombre d'octets envoyés (utilises un readBytes du coté de l'Arduino ), un thread est utilisé pour la réception, etc etc..

Bonne suite... :mrgreen:

Ah oui, il faut une pause de 20 millis entre chaque envoi au servos, donc 60fps.... Plutôt 50 maxi sinon ils risquent de "décrocher". :P

Proautobot
Messages : 20
Inscription : 25 Mars 2018 01:04

Re: Communication Port Série avec Arduino.

Message par Proautobot » 03 Juin 2018 18:00

Bonjour, et merci.

J'avais omis de partager l'avancement de mon code désolé.
ZJP a écrit :
02 Juin 2018 03:17

Dans ton code, ta fonction ouvre, envoie, et referme le port à chaque envoi ?!!!. :shock: :shock:

L'ouverture/fermeture du port prend du temps !!!.
J'avais réglé ce problème, voici mon code actuel unity :

Code : Tout sélectionner

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using System.IO.Ports;
using System.Threading;

public class control : MonoBehaviour {

	    //communication
	    SerialPort serial;
	    public string myString;
	    float temps = 0.0f;
	    float delay = 0.0f;
	    public float comRapidity = 2.0f;
	    public string portName;
	    bool setPort = true;

	    //Servo 1 
	    public int servoDegre1; //Degré value
	    public Transform servo1;
	    public Slider sliderServo1;
	    public Button buttonS1plus;
	    public Button buttonS1moins;
	    public Text textS1;
	    string A;

	    // Servo 2
	    public int servoDegre2; //Degré value
	    public Transform servo2;
	    public Slider sliderServo2;
	    public Button buttonS2plus;
	    public Button buttonS2moins;
	    public Text textS2;
	    string B;

	    // Autre servo test


	    // Use this for initialization
	    void Start () {
		        //communication
		        serial = new SerialPort();

		        //Button S1
		        buttonS1plus.onClick.AddListener(TaskOnClickS1plus);
		        buttonS1moins.onClick.AddListener(TaskOnClickS1moins);

		        //Button S2
		        buttonS2plus.onClick.AddListener(TaskOnClickS2plus);
		        buttonS2moins.onClick.AddListener(TaskOnClickS2moins);
		    }
	    
	    // Update is called once per frame
	    void Update () {

		        //Servo 1 
		        if (servoDegre1 != (sliderServo1.value)) 
			        {
			            if (servoDegre1 < (sliderServo1.value) ) 
				            {
				                servoDegre1 = servoDegre1 + 1 ;        
				            } 
			            if (servoDegre1 > (sliderServo1.value) )
				            {
				                servoDegre1 = servoDegre1 - 1;
				            }
			        }
		        servo1.localRotation = Quaternion.AngleAxis (-servoDegre1, Vector3.up);
		        textS1.text = servoDegre1.ToString() + "°";

		        //Servo 2 
		        if (servoDegre2 != (sliderServo2.value)) 
			        {
			            if (servoDegre2 < (sliderServo2.value)) 
				            {
				                    servoDegre2 = servoDegre2 + 1;
				            } 

			            if (servoDegre2 > (sliderServo2.value))
				            {
				                servoDegre2 = servoDegre2 - 1;
				            } 
			        }
		        servo2.localRotation = Quaternion.AngleAxis (-servoDegre2, Vector3.up);
		        textS2.text = servoDegre2.ToString() + "°";
		    
		        //communication

		        A = servoDegre1.ToString("000");
		        B = servoDegre2.ToString("000");

		        myString=string.Concat(A,B);

		        temps = Time.time;
		        if ((delay + comRapidity) < temps)
			        {
			            Envoyer ();
			            delay = temps;
			            //Debug.Log (myString);
			        }
		            
		    }

	    // button S1
	    void TaskOnClickS1plus ()
	    {
		        (sliderServo1.value) = (sliderServo1.value) + 1;
		    }

	    void TaskOnClickS1moins ()
	    {
		        (sliderServo1.value) = (sliderServo1.value) - 1;
		    }
	    // button S2
	    void TaskOnClickS2plus ()
	    {
		        (sliderServo2.value) = (sliderServo2.value) + 1;
		    }

	    void TaskOnClickS2moins ()
	    {
		        (sliderServo2.value) = (sliderServo2.value) - 1;
		    }

	    //communication
	    public void Envoyer ()
	    {
		        if (setPort == true)
			        {
			        serial.PortName = portName;
			        serial.Parity = Parity.None;
			        serial.BaudRate = 9600;
			        serial.DataBits = 8;
			        serial.StopBits = StopBits.One;
			        serial.Open ();
			        setPort = false;
			        }
		        serial.Write ( myString + "\n" );
		       
		    }

	    void OnApplicationQuit()
	    {
		        Debug.Log("Application ending after " + Time.time + "seconds");
		        serial.Close ();
		    }
	        
}
Jusqu'à présent, j'ai réussi à piloter 2 servomoteurs en envoyant un string de ce format "000000/n". Ceci est possible grace à une modification du code arduino. => Serial.setTimeout(10).

Code : Tout sélectionner

void setup()
{
  Serial.begin(9600);
  Serial.setTimeout(10);
  ...
  
La variable ComRapidity doit être supérieur à la valeur indiqué dans le setTimeout de l'arduino. D'origine celui ci est paramétré à 1 seconde. D'ou le problème que j'avais précédemment.

Chez nos confrère Arduino on ma tout de même déconseillé de modifier le setTimeout. Pour favoriser l'utilisation d'un CRC. Mais j'ai pas le niveau pour l'appliquer. Tout comme j'ai du mal à comprendre ton code, tu m'en excuseras, ZJP.

Ceci étant dit, pour avancer dans mon projet, malgré les avertissements, j'ai essayé de piloter 6 servos-moteurs. Ce, avec la méthode actuel. C'est à dire envoyer un string de ce type 000000000000000000. Côté moniteur de l'IDE Arduino çà fonctionne. Mais lorsque j'envois ça avec unity mes servos ne réponde plus. même avec un envoie/secondes. Si j'essaye avec trois servos (dont un virtuel j'ai que deux servos en réalité) ça bouge mais c'est saccadé et le servo 2 prend la position du servo 1...c'est mélangé.

Je ne sais pas comment vérifier ce que lit ma carte Arduino. Peut-être avec un écran LCD, je vais me renseigner si c'est possible en même temps que j'utilise le port série.

:hello:

Proautobot
Messages : 20
Inscription : 25 Mars 2018 01:04

Re: Communication Port Série avec Arduino.

Message par Proautobot » 03 Juin 2018 20:42

-J'ai corrigé quelque erreurs du au multiples dupliques de ligne dans mon code Arduino.
"J'utilise pas trop les fonctions quand je fabrique un code "
Je pense aussi qu'un tableau et une boucle "for" devrais simplifier la taille de mon code

-J'ai positionner la vitesse de transmission à 20ms comme tu me l'a conseiller dans unity

-Je parviens à contrôler 6 servos-moteurs. Du moins j'envois pour l'instant un message de ce type 000000000000175090. En effet j'ai que deux servos moteurs pour les essaies. Mais cela prouve que j'envoie bien le message et que j'arrive à lire la fin de celui-ci.

Seulement je reste perplexe quand aux avertissements et à l'utilisation du CRC. Je retenterais lorsque mon projet sera "fini". Pour l'instant je me contente d'émettre. Car oui si j'envoie des donné avec l'arduino UNITY plante =). Certainement parce que je ne lit aucune donné et qu'elles doivent saturer quelque part.

Répondre

Revenir vers « (C#) CSharp »