18浏览
查看: 18|回复: 2

[项目] 【Arduino 动手做】TN24,您迷人且可定制的桌面伴侣机器人

[复制链接]
认识 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 并探索机器人世界。

【Arduino 动手做】TN24,您迷人且可定制的桌面伴侣机器人图2

【Arduino 动手做】TN24,您迷人且可定制的桌面伴侣机器人图4

【Arduino 动手做】TN24,您迷人且可定制的桌面伴侣机器人图3

【Arduino 动手做】TN24,您迷人且可定制的桌面伴侣机器人图1

【Arduino 动手做】TN24,您迷人且可定制的桌面伴侣机器人图5

【Arduino 动手做】TN24,您迷人且可定制的桌面伴侣机器人图6

驴友花雕  中级技神
 楼主|

发表于 7 小时前

【Arduino 动手做】TN24,您迷人且可定制的桌面伴侣机器人

项目代码

  1. #include <ESP32Servo.h>
  2. #include <Wire.h>
  3. #include <Adafruit_MPU6050.h>
  4. #include <Adafruit_Sensor.h>
  5. #include "expressions.h"
  6. #define SCREEN_WIDTH 128
  7. #define SCREEN_HEIGHT 64
  8. #define OLED_RESET -1
  9. #define TAP_THRESHOLD 1    // Adjust based on testing
  10. #define TAP_WINDOW 500     // Time window for double tap (ms)
  11. Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
  12. Adafruit_MPU6050 mpu;
  13. // Servo objects and pins
  14. Servo frontLeftServo;
  15. Servo frontRightServo;
  16. Servo backLeftServo;
  17. Servo backRightServo;
  18. const int frontLeftPin = D0;
  19. const int frontRightPin = D1;
  20. const int backLeftPin = D2;
  21. const int backRightPin = D3;
  22. // State definitions
  23. enum RobotState {
  24.   SLEEPING,
  25.   ACTIVE,
  26.   UPSIDE_DOWN
  27. };
  28. // Servo angles
  29. const int standAngle = 90;
  30. const int sleepAngle = 0;
  31. const int forwardStep = 60;
  32. const int backwardStep = 150;
  33. const int sitAngle = 20;
  34. const int danceAngle1 = 60;
  35. const int danceAngle2 = 120;
  36. // Global variables
  37. RobotState currentState = SLEEPING;
  38. unsigned long lastTapTime = 0;
  39. int tapCount = 0;
  40. int delay_time = 200;
  41. bool isUpright = true;
  42. unsigned long lastActionTime = 0;
  43. const unsigned long IDLE_TIMEOUT = 30000; // 30 seconds before sleeping
  44. void setup() {
  45.   Serial.begin(115200);
  46.   
  47.   // Initialize display
  48.   if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
  49.     Serial.println(F("SSD1306 allocation failed"));
  50.     for(;;);
  51.   }
  52.   
  53.   // Show boot animation
  54.   showBootAnimation();
  55.   
  56.   // Initialize servos
  57.   frontLeftServo.attach(frontLeftPin);
  58.   frontRightServo.attach(frontRightPin);
  59.   backLeftServo.attach(backLeftPin);
  60.   backRightServo.attach(backRightPin);
  61.   
  62.   // Initialize MPU6050
  63.   initializeMPU();
  64.   mpu.setHighPassFilter(MPU6050_HIGHPASS_0_63_HZ);
  65.   mpu.setMotionDetectionThreshold(9);
  66.   mpu.setMotionDetectionDuration(20);
  67.   mpu.setInterruptPinLatch(true);        // Keep it latched.  Will turn off when reinitialized.
  68.   mpu.setInterruptPinPolarity(true);
  69.   mpu.setMotionInterrupt(true);
  70.   
  71.   stand();
  72.   delay(1000);
  73.   // Start in sleeping position
  74.   sleep();
  75.   sleepy();
  76. }
  77. void showBootAnimation() {
  78.   display.clearDisplay();
  79.   display.setTextSize(2);
  80.   display.setTextColor(WHITE);
  81.   
  82.   // Fade in effect
  83.   for(int i = 0; i < 255; i += 51) {
  84.     display.clearDisplay();
  85.     display.setTextColor(WHITE);
  86.     display.setCursor(35, 25);
  87.     display.println("TN 24");
  88.     display.display();
  89.     delay(100);
  90.   }
  91.   delay(1000);
  92.   display.clearDisplay();
  93.   display.display();
  94. }
  95. void loop() {
  96.   checkOrientation();
  97.   checkTaps();
  98.   
  99.   switch(currentState) {
  100.     case SLEEPING:
  101.       handleSleepingState();
  102.       break;
  103.       
  104.     case ACTIVE:
  105.       handleActiveState();
  106.       break;
  107.       
  108.     case UPSIDE_DOWN:
  109.       handleUpsideDownState();
  110.       break;
  111.   }
  112. }
  113. void checkTaps() {
  114.   sensors_event_t a, g, temp;
  115.   mpu.getEvent(&a, &g, &temp);
  116.   
  117.   // float acceleration = sqrt(sq(a.acceleration.x) + sq(a.acceleration.y) + sq(a.acceleration.z));
  118.   // Serial.println(acceleration);
  119.   if(mpu.getMotionInterruptStatus()) {
  120.     unsigned long currentTime = millis();
  121.     Serial.println("Tap");
  122.    
  123.       tapCount++;
  124.       if (tapCount == 2) { // Double tap detected
  125.         Serial.println("Double Tap");
  126.         if (currentState == SLEEPING) {
  127.           wakeUp();
  128.         }
  129.         tapCount = 0;
  130.       }
  131.     lastTapTime = currentTime;
  132.   }
  133. }
  134. void handleSleepingState() {
  135.   sleepy();
  136.   if (millis() - lastActionTime > 5000) {
  137.     // Occasionally show sleeping animation
  138.     if (random(100) < 10) {
  139.       for (int i = 0; i < 3; i++) {
  140.         dizzy();
  141.         delay(500);
  142.       }
  143.     }
  144.   }
  145. }
  146. void handleActiveState() {
  147.   // Check if idle for too long
  148.   if (millis() - lastActionTime > IDLE_TIMEOUT) {
  149.     goToSleep();
  150.     return;
  151.   }
  152.      mischievous();
  153.      walkForward();
  154.      delay(1000);
  155.     cute();
  156.     dance();
  157.     delay(1000);
  158.     wink();
  159.     react();
  160.     delay(1000);
  161.     thinking();
  162.     sit();
  163.     delay(1000);
  164.     sleep();
  165.     mischievous();
  166.     delay(1000);
  167.     stand();
  168.     love();
  169.     delay(1000);
  170.     play();
  171.     mischievous();
  172.     delay(1000);
  173.   
  174.   // Random playful behaviors
  175.   // int action = random(100);
  176.   // if (action < 20) {
  177.   //    mischievous();
  178.   //    walkForward();
  179.   // } else if (action < 40) {
  180.   //   cute();
  181.   //   dance();
  182.   // } else if (action < 60) {
  183.   //   wink();
  184.   //   react();
  185.   // } else if (action < 80) {
  186.   //   thinking();
  187.   //   sit();
  188.   //   delay(1000);
  189.   //   stand();
  190.   // } else {
  191.   //   love();
  192.   //   play();
  193.   // }
  194.   
  195.   lastActionTime = millis();
  196. }
  197. void handleUpsideDownState() {
  198.   static unsigned long lastUpsideDownAction = 0;
  199.   const unsigned long ACTION_INTERVAL = 1000;
  200.   
  201.   if (millis() - lastUpsideDownAction > ACTION_INTERVAL) {
  202.     crying();
  203.     panicMovement();
  204.     lastUpsideDownAction = millis();
  205.   }
  206. }
  207. void panicMovement() {
  208.   // Random panicked leg movements
  209.   for (int i = 0; i < 4; i++) {
  210.     frontLeftServo.write(random(0, 180));
  211.     frontRightServo.write(random(0, 180));
  212.     backLeftServo.write(random(0, 180));
  213.     backRightServo.write(random(0, 180));
  214.     delay(200);
  215.   }
  216.   stand(); // Try to return to standing position
  217. }
  218. void wakeUp() {
  219.   currentState = ACTIVE;
  220.   happy();
  221.   for (int angle = sleepAngle; angle <= standAngle; angle += 5) {
  222.     frontLeftServo.write(angle);
  223.     frontRightServo.write(angle);
  224.     backLeftServo.write(angle);
  225.     backRightServo.write(angle);
  226.     delay(20);
  227.   }
  228.   lastActionTime = millis();
  229. }
  230. void goToSleep() {
  231.   currentState = SLEEPING;
  232.   sleepy();
  233.   for (int angle = standAngle; angle >= sleepAngle; angle -= 5) {
  234.     frontLeftServo.write(angle);
  235.     frontRightServo.write(180 - angle);
  236.     backLeftServo.write(180);
  237.     backRightServo.write(angle);
  238.     delay(20);
  239.   }
  240. }
  241. void checkOrientation() {
  242.   sensors_event_t a, g, temp;
  243.   mpu.getEvent(&a, &g, &temp);
  244.   
  245.   float zAcceleration = a.acceleration.z;
  246.   bool wasUpright = isUpright;
  247.   isUpright = zAcceleration > 1.0;
  248.   
  249.   if (!isUpright && wasUpright) {
  250.     currentState = UPSIDE_DOWN;
  251.     crying();
  252.   } else if (isUpright && !wasUpright) {
  253.     currentState = ACTIVE;
  254.     happy();
  255.   }
  256. }
  257. void initializeMPU() {
  258.   if (!mpu.begin()) {
  259.     Serial.println("Failed to find MPU6050 chip!");
  260.     while (1) delay(10);
  261.   }
  262.   
  263.   mpu.setAccelerometerRange(MPU6050_RANGE_8_G);
  264.   mpu.setGyroRange(MPU6050_RANGE_500_DEG);
  265.   mpu.setFilterBandwidth(MPU6050_BAND_21_HZ);
  266.   delay(100);
  267. }
  268. void selfRight() {
  269.   Serial.println("Attempting to self-right...");
  270.   // Add logic to try returning to upright position
  271.   frontLeftServo.write(sleepAngle);
  272.   frontRightServo.write(180 - sleepAngle); // Inverted for the right side
  273.   backLeftServo.write(180);
  274.   backRightServo.write(sleepAngle); // Inverted for the right side
  275.   delay(delay_time);
  276.   stand();
  277.   delay(delay_time);
  278. }
  279. // Rest of the movement functions (walkForward, walkBackward, etc.) remain unchanged
  280. void stand() {
  281.   frontLeftServo.write(standAngle);
  282.   frontRightServo.write(standAngle);
  283.   backLeftServo.write(standAngle);
  284.   backRightServo.write(standAngle);
  285. }
  286. void sleep() {
  287.   frontLeftServo.write(sleepAngle);
  288.   frontRightServo.write(180 - sleepAngle); // Inverted for the right side
  289.   backLeftServo.write(180);
  290.   backRightServo.write(sleepAngle); // Inverted for the right side
  291.   delay(1000);
  292. }
  293. void walkForward() {
  294.   // Lift and move front-left and back-right legs
  295.   frontLeftServo.write(forwardStep);
  296.   backRightServo.write(180 - forwardStep); // Inverted for the right side
  297.   delay(delay_time);
  298.   // Reset to standing position
  299.   frontLeftServo.write(standAngle);
  300.   backRightServo.write(standAngle);
  301.   delay(delay_time);
  302.   // Lift and move front-right and back-left legs
  303.   frontRightServo.write(180 - forwardStep); // Inverted for the right side
  304.   backLeftServo.write(forwardStep);
  305.   delay(delay_time);
  306.   // Reset to standing position
  307.   frontRightServo.write(standAngle);
  308.   backLeftServo.write(standAngle);
  309.   delay(delay_time);
  310. }
  311. void walkBackward() {
  312.   // Similar to moveForward but in reverse
  313.   frontLeftServo.write(backwardStep);
  314.   backRightServo.write(180 - backwardStep); // Inverted for the right side
  315.   delay(500);
  316.   frontLeftServo.write(standAngle);
  317.   backRightServo.write(standAngle);
  318.   delay(500);
  319.   frontRightServo.write(180 - backwardStep); // Inverted for the right side
  320.   backLeftServo.write(backwardStep);
  321.   delay(500);
  322.   frontRightServo.write(standAngle);
  323.   backLeftServo.write(standAngle);
  324.   delay(500);
  325. }
  326. void turnLeft() {
  327.   // Move only one side of the legs forward
  328.   frontLeftServo.write(forwardStep);
  329.   backLeftServo.write(forwardStep);
  330.   delay(500);
  331.   frontLeftServo.write(standAngle);
  332.   backLeftServo.write(standAngle);
  333.   delay(500);
  334. }
  335. void turnRight() {
  336.   // Move only the other side of the legs forward
  337.   frontRightServo.write(180 - forwardStep); // Inverted for the right side
  338.   backRightServo.write(180 - forwardStep); // Inverted for the right side
  339.   delay(500);
  340.   frontRightServo.write(standAngle);
  341.   backRightServo.write(standAngle);
  342.   delay(500);
  343. }
  344. void sit() {
  345.   // Set all legs to a sitting angle
  346.   frontLeftServo.write(90);
  347.   frontRightServo.write(90); // Inverted for the right side
  348.   backLeftServo.write(180 - sitAngle);
  349.   backRightServo.write(sitAngle); // Inverted for the right side
  350.   delay(1000);
  351. }
  352. void dance() {
  353.   for (int i = 0; i < 3; i++) {
  354.     frontLeftServo.write(danceAngle1);
  355.     backRightServo.write(180 - danceAngle1); // Inverted for the right side
  356.     delay(200);
  357.     frontRightServo.write(180 - danceAngle2); // Inverted for the right side
  358.     backLeftServo.write(danceAngle2);
  359.     delay(200);
  360.     frontLeftServo.write(danceAngle2);
  361.     backRightServo.write(180 - danceAngle2); // Inverted for the right side
  362.     delay(200);
  363.     frontRightServo.write(180 - danceAngle1); // Inverted for the right side
  364.     backLeftServo.write(danceAngle1);
  365.     delay(200);
  366.   }
  367.   // Return to standing position
  368.   frontLeftServo.write(standAngle);
  369.   frontRightServo.write(standAngle);
  370.   backLeftServo.write(standAngle);
  371.   backRightServo.write(standAngle);
  372. }
  373. void react() {
  374.   frontLeftServo.write(90);
  375.   frontRightServo.write(90); // Inverted for the right side
  376.   backLeftServo.write(180 - sitAngle);
  377.   backRightServo.write(sitAngle); // Inverted for the right side
  378.   delay(1000);
  379.   // Return to standing position
  380.   stand();
  381.   delay(500);
  382.   frontLeftServo.write(sitAngle);
  383.   frontRightServo.write(180 - sitAngle); // Inverted for the right side
  384.   backLeftServo.write(90);
  385.   backRightServo.write(90); // Inverted for the right side
  386.   delay(1000);
  387. int del = 250;
  388.   frontLeftServo.write(0);
  389.   frontRightServo.write(160); // Inverted for the right side
  390.   delay(del);
  391.   frontLeftServo.write(20 );
  392.   frontRightServo.write(180 ); // Inverted for the right side
  393.   delay(del);
  394.   frontLeftServo.write(0);
  395.   frontRightServo.write(180 - sitAngle); // Inverted for the right side
  396.   delay(del);
  397.   frontLeftServo.write(sitAngle );
  398.   frontRightServo.write(180); // Inverted for the right side
  399.   delay(del);
  400.   frontLeftServo.write(0);
  401.   frontRightServo.write(160); // Inverted for the right side
  402.   delay(del);
  403.   frontLeftServo.write(20 );
  404.   frontRightServo.write(180 ); // Inverted for the right side
  405.   delay(del);
  406.   frontLeftServo.write(0);
  407.   frontRightServo.write(180 - sitAngle); // Inverted for the right side
  408.   delay(del);
  409.   frontLeftServo.write(sitAngle );
  410.   frontRightServo.write(170); // Inverted for the right side
  411.   delay(del);
  412.   // Return to standing position
  413.   stand();
  414.   delay(500);
  415. }
  416. void play()
  417. {
  418.   for (int i = 0; i < 3; i++) {
  419.     frontLeftServo.write(danceAngle1);
  420.     backRightServo.write(180 - danceAngle1); // Inverted for the right side
  421.     delay(200);
  422.     frontRightServo.write(180 - danceAngle2); // Inverted for the right side
  423.     backLeftServo.write(danceAngle2);
  424.     delay(200);
  425.     frontLeftServo.write(danceAngle2);
  426.     backRightServo.write(180 - danceAngle2); // Inverted for the right side
  427.     delay(200);
  428.     frontRightServo.write(180 - danceAngle1); // Inverted for the right side
  429.     backLeftServo.write(danceAngle1);
  430.     delay(200);
  431.   }
  432.   // Return to standing position
  433.   frontLeftServo.write(standAngle);
  434.   frontRightServo.write(standAngle);
  435.   backLeftServo.write(standAngle);
  436.   backRightServo.write(standAngle);
  437. }
复制代码


回复

使用道具 举报

驴友花雕  中级技神
 楼主|

发表于 7 小时前

【Arduino 动手做】TN24,您迷人且可定制的桌面伴侣机器人

【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

【Arduino 动手做】TN24,您迷人且可定制的桌面伴侣机器人图2

【Arduino 动手做】TN24,您迷人且可定制的桌面伴侣机器人图1

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

为本项目制作心愿单
购买心愿单
心愿单 编辑
[[wsData.name]]

硬件清单

  • [[d.name]]
btnicon
我也要做!
点击进入购买页面
上海智位机器人股份有限公司 沪ICP备09038501号-4 备案 沪公网安备31011502402448

© 2013-2025 Comsenz Inc. Powered by Discuz! X3.4 Licensed

mail