Roboter mit Servo und Ultraschallsensor

Hallo,

ich habe einen kleinen Roboter gekauft und zusammengebaut (siehe Roboter bauen mit Arduino, Rheinwerkverlag). An diesem Roboter habe ich den IR-Entferungsmesser gegen einen Ultraschallsensor Ping ))) getauscht.

Will mein Projekt quasi Schritt für Schritt aufbauen. Erstmal habe ich nun den Sensor per Servo sich bewegen lassen.
Um das weiter ausbauen zu können habe ich den Code stärker in Funktionen geschachtelt und Felder erzeugt in denen die Messwerte des Sensors gespeichert werden. Soweit so gut. Nun habe ich versucht den Code so umzuschreiben, dass mittels Referenzübergabe der Felder, Funkionen die Arrays verändern und auswerten können/könnten.

Dabei entstand das Problem das der Servo sich nur noch bis zu einem bestimmten Winkel weiterbewegt. Danach wird immernoch die Entfernung gemessen und korrekt Ausgewertet. Die Übergabe der Parameter scheint also immernoch zu funktionieren?

Ich habe mal genauer in das Programm hineingeschaut und die Funktionen nochmal einzeln getestet. Dabei stellte sich heraus, dass der Servo solange ordnungsgemäß läuft, wie der Sensor keine Entfernungen misst. Zeilen 71 und 79 sind auskommentiert.
Wenn ich mir das genauer anschauen, habe ich die Anweisungen so umgeschrieben, das jeweils eine 1 übergeben wird und gar nicht erst gemessen. Dabei entstand der selbe Fehler. Übergabe funktioniert, aber, der Servo bleibt stehen.

Könnt Ihr die Ursache und/ oder Lösung des Problemes finden?

Vielen Dank.
Der Code lautet:

Ardubot_-_MotionTracker.ino (2.59 KB)

Bring nicht Referenzen und Zeiger durcheinander

Array Variablen in C sind Zeiger auf das erste Element. Wenn du "int array[5]" machst ist "array" ein int*

Arrays übergibt man daher so:

void func(int* array)
{
    array[0] = ...;
}

Eventuell noch die Größe in einer extra Variablen übergeben wenn nötig

Und der Aufruf:

func(array);

Es geht auch:

void func(int[] array)
{
}

Ist aber seltener

Das heißt, wenn die Variable von Typ Array es ein Zeiger auf int ist? Muss ich dann nicht dennoch als Referenzparameter übergeben, um in der Funktion den Werte der Elemente ändern zu können?

Ich habe auch Mal als Zeiger auf Int übergeben, allerdings hat dies nichts an meinen Problem geändert.

Hier nochmal der Code:

#include <Servo.h>

#define SERVO 6  //Servo at Pin 6 etc.
//#define IRSENS 2
# define PING 2
#define PAUSE 20
#define STEP 1
#define FRONT 99
#define LEFT 79
#define RIGHT 119
//got massive error for distance messurement using large angles

const byte DIFFERENCE = (RIGHT - LEFT)/STEP;
const byte DELAY = PAUSE*STEP;
Servo head;

byte Start[DIFFERENCE];
byte Final[DIFFERENCE];


/*float getIR() {
  pinMode(IRSENS, INPUT);
  float voltage = analogRead(IRSENS);
  float distance = (6787/ (voltage - 3))-4;
  
  if (distance > 80.0)
    distance = 80.0;
  if (distance < 10.0)
    distance = 10.0;
    
  return distance;
}*/

byte getPing(int pingPin) {
  pinMode(pingPin, OUTPUT);
  digitalWrite(pingPin, LOW);
  delayMicroseconds(2);
  digitalWrite(pingPin, HIGH);
  delayMicroseconds(5);
  digitalWrite(pingPin, LOW);
  
  pinMode(pingPin, INPUT);
  
  float microseconds = pulseIn(pingPin, HIGH, 14500);//set to maximal 2.5 m
  byte cm = byte(microseconds/29/2);//output in cm, Ultrasound travels with 29 µs/cm
  
  return cm;  
}

byte getDistance() {
  int Ending = 10;
  int i = 0;
  int distance;
  do {
    //int distance = int(getIR());
    distance = getPing(PING);
    i++;
  } while ((distance > 240) || (distance <= 5) || (i == Ending));
  return distance;
}
  
    
/* Moving the Sensor mounted Servo
   get Sensor data */
void moveSensor(byte (&outStart)[DIFFERENCE], byte (&outFinal)[DIFFERENCE], byte Ending ) {
  int k = 0;
  int phi = 0;

  for (int i = LEFT; i <= RIGHT; i = i + STEP) {
    head.write(i);
    outStart[k] = getDistance ();
    k++;
    delay(Ending);
  }
    
  for (int i = RIGHT; i >= LEFT; i = i - STEP) {
    k--;
    head.write(i);
    outFinal[k] = getDistance();
    delay(Ending);
  }
}

void trackMotion (byte (&inStart)[DIFFERENCE], byte (&inFinal)[DIFFERENCE]) {
  
  int motion[DIFFERENCE];
  
  for (int k = 0; k <= DIFFERENCE; k++)/*for k = 0 allway returns wrong number*/ {
    int phi =  k*STEP + LEFT - FRONT;
    motion[k] = inFinal[k] - inStart[k];
    Serial.print("Die Entfernnung bei ");
    Serial.print(phi);
    Serial.print(" Grad betraegt: ");
    Serial.print(inStart[k]);
    Serial.print(" und ");
    Serial.print(inFinal[k]);
    Serial.print(" cm, Bewegung ist: ");
    Serial.print(motion[k]);
    Serial.println(" cm.");
    
    
  }
}

void transposetoXY() {
}


void setup() {
  Serial.begin(115200);
  head.attach(SERVO);
  //head.write(LEFT);
   Serial.println (DIFFERENCE);
}

void loop() {
  moveSensor(Start, Final, DELAY);
  /*stops Motion after reaching about +20°
  dosen't do antything when using STEP = 5, and Array of 5 integer*/
  trackMotion(Start, Final);

}

Es kann sein, dass das funktioniert hat, aber die Syntax ist ziemlich ungewöhnlich. Deshalb habe ich es angesprochen.

Es ist auch möglich, dass du irgendwo ein Problem mit der Programm-Logik hast.

Muss ich dann nicht dennoch als Referenzparameter übergeben, um in der Funktion den Werte der Elemente ändern zu können?

Kommt darauf an was genau du mit "Referenzparameter" meinst. In C++ etwas das sich Referenz nennt: http://de.wikibooks.org/wiki/C++-Programmierung/_Weitere_Grundelemente/_Referenzen

Und man kann auch Zeiger als solche übergeben. Referenzen und Zeiger sind beides Adressen, aber die verhalten sich anders und haben eine andere Syntax.

Da wird oft zwischen "call by reference" und "call by pointer" unterschieden um das deutlich zu machen.

Das ist eine Referenz:

void func(int& value)
{
    value = 5;
}

...
int value;
func(value);

Das ist ein Zeiger:

void func(int* value)
{
   *value = 5;
}

...
int value;
func(&value);

Das sieht hier sehr ähnlich aus und macht das gleiche. Den Unterschied sieht man in anderen Beispielen. Generell kann man mit Zeigern mehr machen, z.B. damit rechnen. Referenzen müssen sofort initialisiert werden und können nicht Null sein. Zeiger sind allerdings daher auch wesentlich fehleranfälliger.

Wie du sieht muss man einem Zeiger-Parameter die Adresse mit & übergeben. Bei Arrays ist das nicht nötig, da Array Variablen eben schon von sich aus in Zeiger zerfallen.

Sehr interressant, vielleicht liegt es doch nicht ganz an meiner Dummheit, sondern dass ich meine ersten tieferen Erfahrungen mit Standard-Pascal gesammelt habe... das wir etwas aneinander vorbei reden. Obwohl es ja auch dann noch mein Unvermögen ist, immerhin programmieren wir die AVR Chips in C.

In Pascal muss man auch bei Feldern explizit mittels call-by-reference übergeben. Schon wegen des Speicherplatzes...

Habe also, einfach die Referenzübergabe (Call-by-reference) sein lassen und siehe Funktionskopf:

Deklarierung:

byte Start[DIFFERENCE];
byte Final[DIFFERENCE];

Funktionskopf:

void moveSensor(byte outStart[], byte outFinal[], byte Ending ) {

Initalisierung:

moveSensor(Start, Final, DELAY);

ganz klassisch übergeben.

Wenn ich dich richtig verstanden habe. Ist es dass, was du rietest. Außer das der Binärcode etwas kürzer geworden ist, (immer gut) hat sich, wenn ich das recht sehe, nichts geändert.

Denn vielen Dank. :)

Mummel:
In Pascal muss man auch bei Feldern explizit mittels call-by-reference übergeben. Schon wegen des Speicherplatzes…

Weil in Pascal wahrscheinlich alles außer primitiven Datentypen ein Objekt ist. Arrays in C/C++ dagegen sind ganz primitiv.

Da gibt es auch noch andere Varianten. In Java und C# sind Arrays auch Objekte. Und jetzt wird es interessant: es wird immer die Adresse von Objekten by-value übergeben. Das heißt Änderungen in einer Methode sind nach außen sichtbar, aber man kann an die Parameter-Variable kein neues Objekt anhängen (bzw. man kann, aber es bringt nichts). In C# gibt es allerdings mit “ref” auch Referenzparameter.

Aber zum eigentlichen Thema:

Diese Zeile sieht etwas seltsam aus:

} while ((distance > 240) || (distance <= 5) || (i == Ending));

i == Ending ist nur nur einmal gültig. Ich sehe da den Sinn nicht. Willst du nur maximal 10 Durchläufe machen?

Dein Problem könnte hier liegen:

float microseconds = pulseIn(pingPin, HIGH, 14500);

Diese Funktion ist blockierend. Und zwar maximal 14,5 Sekunden lang! Deshaln ist pulseIn() sehr problematisch wenn mehrere Dinge quasi-gleichzeitig ablaufen sollen.

Das machst du dann auch noch mehrmals hintereinander in einer Schleife.

Hi, erstmals dank, dass du dadurchgesehen hast.

Zum ersten, ich habe da die do while Schleife gewählt, um Messwerte herauszufiltern, die nicht stimmen können. Dann soll es einfach nochmal messen, aber nach ein paar Messugen mit ähnlichen Randwerten als Ergebnis möchte ich abbrechen. Ansonsten erzeuge ich eine Endlosschleife, wenn der Messwert stimmt. Dann wird der Sensor immer und immer solch einen Randwert liefern.

Die PulseIn-funktion muss ich, glaube ich nehmen, denn der Ping ))) funktioniert wohl wie folgt:

Er sendet einen Ping aus, glaube mit:

digitalWrite(pingPin, HIGH);

und dann warte ich solange bis der Schall reflektiert wird und wieder auf den Sensor trifft.

Zum PulseIn selbst. Habe gerade nochmal nachgeschaut. Die dritte übergebene Zahl ist Zeit in µs. Das heißt, der PulseIn wartet maximal 14,5 ms, Standard ist 1 s, also rund 70 Mal so lange.

Die Zahl, ist an sich etwas seltsam. Entspricht aber einer maximalen Sichtweite von 2.5 m. Der Sensor an sich könnte aber 3.12 m weit sehen. Habe aber die Beschränkung, wegen des Datentypes Byte so niedrig gesetzt. Ursprünglich hatte, ich das alles auf Int und dann habe ich 62 cm, die der Roboter weiter sehen kann. Aber ich dachte, dass ich vielleicht einen Überlauf, oder so, generiere und habe deswegen mit einem Datentypen versucht, der eine geringere Breite hat.
Hat aber bekanntlich nichts genützt.
Habe wirklich schon zwei Abende in das Problem investiert, bevor ich das Forum behelligen wollte. :slight_smile:

Mhh, stimmt es sind µs, nicht ms. Oops. Dann ist es nicht ganz so problematisch. Die Funktion verursacht aber trotzdem oft Probleme. Das merkst du auch wenn du im Forum danach suchst.

Auf while-Schleifen sollte man aber verzichten wenn man nicht-blockierenden Code will. Normal versucht man ein Programm so zu schreiben, dass loop() so schnell wie möglich durchlaufen wird. Wenn man dann etwas mehrmals machen will muss die Logik um eine Zählvariable und if-Abfragen erweitern.
So kann man dann erst mal eine Messung machen. Dann kann kurz was anderes dazwischen kommen. Dann wieder eine Messung, etc.

Bei dem “i == Ending” Ding ging es mir darum dass die Logik falsch ist. Wenn du maximal 10 Messungen willst muss das glaube ich so sein:

} while (((distance > 240) || (distance <= 5)) && i < Ending);

Dann wird die Und-Verknüpfung irgendwann false und die Schleife bricht ab. Oder Frage auf i in der Schleife ab und Springe mit break raus.

Das mit dem dem non-Blocking fand ich jetzt nicht so schlimm, dass sind Maximal, wie der Code gerade ausschaut etwa 0.1 Sekunden, im Normalfall ist es nur eine Messung. Die Zahl 10, sollte ich dann an der Praxis wohl auf drei oder so setzen... das ist das eigentlich auch ok. Mit einer If-Abfrage wäre man doch auch nicht schneller?

Mit der Abbruchbedingung der Whileschleife, hast du vollkommen recht! Habe den Code eingeflegt aber es zeigte sich keine Änderung... :(

Danke.

Freue mich natürlich immer wieder über hilfreiche Tipps.

Habe das Problem zu voller Zufriedenheit lösen können. Das Problem war der das Abbruchkriterium war um 1 zu niedrig und das hatte ich zwar mit einem <= ausgeglichen gab aber wohl ein kuddelmuddel.

Hallo, da ich es zum dritten Mal tippe, nur kurz. Wollte mich Mal wieder melden. An Software hat sich nicht viel getan, meiste funktioniert irgendie als Testtreiber. An dem eigentlichen Programm hat sich nicht viel getan. Hab nun aus den Staaten die XBee Module erhalten (langsamste Versandtart) und war lange andererseitig beschäftigt. Ich hatte die Fernsteuerung und Programmierung schonmal per DYP Bluetoothshield versucht. http://makezine.com/projects/diy-arduino-bluetooth-programming-shield/

Geglückt ist es mir nicht!!! Stattdessen nun 802.15.04. https://learn.adafruit.com/xbee-radios/arduino-link

Das klappte mit kleinen Macken. Muss vor den Upload das Board reseten und dann klappt es auch nicht jedes Mal. Aber Kabellos ist schon eine schöne Sache!

Probleme habe ich bisher vorallem beim Einlesen der Befehle aus dem Terminal. Mittels String-Typen aus Arduino funktionierts. Aber ich möchte es auch mittels reinen Befehlen für Zeichenfelder aus der C-Standardbibleothek. Da klappte Trennen von Mehrenbefehlen nicht. Das erste Trennen funktionierte noch und dann begann das Kudelmuddel...

Softwaretechnisch liegt gerade mein Augenmerk auf der Datenstruktur zum Speichern der Positionsdaten. Im habe erstenmal in C11 einen Treiber begonnen zuprogrammieren, der auf einem Ringspeicher mittels Feld basiert. Damit habe schön eine maximale Größe und auch das Überschreiben alter Werte. Leider funktioniert das suchen nur in linearer Zeit. Das überschreiben, aber in konstanter. Wenn ich einen Baum verwenden würde, würde das suchen in logarithmischer Zeit funkionieren, dass überschreiben alter Werte aber in Konstanter Zeit. Gibt es eine Datenstruktur oder Trick, wie ich beide Eigenschaften vereine?

Mit freundlichen Grüßen,

S. Heiden

Probleme habe ich bisher vorallem beim Einlesen der Befehle aus dem Terminal. Mittels String-Typen aus Arduino funktionierts. Aber ich möchte es auch mittels reinen Befehlen für Zeichenfelder aus der C-Standardbibleothek. Da klappte Trennen von Mehrenbefehlen nicht. Das erste Trennen funktionierte noch und dann begann das Kudelmuddel...

Das Einlesen von C Strings macht am besten so, dass man den String mit einem CR oder LF abschließt. Dann liest man solange in ein Array ein bis das Endzeichen kommt und terminiert dann den String.

Das Splitten geht dann einfach per strtok()

Siehe hier: http://forum.arduino.cc/index.php?topic=329469.msg2273780#msg2273780

Da wird strtok() immer per Hand aufgerufen. Aber das kann man auch in einer while-Schleife machen wenn die Anzahl der Tokens nicht bekannt ist

Wenn du CR und LF hast (z.B. weil du einfach per println() sendest), dann statt:

else if (c == '\r' || c == '\n')

so:

else if (c == '\n')

Übrigens mal zurück zu deinen Array Parameter die ich am Anfang angemeckert hatte. Ich habe kürzlich gelernt wieso das funktioniert. Der Vorteil dabei ist, dass das Array nicht in einen Zeiger zerfällt. Das heißt man kann innerhalb der Funktion sizeof() machen. Der Nachteil ist aber, dass die Größe des Arrays im Parameter steckt. Das heißt es nicht sehr universell und das ist der Grund weshalb man das so sehr selten sieht.

Ich habe diese Version auch nie Mehr gesehen und habe in der letzten Zeit so einige Bücher zu C gelesen. Nutze seit dem nur noch die Feldschreibweise. Die Datenspeicherung hatte ich nochmal komplett neu konzipiert. Hatte zwischendurch auf die Datenspeicherung im Array ganz verzichtet. Diese Art der Übergabe hatte ich damals auf einer Webseite gefunden. ;)

Ich hatte beim Schreiben der Funktionen für das Einlesen der Befehle aus dem Terminal schon strtok verwendet und einige C-Referenzen und Arduinoseite auch die Seite hinter deinem LInk kommt mir sehr bekannt vor. ;) Die jetzige Version des Ringpuffers schiebe ich später nach.

Liebe Grüße.

So nun zu dem Ringspeicher:

muss die Nachricht leider in Teile aufsplitten.

Der Header schaut wie folgt aus:

#ifndef RingBufferImplementation
#define RingBufferImplementation

/****************************************
 * 
 * implementation of a ring buffer
 * 
 * using following (standard) functions:
 * createRing (empty)
 * front
 * enqueue
 * dequeue
 * 
 * additionally isEmpty
 *  
 * and printRing and to come sortedPrint with compare
 * 
 * 
 ***************************************/

#define QUEUELENGTH 3 //Number of Elements in Ring

#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>

struct RingBufferImpl{
 uint16_t firstElem; //unigned because only positive Index possible and 
 uint16_t lastElem; //fewer problems with overflow
 uint16_t length;
 int16_t data[QUEUELENGTH];
};

typedef struct RingBufferImpl ringBufferType;


/*
 * Creation of an new Instance of a ring
 */
extern ringBufferType createRing();


/*
 * Returning the first Element of the Queue
 * 
 * Parameters:
 * 1.: structure  ringBufferType, storing start and end position, length and data array: input
*/
extern int front(ringBufferType InRingInstance);

/*
 * Enqueues an Element (add an Element in the last position of the Queue)
 * 
 * 1.: structure  ringBufferType, storing start and end position, length and data array: input, output(pointer)
 * 2. integer value, the element to be stored: input
*/
extern void enqueue(ringBufferType *inOutRingInstance, int16_t inElem);

/*
 * Dequeues an Element (deletes the first element)
 * 
 * 1.: structure  ringBufferType, storing start and end position, length data array: input, output(pointer)
 * 
*/
extern void dequeue(ringBufferType *inOutRingInstance);

/*****************************************
 * Returns whether Queue is empty or not:
 * true: if Queue empty
 * false: else
*****************************************/
extern bool isEmpty(ringBufferType InRingInstance);

/*
 * Returns the elements saved in the ringbuffer
 */
 void printRing(ringBufferType outRingInstance);
 
#endif
/*
 * ringBuffer.c
 * 
 */


/****************************************
 * 
 * implementation of a ring buffer
 * 
 * using following (standard) functions:
 * createRing (empty)
 * front
 * enqueue
 * dequeue
 * 
 * additionally isEmpty
 *  
 * and printRing and to come sortedPrint with compare
 * 
 * firstElem and lastElem point to the first
 * and the last elements in that ring.
 * 
 * 
 * Attention:
 * it has allways to follow, that:
 * lastElem - firstElem + 1= length
 * 
 * (else last -firstElem = 0 could mean, ONE OR NONE element)
 * thats why, lastElem starts with -2 (equals: maxInt-1)
 * 
 ***************************************/
#include "ringBuffer.h"



/*
 * Creation of an new Instance of a RingBuffer
*/
 ringBufferType createRing()
{
 ringBufferType RingInstance;
 
 RingInstance.firstElem = 0; //because no value stored
 RingInstance.lastElem = -2; 
 RingInstance.length = 0;
 
 for(int i = 0; i <QUEUELENGTH; i++)
 {
 RingInstance.data[i] = 0;
 }
 
 return RingInstance;
}

/*
 * Returning the first Element of the ring buffer
 * Parameters:
 * 1.: structure  ringBufferType, storing start and end position, length and the data array: input
*/
int front(ringBufferType InRingInstance)
{
 return InRingInstance.data[(InRingInstance.firstElem)%QUEUELENGTH];
}

/*
 * Enqueues an Element (add an Element in the last position of the ring buffer)
 * 1.: structure  ringBufferType, storing start and end position, length and the data array: input, output(pointer)
 * 2. integer value, the element to be stored: input
*/
void enqueue(ringBufferType *inOutRingInstance, int16_t inElem)
{

 //printf("%d, ", inOutRingInstance->lastElem);
 
 //using save overflow of the uint types
 inOutRingInstance->lastElem = ((inOutRingInstance->lastElem +1) % (QUEUELENGTH));
 //printf("%d, ", inOutRingInstance->lastElem);
 inOutRingInstance->data[inOutRingInstance->lastElem]= inElem;
 
 
 if (inOutRingInstance->length < QUEUELENGTH)
 { 
 //normal case, no overriding
 ++(inOutRingInstance->length);
 //printf("\r %d \n", inOutRingInstance->length);
 
 }
 else
 { //overriding
 inOutRingInstance->firstElem = ((inOutRingInstance->lastElem) + 1) % (QUEUELENGTH);
 //printf("\r %d \n", inOutRingInstance->length);
 //length stays the same
 }
 
 //printf("%d %d %d\n", inOutRingInstance->data[inOutRingInstance->lastElem],inOutRingInstance->firstElem, inOutRingInstance->lastElem);

 
}

/*
 * Dequeues an Element (deletes the first element)
 * 1.: structure  ringBufferType, storing start and end position, length and data array: input, output(pointer)
 * 
*/
void dequeue(ringBufferType *inOutRingInstance)
{
 if(inOutRingInstance->length <= 0)
 printf("Error: too few elements to dequeue\n");
 else 
 {
 
 inOutRingInstance->data[inOutRingInstance->firstElem] =  0;
 //int test =(inOutRingInstance->firstElem) + 1;
 //printf("\r %d %d %d \n", inOutRingInstance->firstElem, test, QUEUELENGTH);
 inOutRingInstance->firstElem = (((inOutRingInstance->firstElem) + 1)% QUEUELENGTH);
 //printf("\r %d \n", inOutRingInstance->firstElem); 
 --(inOutRingInstance->length);
 }
}

/*
 * Returns whether Queue is empty or not:
 * true: if Queue empty
 * false: else
*/
bool isEmpty(ringBufferType inRingInstance)
{
 return (inRingInstance.length == 0);
}


/*
 * Returns the elements saved in the ringbuffer
 * does not work as wanted...
 */
 void printRing(ringBufferType outRingInstance)
 { 
 //print only when at least one Element is included:
 if (outRingInstance.length <= 0)
 printf("Error: too few elements print\n");
 else
 {
 //printf("%d, %d \n", outRingInstance.firstElem, outRingInstance.lastElem);
 if (outRingInstance.firstElem <= outRingInstance.lastElem)
 {
 for (int i = outRingInstance.firstElem; i <=outRingInstance.lastElem; i++) //not over
 {
 
 printf("%d",outRingInstance.data[i]);
 if (i == outRingInstance.lastElem)
 printf(".");
 else
 printf(",\t");
 
 }//end for
 printf("\n");
 
 }// end if
 else
 {
 //overriden
 for (int i =outRingInstance.firstElem; i < QUEUELENGTH; i++)
 { 
 printf("%d,\t ",outRingInstance.data[i]);
 }//end for
 
 for (int i = 0; i <= outRingInstance.lastElem; i++)
 {
 printf("%d",outRingInstance.data[i]);
 if (i == outRingInstance.lastElem)
 printf(".");
 else
 printf(",\t");
 }//end for
 printf("\n");
 
 }//end else
 }//end else
}//end printRing

Der Genutzte Testtreiber:

#include "ringBuffer.h"

/*
 * Test driver for the CyclQueue Implementation, as Array
*/


int main()
{
 ringBufferType newRing = createRing();
 

 for (int i =0; i < 5; i++)
 {
 enqueue(&newRing, i*11);
 printRing(newRing);
 }
 
 
 for (int i = 0; i < 5; i++)
 {
 dequeue(&newRing);
 //printf("dequeue");
 printRing(newRing);
 //printf("printRing\n\n");
 }
 
 for (int i =0; i < 5; i++)
 {
 enqueue(&newRing, i*11);
 //printf("enqueue");
 printRing(newRing);
 //printf("printRing\n\n");
 }
 
 for (int i = 0; i < 5; i++)
 {
 dequeue(&newRing);
 //printf("dequeue");
 printRing(newRing);
 //printf("printRing\n\n");
 }
 
 
 return 0;
}

das genutzte Makefile:

#-----------------------------------------------#
# Makefile #
#    using a GNU C compiler #
#-----------------------------------------------#
CC = gcc
CFLAGS = -g -Wall  -Wextra -D__USE_FIXED_PROTOTYPES__ -std=c11
OBJ = ringBuffer_testdriver.o ringbuffer.o

all: ringBuffer
 
ringBuffer: $(OBJ)
 $(CC) $(CFLAGS) -o ringBuffer $(OBJ)

%.o: %.c ringBuffer.h

clean:
 rm -f ringBuffer.exe $(OBJ)

Ich hoffe, der Code sieht einiger Maßen aus und ist gut verständlich, dokumentiert. Über Tipps freue ich mich besonders. Bringt es was den Ringpuffer als Liste zu implementieren? Eigentlich nicht, oder?

Als nächstes versuche ich Mal die wichtigsten Winkelfunktionen für Integer zum implementieren. Ich wollte wegen Rechenzeit und Speicherplatz Fließkommazahlen vermeiden.
Die gesagt es ist erst mal eine Testimplementierung deswegen ist das Feld auch so kurz hat nur ein Integerarray und keine richtigen Positionsdaten gespeichert.

Danach habe ich mir in den Kopf gesetzt die für mich wichtigsten Winkelfunktionen für Integer zu implementieren. Ich habe 'ne Liste der Relativen Kosten einzelner Operationen von für C aus den frühen neuzigern gefunden. Wenn sich das bei dem AtMega ähnlich verhält, hat ja keine Floatingpoint unit, oder? Dann dürfte ich doch etwas an Zeit sparen und ich muss zwischendurch nicht zwischen Flusskomma und Ganzzahl konvertieren.

Ich brauche ja eingentlich nur cos, sin und atan2, und wenn ich zwei Byte im Zentimetermaß nutzte, hätte ich über 300 m in jede Richtung. Das reichte ja… Allerdings ob Zentimetermaßstab reicht muss ich schauen. Bei Millimetermaßstab, wären die etwas über 30 m in alle Richtung doch etwas problematisch. Dann bräuchte ich vier wohl Byte, dann hätte ich aber auch über 2000 km in jede Richtung und so lange reicht die Batterie nie^^… Mal sehen, was klappt. Oder ich nähme ein zölliges Maß^^… Mal sehen was mir da einflätt.
Was haltet ihr von der Idee? Gäbe es noch andere Winkelfunktionen die für euch interessant wären?

Mfg,

Mümmel

P.S.: Ich hoffe, die Art und Weise des Postings ist ok, denn dachte, wenn ich es als Datei anhänge, ließt es erst recht keiner?

Bringt es was den Ringpuffer als Liste zu implementieren? Eigentlich nicht, oder?

Nein. Das kostet nur mehr Speicher für die Zeiger

Listen sind nur wirklich sinnvoll wenn man öfters mitten in der Liste Elemente hinzufügt oder ändert. Diese Operationen hast du nicht.

Ich hoffe, die Art und Weise des Postings ist ok, denn dachte, wenn ich es als Datei anhänge, ließt es erst recht keiner

Ist sicher ok, aber hier bei uns Arduinos liest es auch kaum keiner, wenn er was von makefile sieht… :wink:
Oder stdio.h.

Warum du mehrere Ringbuffer erzeugen können willst, die aber alle die gleiche vordefinierte QUEUELENGTH haben, verstehe ich nicht, bzw. halte ich für Unsinn.

Wenn du ein Ringbuffer template “erfunden” hättest, mit dem man auch effiziente byte-Puffer bauen könnte so wie sie ohne großes Federlesen z.B. in HardwareSerial uvm. drin sind, … oder auch Puffer für größere Elemente…
Auch sind buffer die mehr als 256 Elemente haben können, bei kleinen Arduinos eher seltener.
Effiziente kleine Ringbuffer - Implementationen kann man sich z.B. in HardwareSerial.cpp ansehen.

Eine first() - Funktion sollte man nicht brauchen, wenn es ein Ring ist, eher ein available() oder peek()

Arduino Libraries sollten 1. RAM- und 2. FLASH-optimiert sein. In zeitkritischen Bereichen darf man eh keine fremden Funktionen verwenden…

Hallo,

gut vielen Dank für den Tipp. Habe mir Mal HardwareSerial.cpp bei Github angeschaut. Frage mich nur wofür man Peak braucht. Für Available hab ich das irgendwann verstanden, dass man ja vielleicht auch prüfen will, ob mehr als eine bestimmte Menge Daten da sind. Meistens sollte ja eine Funktion vom Typ _Bool ausreichen.

Ich habe dann noch ganz schon viel über optimierung gelesen, aber so richtig, habe ich danach deine Nachricht noch nicht ganz verstanden. Was den letzten Satz deiner Nachricht angeht.

Bedingte Kompilierung hatte ich noch nicht wirklich auf dem Schirm also vielen Dank hierfür nochmal.

Habe mich nun erstmal eine Cos-Fkt für Festkommazahlen geschrieben. Das läuft so auf dem Rechner einiger Maßen, wollte es nun zum Test und erhöhen der Performance auf dem Arduino testen. Dabei kam es zur Meldung eines Pufferüberlaufes in einem Makro. Nur verstehe ich nicht warum es dazu kommen kann, am PC wird das nicht gemeldet und die Ergebnisse die er Meldet sind ok.

Der Testtreiber, zeigte einen Durchschnittlichen Fehler von
4,6 % bei einer Nachkommastelle
0,57 % bei zwei Nachkommastellen (wohl der Unterschie von 3,1 zu 3,14)
4,7 % bei drei Nachkommastellen (??? Liegts am zu frühen Abbruchs des Taylor-Polynoms?)

Nun aber zum Code:
der Header ist:

/**********************************
 * 
 *  fixed point arithemics 
 * 
 * including int_cos, conversion between rad and deg
 * 
 * unit: degrees for degrees, and normal uint * SCALE (factor)
 * 
 *********************************/ 

#ifndef INT_MATH
#define INT_MATH

#include <stdint.h>
#include <math.h>

#ifndef SCALE
#define SCALE 100		//all integers are fixed point numbers,
						//with 2 decimal plaxes
#endif

#define PI_INT ((314159 * SCALE)/ 100000)

#if SCALE > 0
#if SCALE <= 100
typedef int16_t math_t;	//at least int16_t because of min
						//360 degrease of which
						//max 90° are used for calculation
#else
typedef int32_t math_t;
#endif
#else
printf("Please select positiv SCALE (factor)");
#endif

#define sqr(x) ((x)*(x))
#define bi_sqr(x) (sqr(x)*sqr(x))
#define cub(x) (sqr(x)*(x))


extern math_t int_cos(math_t radxSCALE);
extern math_t int_deg(math_t radxSCALE);
extern math_t int_rad(math_t degree);

#endif

Der Quellcode:

#include "int_math.h"
#include <stdio.h>

math_t int_cos(math_t radxSCALE)
{
	//printf("SCALE factor: %d\n\n", SCALE); 
	math_t cosi;
	math_t multi = 1;
	 

	while (radxSCALE >= (2 * PI_INT))				// cos repeats itself every 360°
	{
		radxSCALE = radxSCALE - (2*PI_INT);
	}
	
	while(radxSCALE >= PI_INT/2)
	{
		radxSCALE = radxSCALE - PI_INT;
		multi = - multi;
	}
	
	if (radxSCALE < 0)
		radxSCALE = -radxSCALE;					//cos is axissymmetric		

	cosi = multi*(SCALE - sqr(radxSCALE)/SCALE / 2 + bi_sqr(radxSCALE)/24/cub(SCALE));
	
	return cosi;
}//end int_cos


inline math_t int_deg(math_t radxSCALE)
{
	math_t degree = (180 * radxSCALE)/ PI_INT;
	
	return degree;
}//end int_degree


inline math_t int_rad(math_t degree)
{
	math_t radixScale = (degree*PI_INT)/180;
	//math_t ergebnis = degree*PI_INT;
	//printf("%d & %d & %d & %d\n", PI_INT, degree, ergebnis, radixScale);
	return radixScale;
}//end int_rad

Das ist auch nett:

Einfache Mittel und der Fehler ist < 2%

Edit:
Der Fehler wird hier kommen:

#define PI_INT ((314159 * SCALE)/ 100000)

Korrekt:

#define PI_INT ((314159UL * SCALE)/ 100000)

Auch bei Makros gilt, der Standard-Datentyp ist int! Und das ist auf dem Arduino mit AVR nur 16 Bit. Nicht 32 wie auf dem PC. Der entsprechende PC-Datentyp ist short.

Das ist ein Integer-Überlauf. Kein Puffer-Überlauf.