Beschrijving
SINCE I AM NOT GOING TO USE THESE ANYWAY, why not cannibalize them? They contain a encased photo interrupter which fits perfectly in the chassis. It is the WYC-H206 (Pict.1). More the less as expected the IR LED turns hot when supplied with 5vDC AND without a current limiting resistor.
ABANDONED FOR NOW. I cannot get these parts to function correctly since I have to solder a resistor on a point so small I cannot see it. Basically the problem is due to some redundant hardware on the brick, like the comparator chip. An Infra Red LED and receiver bridge without further hardware is the best solution with an Arduino.
electronics-tutorials northwestern.edu utopiamechanicus RPI-574 Photointerrupter encased type electroschematics.com .
Once the HC-020K is powered up a LED will blink as soon as you turn the wheel a bit.
Transmit tube pressure drop | Vf = 1.6 V |
Signal output | A, B two way; The TTL level |
Distinguish precision | 0.01 mm |
Measurement frequency | 100 KHZ |
Encoder diameter | 24 mm |
Encoder type D hole diameter | 4 mm |
The encoder resolution | 20 line |
Start at Brainy bits and download and install the TimerOne.h library. You will need 2 interrupts, one for each encoder. On my Mega 2560 there are 6 available interrupt-pins (2,3,18,19,20,21). Two of them are covered by the motor shield. I took the 2 TWI interface pins: 20 ,21. I will have to sacrifice them to my encoders. The newly installed TimerOne seems to work OK.
Research after interrupts shows that the LED on the encoder needs to toggle twice before the output will toggle once if the Interrupt is triggered by a HIGH signal. If we select the CHANGE as trigger our LED will toggle along with the LED on the sensor. However NOT every change we see on the sensor is recognized by our Arduino board. The LM393 functions without problems. There seem to be 2 problems with these encoders:
- Bouncing/Oscillating at transitions
The problem seems to be that an external hysteresis is required (explanation). The module is a standard IR emitter/detector pair that shines IR light through the slots in the encoder wheel. As the wheel turns, the detector sees light pulses, which are turned into electrical pulses via a simple comparator circuit. The circuit problem is that the comparator circuit was designed without hysteresis, so the output oscillates at the transitions between light and dark. Anyway, soldering a 30K resistor from the comparator output back to the positive input pin solved the problem (source1). As shipped the output is so noisy that it`s almost useless because of the terrible signal bouncing at the transitions. Adding a 33k resistor between the output and positive input of the comparator as suggested cleaned this up into a nice square wave.
- Rough edges
I tried:
- I also needed a 30pF cap on the output to slow the edge down just a tad to create a clean signal with no reflections or reversals. These devices basically worked, but there is no supply bypassing which basically causes the encoder edges to be very noisy. Without added supply bypassing my Arduino Uno would sometimes count multiple pulses per edge, or miss an edge all together. Luckily I have a oscilloscope so I could look at the sensor output and the edges looked horrendous. I added a small 0.1uF ceramic cap between the 5V and GND pins of the board and the edges cleaned up nicely. Now both encoders work perfectly with my Arduino. I mounted a 0.1uF ceramic cap between the 5Vin and GND pin and I just get even much more pulses.
- Check the data sheet on the encoder. Does it have an open collector output? If so, enable the pull up on your interrupt pin. (I did implement the internal pull up but is does not seem to help a lot. Not enough to save the encoders anyway.)
- Also, decoupling caps on the motor (if there is one) is a MUST! (source) (While testing I have to drive the wheel by hand and therefore no spikes can be created by my motor.
Legacy
Odometrie is het meten van de draaing van de wielen van de robot. Een veel gebruikte toepassing is het synchroniseren van 2 sturende aandrijfwielen zodat een robot rechtuit kan rijden. Ook voor positiebepaling kan odometrie worden gebruikt. Als er een bekend startpunt wordt genomen kan de robot zijn positie berekenen ten opzichte van dat startpunt. In de praktijk valt de nauwkeurigheid van de positiebepaling helaas tegen. Wielen zullen slip hebben en de ondergrond is niet altijd vlak.
Een lampje en een lichtsensor zijn gemonteerd aan weerszijden van een tandwieltje met gaatjes. Allereerst heb ik een muis gesloopt en een testopstelling op mijn breadboard gemaakt. Daaruit blijkt voor de onderdelen die uit mijn Logitech First / Pilot Wheel Mouse komen op de volgende manier moeten worden aangesloten om een effect te kunnen zien. LED (liggend op de rug met de pins naar mij toe) heeft 3 pins. De middelste is niet doorgevoerd of aangesloten. De linker (grijs) gaat via een 270 Ohm weerstand naar de aarde. De rechter (geel) gaat naar de 5Vdc. Ook de IR-sensor heeft 3 pennen. De beschrijving is ook liggend op de rug met de pennen naar mij toe. Het uitstekende nokje is dus de rug van de sensor. De linker is niet aangesloten. De middelste (bruin) is via een 270 Ohm weerstand aangesloten op de D/A convertor van een ATmega16 op een STK500. De rechter draad (groen) is aangesloten op de aarde.
Als de sensor vol belicht wordt, dan gaan alle ledjes op de STK500 uit. Deze ledjes zijn geinverteerd, dus de meting is FF. Als de sensor wordt afgedekt dan gaan de onderste 7 ledjes branden. Kortom het meetverschil is groot genoeg. De volgende stap is het uitzoeken van een handig programma en de bijbehorende timing. Het programma STKmega16sens99 is gemaakt om de odometer te testen. In eerste instantie is dit programma slechts bedoeld om via een A/D conversie verschillen te kunnen meten van de IR ontvanger. Maar de bedoeling is dat dit verandert in een interrupt gestuurde counter. Uiteindelijk moet de robot tenslotte het soort opdrachten kunnen verwerken zoals: Rijdt 10 cm vooruit. Timing gaat hier ook een rol spelen. Kan 1 ATmega8 2 odometers aansturen? Op 16mHz: Makkelijk.
Er kan binnen de controller voor een timer worden gekozen om de odometer uit te lezen. Het grote voordeel is dat dit nauwelijks processortijd kost. De bedoeling is om een processor te nemen voor de totale aandrijving. Ik noem deze processor voor het gemak hier de odoprocessor. De hoofdprocessor kan dan met simpele en begrijpbare instructies de robot sturen. Deze instructies worden met een protocol doorgegeven van de hoofdprocessor naar de odoprocessor. Als de uitvoering klaar is dan krijgt de hoofdprocessor een seintje terug. Een voorbeeld van een dergelijke instructie is bijvoorbeeld: Rij 5 cm vooruit of bijvoorbeeld: Draai 45 graden naar rechts. Indien ieder chassis deze instructies begrijpt dan wordt het makkelijk om een applicatie over te zetten van een testomgeving naar een werkomgeving.
Met een Atmel microcontroller kan zowel de stijgende flank als de dalende flank worden gemeten. Kortom open en dicht dienen even groot te zijn. Dit vereist nog een aparte berekening gebaseerd op enerzijds standaard boortjes zoals M3 of M4 en anderzijds op de diameter van de gaatjescircel. Belangrijk is dus om een diameter te vinden waarop met een standaard boortje, gelijke openingen kunnen worden geboord. Het beschrijven van de boormethode is ook niet onbelangrijk, sinds het zo nauwkeurig moet gebeuren.
Er is eigenlijk alleen bij het middelste tandwiel voldoende plek om de electronica in te bouwen. Het motortandwiel is weliswaar 4 keer zo nauwkeurig, maar mijn onderdelen zijn helaas 1 mm te dik om ze daar te kunnen bevestigen. De diameter van het wiel is bepaald op 55mm en de omtrek is dus 172,8mm. De werksnelheid van Farmbot is bepaald op 5km per uur. Dat betekent dat de robot 5000 /3600 * 1000 = 1388,9 mm per seconde aflegt. Dat zijn 8,04 rotaties van het wiel per seconde. Het tandwiel aan het rijwiel heeft 48 tanden en het kleine tandwiel aan ons meetwiel heeft 12 tanden. Dat betekent dat ons meettandwiel 4 keer zo snel draait als het rijwiel. De gaatjes in het tandwiel worden 2 mm met tussenruimtes van 2 mm. Er is dan plek voor 12 gaatjes, oftewel iedere 4 tandjes. De nauwkeurigheid bij een enkele meting is dan 55 / 4 / 12 = 1,15 mm.
De tijd die beschikbaar is om 1 gaatje te meten is: 1 / 8,04 / 12 = 0,01 s oftewel 10 milliseconden. Dus bij een 16 Mhz werksnelheid is er ruim voldoende tijd.
De volgende stap is het inbouwen van de odometer in een Cybot wielkast. Oeps, de tandwielen blijken IR licht gewoon door te laten, alsof ze van glas zijn. Tandwiel zwart gespoten. Dat blijkt te helpen.
//This is an include, so no parsing needed
$qry2 = ("SELECT onderdelen.*,
soortonderdeel.id
FROM onderdelen
JOIN soortonderdeel ON onderdelen.idsrtodd = soortonderdeel.id
WHERE onderdelen.partid = $idpart
;");
$result2 = pg_query($con1, $qry2);
$row4 = pg_fetch_array($result2); //Make the fields accesible
foreach( $row4 AS $key => $val ){
$$key = stripslashes( $val );
}
$jaar = substr($changeddate,0,4);
$maand = substr($changeddate,5,2);
$dag = substr($changeddate,8,2);
$lastupdate = ("$jaar $maand $dag");
$prtsrttxt = get_txt($con1, 26, 1, $idsrtodd);
$jaar = substr($createddate,0,4);
$maand = substr($createddate,5,2);
$dag = substr($createddate,8,2);
$create_stamp = ("$dag $maand $jaar");
//GENERAL INFO FOR EVERY USER **************************************************
echo (" ");
echo (""); //Header
$tmp = get_txt($con1, 9, $koekje[2], 3923); //Fieldname at screen
if ($koekje[3]=="1") { //If this user wants to translate then
echo ("$tmp"); //If desired
}else{ echo ("$tmp"); } //Else just show if no need to translate
if ($koekje[0]>0 and $koekje[3]>0){ //User is logged in and translates
echo (" (edit)");
}
echo ("Algemene informatie test | ");
if ($codetext) { //Code like KY-026 or NA-41
echo ("");
$tmp = get_txt($con1, 9, $koekje[2], 3739); //Fieldname at screen
if ($koekje[3]=="1") { //If this user wants to translate then
echo ("$tmp"); //Class for colour
}else{ //So you are not allowed to translate
echo $tmp; //Show screenname of this field in user language
} //End of If you want to translate this fieldname at screen
echo ("  | ");
if ($codelink) {
echo ("$codetext");
}else{
echo ("$codetext");
} //End of if ($pinoutlink)
echo (" | ");
} //End of if ($codetext)
$tmp = get_txt($con1, 9, $koekje[2], 3741); //Fieldname at screen
if ($prijseurinclbtw > 0){ //Show price indication
$prijs_formatted = number_format($prijseurinclbtw / 100, 2, ',', '.');
echo ("$tmp   | ");
echo ("");
if ($urlprijs){
echo ("€  $prijs_formatted");
}else{
echo ("€  $prijs_formatted");
} //End of if ($urlprijs > "")
echo (" | ");
} //End of if ($prijseurinclbtw > 0)
if ($sketchtext) { //Link to OWN SOFTWARE
$tmp = get_txt($con1, 9, $koekje[2], 3738); //Fieldname at screen
echo ("$tmp   | ");
if ($sketchlink) {
echo ("$sketchtext");
}else{
echo ("$sketchtext");
} //End of if ($sketchlink)
echo (" | ");
} //End of if ($sketchtext)
$tmp = get_txt($con1, 9, $koekje[2], 3744); //Fieldname at screen
if ($pinouttext) { //Pinout
echo ("$tmp   | ");
if ($pinoutlink) {
echo ("$pinouttext");
}else{
echo ("$pinouttext");
} //End of if ($pinoutlink)
echo (" | ");
}
$tmp = get_txt($con1, 9, 1, 3735); //Fieldname at screen
$tmp2 = get_txt($con1, 26, 1, $row4[16]); //Group name in its language
echo ("$tmp   | "); //MENU GROUP
echo ("");
echo ("$tmp2 | ");
if ($koekje[0]>0 and $koekje[3]>0){ //User is logged in and translates
$tmp = get_txt($con1, 9, $koekje[2], 3918); //Fieldname at screen LANGUAGE
if ($row4[26] == 1) {$imgname = "nederland.png";}
if ($row4[26] == 2) {$imgname = "deutschland.png";}
if ($row4[26] == 3) {$imgname = "uk.png";}
if ($row4[26] == 4) {$imgname = "france.png";}
echo ("$tmp | ");
echo ("");
echo (" | ");
$tmp = get_txt($con1, 9, $row4[27], 3919); //Fieldname at screen
$statuskey = get_status_key($con1, $status);
$tmp2 = get_txt($con1, 61, $koekje[2], $statuskey); //Fieldname at screen
echo ("$tmp   | "); //STATUS
echo ("$status $tmp2 | ");
}
$tmp = get_txt($con1, 9, 1, 3966); //Fieldname at screen
echo ("$tmp   | "); //LAST CHANGED
$tmp = substr($changeddate, 0, 10);
echo ("$tmp | ");
echo (" ");
return;
////////////////////////////////////////////////////////////////////////////////
function get_status_key($con, $num){
$qry = ("SELECT id FROM status WHERE nr = $num ;");
$result = pg_query($con, $qry);
$row = pg_fetch_array($result);
return ("$row[0]");
}
// LIST ALL SOFTWARE THAT WILL RUN OM THIS PART *****************************
// INCLUDED IN ALL ROBOTS TO CHECK THEIR DIRECTORY
$qry2=("SELECT tblsystext.txtname
, software.urlmain
, software.sftname
, software.sftid
, software.controller
FROM software
LEFT JOIN tblsystext ON software.controller = tblsystext.recordid
WHERE position(' $idpart ' in software.prtlst) > 0
AND software.rstid = 2
AND tblsystext.lanid = $koekje[2]
AND tblsystext.txlid = 19
ORDER BY sftname ASC
, controller ASC
;");
$result = pg_query($con1, $qry2);
if (pg_num_rows($result)>0) { //Only if at least 1 program exists
echo (' ');
echo (""); //Header
$tmp = get_txt($con1, 9, $koekje[2], 3922); //Fieldname at screen
if ($koekje[3]=="1") { //If this user wants to translate then
echo ("$tmp"); //If desired
}else{ echo ("$tmp"); } //Else just show if no need to translate
echo (" |
---|
");
while($col = pg_fetch_array($result)){
$col[0] = ("$col[0]");
echo (" ");
echo ("$col[2]   | ");
echo ("$col[0] | ");
if ($koekje[0] > 0 and $koekje[3]>0){ //User is logged in and translates
echo ("");
echo ("(Edit) | ");
}
echo (' ');
}
echo (' ');
}
?>
// LIST ALL ROBOTS THAT INCLUDE THIS PART ***********************************
// $idpart
// INCLUDED IN ALL ROBOTS TO CHECK THEIR DIRECTORY
$qry_bpp=("SELECT farmbotversie.id
, farmbotversie.part_list
, tblsystext.txtname
FROM farmbotversie
LEFT JOIN tblsystext ON farmbotversie.id = tblsystext.recordid
WHERE tblsystext.txlid = 27
AND tblsystext.lanid = $koekje[2]
ORDER BY tblsystext.txtname ASC
;");
$result_bpp = pg_query($con1, $qry_bpp);
$header = 0;
while($colbpp = pg_fetch_array($result_bpp)){ //Only if at least 1 robot exists
if(substr_count($colbpp[1], " $idpart ") > 0){
if ($header == 0) {
echo ("");
echo (""); //Header
$tmp = get_txt($con1, 9, $koekje[2], 3879); //Fieldname at screen
if ($koekje[3]=="1") { //If this user wants to translate then
echo ("$tmp"); //If desired
}else{ echo ("$tmp"); } //Else just show if no need to translate
echo (" | ");
$header++;
} //End of if ($header == 0)
echo ("");
echo ("$colbpp[2]");
//Show software van deze bot
$qry=("SELECT software_basedir
FROM farmbotversie
WHERE id = $colbpp[0];");
$result = pg_query($con1, $qry);
$col = pg_fetch_array ($result ,0);
$dir1 = "/var/www/html/robotigs/software/$col[0]";
$arr1 = glob($dir1, GLOB_ONLYDIR);
if (isset($arr1[0])){
$folder = $arr1[0];
$countertmp = 0;
echo ("");
foreach (scandir($folder, 1) as $filename) {
if ($filename<>"." AND $filename <>".."){
if ($countertmp == 0){
$countertmp++;
} //End of if ($countertmp == 0)
$tmp = "/";
$dir = substr($folder, 14) ."/";
$link = $tmp .$dir .$filename;
$showname = substr($filename, 0 ,-4);
echo ("- ");
echo "$showname";
echo ("
");
} //End of if ($filename<>"." AND $filename <>".."){
} //End of foreach (scandir($folder, 1) as $filename)
echo ("");
} //End of if (isset($arr1[0]))
echo (" | ");
}
}
if ($header > 0){
echo (" ");
}
?>
Warning: include(/var/www/html/robotigs/includes/wishlistperpart.php): Failed to open stream: No such file or directory in /var/www/html/robotigs/includes/parts_header.php on line 128
Warning: include(): Failed opening '/var/www/html/robotigs/includes/wishlistperpart.php' for inclusion (include_path='.:/usr/share/php') in /var/www/html/robotigs/includes/parts_header.php on line 128
| //Location:/robotigs/includes/ picts_show.php
// Requieres: $pict_list and several picture handling javascript routines
//*** FROM HERE ON THE PICTURE CORNER STARTS -----------------------------------
if ($pict_list){ //There are 1 or more pictures to be printed
echo ("");
$counter = 0;
$aantal = str_word_count($pict_list, 0, "0123456789");
$lijst = str_word_count($pict_list, 1, "0123456789");
while($counter < $aantal){
$pictkey = $lijst[$counter];
$qryy1 = ("SELECT *
FROM illustrations
WHERE ill_id = $pictkey
;");
$resulty1 = pg_query($con1, $qryy1);
while($row = pg_fetch_array($resulty1)){ //Fields accesible by their name
foreach( $row AS $key => $val ){
$$key = stripslashes( $val );
}
}
$image = ("/robotigs/documentatie/plaatjes/$ill_name");
$pictfull = str_replace('_small', '', $image); //Pictfull=biggest version
$alt = ("Just wait till your 57....");
$txtname = get_txt($con1, 18, $koekje[2], $ill_id); //Robot illustrations subtitles
echo ("");
echo (' ');
$nummer = $counter + 1;
$txtAfb = get_txt($con1, 9, $koekje[2], 3865); //Pic Afb
if ($koekje[0]>0 and $koekje[3]>0){ //User is logged in and translates
echo ("$txtAfb $nummer: ");
echo ("$txtname");
echo (" ");
echo ("Ontkoppelen ");
if ($counter==0){
echo ("Up ");
}else{
echo ("Up ");
}
if ($nummer == $aantal){
echo ("Down");
}else{
echo ("Down");
}
}else{ //Else of if Meaning the current useer is logged in
echo ("$txtAfb $nummer: $txtname");
}
echo (''); //End of this picture
$counter++; //Prepair for next picture
}
echo (' | '); //All pictures are printed
} //End of if there are 1 or more pictures to be printed ----------------------
?>