ClawdMochi 从 0 到 1 完整搭建教程

给一只毛绒玩偶装上感官,全程免焊接。板子按连到哪个 WiFi 自动切换在家 / 外出两种模式,出门不用重刷固件:

在家(家里WiFi)                        外出(手机热点)
[玩偶] ──流式原始值──▶ 本机翻译官         [玩偶] ──板上判定动作──▶ 公网中继(nginx+relay.py)
        touch_server.py                          │ HTTPS 只在有事件时发
        自动体检 / 动作分类                        ▼
        │                                    本机/服务器 poller.py 轮询取事件
        ▼                                         │
   ┌────────────────── 汇合 ───────────────────────┘
   ▼
[记录本] touch_moments.jsonl  +  异常触发 trigger.py → Claude主动推送给你

目录

  1. 方案:从"想法"到"能落地的清单"
  2. 硬件准备(购买清单)
  3. 认识板子:先找到 6 类引脚
  4. 组装第一步:搭两条电源总线
  5. 接 5 路 FSR 压感
  6. 接泪水电极
  7. 接 MPU6050 发抖模块
  8. 塞进玩偶:位置布局
  9. 烧固件
  10. 跑起来 & 自检
  11. 调参与排错

1. 方案:

几个关键决策,以及为什么这么选:

决策点 选择 原因
主控 ESP32-S3 自带 WiFi(能连家里/热点/VPS)、ADC 通道够用、板载 WS2812 灯可做反馈
免焊方式 带线传感器 + Wago/螺丝端子 传感器出厂就带引线,配 Wago 免焊连接器即可,全程无需烙铁、无需面包板
压感选型 FSR402 ×4 + FSR406 ×1 FSR402 小圆片贴爪子/头顶/后背;FSR406 面积大,贴肚子这种大面
发抖 MPU6050 便宜、I2C 好接、能测三轴加速度,用来算抖动能量
泪水 导电线自制电极+导电布 两根导电线当"液体开关",比买成品液位计更贴合"缝在爪子表面"的需求,导电布增加接触面积
分压 每路一个 10K 电阻 FSR / 液体本身是"可变电阻",必须配一个固定电阻组成分压才能被 ADC 读出

2. 硬件准备(购买清单)

器件 数量 说明
ESP32-S3 开发板(如 YD-ESP32-S3,N8R2/N8R8/N16R8 均可) 1 板载 WS2812 灯在 GPIO48;双 Type-C,认准带 COM 标记的口下载
ESP32-S3 扩展底板(螺丝端子/排针) 1 把细密排针变成好接的螺丝端子,免焊更省心
带线 FSR402(小圆片压感) 4 左爪、右爪、头顶、后背
带线 FSR406(大面压感) 1 肚子
MPU6050 模块 1 发抖检测(I2C)
不锈钢导电线 / 缝纫用导电线 少量 做泪水电极(两根)
导电布 两块 连接泪水电极
10K 电阻(1/4W 直插) 6 5 个给 FSR、1 个给泪水,做分压
Wago 免焊连接器 5 孔 ×2、3 孔 ×4 用来搭 3.3V 和 GND 两条"总线"
软硅胶线(22~26AWG) 一小卷 加长/连接用,柔软不硌玩偶
USB-C 数据线 1 ⚠ 一定要数据线,纯充电线认不到端口
热缩管 / 绝缘胶带 少量 包裸露接头,防短路

除了 ESP32-S3、FSR、MPU6050、10K 电阻这几样要选对型号,其余(线、Wago、热缩管)
随便买规格匹配的就行。


3. 认识板子:先找到 6 类引脚

接线前,在板子(或扩展底板)上把这几类脚认出来:

标记 用途 本项目用到
3V3 3.3V 电源输出 所有传感器的"热脚"都接它(可能有多个 3V3 脚,都通用)
GND 所有 10K 电阻的"冷脚"、MPU 的 GND(多个 GND 脚都通用)
GPIO3/5/6/7/8 模拟输入(ADC) 接 5 路 FSR 的信号
GPIO4 模拟输入(ADC) 接泪水信号
GPIO12 / GPIO13 I2C MPU6050 的 SDA / SCL
GPIO48 板载灯 固定,不用接线

最终接线一览(和固件里完全对应):

通道 位置 器件 GPIO
bd 肚子 FSR406 5
hd 头顶 FSR402 7
pr 右爪 FSR402 6
pl 左爪 FSR402 3
bk 后背 FSR402 8
tear 右爪表面 导电线电极 4
发抖 胸/背 MPU6050 SDA=12 / SCL=13

总接线示意图

一张图看全:上面一条 3.3V 总线、下面一条 GND 总线,中间每一路都是
"传感器 + 10K"的分压,信号从中间的"分压点"引到对应 GPIO。MPU6050 不用分压,
直接 4 根线。

   3.3V 总线 ●━━━┳━━━━┳━━━━┳━━━━┳━━━━┳━━━━━┳━━━━●
             │   │    │    │    │    │     │
         FSR406 FSR  FSR  FSR  FSR  电极A  MPU
           肚子  头顶 右爪 左爪 后背  泪水  VCC
             │   │    │    │    │    ┊水    │
   信号点 →  •   •    •    •    •   电极B•   │   (• 引线去 GPIO)
            G5   G7   G6   G3   G8    G4     │
             │   │    │    │    │    │       │
            10K  10K  10K  10K  10K  10K     │
             │   │    │    │    │    │       │
   GND 总线  ●━━━┻━━━━┻━━━━┻━━━━┻━━━━┻━━━━━━━●
                                       MPU: SDA→G12  SCL→G13

4. 组装第一步:搭两条电源总线

  • 所有传感器有一只脚要吃 3.3V(5 个 FSR 各一脚 + 泪水电极 A + MPU 的 VCC = 7 处)。
  • 所有 10K 电阻的另一脚要接 GND(6 个电阻 + MPU 的 GND = 7 处)。

板子上 3V3 / GND 的孔位有限,塞不下这么多线。Wago 就是个"排插"——
把它想象成插线板:一个 Wago 里所有孔都是内部导通的,插进去的线彼此相连。
于是我们用 Wago 把"1 个 3.3V"扩成"7 个 3.3V",GND 同理。

搭 3.3V 总线(要 7 个出口):把两个 5 孔 Wago 串起来:

板子 3V3 ──▶[5孔Wago A]── 其中1孔用短线跳到 ──▶[5孔Wago B]
                │ │ │                            │ │ │ │
                ▼ ▼ ▼                            ▼ ▼ ▼ ▼
              3个出口                          4个出口     = 共 7 个 3.3V 出口
  • Wago A:1 孔进(来自板子 3V3),1 孔用一小段线"跳线"到 Wago B,剩 3 孔做出口。
  • Wago B:1 孔进(来自跳线),剩 4 孔做出口。
  • 合计 3 + 4 = 7 个 3.3V 出口,正好够 5 FSR + 泪水 A + MPU VCC。

搭 GND 总线(要 7 个出口):优先直接用板子上多个 GND 脚(扩展底板一般有好几个),
不够再用 3 孔 Wago 补。比如用 2 个 3 孔 Wago:

板子 GND ─▶[3孔Wago]  另一路 板子 GND ─▶[3孔Wago]
            │  │                          │  │
            ▼  ▼                          ▼  ▼
          2出口                          2出口   + 板子自带 GND 脚若干 = 凑够 7

5. 接 5 路 FSR 压感

每个 FSR 都是分压接法,三根线:

 3.3V总线 ──── FSR一只脚
 FSR另一只脚 ──┬── GPIO端子(信号)
             │
           [10K]
             │
           GND总线

也就是:FSR 一脚 → 3.3V 总线FSR 另一脚 → 同时接到 GPIO 和一个 10K 电阻的一脚
10K 电阻另一脚 → GND 总线。GPIO 读到的就是"FSR 和 10K 的分压点",越用力压读数越高。

带线 FSR 和 10K 电阻怎么免焊接到一起:把 FSR 的信号线、10K 的一只脚、去 GPIO 的线,
三根拧在一起塞进同一个 Wago 孔(或螺丝端子),10K 另一脚单独进 GND 总线的 Wago。


6. 接泪水电极

泪水检测本质是一个液体导通开关,用的是和 FSR 一模一样的分压电路,只是把"压敏电阻"换成"两根裸露的导电电极+两块隔开的导电布":

  3.3V ──┐
         │
      [电极 A +导电布]      ← 埋在爪子表面
         ┊  ← 干燥时:两极之间是干燥的吸水布料,电阻≈无穷大
      [电极 B +导电布]      ← 埋在爪子表面,另一根
         │
         ├──────────▶ GPIO4 (ADC 读这里的电压)
         │
       [10K]
         │
        GND

  • 干燥:A、B 两极之间是布料,几乎不导通 → 分压点被 10K 拉到接近 GND → ADC 读数很低。
  • 有泪水/水滴:水把两极桥接起来,电阻骤降 → 3.3V 经过"水+电极"分到 GPIO,读数抬高。

所以"湿不湿"就是看 ADC 读数是否越过一个阈值(电阻越大越灵敏,但也越容易被噪声/汗气误触)。

电极材料:不锈钢导电线 / 导电布 / 缝纫用导电线都行,两根互不接触地缝在爪子表面,间距几毫米。


7. 接 MPU6050 发抖模块

发抖 ≠ 倾斜,也 ≠ 移动。发抖的特征是高频、来回、幅度小的抖动。所以我们不能看加速度的"绝对值"(那会把"被举起来"也当成抖),而要看它波动得有多厉害

做法分三步:

  1. 合成幅值:MPU6050 给出三轴加速度 ax, ay, az(单位 g)。合成总幅值
    [ m = \sqrt{a_x^2 + a_y^2 + a_z^2} ]
    静止时 m ≈ 1(只有重力)。不管玩偶朝哪放,静止时 m 都稳定在 1 附近。
  2. 在一个时间窗内收集很多个 m(固件里每拍收满一小段 buffer)。
  3. 算这段 m 的标准差(就是"波动能量"):
    [ E = \sqrt{\dfrac{1}{n}\sum (m_i - \bar m)^2} ]

    • 静止:m 几乎不变 → 标准差 ≈ 0。
    • 缓慢倾斜/平移:m 变化平滑但也小 → 标准差小。
    • 发抖:m 快速上下跳 → 标准差大。

标准差而不是均值,正好过滤掉"重力/姿态"这种恒定分量,只留下"抖动"这种交流分量。这就是为什么倾斜、举起来都不会误报成发抖。

  • 接线

MPU6050 是 I2C 器件,四根线:

MPU6050 引脚 接到
VCC 3V3(⚠ 别接 5V)
GND GND
SDA GPIO12
SCL GPIO13

I2C 地址默认 0x68(AD0 悬空/接地)。接反 SDA/SCL 或接了 5V 是最常见的接不上原因。其余引脚(XCL/XDA/AD0/INT)悬空即可。

  • 固件实现

初始化:MPU6050 上电默认在睡眠,要往电源管理寄存器 0x6B 写 0 唤醒:

void setup() {
  Serial.begin(115200);
  delay(200);
  analogReadResolution(12);
  Wire.begin(I2C_SDA, I2C_SCL);
  mpuWrite(0x6B, 0x00);
  delay(50);

读加速度:从寄存器 0x3B 连读 6 字节(X/Y/Z 各两字节),量程 ±2g 对应比例因子 16384

bool mpuReadAccel(float &ax, float &ay, float &az) {
  Wire.beginTransmission(MPU_ADDR);
  Wire.write(0x3B);
  if (Wire.endTransmission(false) != 0) return false;
  Wire.requestFrom(MPU_ADDR, 6);
  if (Wire.available() < 6) return false;
  int16_t x = (Wire.read() << 8) | Wire.read();
  int16_t y = (Wire.read() << 8) | Wire.read();
  int16_t z = (Wire.read() << 8) | Wire.read();
  ax = x / 16384.0; ay = y / 16384.0; az = z / 16384.0;
  return true;
}

算波动能量(标准差):

float shakeEnergy() {
  if (shakeN < 2) return 0;
  float mean = 0;
  for (int i = 0; i < shakeN; i++) mean += shakeBuf[i];
  mean /= shakeN;
  float var = 0;
  for (int i = 0; i < shakeN; i++) { float d = shakeBuf[i] - mean; var += d * d; }
  return sqrt(var / shakeN);
}

采样节奏:主循环里每次都读一发 MPU 塞进 buffer(比主节拍高频,采得密),到了节拍才把这段 buffer 算成一个能量值、然后清空重来。注意末尾 * 1000.0 是把很小的标准差放大到好设阈值的量级:

void loop() {
  // 高频采 MPU
  float ax, ay, az;
  if (mpuReadAccel(ax, ay, az) && shakeN < 64) shakeBuf[shakeN++] = sqrt(ax*ax + ay*ay + az*az);

  unsigned long now = millis();
  unsigned long interval = awayMode ? AWAY_TICK_MS : HOME_SEND_MS;
  if (now - lastTick >= interval) {
    lastTick = now;

    int vals[N], maxv = 0;
    for (int i = 0; i < N; i++) { vals[i] = readCh(i); if (vals[i] > maxv) maxv = vals[i]; }
    float shake = shakeEnergy() * 1000.0;
    shakeN = 0;

外出模式判定:能量超阈值 → 进入"抖动中"并记峰值;掉回阈值以下且持续够久(>0.3s)→ 报一条"发抖":

  // 发抖
  if (shake > EV_SHAKE_THR) {
    if (!shakeActive) { shakeActive = true; shakeStart = now; shakePeak = shake; }
    else if (shake > shakePeak) shakePeak = shake;
  } else if (shakeActive) {
    shakeActive = false;
    unsigned long d = now - shakeStart;
    if (d > 300) postEvent("全身", "发抖", (int)shakePeak, d / 1000.0);
  }

8. 塞进玩偶:位置布局

  • FSR 贴在对应部位的皮下(玩偶布料和棉花之间),感压面朝外,
    用一点点棉花垫在上面让按压能传导过来。
  • 泪水电极缝在右爪表面(要能碰到"眼泪")。
  • MPU6050 固定在身体中心(胸/背),别晃动,否则自身松动会被当成抖动。
  • 所有线顺着一个方向理到 ESP32-S3 所在的位置(一般放后背开口处),
    裸露接头用热缩管/胶带包好,避免棉花里两根线打架短路。

9. 烧固件

打开 Arduino IDE :

  1. BoardESP32S3 Dev Module(选成普通 "ESP32" 会报This chip is ESP32-S3, not ESP32)。
  2. Flash Size 按你的模组选(如 16MB),PSRAM 选 OPI PSRAM(N8R8/N16R8)。
  3. Tools → Port 选出现的串口(看不到端口:换数据线 / 按住 BOOT 再插 / 装 CH343 驱动)。
  4. 点上传。串口监视器(115200)会打印连网状态、IP、以及上报日志。

10. 跑起来 & 自检

去摸玩偶,终端应实时打印,例如:

13:04:23  肚子 · 抱住不放 · 力度3182 · 5.74秒
13:04:40  右爪 · 擦眼泪 · 力度1487 · 1.20秒
13:05:02  全身 · 发抖 · 力度88.0 · 2.10秒

逐路自检建议

  • 依次按 肚子/头顶/左爪/右爪/后背,确认每路都能打印且部位名对得上(对不上=GPIO 接错)。
  • 蘸一点水抹右爪 + 同时按右爪,应打出"擦眼泪"(只沾水不按 → 不报,这是双条件设计)。
  • 拿起来快速抖几下,应打出"发抖"(缓慢倾斜/举高 → 不报)。

11.调参与排错

泪水

现象 原因 解决
干燥时也报湿 阈值太低 / 电极距离太近 / 下拉电阻太大 提高 EV_TEAR_THR(固件) 或 TEAR_THRESHOLD(主机);电极拉开一点;确认用的是 10K 下拉
真擦了不报 阈值太高 / GND 没接好 / 只满足单条件 降低阈值;检查 10K 到 GND;确认擦时右爪也被按住(双条件缺一不可)
一闪而过误报 去抖太松 增大 EV_TEAR_HOLD / TEAR_HOLD_TICKS(每 +1 约多要求 50~100ms 持续湿)
读数整体偏移 电极氧化/汗渍 擦净电极;必要时换导电线

发抖

现象 原因 解决
串口/日志里 MPU 读不到 接了 5V / SDA-SCL 接反 / 地址不对 VCC 必须 3V3;核对 SDA=12、SCL=13;地址默认 0x68
静止也报发抖 阈值太低 / 放在震动面上 提高 EV_SHAKE_THR / SHAKE_THRESHOLD
真抖不报 阈值太高 / 采样太稀 降低阈值;确认 loop() 里每圈都在采 MPU(buffer 要够密)
举起来/走动被当成发抖 理论上不会(用的是标准差不是幅值);若发生多半是持续颠簸 适当提高阈值,或把 d > 300(ms) 的最短持续时间调长

剩余异常事件主动触发等已有成熟方案实现,此教程略。

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇