【Arduino 动手做】认识 TN24,您迷人且可定制的桌面伴侣机器人
《Arduino 手册(思路与案例)》栏目介绍:
在电子制作与智能控制的应用领域,本栏目涵盖了丰富的内容,包括但不限于以下主题:Arduino BLDC、Arduino CNC、Arduino E-Ink、Arduino ESP32 SPP、Arduino FreeRTOS、Arduino FOC、Arduino GRBL、Arduino HTTP、Arduino HUB75、Arduino IoT Cloud、Arduino JSON、Arduino LCD、Arduino OLED、Arduino LVGL、Arduino PID、Arduino TFT,以及Arduino智能家居、智慧交通、月球基地、智慧校园和智慧农业等多个方面与领域。不仅探讨了这些技术的基础知识和应用领域,还提供了众多具体的参考案例,帮助读者更好地理解和运用Arduino平台进行创新项目。目前,本栏目已有近4000篇相关博客,旨在为广大电子爱好者和开发者提供全面的学习资源与实践指导。通过这些丰富的案例和思路,读者可以获取灵感,推动自己的创作与开发进程。
https://blog.csdn.net/weixin_41659040/category_12422453.html
认识 TN24,您迷人且可定制的桌面伴侣机器人。它结合了运动、表情和方向感应,为任何工作空间带来欢乐和活力。我在设计时考虑到了简单性,TN24 易于构建,非常适合初学者和经验丰富的制造商。它的机身由回收纸板制成,不仅重量轻而且环保。无论您是想调整其动作、表情还是设计,TN24 都为个性化提供了无限的可能性。TN24 不仅仅是一个小工具,它还是一个回应和娱乐的小朋友。
用品
以下是构建 TN24 所需的内容:
电子学
Xiao ESP32S3 — 主微控制器。
4 个伺服电机 — 用于腿部运动(例如 SG90 或 MG90S)。
OLED 显示屏 — 用于眼睛动画(例如,0.96 英寸SSD1306)。
加速度计 — 用于方向检测(例如,MPU6050 或 ADXL345)。
锂离子电池 — 为机器人供电(例如,3.7V 1200mAh)。
接头引脚 - 连接伺服器
结构组件
用于身体和腿部部件的回收纸板。
工具
烙铁和焊料。
热胶枪
剪线钳和剥线钳。
软件
Arduino IDE (用于编写 Xiao ESP32S3)。
必需库:Servo、Adafruit_GFX Adafruit_SSD1306。
第 1 步:设计和组装
车身设计
我设计了一个紧凑的机器人身体,为微控制器、电池、伺服系统和 OLED 显示屏提供了空间。随意尝试设计,但请确保它是矩形的。
使用回收的纸板制作身体和腿部。
腿机构
每条腿都由伺服电机提供动力,以实现行走运动。
使用螺钉或胶水将伺服喇叭连接到纸板腿上。
确保腿部对称并对齐以实现平衡运动。
眼睛显示
将 OLED 显示屏牢固地安装在机器人的正面。
显示屏将显示动画眼睛,这些眼睛会根据机器人的方向或输入改变表情。
方向传感器
将加速度计安装在机器人的中心,以实现准确的方向检测。
用双面胶带或螺丝固定。
第 2 步:电子元件和布线
我们将使用 tinkercad 来测试和体验伺服系统,以确保它们运行良好。
小ESP32S3:连接舵机、OLED、加速度计和电池。
舵机:将信号引脚连接到 GPIO 引脚(例如,D0 - 左前舵机,D1 - 右前舵机,D2 - 后舵机,D3 - 右后舵机)。
OLED:将 SDA 和 SCL 连接到 I2C 引脚。D4(SDA) 和 D5(SCl)
加速度计:将 SDA 和 SCL 连接到 I2C 引脚。D4(SDA) 和 D5(SCl)
电池:连接小ESP32S3的电源输入(确保极性正确)。
我使用了穿孔板来实现干净的连接并减少电线缠结。
焊接每个伺服器、IMU 和电池的接头引脚,使其易于接近。
捆绑并固定电线以防止缠结。
第 3 步:编程
将 Xiao ESP32S3 编程为:
协调行走的伺服运动。
在 OLED 上显示动画眼睛。
使用加速度计检测方向并触发反应。
Arduino 代码
从我的 GitHub 存储库下载代码
确保 expressions.h 与 TN24.ino 位于同一文件夹中,以便 OLED 动画正常工作。
您可以根据需要随意自定义代码,以实现与机器人;)一起玩乐所需的乐趣
此处提供了完整的代码。
解压缩文件夹后。在 arduino IDE 中打开代码,确保您选择了正确的板子并上传代码
第 4 步:电源和电池管理
使用锂离子电池为机器人供电。
如果您希望 TN24 可充电,请集成充电模块。
为方便起见,请添加电源开关。
第 5 步:测试和校准
单独测试每个伺服以确保平稳运动。
校准步行模式以实现平衡运动。
调整眼睛动画以获得所需的表情。
测试加速度计以实现准确的方向检测。
第 6 步:玩得开心
打开机器人的电源并将其放在办公桌或地板上,您将看到奇迹发生。要使机器人移动或玩耍,请点击它或摇晃它两次。这些动作在代码上是完全可定制的。我经常更新存储库以添加新的动作、游戏和自定义。
第 7 步:结论
TN24 是一款令人愉悦的桌面伴侣,展示了创造力、工程和运动的乐趣。TN24 由回收纸板制成,不仅具有创新性,而且环保,激发了可持续机器人技术的灵感。我希望本指南能激发您构建自己的 TN24 并探索机器人世界。
项目代码
#include #include #include #include #include \"expressions.h\"#define SCREEN_WIDTH 128#define SCREEN_HEIGHT 64#define OLED_RESET -1#define TAP_THRESHOLD 1 // Adjust based on testing#define TAP_WINDOW 500 // Time window for double tap (ms)Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);Adafruit_MPU6050 mpu;// Servo objects and pinsServo frontLeftServo;Servo frontRightServo;Servo backLeftServo;Servo backRightServo;const int frontLeftPin = D0;const int frontRightPin = D1;const int backLeftPin = D2;const int backRightPin = D3;// State definitionsenum RobotState { SLEEPING, ACTIVE, UPSIDE_DOWN};// Servo anglesconst int standAngle = 90;const int sleepAngle = 0;const int forwardStep = 60;const int backwardStep = 150;const int sitAngle = 20;const int danceAngle1 = 60;const int danceAngle2 = 120;// Global variablesRobotState currentState = SLEEPING;unsigned long lastTapTime = 0;int tapCount = 0;int delay_time = 200;bool isUpright = true;unsigned long lastActionTime = 0;const unsigned long IDLE_TIMEOUT = 30000; // 30 seconds before sleepingvoid setup() { Serial.begin(115200); // Initialize display if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { Serial.println(F(\"SSD1306 allocation failed\")); for(;;); } // Show boot animation showBootAnimation(); // Initialize servos frontLeftServo.attach(frontLeftPin); frontRightServo.attach(frontRightPin); backLeftServo.attach(backLeftPin); backRightServo.attach(backRightPin); // Initialize MPU6050 initializeMPU(); mpu.setHighPassFilter(MPU6050_HIGHPASS_0_63_HZ); mpu.setMotionDetectionThreshold(9); mpu.setMotionDetectionDuration(20); mpu.setInterruptPinLatch(true);// Keep it latched. Will turn off when reinitialized. mpu.setInterruptPinPolarity(true); mpu.setMotionInterrupt(true); stand(); delay(1000); // Start in sleeping position sleep(); sleepy();}void showBootAnimation() { display.clearDisplay(); display.setTextSize(2); display.setTextColor(WHITE); // Fade in effect for(int i = 0; i < 255; i += 51) { display.clearDisplay(); display.setTextColor(WHITE); display.setCursor(35, 25); display.println(\"TN 24\"); display.display(); delay(100); } delay(1000); display.clearDisplay(); display.display();}void loop() { checkOrientation(); checkTaps(); switch(currentState) { case SLEEPING: handleSleepingState(); break; case ACTIVE: handleActiveState(); break; case UPSIDE_DOWN: handleUpsideDownState(); break; }}void checkTaps() { sensors_event_t a, g, temp; mpu.getEvent(&a, &g, &temp); // float acceleration = sqrt(sq(a.acceleration.x) + sq(a.acceleration.y) + sq(a.acceleration.z)); // Serial.println(acceleration); if(mpu.getMotionInterruptStatus()) { unsigned long currentTime = millis(); Serial.println(\"Tap\"); tapCount++; if (tapCount == 2) { // Double tap detected Serial.println(\"Double Tap\"); if (currentState == SLEEPING) { wakeUp(); } tapCount = 0; } lastTapTime = currentTime; }}void handleSleepingState() { sleepy(); if (millis() - lastActionTime > 5000) { // Occasionally show sleeping animation if (random(100) < 10) { for (int i = 0; i < 3; i++) { dizzy(); delay(500); } } }}void handleActiveState() { // Check if idle for too long if (millis() - lastActionTime > IDLE_TIMEOUT) { goToSleep(); return; } mischievous(); walkForward(); delay(1000); cute(); dance(); delay(1000); wink(); react(); delay(1000); thinking(); sit(); delay(1000); sleep(); mischievous(); delay(1000); stand(); love(); delay(1000); play(); mischievous(); delay(1000); // Random playful behaviors // int action = random(100); // if (action < 20) { // mischievous(); // walkForward(); // } else if (action < 40) { // cute(); // dance(); // } else if (action < 60) { // wink(); // react(); // } else if (action < 80) { // thinking(); // sit(); // delay(1000); // stand(); // } else { // love(); // play(); // } lastActionTime = millis();}void handleUpsideDownState() { static unsigned long lastUpsideDownAction = 0; const unsigned long ACTION_INTERVAL = 1000; if (millis() - lastUpsideDownAction > ACTION_INTERVAL) { crying(); panicMovement(); lastUpsideDownAction = millis(); }}void panicMovement() { // Random panicked leg movements for (int i = 0; i < 4; i++) { frontLeftServo.write(random(0, 180)); frontRightServo.write(random(0, 180)); backLeftServo.write(random(0, 180)); backRightServo.write(random(0, 180)); delay(200); } stand(); // Try to return to standing position}void wakeUp() { currentState = ACTIVE; happy(); for (int angle = sleepAngle; angle <= standAngle; angle += 5) { frontLeftServo.write(angle); frontRightServo.write(angle); backLeftServo.write(angle); backRightServo.write(angle); delay(20); } lastActionTime = millis();}void goToSleep() { currentState = SLEEPING; sleepy(); for (int angle = standAngle; angle >= sleepAngle; angle -= 5) { frontLeftServo.write(angle); frontRightServo.write(180 - angle); backLeftServo.write(180); backRightServo.write(angle); delay(20); }}void checkOrientation() { sensors_event_t a, g, temp; mpu.getEvent(&a, &g, &temp); float zAcceleration = a.acceleration.z; bool wasUpright = isUpright; isUpright = zAcceleration > 1.0; if (!isUpright && wasUpright) { currentState = UPSIDE_DOWN; crying(); } else if (isUpright && !wasUpright) { currentState = ACTIVE; happy(); }}void initializeMPU() { if (!mpu.begin()) { Serial.println(\"Failed to find MPU6050 chip!\"); while (1) delay(10); } mpu.setAccelerometerRange(MPU6050_RANGE_8_G); mpu.setGyroRange(MPU6050_RANGE_500_DEG); mpu.setFilterBandwidth(MPU6050_BAND_21_HZ); delay(100);}void selfRight() { Serial.println(\"Attempting to self-right...\"); // Add logic to try returning to upright position frontLeftServo.write(sleepAngle); frontRightServo.write(180 - sleepAngle); // Inverted for the right side backLeftServo.write(180); backRightServo.write(sleepAngle); // Inverted for the right side delay(delay_time); stand(); delay(delay_time);}// Rest of the movement functions (walkForward, walkBackward, etc.) remain unchangedvoid stand() { frontLeftServo.write(standAngle); frontRightServo.write(standAngle); backLeftServo.write(standAngle); backRightServo.write(standAngle);}void sleep() { frontLeftServo.write(sleepAngle); frontRightServo.write(180 - sleepAngle); // Inverted for the right side backLeftServo.write(180); backRightServo.write(sleepAngle); // Inverted for the right side delay(1000);}void walkForward() { // Lift and move front-left and back-right legs frontLeftServo.write(forwardStep); backRightServo.write(180 - forwardStep); // Inverted for the right side delay(delay_time); // Reset to standing position frontLeftServo.write(standAngle); backRightServo.write(standAngle); delay(delay_time); // Lift and move front-right and back-left legs frontRightServo.write(180 - forwardStep); // Inverted for the right side backLeftServo.write(forwardStep); delay(delay_time); // Reset to standing position frontRightServo.write(standAngle); backLeftServo.write(standAngle); delay(delay_time);}void walkBackward() { // Similar to moveForward but in reverse frontLeftServo.write(backwardStep); backRightServo.write(180 - backwardStep); // Inverted for the right side delay(500); frontLeftServo.write(standAngle); backRightServo.write(standAngle); delay(500); frontRightServo.write(180 - backwardStep); // Inverted for the right side backLeftServo.write(backwardStep); delay(500); frontRightServo.write(standAngle); backLeftServo.write(standAngle); delay(500);}void turnLeft() { // Move only one side of the legs forward frontLeftServo.write(forwardStep); backLeftServo.write(forwardStep); delay(500); frontLeftServo.write(standAngle); backLeftServo.write(standAngle); delay(500);}void turnRight() { // Move only the other side of the legs forward frontRightServo.write(180 - forwardStep); // Inverted for the right side backRightServo.write(180 - forwardStep); // Inverted for the right side delay(500); frontRightServo.write(standAngle); backRightServo.write(standAngle); delay(500);}void sit() { // Set all legs to a sitting angle frontLeftServo.write(90); frontRightServo.write(90); // Inverted for the right side backLeftServo.write(180 - sitAngle); backRightServo.write(sitAngle); // Inverted for the right side delay(1000);}void dance() { for (int i = 0; i < 3; i++) { frontLeftServo.write(danceAngle1); backRightServo.write(180 - danceAngle1); // Inverted for the right side delay(200); frontRightServo.write(180 - danceAngle2); // Inverted for the right side backLeftServo.write(danceAngle2); delay(200); frontLeftServo.write(danceAngle2); backRightServo.write(180 - danceAngle2); // Inverted for the right side delay(200); frontRightServo.write(180 - danceAngle1); // Inverted for the right side backLeftServo.write(danceAngle1); delay(200); } // Return to standing position frontLeftServo.write(standAngle); frontRightServo.write(standAngle); backLeftServo.write(standAngle); backRightServo.write(standAngle);}void react() { frontLeftServo.write(90); frontRightServo.write(90); // Inverted for the right side backLeftServo.write(180 - sitAngle); backRightServo.write(sitAngle); // Inverted for the right side delay(1000); // Return to standing position stand(); delay(500); frontLeftServo.write(sitAngle); frontRightServo.write(180 - sitAngle); // Inverted for the right side backLeftServo.write(90); backRightServo.write(90); // Inverted for the right side delay(1000);int del = 250; frontLeftServo.write(0); frontRightServo.write(160); // Inverted for the right side delay(del); frontLeftServo.write(20 ); frontRightServo.write(180 ); // Inverted for the right side delay(del); frontLeftServo.write(0); frontRightServo.write(180 - sitAngle); // Inverted for the right side delay(del); frontLeftServo.write(sitAngle ); frontRightServo.write(180); // Inverted for the right side delay(del); frontLeftServo.write(0); frontRightServo.write(160); // Inverted for the right side delay(del); frontLeftServo.write(20 ); frontRightServo.write(180 ); // Inverted for the right side delay(del); frontLeftServo.write(0); frontRightServo.write(180 - sitAngle); // Inverted for the right side delay(del); frontLeftServo.write(sitAngle ); frontRightServo.write(170); // Inverted for the right side delay(del); // Return to standing position stand(); delay(500);}void play(){ for (int i = 0; i < 3; i++) { frontLeftServo.write(danceAngle1); backRightServo.write(180 - danceAngle1); // Inverted for the right side delay(200); frontRightServo.write(180 - danceAngle2); // Inverted for the right side backLeftServo.write(danceAngle2); delay(200); frontLeftServo.write(danceAngle2); backRightServo.write(180 - danceAngle2); // Inverted for the right side delay(200); frontRightServo.write(180 - danceAngle1); // Inverted for the right side backLeftServo.write(danceAngle1); delay(200); } // Return to standing position frontLeftServo.write(standAngle); frontRightServo.write(standAngle); backLeftServo.write(standAngle); backRightServo.write(standAngle);}
【Arduino 动手做】认识 TN24,您迷人且可定制的桌面伴侣机器人
项目链接:https://www.instructables.com/Cute-Desktop-Companion-Robot-TN24/
项目作者: tech_nickk
项目视频 :https://www.youtube.com/watch?v=3hISVFGLEw0
项目代码:https://github.com/tech-nickk/TN24