Wegmessung mit ADNS-9800 Maus Sensor

Hallo,

ich hab folgendes Problem:

Ich habe mir im Internet (https://www.tindie.com/products/jkicklighter/adns-9800-optical-laser-sensor/) dieses Maus Sensor gekauft, weil ich damit Winkelmessung beziehungsweise Längenmessung machen möchte.

Ich hab den Sketch von Jkicklighter verwendet. Das Programm läuft soweit ganz gut.
Nun habe ich habe ich folgendes Problem:
Der Sensor gibt mir bei unterschiedlichen Geschwindigkeiten und unterschiedlichen Auflösung immer eine andere Anzahl an Werten aus (Delta_x und Delta_y Werte) aus. Um eine sinnvolle Anwendung zu haben sollte ich möglichst immer die selben Werte erhalten.
Woran könnte das liegen? Könnt ihr mir dabei weiterhelfen?
Ist eventuell die Verbindung per USB zu langsam, ich habe 250000 baud eingestellt.
Oder gibt es irgendwelche Einstellungen, die ich noch vergessen habe?

byte initComplete = 0;
byte testctr = 0;
unsigned long currTime;
unsigned long timer;
volatile int xydat[2];
volatile byte movementflag = 0;
const int ncs =53;

extern const unsigned short firmware_length;
extern const unsigned char firmware_data[];

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

 pinMode (ncs, OUTPUT);

 attachInterrupt(0, UpdatePointer, FALLING);

 SPI.begin();
 SPI.setDataMode(SPI_MODE3);
 SPI.setBitOrder(MSBFIRST);
 SPI.setClockDivider(8);

 performStartup();
 dispRegisters();
 delay(100);
 initComplete = 9;

}

void adns_com_begin() {
 digitalWrite(ncs, LOW);
}

void adns_com_end() {
 digitalWrite(ncs, HIGH);
}

byte adns_read_reg(byte reg_addr) {
 adns_com_begin();

 // send adress of the register, with MSBit = 0 to indicate it's a read
 SPI.transfer(reg_addr & 0x7f );
 delayMicroseconds(100); // tSRAD
 // read data
 byte data = SPI.transfer(0);

 delayMicroseconds(1); // tSCLK-NCS for read operation is 120ns
 adns_com_end();
 delayMicroseconds(19); //  tSRW/tSRR (=20us) minus tSCLK-NCS

 return data;
}

void adns_write_reg(byte reg_addr, byte data) {
 adns_com_begin();

 //send adress of the register, with MSBit = 1 to indicate it's a write
 SPI.transfer(reg_addr | 0x80 );
 //sent data
 SPI.transfer(data);

 delayMicroseconds(20); // tSCLK-NCS for write operation
 adns_com_end();
 delayMicroseconds(100); // tSWW/tSWR (=120us) minus tSCLK-NCS. Could be shortened, but is looks like a safe lower bound
}

void adns_upload_firmware() {
 // send the firmware to the chip, cf p.18 of the datasheet
 Serial.println("Uploading firmware...");
 // set the configuration_IV register in 3k firmware mode
 adns_write_reg(REG_Configuration_IV, 0x02); // bit 1 = 1 for 3k mode, other bits are reserved

 // write 0x1d in SROM_enable reg for initializing
 adns_write_reg(REG_SROM_Enable, 0x1d);

 // wait for more than one frame period
 delay(10); // assume that the frame rate is as low as 100fps... even if it should never be that low

 // write 0x18 to SROM_enable to start SROM download
 adns_write_reg(REG_SROM_Enable, 0x18);

 // write the SROM file (=firmware data)
 adns_com_begin();
 SPI.transfer(REG_SROM_Load_Burst | 0x80); // write burst destination adress
 delayMicroseconds(15);

 // send all bytes of the firmware
 unsigned char c;
 for (int i = 0; i < firmware_length; i++) {
   c = (unsigned char)pgm_read_byte(firmware_data + i);
   SPI.transfer(c);
   delayMicroseconds(15);
 }
 adns_com_end();
}


void performStartup(void) {
 adns_com_end(); // ensure that the serial port is reset
 adns_com_begin(); // ensure that the serial port is reset
 adns_com_end(); // ensure that the serial port is reset
 adns_write_reg(REG_Power_Up_Reset, 0x5a); // force reset
 delay(50); // wait for it to reboot
 // read registers 0x02 to 0x06 (and discard the data)
 adns_read_reg(REG_Motion);
 adns_read_reg(REG_Delta_X_L);
 adns_read_reg(REG_Delta_X_H);
 adns_read_reg(REG_Delta_Y_L);
 adns_read_reg(REG_Delta_Y_H);
 // upload the firmware
 adns_upload_firmware();
 delay(10);
 //enable laser(bit 0 = 0b), in normal mode (bits 3,2,1 = 000b)
 // reading the actual value of the register is important because the real
 // default value is different from what is said in the datasheet, and if you
 // change the reserved bytes (like by writing 0x00...) it would not work.
 byte laser_ctrl0 = adns_read_reg(REG_LASER_CTRL0);
 adns_write_reg(REG_LASER_CTRL0, laser_ctrl0 & 0xf0 );

 delay(1);

 Serial.println("Optical Chip Initialized");
}

void UpdatePointer(void) {
 if (initComplete == 9) {

   digitalWrite(ncs, LOW);
   xydat[0] = (int)adns_read_reg(REG_Delta_X_L);
   xydat[1] = (int)adns_read_reg(REG_Delta_Y_L);
   digitalWrite(ncs, HIGH);

   movementflag = 1;
 }
}

void dispRegisters(void) {
 int oreg[7] = {
   0x00, 0x3F, 0x2A, 0x02
 };
 char* oregname[] = {
   "Product_ID", "Inverse_Product_ID", "SROM_Version", "Motion"
 };
 byte regres;

 digitalWrite(ncs, LOW);

 int rctr = 0;
 for (rctr = 0; rctr < 4; rctr++) {
   SPI.transfer(oreg[rctr]);
   delay(1);
   Serial.println("---");
   Serial.println(oregname[rctr]);
   Serial.println(oreg[rctr], HEX);
   regres = SPI.transfer(0);
   Serial.println(regres, BIN);
   Serial.println(regres, HEX);
   delay(1);
 }
 digitalWrite(ncs, HIGH);
}


int convTwosComp(int b) {
 //Convert from 2's complement
 if (b & 0x80) {
   b = -1 * ((b ^ 0xff) + 1);
 }
 return b;
}

void loop() {
 currTime = millis();

 if (currTime > timer) {
   Serial.println(testctr++);
   timer = currTime + 500;
 }

 if (movementflag) {
   Serial.print("x = ");
   Serial.print( convTwosComp(xydat[0]) );
   Serial.print(" | ");
   Serial.print("y = ");
   Serial.println( convTwosComp(xydat[1]) );
   movementflag = 0;
 }

}

Natürlich hast Du bei verschiedenen Auflösungen verschiedene Werte.

Beschreib aber mal Deinen Aufbau. Wie weit ist der Sensor von der Oberflächer entfernt? Welches Material hat die Oberfläche?

Grüße Uwe

hi,

es gibt am PC die zeigerbeschleunigung, das heißt, je schneller Du die maus bewegst, um so weiter bewegt sich der cursor bei gleicher wegstrecke der maus. könnte so etwas hier schon in der hardware implementiert sein?

gruß stefan

Das wusste ich auch das ich bei unterschiedlichen Auflösungen unterschieldiche Werte bekommen.
Der Aufbau sieht wie folgt aus:
Der Sensor ist mit einem Abstand von ca 2,4mm weg von einem weißem Papier. Dieses Papier liegt auf einem Aufbau, der von einem Schrittmotor angetrieben wird. Damit ich sehr fein Auflösen kann.

Bei der höchsten Auflösung bekomme ich bei einer geringen Geschwindigkeit sehr viele Werte angezeigt, doch je höher die Geschwindigkeit wird umso weniger Werte zeigt mir das Programm an. Kann dies mit der Übertragungsrate zusammenhängen?

hi,

ok, unterschiedliche auflösungen, unterschiedliche werte ist klar. es hat nur in Deinem ersten post so ausgesehen, als gäbe es da auch ungereimtheiten.

ich nehme mal an, daß er dir bei höheren geschwindigkeiten nicht weniger werte anzeigt, sondern gleich viele, allerdings bezogen auf zeit. also sagen wir mal, als hausnummer, 100 pro sekunde. wenn Du dann eine definierte strecke in 3 sekunden abfährst, bekommst Du 300 werte, wenn Du die geschwindigkeit verdreifachst, nur 100 werte.

hauptsache, der anfangs- und der endwert sind in beiden fällen gleich (falls nicht, könnte das etwas mit der zeigerbeschleunigung zu tun haben).

wenn Du dann ganz genau arbeiten willst, mußt Du halt gegen ende der strecke mit der geschwindigkeit runtergehen.

gruß stefan

Die Geschwindigkeit wird durch einen Schrittmotor mit einer Rampe als Anlauf und Bremse betrieben.

Okay das werde ich mal ausprobieren, ob es sicht tatsächlich so verhält, wie du es beschrieben hast.

Bei der Verwendung des Schrittmotors bei einer Auflösung von 8200 dpi werden zu beginn mehrere Werte aufgezeichnet, sobald der Schrittmotor seine Sollgeschwindigkeit hat, werden fast gar keine Werte mehr aufgezeichnet un sobald die Geschwindigkeit wieder abnimmt werden die Werte wieder mehr. Könnte dies auch mit der Übertragungsrat oder der Geschwindigkeit des Arduino Mega Boards zusammenhängen? Denn am Anfang hatte ich die Baud bei 9600 und später bei 250000 dort wurden die doppelte Anzahl der Werte angezeigt.

30mb90: Könnte dies auch mit der Übertragungsrat oder der Geschwindigkeit des Arduino Mega Boards zusammenhängen? Denn am Anfang hatte ich die Baud bei 9600 und später bei 250000 dort wurden die doppelte Anzahl der Werte angezeigt.

500.000 Baud geht auch noch

Was da noch hinzukommen kann ist dass der Ausgangspuffer das Programm blockiert wenn er voll ist und du weiter rein schreiben willst. Den kannst du in x:\Arduino\hardware\arduino\avr\cores\arduino\HardwareSerial.h vergrößern. Bei SERIAL_TX_BUFFER_SIZE. Standard sind 64 Bytes. Weiß aber nicht ob das was bringt. Wenn du es ändert nimmt eine Zweier-Potenz. Also z.B. 128 oder 256

Ich habe jetzt den Ausgangspuffer geändert, aber es hat keinen Unterschied gemacht.

Nochmal mein Problem:

Ich möchte z.B. eine Strecke von 3 cm mit einer gleichen Auflösung des Sensors messen. Dabei soll der Sensor bei unterschiedlichen Geschwindigkeiten nahezu immer die gleichen Anzahl an Werte ausgeben. Tut er aber nicht.

Wie bekomme ich dieses PRoblem gelöst?

Ich würde nicht davon ausgehen, daß die Maus-Elektronik zuverlässige Werte liefert. Möglicherweise funktioniert sie erst ab einer gewissen Mindestgeschwindigkeit. Für ein Eingabegerät ist das tragbar, schließlich kontrolliert der Benutzer die erreichte Position am Bildschirm, nicht auf dem Mauspad.

Eben mal getestet: Die Maus zwischen zwei Anschlägen hin und her bewegen, in der einen Richtung schnell, in der anderen langsam. Dann verschiebt sich der Cursor stetig in Richtung der schnellen Bewegung. Das würde sich mit Deiner Beobachtung decken, daß bei höherer Geschwindigkeit mehr Mickeys geliefert werden.

Hier nochmal eine genaue Beschreibung meines Versuchs:

Ich möchte mit dem genannten Sensor http://datasheet.octopart.com/ADNS-9800-Avago-datasheet-10666463.pdf
eine möglichst genaue Weg (Winkel) Messung aufbauen. Die zumindest in einem bestimmten „Geschwindigkeitsfenster“ genau ist.
Im Prinzip möchte ich einen Encoder bauen der mir beispielsweise reproduzierbar 3600 Counts/Umdrehung (vielleicht auch nur 360 je nachdem wie gut der Sensor geht) ausspuckt. Der Abstand zwischen Sensor und Drehpunkt der Scheibe die ich detektiere ist immer gleich. Die Drehgeschwindigkeit bleibt aber nicht konstant und variiert beispielsweise zwischen 3 und 30 U/min.
Ich habe bereits auf dem weißem Blatt Papier schwarze Striche angebracht (eine Art Encoder). Die Erkennung der Striche erfolgt über ein Shutter Register des Sensors. Die Striche erkennt der Sensor bei hoher Auflösung und geringer Geschwindigkeit sehr gut.

Den Versuchs Aufbau habe ich ja schon beschrieben. Aber nochmal kurz: Der Sensor befindet sich in 2.4mm höhe (laut Datenblatt) im fixen Abstand (aktuell zirka 30mm) zum Drehpunkt über einer festen Scheibe die mit Papier beklebt ist. Diese Scheibe wird mit einem Schrittmotor+ Getriebe angesteuert. Der Sensor ist Tangential montiert sodass ich nur X oder nur Y Änderungen detektiere.

Zum meine bisherigen Versuchen:

1 Umdrehung der Scheibe mit Winkelgeschwindigkeit 1 U/min bei 800dpi des Sensors = 5400 Counts
1 Umdrehung der Scheibe mit Winkelgeschwindigkeit 3 U/min bei 800dpi des Sensors = 5000 Counts
1 Umdrehung der Scheibe mit Winkelgeschwindigkeit 8 U/min bei 800dpi des Sensors = 4800 Counts
1 Umdrehung der Scheibe mit Winkelgeschwindigkeit 20 U/min bei 800dpi des Sensors = 4600 Counts
Bei dieser Auflösung bleiben die Counts nahezu konstant.

1 Umdrehung der Scheibe mit Winkelgeschwindigkeit 1 U/min bei 8200dpi des Sensors = 32000 Counts
1 Umdrehung der Scheibe mit Winkelgeschwindigkeit 3 U/min bei 8200dpi des Sensors = 4000 Counts
1 Umdrehung der Scheibe mit Winkelgeschwindigkeit 8 U/min bei 8200dpi des Sensors = 400 Counts
1 Umdrehung der Scheibe mit Winkelgeschwindigkeit 20 U/min bei 8200dpi des Sensors = 160 Counts

Wie man anhand der Counts sehen kann, liefert der Sensor bei langsamen Geschwindigkeiten gute, reproduzierbare Ergebnisse. Selbst bei kleinen Winkeländerungen (<1 Grad) bei 8200dpi spukt der mir noch viele Werte aus. Alles toll.

Sobald ich aber mit unterschiedlicher Drehgeschwindigkeit „messe“ gehen die Counts nach oben oder unten (sieht Counts bei unterschiedlichen Umdrehungen pro Minute) Also ich verändere nur die Drehgeschwindigkeit.
Ich würde gerne mit 8200dpi messen da ich mit dieser Sensorauflösung auch kleinste Winkeländerungen detektiere. Das Problem: ab einer bestimmten Drehgeschwindigkeit (Der Sensor kann 30g Beschleunigung und bis zu 3,8 m/s Änderungen detektieren) brechen die Counts ein.
Ich hab das Gefühl dass der Arduino „nichtmehr hinter her“ kommt. Da es sich ja um sehr große Datenmenden handelt, die vom Sensor zum Arduino geschickt werden. Ist möglicherweise die Datenübertragung des Arduino Mega zu langsam?

Zum anderen will ich aber immer eine konstante Count zahl unabhängig von der Drehgeschwindigkeit. Also bsp. Für 180 Grad will ich 1800 counts… egal ob ich mit 3 oder 30 U/min fahre.

Idee : einen Art Umrechnungsfaktor??! ODER weitere Möglichkeiten des Sensors nutzen (Siehe Datenblatt) Der Sensor kann viel mehr also nur X und Y ausspucken.

Hoffe Ich konnte alles nochmal genau beschreiben und hoffe auf Ideen 

hi,

also ich verstehe das nicht ganz. warum willst Du "counts"? bekommst Du denn keine werte?

wenn Du eine achse abfährst, solltest Du doch werte erhalten, wie weit Du verfahren bist. daß bei größeren geschwindigkeiten weniger werte für die gleiche strecke kommen, scheint mir normal. aber die werte müssen die gleichen sein.

also wenn Du (von angenommenen 0 bis 100) sehr langsam fährst, kommt:

0 - 1 - 2 - 3 - 4 - ...... - 99 - 100

wenn Du schneller fährst:

0 - 12 - 25 - 37 - 51 - 65 - 77 - 90 - 103

wenn Du sehr schnell fährst, eben:

0 - 30 - 60 - 90 - 120

oder liege ich da falsch?

gruß stefan

Nein ich bekommen keine Werte, sonder der Sensor liefert sobald er ein delta in y bzw. x erfährt, je nach Bewegungsrichtung, eine 1 oder eine -1 in dieser Achs. Eine Änderung zeigt er entweder für die y-Achse oder die x-Achse an

Ich kann die Änderungen in einer Richtung, über einen Zähler zählen lassen, dabei entstehen die oben bereits genannten „counts“ oder Werte pro Umdrehung und diese unterscheiden sich doch sehr.

hi,

das kann ich mir beim besten willen nicht vorstellen, so arbeitet kein maus-sensor. da werden IMMER werte geliefert, der computer muß NICHT zählen.

es werden doch immer diese werte ausgegeben:

 if (movementflag) {
   Serial.print("x = ");
   Serial.print( convTwosComp(xydat[0]) );
   Serial.print(" | ");
   Serial.print("y = ");
   Serial.println( convTwosComp(xydat[1]) );
   movementflag = 0;
 }

wie sieht das im monitor aus?

gruß stefan

Die Ausgabe auf dem Monitor sieht bei einer Drehung in einer Richtung wie folgt aus:

Serieller Monitor: x = 0 | y = 1 x = 0 | y = 1 x = 0 | y = 1 x = 0 | y = 1 x = 0 | y = 1 x = 0 | y = 1 x = 0 | y = 1 x = 0 | y = 1 x = 0 | y = 1 x = 0 | y = 1 x = 0 | y = 1 x = 0 | y = 1 x = 0 | y = 1 x = 0 | y = 1 x = 0 | y = 1 x = 0 | y = 1 x = 0 | y = 1 x = 0 | y = 1 x = 0 | y = 1 x = 0 | y = 1 x = 0 | y = 1

Ich habe ja nur den reinen Maussensor-Chip ohne irgendwelche zusätzliche Elektronik. Bei der Computer-Maus ist ja zusätzlich noch ein Chip verbaut, der diese Daten dan verwertet.

hi,

3 möglichkeiten:

ding kaputt ding falsch angeschlossen sketch fehlerhaft

hier:

 adns_read_reg(REG_Delta_X_L);
 adns_read_reg(REG_Delta_X_H);
 adns_read_reg(REG_Delta_Y_L);
 adns_read_reg(REG_Delta_Y_H);

sollen 2 int gebildet werden, und zwar die veränderung für x und für y. DELTA wird als ausdruck für differenz verwendet. ist aber irgendwie an seltsamer stelle im sketch.

das paßt auch mit den angaben im datenblatt zusammen:

BYTE [02] = Delta_X_L BYTE [03] = Delta_X_H BYTE [04] = Delta_Y_L BYTE [05] = Delta_Y_H

ich kann Dir jetzt da nicht weitehelfen, wie auch ohne sensor, aber Du gehst von falschen erwartungen aus. der sensor liefert sicher mehr als clicks.

gruß stefan

Ändert sich eigentlich was an Deiner Ausgabe, wenn Du die Encoderscheibe bzw. den Sensor hin und her bewegst, also nicht nur in einer Richtung? Dann sollte doch auch mal -1 ausgegeben werden, wenn sonst alles richtig ist.

Ich könnte mir vorstellen, daß Du nur "1" bekommst wenn sich die Position in dieser Richtung ändert, und garnicht die Anzahl der Schritte (DELTA_X, DELTA_Y).

@DrDiettrich: Jupp sobald ich die Richtung wechsle erhalte ich ein -1

Danke für Eure Antworten!

Ich habe das Problem gelöst, ich habe das Auslesen der Delta Werte in den Loop verschoben und einen Counter daraus gebastelt und jetzt zeigt er mir bei gleichem Weg und unterschiedlichen Geschwindigkeiten nahezu die gleichen Werte an.

Gruß