Sisällysluettelo:
2025 Kirjoittaja: John Day | [email protected]. Viimeksi muokattu: 2025-01-23 14:44
Tässä projektissa rakennamme robotin, joka lajittelee Perler -helmet värin mukaan.
Olen aina halunnut rakentaa värinlajittelurobotin, joten kun tyttäreni kiinnostui Perler -helmien valmistuksesta, näin tämän täydellisenä mahdollisuutena.
Perler -helmiä käytetään sulatettujen taideprojektien luomiseen asettamalla monia helmiä pegboardille ja sulattamalla ne sitten yhdessä raudan kanssa. Yleensä ostat näitä helmiä jättimäisissä 22 000 helmipakkauksessa ja käytät paljon aikaa haluamasi värin etsimiseen, joten ajattelin, että niiden lajittelu lisäisi taiteen tehokkuutta.
Työskentelen Phidgets Inc.:ssä, joten käytin enimmäkseen Phidgetsia tähän projektiin - mutta tämä voidaan tehdä millä tahansa sopivalla laitteistolla.
Vaihe 1: Laitteisto
Tässä on mitä olen rakentanut tämän. Rakensin sen 100% osilla phidgets.comista ja tavaroista, jotka minulla oli talon ympärillä.
Phidgets -levyt, moottorit, laitteistot
- HUB0000 - VINT Hub Phidget
- 1108 - Magneettianturi
- 2x STC1001 - 2,5A Stepper Phidget
- 2x 3324 - 42STH38 NEMA -17 Bipolar Gearless Stepper
- 3x 3002 - Phidget -kaapeli 60 cm
- 3403 - 4 -porttinen USB2.0 -keskitin
- 3031 - Naarasliha 5.5x2.1mm
- 3029 - 2 -johtiminen 100 'kierretty kaapeli
- 3604-10 mm valkoinen LED (10 kpl pussi)
- 3402 - USB -verkkokamera
Toiset osat
- 24VDC 2.0A virtalähde
- Romu puuta ja metallia autotallista
- Vetoketjut
- Muovisäiliö, jonka pohja on katkaistu
Vaihe 2: Suunnittele robotti
Meidän on suunniteltava jotain, joka voi ottaa yhden helmen syöttösuppilosta, sijoittaa sen verkkokameran alle ja siirtää sen sitten sopivaan roskakoriin.
Helmen nouto
Päätin tehdä ensimmäisen osan kahdella pyöreällä vanerilla, joista jokaisessa on reikä porattu samaan paikkaan. Alaosa on kiinteä ja yläosa on kiinnitetty askelmoottoriin, joka voi kiertää sitä helmillä täytetyn säiliön alla. Kun reikä kulkee suppilon alle, se ottaa yhden helmen. Voin sitten kiertää sitä verkkokameran alla ja kiertää sitten edelleen, kunnes se vastaa alakappaleen reikää, jolloin se putoaa läpi.
Tässä kuvassa testaan, että järjestelmä voi toimia. Kaikki on kiinnitetty paitsi ylempi pyöreä vanerikappale, joka on kiinnitetty askelmoottoriin näkyvistä alla. Verkkokameraa ei ole vielä asennettu. Käytän vain Phidget -ohjauspaneelia moottorin kääntämiseen tässä vaiheessa.
Helmien säilytys
Seuraava osa on suunnitella säiliöjärjestelmä kunkin värin säilyttämiseksi. Päätin käyttää alla olevaa toista askelmoottoria tukemaan ja pyörittämään pyöreää astiaa, jossa on tasaisesti sijoitetut lokerot. Tätä voidaan käyttää pyörittämään oikeaa osastoa sen reiän alla, josta helmi putoaa.
Rakensin tämän pahvilla ja teipillä. Tärkeintä tässä on johdonmukaisuus - jokaisen lokeron tulee olla samankokoinen ja koko asia on painotettava tasaisesti, jotta se pyörii ohittamatta.
Helmien poistaminen tapahtuu tiiviin kannen avulla, joka paljastaa yhden osaston kerrallaan, joten helmet voidaan kaataa ulos.
Kamera
Verkkokamera on asennettu ylälevyn päälle suppilon ja alalevyn reiän paikan väliin. Näin järjestelmä voi katsoa helmeä ennen sen pudottamista. LED -valoa käytetään valaisemaan kameran alla olevia helmiä ja ympäristön valo estetään, jotta valaistusympäristö olisi tasainen. Tämä on erittäin tärkeää tarkan väritunnistuksen kannalta, koska ympäristön valaistus voi todella heittää pois havaitut värit.
Sijainnin tunnistus
On tärkeää, että järjestelmä pystyy havaitsemaan helmierottimen pyörimisen. Tätä käytetään alkuasennon asettamiseen käynnistyksen yhteydessä, mutta myös sen havaitsemiseen, onko askelmoottori synkronoitu. Järjestelmässäni helmi juuttuu joskus kiinni otettaessa, ja järjestelmän piti havaita ja käsitellä tämä tilanne - varmuuskopioimalla hieman ja yrittämällä uudelleen.
On monia tapoja hoitaa tämä. Päätin käyttää 1108 -magneettianturia, jonka ylälevyn reunaan on upotettu magneetti. Tämän avulla voin tarkistaa asennon joka kierroksella. Parempi ratkaisu olisi luultavasti askelmoottorin anturi, mutta minulla oli 1108 makaamassa, joten käytin sitä.
Viimeistele robotti
Tässä vaiheessa kaikki on tehty ja testattu. On aika asentaa kaikki kauniisti ja siirtyä kirjoitusohjelmistoon.
Kahta askelmoottoria käyttävät STC1001 -askelohjaimet. HUB000 - USB VINT -keskitintä käytetään askelohjaimien käyttämiseen, magneettianturin lukemiseen ja LED -valon käyttämiseen. Verkkokamera ja HUB0000 on liitetty pieneen USB -keskittimeen. Moottorien virransyöttöön käytetään 3031 -letkua ja johtoa sekä 24 V: n virtalähdettä.
Vaihe 3: Kirjoita koodi
Tässä projektissa käytetään C# ja Visual Studio 2015. Lataa lähde tämän sivun yläreunasta ja seuraa sitä - pääkohdat on kuvattu alla
Alustus
Ensin meidän on luotava, avattava ja alustettava Phidget -objektit. Tämä tehdään lomakkeen lataustapahtumassa ja Phidget -liitäntäkäsittelijöissä.
private void Form1_Load (objektin lähettäjä, EventArgs e) {
/ * Alusta ja avaa Phidgets */
alkuun. HubPort = 0; top. Attach += Top_Attach; top. Detach += Top_Detach; top. PositionChange += Top_PositionChange; alkuun. Avaa ();
bottom. HubPort = 1;
bottom. Attach += Bottom_Attach; bottom. Detach += Bottom_Detach; bottom. PositionChange += Bottom_PositionChange; pohja (Avaa) ();
magSensor. HubPort = 2;
magSensor. IsHubPortDevice = true; magSensor. Attach += MagSensor_Attach; magSensor. Detach += MagSensor_Detach; magSensor. SensorChange += MagSensor_SensorChange; magSensor. Open ();
led. HubPort = 5;
led. IsHubPortDevice = true; led. Kanava = 0; led. Attach += Led_Attach; led. Detach += Led_Detach; led. Open (); }
private void Led_Attach (objektin lähettäjä, Phidget22. Events. AttachEventArgs e) {
ledAttachedChk. Checked = true; led. State = tosi; ledChk. Checked = true; }
private void MagSensor_Attach (objektin lähettäjä, Phidget22. Events. AttachEventArgs e) {
magSensorAttachedChk. Checked = true; magSensor. SensorType = VoltageRatioSensorType. PN_1108; magSensor. DataInterval = 16; }
private void Bottom_Attach (objektin lähettäjä, Phidget22. Events. AttachEventArgs e) {
bottomAttachedChk. Checked = true; bottom. CurrentLimit = bottomCurrentLimit; bottom. Engaged = true; bottom. VelocityLimit = bottomVelocityLimit; bottom. Acceleration = bottomAccel; bottom. DataInterval = 100; }
private void Top_Attach (objektin lähettäjä, Phidget22. Events. AttachEventArgs e) {
topAttachedChk. Checked = true; top. CurrentLimit = topCurrentLimit; alkuun. Sidottu = totta; top. RescaleFactor = -1; top. VelocityLimit = -topVelocityLimit; alkuun. Kiihtyvyys = -topAccel; top. DataInterval = 100; }
Luemme myös kaikki tallennetut väritiedot alustuksen aikana, joten edellistä ajoa voidaan jatkaa.
Moottorin paikannus
Moottorin käsittelykoodi koostuu kätevistä toiminnoista moottoreiden siirtämiseksi. Käyttämäni moottorit ovat 3 200 1/16 askelta kierrosta kohden, joten loin vakion tätä varten.
Ylämoottorissa on kolme asentoa, jotka haluamme lähettää moottorille: verkkokamera, reikä ja paikannusmagneetti. On toiminto, jolla voit matkustaa jokaiseen näistä paikoista:
private void nextMagnet (Boolen odotus = false) {
double posn = top. Position % stepsPerRev;
top. TargetPosition += (stepsPerRev - posn);
jos (odota)
while (top. IsMoving) Thread. Sleep (50); }
private void nextCamera (Boolen odotus = false) {
double posn = top. Position % stepsPerRev; if (posn <Properties. Settings. Default.cameraOffset) top. TargetPosition += (Properties. Settings. Default.cameraOffset - posn); else top. TargetPosition + = ((Properties. Settings. Default.cameraOffset - posn) + stepsPerRev);
jos (odota)
while (top. IsMoving) Thread. Sleep (50); }
private void nextHole (Boolen odotus = false) {
double posn = top. Position % stepsPerRev; if (posn <Properties. Settings. Default.holeOffset) top. TargetPosition += (Properties. Settings. Default.holeOffset - posn); else top. TargetPosition + = ((Properties. Settings. Default.holeOffset - posn) + stepsPerRev);
jos (odota)
while (top. IsMoving) Thread. Sleep (50); }
Ennen ajon aloittamista ylälevy kohdistetaan magneettianturin avulla. AlignMotor -toiminto voidaan kutsua milloin tahansa ylälevyn kohdistamiseksi. Tämä toiminto kääntää levyn ensin nopeasti täyteen kierrokseen, kunnes se näkee magneettidatan kynnyksen yläpuolella. Sitten se varmuuskopioi hiukan ja siirtyy jälleen hitaasti eteenpäin, keräämällä anturitietoja. Lopuksi se asettaa sijainnin magneettitietojen enimmäissijaintiin ja palauttaa sijainnin siirtymän arvoon 0. Näin ollen magneetin maksimiasennon tulisi aina olla (ylhäällä. Sijainti % stepsPerRev)
Langan kohdistusMotorThread; Boolen sahaMagnet; kaksinkertainen magSensorMax = 0; private void alignMotor () {
// Etsi magneetti
top. DataInterval = top. MinDataInterval;
sawMagnet = epätosi;
magSensor. SensorChange += magSensorStopMotor; top. VelocityLimit = -1000;
int tryCount = 0;
yritä uudelleen:
top. TargetPosition += stepsPerRev;
while (top. IsMoving &&! sawMagnet) Thread. Sleep (25);
jos (! sawMagnet) {
if (tryCount> 3) {Console. WriteLine ("Tasaus epäonnistui"); alkuun. Engaged = false; bottom. Engaged = false; runtest = väärä; palata; }
tryCount ++;
Console. WriteLine ("Olemmeko jumissa? Yritetään varmuuskopiointia …"); top. TargetPosition -= 600; while (top. IsMoving) Thread. Sleep (100);
kokeile uudelleen;
}
top. VelocityLimit = -100;
magData = uusi lista> (); magSensor. SensorChange += magSensorCollectPositionData; top. TargetPosition += 300; while (top. IsMoving) Thread. Sleep (100);
magSensor. SensorChange -= magSensorCollectPositionData;
top. VelocityLimit = -topVelocityLimit;
KeyValuePair max = magData [0];
foreach (KeyValuePair -pari magDatassa) if (pair. Value> max. Value) max = pair;
top. AddPositionOffset (-max. Key);
magSensorMax = max. Value;
top. TargetPosition = 0;
while (top. IsMoving) Thread. Sleep (100);
Console. WriteLine ("Tasaus onnistui");
}
Lista> magData;
private void magSensorCollectPositionData (objektin lähettäjä, Phidget22. Events. VoltageRatioInputSensorChangeEventArgs e) {magData. Add (uusi KeyValuePair (top. Position, e. SensorValue)); }
private void magSensorStopMotor (objektin lähettäjä, Phidget22. Events. VoltageRatioInputSensorChangeEventArgs e) {
if (top. IsMoving && e. SensorValue> 5) {top. TargetPosition = top. Position - 300; magSensor. SensorChange -= magSensorStopMotor; sawMagnet = totta; }}
Lopuksi alamoottoria ohjataan lähettämällä se johonkin helmesäiliön asentoon. Tässä projektissa meillä on 19 paikkaa. Algoritmi valitsee lyhyimmän polun ja kääntyy joko myötä- tai vastapäivään.
private int BottomPosition {get {int posn = (int) bottom. Position % stepsPerRev; jos (posn <0) posn += vaiheetPerRev;
return (int) Math. Round ((((posn * beadCompartments) / (double) stepsPerRev));
} }
private void SetBottomPosition (int posn, bool wait = false) {
posn = posn % beadCompartments; double targetPosn = (posn * stepsPerRev) / beadCompartments;
kaksoisvirtaPosn = alhaalla. Position % stepsPerRev;
double posnDiff = targetPosn - currentPosn;
// Pidä se täydellisinä vaiheina
posnDiff = ((int) (posnDiff / 16)) * 16;
jos (posnDiff <= 1600) bottom. TargetPosition += posnDiff; else bottom. TargetPosition - = (stepsPerRev - posnDiff);
jos (odota)
while (bottom. IsMoving) Thread. Sleep (50); }
Kamera
OpenCV: tä käytetään kuvien lukemiseen verkkokamerasta. Kameralanka aloitetaan ennen päälajittelulangan aloittamista. Tämä säie lukee jatkuvasti kuvia, laskee tietyn alueen keskimääräisen värin keskiarvon avulla ja päivittää yleisen värimuuttujan. Lanka käyttää myös HoughCirclesiä, joka yrittää tunnistaa joko helmen tai ylälevyn reiän tarkentaakseen värin havaitsemiseen etsimäänsä aluetta. Kynnys- ja HoughCircles -numerot määritettiin yrityksen ja erehdyksen kautta, ja ne riippuvat suuresti verkkokamerasta, valaistuksesta ja etäisyydestä.
bool runVideo = true; bool videoRunning = false; VideoCapture -kaappaus; Lanka cvThread; Väri havaittuVäri; Boolen havaitseminen = false; int havaitseCnt = 0;
private void cvThreadFunction () {
videoRunning = epätosi;
kaapata = uusi VideoCapture (valittu kamera);
käyttämällä (Ikkuna -ikkuna = uusi ikkuna ("sieppaus")) {
Mat -kuva = uusi Mat (); Mat kuva2 = uusi Mat (); while (runVideo) {capture. Read (kuva); if (image. Empty ()) tauko;
jos (havaitsee)
detectCnt ++; muuten detectCnt = 0;
if (havaitsee || circleDetectChecked || showDetectionImgChecked) {
Cv2. CvtColor (kuva, kuva2, ColorConversionCodes. BGR2GRAY); Mat thres = image2. Rhreshold ((double) Properties. Settings. Default.videoThresh, 255, ThresholdTypes. Binary); thres = thres. GaussianBlur (uusi OpenCvSharp. Size (9, 9), 10);
jos (showDetectionImgChecked)
kuva = thres;
if (havaitsee || circleDetectChecked) {
CircleSegment helmi = thres. HoughCircles (HoughMethods. Gradient, 2, /*thres. Rows/4*/ 20, 200, 100, 20, 65); if (bead. Length> = 1) {image. Circle (helmi [0]. Keskus, 3, uusi skalaari (0, 100, 0), -1); kuva. Ympyrä (helmi [0]. Keskus, (int) helmi [0]. Säde, uusi skalaari (0, 0, 255), 3); if (bead [0]. Radius> = 55) {Properties. Settings. Default.x = (desimaali) helmi [0]. Center. X + (desimaali) (helmi [0]. Radius / 2); Properties. Settings. Default.y = (desimaali) helmi [0]. Center. Y - (desimaali) (helmi [0]. Säde / 2); } else {Properties. Settings. Default.x = (desimaali) helmi [0]. Center. X + (desimaali) (helmi [0]. Säde); Properties. Settings. Default.y = (desimaali) helmi [0]. Center. Y - (desimaali) (helmi [0]. Säde); } Properties. Settings. Default.size = 15; Properties. Settings. Default.height = 15; } muuta {
CircleSegment ympyrät = thres. HoughCircles (HoughMethods. Gradient, 2, /*thres. Rows/4*/ 5, 200, 100, 60, 180);
if (ympyrät.pituus> 1) {Lista xs = ympyrät. Valitse (c => c. Center. X). ToList (); xs. Sort (); Lista ys = ympyrät. Valitse (c => c. Center. Y). ToList (); ys. Sort ();
int mediaaniX = (int) xs [xs. Count / 2];
int mediaaniY = (int) ys [ys. Count / 2];
if (mediaaniX> kuva. leveys - 15)
mediaaniX = kuva. leveys - 15; if (mediaani> kuva. korkeus - 15) mediaaniY = kuva. korkeus - 15;
kuva. Ympyrä (mediaaniX, mediaaniY, 100, uusi skalaari (0, 0, 150), 3);
jos (havaitsee) {
Properties. Settings. Default.x = mediaaniX - 7; Properties. Settings. Default.y = mediaaniY - 7; Properties. Settings. Default.size = 15; Properties. Settings. Default.height = 15; }}}}}
Rect r = new Rect ((int) Properties. Settings. Default.x, (int) Properties. Settings. Default.y, (int) Properties. Settings. Default.size, (int) Properties. Settings. Default.height);
Mat beadSample = uusi Mat (kuva, r);
Skalaari avgColor = Cv2. Mean (beadSample); detectColor = Color. FromArgb ((int) avgColor [2], (int) avgColor [1], (int) avgColor [0]);
kuva. suorakulmio (r, uusi skalaari (0, 150, 0));
window. ShowImage (kuva);
Cv2. WaitKey (1); videoRunning = totta; }
videoRunning = epätosi;
} }
private void cameraStartBtn_Click (objektin lähettäjä, EventArgs e) {
jos (cameraStartBtn. Text == "start") {
cvThread = uusi säie (uusi ThreadStart (cvThreadFunction)); runVideo = tosi; cvThread. Start (); cameraStartBtn. Text = "pysäkki"; while (! videoRunning) Thread. Sleep (100);
updateColorTimer. Start ();
} muuta {
runVideo = epätosi; cvThread. Join (); cameraStartBtn. Text = "aloita"; }}
Väri
Nyt voimme määrittää helmen värin ja päättää tämän värin perusteella mihin säiliöön pudottaa sen.
Tämä vaihe perustuu värien vertailuun. Haluamme pystyä erottamaan värit toisistaan väärien positiivisten rajoittamiseksi, mutta sallimme myös riittävän kynnyksen väärien negatiivien rajoittamiseksi. Värien vertailu on itse asiassa yllättävän monimutkaista, koska tapa, jolla tietokoneet tallentavat värit RGB -muotoon ja tapa, jolla ihmiset havaitsevat värit, eivät korreloi lineaarisesti. Pahentaakseen tilannetta on myös otettava huomioon valon väri, jonka väriä tarkastellaan.
Värierojen laskemiseen on monimutkainen algoritmi. Käytämme CIE2000: ta, joka antaa luvun lähellä 1, jos kaksi väriä olisi ihmiselle erottamattomia. Käytämme ColorMine C# -kirjastoa näiden monimutkaisten laskelmien tekemiseen. DeltaE -arvon 5 on havaittu tarjoavan hyvän kompromissin väärän positiivisen ja väärän negatiivisen välillä.
Koska värejä on usein enemmän kuin säiliöitä, viimeinen paikka on varattu kaappisäiliöksi. Yleensä jätän ne sivuun ajamaan koneen toisella kerralla.
Lista
värit = uusi lista (); luettelo colorPanels = uusi lista (); Luettelon väritTxts = uusi Lista (); Lista colorCnts = uusi Lista ();
const int numColorSpots = 18;
const int tuntematonColorIndex = 18; int findColorPosition (Väri c) {
Console. WriteLine ("Värin etsiminen …");
var cRGB = uusi Rgb ();
cRGB. R = c. R; cRGB. G = c. G; cRGB. B = c. B;
int bestMatch = -1;
kaksinkertainen otteluDelta = 100;
for (int i = 0; i <colors. Count; i ++) {
var RGB = uusi Rgb ();
RGB. R = värit . R; RGB. G = värit . G; RGB. B = värit . B;
kaksinkertainen delta = cRGB. Compare (RGB, uusi CieDe2000Comparison ());
// kaksinkertainen delta = deltaE (c, värit ); Console. WriteLine ("DeltaE (" + i. ToString () + "):" + delta. ToString ()); if (delta <matchDelta) {matchDelta = delta; bestMatch = i; }}
if (matchDelta <5) {Console. WriteLine ("Löytyi! (Posn:" + bestMatch + "Delta:" + matchDelta + ")"); palauta bestMatch; }
if (colors. Count <numColorSpots) {Console. WriteLine ("Uusi väri!"); värit. Lisää (c); this. BeginInvoke (uusi toiminto (setBackColor), uusi objekti {colors. Count - 1}); writeOutColors (); paluu (värit. luku - 1); } else {Console. WriteLine ("Tuntematon väri!"); palaa tuntematonVäriindeksi; }}
Lajittelulogiikka
Lajittelutoiminto kokoaa kaikki palat todella lajittelemaan helmiä. Tämä toiminto toimii erillisessä säikeessä; siirtämällä ylälevyä, tunnistamalla helmen väri, asettamalla se roskakoriin, varmistaen, että ylälevy pysyy linjassa, laskemalla helmet jne. Se myös lakkaa toimimasta, kun roskasäiliö täyttyy - Muuten päädymme vain täynnä helmiä.
Langan väri colourTestThread; Boolean runtest = false; void colourTest () {
jos (! alkuun sitoutunut)
alkuun. Sidottu = totta;
jos (! pohja. sitoutunut)
bottom. Engaged = true;
while (runtest) {
nextMagnet (tosi);
Kierre. Nukkuu (100); kokeile {if (magSensor. SensorValue <(magSensorMax - 4)) alignMotor (); } saalis {alignMotor (); }
nextCamera (totta);
havaitseminen = totta;
while (havaitseCnt <5) Thread. Sleep (25); Console. WriteLine ("Detect Count:" + detectCnt); havaitseminen = epätosi;
Väri c = havaittuVäri;
this. BeginInvoke (uusi toiminto (setColorDet), uusi objekti {c}); int i = findColorPosition (c);
SetBottomPosition (i, tosi);
nextHole (tosi); colorCnts ++; this. BeginInvoke (uusi toiminto (setColorTxt), uusi objekti {i}); Kierre. Nukkuu (250);
if (colorCnts [unknownColorIndex]> 500) {
alkuun. Engaged = false; bottom. Engaged = false; runtest = väärä; this. BeginInvoke (uusi toiminto (setGoGreen), null); palata; }}}
private void colourTestBtn_Click (objektin lähettäjä, EventArgs e) {
if (colourTestThread == null ||! colourTestThread. IsAlive) {colourTestThread = uusi säie (uusi ThreadStart (colourTest)); runtest = totta; colourTestThread. Start (); colourTestBtn. Text = "PYSÄYTÄ"; colourTestBtn. BackColor = Väri. Punainen; } else {runtest = false; colourTestBtn. Text = "MENE"; colourTestBtn. BackColor = Väri. Vihreä; }}
Tässä vaiheessa meillä on työohjelma. Jotkut koodinpätkät jätettiin artikkelista pois, joten katso lähdettä sen suorittamiseksi.
Optikkokilpailun toinen palkinto
Suositeltava:
DIY 37 Leds Arduino -rulettipeli: 3 vaihetta (kuvilla)
DIY 37 Leds Arduino Roulette Peli: Ruletti on kasinopeli, joka on nimetty ranskalaisen sanan mukaan, joka tarkoittaa pientä pyörää
Covid -suojakypärä, osa 1: johdanto Tinkercad -piireihin!: 20 vaihetta (kuvilla)
Covid -suojakypärä, osa 1: johdanto Tinkercad -piireihin!: Hei, ystävä! Tässä kaksiosaisessa sarjassa opimme käyttämään Tinkercadin piirejä - hauskaa, tehokasta ja opettavaista työkalua piirien toiminnasta! Yksi parhaista tavoista oppia on tehdä. Joten suunnittelemme ensin oman projektimme: th
Kuplan lajittelu erässä!: 4 vaihetta
Bubble Sort in Batch!: Oletko koskaan miettinyt yksinkertaisen lajittelualgoritmin tekemistä puhtaassa erässä? Älä huoli, se on yksinkertaista kuin piirakka! Tämä osoittaa myös sen lajitteluprosessin. (Huomautus: Tein tämän Windows XP -tietokoneella, joten jotkin koodit eivät ehkä toimi. En ole varma. Anteeksi …)
Pultti - DIY -langaton latauskello (6 vaihetta): 6 vaihetta (kuvilla)
Pultti - DIY -langaton latausyökello (6 vaihetta): Induktiiviset lataukset (tunnetaan myös nimellä langaton lataus tai langaton lataus) on langattoman voimansiirron tyyppi. Se käyttää sähkömagneettista induktiota sähkön tuottamiseen kannettaville laitteille. Yleisin sovellus on langaton Qi -latauslaite
PhantomX Pincher -värin lajittelu: 4 vaihetta
PhantomX Pincher -värin lajittelu: Johdanto Tämän ohjeen on laatinut 2 automaatiotekniikan opiskelijaa UCN: stä (Tanska) .Ohje kuvaa, kuinka PhantomX Pncher -laitteella voidaan lajitella laatikot värin mukaan CMUcam5 Pixy -laitteen avulla ja pinota ne. Tämä sovellus