ASi (Anansi) Robotcompanion: Bouw een vriendelijke robot met Arduino Nano & LED-display
Onderdelen en benodigdheden
| Onderdeel | Aantal | Opmerkingen |
|---|---|---|
| Arduino Nano R3 | 1 | |
| SG90 Micro-servomotor | 1 | |
| MAX7219 Rode LED Dot Matrix Display Module | 1 | |
| 4xAA batterijhouder | 1 | Een lithium-ion batterij 3.7V 200mAh werkt ook |
| Schroefjes | 1 set | |
| Transparent plastic acrylaat badbom vorm dopjes | 1 | Voor de kop |
| Adafruit Trinket - Mini Microcontroller - 5V Logic | 1 |
Benodigde tools en machines
- 3D-printer (generic)
- Lijm pistool (generic)
Over dit project
Verhaal
Dit project was oorspronkelijk bedoeld als een Xpider van Thingiverse. Maar toen zag ik Archimedes van Alex Glow. Het boorde me open. Ik wilde er zo graag een hebben. Dus ik ging aan de slag met mijn eigen robotische companion. Ik besefte dat ik geen uil-type was, en ik wilde dat mijn familiar bijzonder was. Toen herinnerde ik me het Afrikaanse verhaal van Anansi de spin, kwakzalver en god van verhalen. Ik besloot de bot te ontwerpen met het idee van een verhaal, en zo werd Asi geboren (eigenlijk Asi_v4, v1-3 waren prototypes).
Montage
Het originele ontwerp was een Xpider, maar ik heb het een stuk opgetild door veel dingen aan te passen en veel weg te laten. Dit zijn de meeste onderdelen die je moet printen.
De montage is redelijk zelfverklarend, maar hier zijn toch wat foto's.
Voor de elektronica kun je ze meestal in de boven- of onderkant van het hoofd plaatsen (jouw keuze), maar de servo moet zo worden bevestigd. Zorg dat het tandwiel erop zit.
Asi wordt bestuurd door twee microcontrollers: een Trinket en een Arduino Nano. De Trinket regelt de beweging van het oog, dankzij de servo draait het willekeurig heen en weer. De Arduino Nano stuurt het oog (het LED-display). Dat is ook willekeurig: hij kijkt rond, en mensen nemen meestal aan dat hij naar hen kijkt.
Let op: ik heb ze op twee kleine breadboards gezet die in serie geschakeld zijn met 4x AA batterijen. (Je kunt ook een lithium-ion batterij 3.7V 200mAh gebruiken. Met de li-ion batterij had ik soms dat het niet werkte, dus ik zou twee losse lithium-batterijen gebruiken en ze aan dezelfde aan/uit schakelaar koppelen zodat alles tegelijkertijd start.)
Het hoofd is gewoon een kerstbal die ik tijdens de feestdagen kocht. Ik sprons er lichtjes zwart verf op. Daarna plak ik het met hete lijm aan het nekstuk van Asi en KABOOM, er is het hoofd!
Tot slot, hoe ik het draag. Ik nam gewoon wat armaturedraad en voerde het door de gaten in het onderstuk van Asi, en wikkelde het om de schouderband van mijn rugzak.
Code
- Asi Oog
- Asi Nek met Trinket
Asi Oog Arduino
De code voor het oog van Asi
#include <LedControl.h>
#include "LedControl.h"
#define PIN_EYES_DIN 12
#define PIN_EYES_CS 11
#define PIN_EYES_CLK 10
LedControl lc = LedControl(PIN_EYES_DIN, PIN_EYES_CLK, PIN_EYES_CS, 2);
bool rotateMatrix0 = false;
bool rotateMatrix1 = false;
byte eyeBall[8] = {
B00111100,
B01111110,
B11111111,
B11111111,
B11111111,
B11111111,
B01111110,
B00111100
};
byte eyePupil = B11100111;
byte eyeCurrent[8];
int currentX;
int currentY;
int cntLoop = 0;
int cntEffect = 0;
#define MIN -2
#define MAX 2
#define DELAY_BLINK 40
#define EFFECT_ITERATION 4
void setup() {
lc.shutdown(0,false);
lc.shutdown(1,false);
lc.setIntensity(0,1);
lc.setIntensity(1,1);
lc.clearDisplay(0);
lc.clearDisplay(1);
// LED test
byte b = B10000000;
for (int c=0; c<=7; c++) {
for (int r=0; r<=7; r++) {
setRow(0, r, b);
setRow(1, r, b);
}
b = b >> 1;
delay(50);
}
b = B11111111;
for (int r=0; r<=7; r++) {
setRow(0, r, b);
setRow(1, r, b);
}
delay(500);
lc.clearDisplay(0);
lc.clearDisplay(1);
delay(500);
randomSeed(analogRead(0));
displayEyes(0, 0);
delay(2000);
blinkEyes(true, false);
blinkEyes(false, true);
delay(1000);
}
void loop() {
moveEyes(random(MIN, MAX + 1), random(MIN, MAX + 1), 50);
delay(random(5, 7) * 500);
if (random(0, 5) == 0) {
delay(500);
blinkEyes();
delay(500);
}
if (EFFECT_ITERATION > 0) {
cntLoop++;
if (cntLoop == EFFECT_ITERATION) {
cntLoop = 0;
if (cntEffect > 6) cntEffect = 0;
switch(cntEffect) {
case 0: crossEyes(); delay(1000); break;
case 1: roundSpin(2); delay(1000); break;
case 2: crazySpin(2); delay(1000); break;
case 3: methEyes(); delay(1000); break;
case 4: lazyEye(); delay(1000); break;
case 5: blinkEyes(true, false); blinkEyes(false, true); delay(1000); break;
case 6: glowEyes(3); delay(1000); break;
default: break;
}
cntEffect++;
}
}
}
void blinkEyes() { blinkEyes(true, true); }
void blinkEyes(boolean blinkLeft, boolean blinkRight) {
if (!blinkLeft && !blinkRight) return;
for (int i=0; i<=3; i++) {
if (blinkLeft) { setRow(0, i, 0); setRow(0, 7-i, 0); }
if (blinkRight) { setRow(1, i, 0); setRow(1, 7-i, 0); }
delay(DELAY_BLINK);
}
for (int i=3; i>=0; i--) {
if (blinkLeft) { setRow(0, i, eyeCurrent[i]); setRow(0, 7-i, eyeCurrent[7-i]); }
if (blinkRight) { setRow(1, i, eyeCurrent[i]); setRow(1, 7-i, eyeCurrent[7-i]); }
delay(DELAY_BLINK);
}
}
void crazySpin(int times) {
if (times == 0) return;
moveEyes(0, 0, 50);
delay(500);
byte row = eyePupil;
for (int t=0; t<times; t++) {
for (int i=0; i<5; i++) {
row = row >> 1; row = row | B10000000;
setRow(0, 3, row); setRow(1, 3, row);
setRow(0, 4, row); setRow(1, 4, row);
delay(50);
if (t == 0) delay((5-i)*10);
}
for (int i=0; i<5; i++) {
row = row >> 1;
if (i>=2) row = row | B10000000;
setRow(0, 3, row); setRow(1, 3, row);
setRow(0, 4, row); setRow(1, 4, row);
delay(50);
if (t == (times-1)) delay((i+1)*10);
}
}
}
void crossEyes() {
moveEyes(0, 0, 50);
delay(500);
byte pupilR = eyePupil;
byte pupilL = eyePupil;
for (int i=0; i<2; i++) {
pupilR = pupilR >> 1; pupilR = pupilR | B10000000;
pupilL = pupilL << 1; pupilL = pupilL | B1;
setRow(0, 3, pupilR); setRow(1, 3, pupilL);
setRow(0, 4, pupilR); setRow(1, 4, pupilL);
delay(100);
}
delay(2000);
for (int i=0; i<2; i++) {
pupilR = pupilR << 1; pupilR = pupilR | B1;
pupilL = pupilL >> 1; pupilL = pupilL | B10000000;
setRow(0, 3, pupilR); setRow(1, 3, pupilL);
setRow(0, 4, pupilR); setRow(1, 4, pupilL);
delay(100);
}
}
void displayEyes(int offsetX, int offsetY) {
offsetX = getValidValue(offsetX);
offsetY = getValidValue(offsetY);
int row1 = 3 - offsetY;
int row2 = 4 - offsetY;
byte pupilRow = eyePupil;
if (offsetX > 0) {
for (int i=1; i<=offsetX; i++) {
pupilRow = pupilRow >> 1; pupilRow = pupilRow | B10000000;
}
} else if (offsetX < 0) {
for (int i=-1; i>=offsetX; i--) {
pupilRow = pupilRow << 1; pupilRow = pupilRow | B1;
}
}
byte pupilRow1 = pupilRow & eyeBall[row1];
byte pupilRow2 = pupilRow & eyeBall[row2];
for(int r=0; r<8; r++) {
if (r == row1) {
setRow(0, r, pupilRow1); setRow(1, r, pupilRow1);
eyeCurrent[r] = pupilRow1;
} else if (r == row2) {
setRow(0, r, pupilRow2); setRow(1, r, pupilRow2);
eyeCurrent[r] = pupilRow2;
} else {
setRow(0, r, eyeBall[r]); setRow(1, r, eyeBall[r]);
eyeCurrent[r] = eyeBall[r];
}
}
currentX = offsetX; currentY = offsetY;
}
int getValidValue(int value) {
if (value > MAX) return MAX;
else if (value < MIN) return MIN;
else return value;
}
void glowEyes(int times) {
for (int t=0; t<times; t++) {
for (int i=2; i<=8; i++) {
lc.setIntensity(0,i); lc.setIntensity(1,i);
delay(50);
}
delay(250);
for (int i=7; i>=1; i--) {
lc.setIntensity(0,i); lc.setIntensity(1,i);
delay(25);
}
delay(150);
}
}
void methEyes() {
moveEyes(0, 0, 50);
delay(500);
byte pupilR = eyePupil;
byte pupilL = eyePupil;
for (int i=0; i<2; i++) {
pupilR = pupilR << 1; pupilR = pupilR | B1;
pupilL = pupilL >> 1; pupilL = pupilL | B10000000;
setRow(0, 3, pupilR); setRow(1, 3, pupilL);
setRow(0, 4, pupilR); setRow(1, 4, pupilL);
delay(100);
}
delay(2000);
for (int i=0; i<2; i++) {
pupilR = pupilR >> 1; pupilR = pupilR | B10000000;
pupilL = pupilL << 1; pupilL = pupilL | B1;
setRow(0, 3, pupilR); setRow(1, 3, pupilL);
setRow(0, 4, pupilR); setRow(1, 4, pupilL);
delay(100);
}
}
void moveEyes(int newX, int newY, int stepDelay) {
int startX = currentX;
int startY = currentY;
newX = getValidValue(newX);
newY = getValidValue(newY);
int stepsX = abs(currentX - newX);
int stepsY = abs(currentY - newY);
if ((stepsX == 0) && (stepsY == 0)) return;
int dirX = (newX >= currentX) ? 1 : -1;
int dirY = (newY >= currentY) ? 1 : -1;
int steps = (stepsX > stepsY) ? stepsX : stepsY;
int intX, intY;
float changeX = (float)stepsX / (float)steps;
float changeY = (float)stepsY / (float)steps;
for (int i=1; i<=steps; i++) {
intX = startX + round(changeX * i * dirX);
intY = startY + round(changeY * i * dirY);
displayEyes(intX, intY);
delay(stepDelay);
}
}
void lazyEye() {
moveEyes(0, 1, 50);
delay(500);
for (int i=0; i<3; i++) {
setRow(1, i+2, eyeBall[i+2]);
setRow(1, i+3, eyeBall[i+3] & eyePupil);
setRow(1, i+4, eyeBall[i+4] & eyePupil);
delay(150);
}
delay(1000);
for (int i=0; i<3; i++) {
setRow(1, 4-i, eyeBall[4-i] & eyePupil);
setRow(1, 5-i, eyeBall[5-i] & eyePupil);
setRow(1, 6-i, eyeBall[6-i]);
delay(25);
}
}
void roundSpin(int times) {
if (times == 0) return;
moveEyes(2, 0, 50);
delay(500);
for (int i=0; i<times; i++) {
displayEyes(2, -1); delay(40); if (i==0) delay(40);
displayEyes(1, -2); delay(40); if (i==0) delay(30);
displayEyes(0, -2); delay(40); if (i==0) delay(20);
displayEyes(-1, -2); delay(40); if (i==0) delay(10);
displayEyes(-2, -1); delay(40);
displayEyes(-2, 0); delay(40);
displayEyes(-2, 1); delay(40); if (i==(times-1)) delay(10);
displayEyes(-1, 2); delay(40); if (i==(times-1)) delay(20);
displayEyes(0, 2); delay(40); if (i==(times-1)) delay(30);
displayEyes(1, 2); delay(40); if (i==(times-1)) delay(40);
displayEyes(2, 1); delay(40); if (i==(times-1)) delay(50);
displayEyes(2, 0); delay(40);
}
}
void setRow(int addr, int row, byte rowValue) {
if (((addr == 0) && (rotateMatrix0)) || (addr == 1 && rotateMatrix1)) {
row = abs(row - 7);
rowValue = bitswap(rowValue);
}
lc.setRow(addr, row, rowValue);
}
byte bitswap(byte x) {
byte result;
asm("mov __tmp_reg__, %[in] \n\t"
"lsl __tmp_reg__ \n\t"
"ror %[out] \n\t"
"lsl __tmp_reg__ \n\t"
"ror %[out] \n\t"
"lsl __tmp_reg__ \n\t"
"ror %[out] \n\t"
"lsl __tmp_reg__ \n\t"
"ror %[out] \n\t"
"lsl __tmp_reg__ \n\t"
"ror %[out] \n\t"
"lsl __tmp_reg__ \n\t"
"ror %[out] \n\t"
"lsl __tmp_reg__ \n\t"
"ror %[out] \n\t"
"lsl __tmp_reg__ \n\t"
"ror %[out] \n\t"
: [out] "=r" (result) : [in] "r" (x));
return result;
}
Asi Nek met Trinket Arduino
Deze code zorgt ervoor dat Asi's nek heen en weer beweegt
#define SERVO_PIN 4 // Pins 1 of 4 worden ondersteund op Trinket
#define SERVO_MIN 4 // ~1 ms puls
#define SERVO_MAX 26 // ~2 ms puls
Adafruit_TiCoServo servo;
void setup(void) {
#if (F_CPU == 16000000L)
clock_prescale_set(clock_div_1);
#endif
servo.attach(SERVO_PIN);
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, HIGH);
}
uint32_t lastLookTime = 0;
void loop(void) {
unsigned long t = millis();
if ((t - lastLookTime) > 500) {
if (random(10) == 0) {
servo.write(random(SERVO_MIN, SERVO_MAX));
lastLookTime = t;
}
}
if (random(10) == 0) {
digitalWrite(LED_PIN, LOW);
delay(random(50, 250));
digitalWrite(LED_PIN, HIGH);
}
delay(100);
}
Aangepaste onderdelen en behuizingen
- Het echte been eindstuk
- Vergeten de draai-tandwiel voor de servo toe te voegen, ik print meestal apart en pas het servo-bevestigingsstuk in het midden en schroef het op de servo
- Het lichaam van Asi
- De onderkant van Asi
- Het dijbeen van Asi
- Been gewricht
- De schroef die ik gebruik om Asi's benen te verbinden
- De moer die bij de schroef hoort
- Het tandwiel voor de servo om Asi's hoofd te laten draaien, aanbevolen een servo-stuk te gebruiken en in het midden te plaatsen
- Het middenstuk van Asi
- De houder van de servo voor Asi's tandwiel
- Asi nekplaat, hier wordt de acrylaat bol aan de nek gelijmd
Schema's
De schakeling die ik voor Asi's nek gebruikte:
De schakelingen die ik gebruikte om Asi te starten. Je kunt ze in serie schakelen om beide van kleine printplaten te voeden:
Productieproces
- Een Arduino-aangedreven Robot Bouwen met Raspberry Pi: Een Stap-voor-Stap Gids
- DIY Wall-E Geïnspireerde Raspberry Pi CD-Doos Robot
- PiCy: Bouw Je Eigen Kleine Raspberry Pi-aangedreven Robot
- Rollende Alarmrobot: Een Slim, Mobiel Wakker-systeem Aangedreven door Raspberry Pi en Google Calendar
- Bouw een gemotoriseerde Minecraft Creeper-robot met Arduino
- Archimedes: De Emotie-Detecterende AI Robot Uil
- Bouw een Spraak-gestuurde Computer Vision Robot op Windows 10 IoT Core
- ROS Robot: Terminal-gecontroleerde Navigatie op Raspberry Pi
- Simple Pi Robot – Bouw een 2-WD Autonome Rover met Raspberry Pi
- Drankrobots begrijpen: technologie, automatisering en rol in drankenindustrie
- Robotautomatisering Boost Batterijproductie Voor De EV Golf