# motor_test
**Repository Path**: yuezht/motor_test
## Basic Information
- **Project Name**: motor_test
- **Description**: No description available
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2024-03-23
- **Last Updated**: 2026-04-11
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# BalanceBot - ESP32-S3 二轮自平衡小车
基于 ESP32-S3 的二轮自平衡机器人,采用串级 PID 控制算法(角度环 + 速度环),MPU6050 互补滤波姿态解算,内置 WiFi 网页遥控与实时调参界面。
## 目录
- [硬件构成](#硬件构成)
- [系统架构](#系统架构)
- [控制原理](#控制原理)
- [软件方案](#软件方案)
- [快速上手](#快速上手)
- [调测方法](#调测方法)
- [参数参考](#参数参考)
- [常见问题](#常见问题)
---
## 硬件构成
### 硬件清单
| 组件 | 型号/规格 | 数量 | 说明 |
| ---------- | ------------------------ | ---- | ----------------------------------- |
| 主控制器 | ESP32-S3 DevKit | 1 | 双核 240MHz,内置 WiFi/BLE |
| 姿态传感器 | MPU6050 | 1 | 六轴 IMU(三轴加速度 + 三轴陀螺仪) |
| 电机驱动 | TB6612FNG | 1 | 双路 H 桥驱动,最大 1.2A/通道 |
| 直流电机 | 减速电机 110RPM 带编码器 | 2 | 减速比视具体型号而定 |
| 车轮 | 65mm 橡胶轮 | 2 | 直径 65mm |
### 接线总图
```mermaid
graph LR
subgraph ESP32-S3
direction TB
GPIO6["GPIO6 (E1A)"]
GPIO7["GPIO7 (E1B)"]
GPIO14["GPIO14 (PWMA)"]
GPIO12["GPIO12 (AIN1)"]
GPIO13["GPIO13 (AIN2)"]
GPIO11["GPIO11 (STBY)"]
GPIO10["GPIO10 (BIN1)"]
GPIO9["GPIO9 (BIN2)"]
GPIO46["GPIO46 (PWMB)"]
GPIO4["GPIO4 (E2A)"]
GPIO5["GPIO5 (E2B)"]
GPIO20["GPIO20 (SDA)"]
GPIO19["GPIO19 (SCL)"]
end
subgraph TB6612FNG
PWMA_IN["PWMA"]
AIN1_IN["AIN1"]
AIN2_IN["AIN2"]
STBY_IN["STBY"]
BIN1_IN["BIN1"]
BIN2_IN["BIN2"]
PWMB_IN["PWMB"]
AO1["AO1/AO2"]
BO1["BO1/BO2"]
end
subgraph LEFT_MOTOR["左电机 (Motor A)"]
LM["电机线圈"]
LE["编码器 A/B"]
end
subgraph RIGHT_MOTOR["右电机 (Motor B)"]
RM["电机线圈"]
RE["编码器 A/B"]
end
subgraph MPU6050
SDA["SDA"]
SCL["SCL"]
end
GPIO14 --> PWMA_IN
GPIO12 --> AIN1_IN
GPIO13 --> AIN2_IN
GPIO11 --> STBY_IN
GPIO10 --> BIN1_IN
GPIO9 --> BIN2_IN
GPIO46 --> PWMB_IN
AO1 --> LM
BO1 --> RM
LE --> GPIO6
LE --> GPIO7
RE --> GPIO4
RE --> GPIO5
GPIO20 --> SDA
GPIO19 --> SCL
```
### GPIO 引脚分配表
| 功能 | GPIO | 方向 | 说明 |
| -------------- | ------- | ------------ | --------------------- |
| 左编码器 A 相 | GPIO 6 | INPUT_PULLUP | 中断触发,计数脉冲 |
| 左编码器 B 相 | GPIO 7 | INPUT_PULLUP | 判断旋转方向 |
| 左电机 PWM | GPIO 14 | OUTPUT | LEDC Channel 0, 50kHz |
| 左电机方向 IN1 | GPIO 12 | OUTPUT | HIGH=正转 |
| 左电机方向 IN2 | GPIO 13 | OUTPUT | HIGH=反转 |
| TB6612 使能 | GPIO 11 | OUTPUT | HIGH=芯片工作 |
| 右电机方向 IN1 | GPIO 10 | OUTPUT | HIGH=正转 |
| 右电机方向 IN2 | GPIO 9 | OUTPUT | HIGH=反转 |
| 右电机 PWM | GPIO 46 | OUTPUT | LEDC Channel 1, 50kHz |
| 右编码器 A 相 | GPIO 4 | INPUT_PULLUP | 中断触发,计数脉冲 |
| 右编码器 B 相 | GPIO 5 | INPUT_PULLUP | 判断旋转方向 |
| MPU6050 SDA | GPIO 20 | I2C | 400kHz |
| MPU6050 SCL | GPIO 19 | I2C | 400kHz |
---
## 系统架构
### 整体架构
```mermaid
graph TB
subgraph SENSORS["传感器层"]
MPU["MPU6050
加速度+陀螺仪"]
ENC_L["左编码器"]
ENC_R["右编码器"]
end
subgraph PROCESS["数据处理层"]
CF["互补滤波
α=0.98"]
SPD["速度计算
脉冲/秒"]
end
subgraph CONTROL["控制层"]
SPID["速度 PID
外环 50Hz"]
APID["角度 PID
内环 200Hz"]
TURN["转向叠加"]
end
subgraph ACTUATOR["执行层"]
TB["TB6612FNG
电机驱动"]
ML["左电机"]
MR["右电机"]
end
subgraph COMM["通信层"]
WIFI["WiFi AP
192.168.4.1"]
WS["WebSocket
Port 81"]
WEB["Web UI
Port 80"]
end
MPU --> CF
ENC_L --> SPD
ENC_R --> SPD
CF -->|"pitch角度"| APID
SPD -->|"平均速度"| SPID
SPID -->|"目标角度
±8°"| APID
APID -->|"电机功率"| TURN
TURN -->|"左轮PWM"| TB
TURN -->|"右轮PWM"| TB
TB --> ML
TB --> MR
WEB <-->|"HTTP"| WIFI
WS <-->|"双向实时"| WIFI
WIFI -->|"目标速度
转向值
PID参数"| SPID
WIFI -->|"转向指令"| TURN
APID -->|"遥测数据"| WS
```
### 控制循环时序
```mermaid
sequenceDiagram
participant TM as Timer
participant ML as MainLoop
participant IM as IMU
participant PD as PID
participant MT as Motor
Note over TM: 5ms HW Timer ISR
TM->>ML: controlFlag = true
ML->>IM: I2C read 14 bytes
IM-->>ML: accel + gyro raw
ML->>ML: complementary filter
Note over ML: pitch angle ready
rect rgb(255,182,193)
Note over ML,PD: Speed loop every 20ms
ML->>ML: read encoder count
ML->>PD: speed PID compute
PD-->>ML: target angle
end
ML->>PD: angle PID compute
PD-->>ML: motor power
ML->>ML: add turn offset
ML->>MT: setMotor L and R
```
---
## 控制原理
### 平衡原理
二轮平衡小车本质上是一个**倒立摆系统**。车体绕轮轴的俯仰角(pitch)是被控量,通过驱动轮子前后运动来抵消重力引起的倾倒趋势:
- 车体前倾 → 轮子向前加速 → 将支撑点移回重心下方
- 车体后倾 → 轮子向后加速 → 同理纠偏
### 串级 PID 控制
系统采用**双环串级 PID** 架构,内环(角度环)负责快速平衡响应,外环(速度环)负责位置/速度控制:
```mermaid
graph LR
TARGET["目标速度
(WiFi指令)"] --> SSUM(("+
-"))
SSUM --> SPID["速度 PID
(PI控制)
50Hz"]
SPID -->|"目标角度
±8°"| ASUM(("+
-"))
ASUM --> APID["角度 PID
(PD控制)
200Hz"]
APID -->|"+转向"| LM["左电机"]
APID -->|"-转向"| RM["右电机"]
AVG_SPD["编码器
平均速度"] -->|"反馈"| SSUM
PITCH["MPU6050
pitch角度"] -->|"反馈"| ASUM
style SPID fill:#2980b9,color:#fff
style APID fill:#e74c3c,color:#fff
style TARGET fill:#27ae60,color:#fff
```
#### 角度环(内环) - 200Hz
- **类型**:PD 控制器(比例 + 微分,不用积分避免低频振荡)
- **输入**:MPU6050 互补滤波后的 pitch 角度
- **设定值**:来自速度环输出(机械零点附近 ±8°)
- **输出**:电机 PWM 功率值 (-255 ~ +255)
- **作用**:快速响应车体倾斜,是维持平衡的核心
#### 速度环(外环) - 50Hz
- **类型**:PI 控制器(比例 + 积分,积分消除稳态速度误差)
- **输入**:左右编码器平均速度
- **设定值**:WiFi 遥控目标速度(静止时为 0)
- **输出**:角度环设定值(限幅 ±8°)
- **作用**:控制小车不会持续漂移,实现定速前进/后退
### 姿态解算 - 互补滤波
MPU6050 提供两种测量角度的方式,各有优缺点:
| 传感器 | 优点 | 缺点 |
| -------- | ---------------------- | ------------------------ |
| 加速度计 | 无漂移,长期稳定 | 对振动敏感,短期噪声大 |
| 陀螺仪 | 短期精确,不受振动影响 | 存在积分漂移,长期不可靠 |
**互补滤波**将两者优势融合:
```
pitch = α × (pitch + gyro × dt) + (1 - α) × accel_pitch
```
- `α = 0.98`:98% 信任陀螺仪(短期精确),2% 信任加速度计(长期校准)
- 计算量极小,适合 200Hz 高频运行
```mermaid
graph LR
GYRO["陀螺仪
角速度 °/s"] -->|"积分"| INT["pitch += gyro × dt"]
INT -->|"× 0.98"| SUM(("+"))
ACCEL["加速度计
atan2计算角度"] -->|"× 0.02"| SUM
SUM --> PITCH["融合后 pitch 角度"]
style GYRO fill:#3498db,color:#fff
style ACCEL fill:#e67e22,color:#fff
style PITCH fill:#2ecc71,color:#fff
```
### 电机驱动
TB6612FNG 通过 IN1/IN2 引脚组合控制方向,PWM 引脚控制转速:
| IN1 | IN2 | PWM | 状态 |
| ---- | ---- | ---- | ---------------- |
| HIGH | LOW | duty | 正转,速度=duty |
| LOW | HIGH | duty | 反转,速度=duty |
| LOW | LOW | - | 刹车(短路制动) |
软件使用**有符号速度** (-255 ~ +255) 统一控制,正值前进、负值后退、零值刹车,并内置**死区补偿**(最小 PWM=25)防止电机在低功率时卡死。
### 编码器测速
采用**硬件中断**方式读取编码器:
- A 相上升沿触发中断
- 在 ISR 中读取 B 相电平判断方向(B=HIGH 则正转,B=LOW 则反转)
- 速度 = 脉冲累计数 / 时间间隔(每 20ms 计算一次)
---
## 软件方案
### 代码结构
全部代码集中在单个 `motor_test.ino` 文件中,通过注释分区组织为 14 个模块:
```mermaid
graph TB
subgraph FILE["motor_test.ino"]
direction TB
S1["1. 头文件引用
Wire, WiFi, WebServer, WebSockets"]
S2["2. 引脚定义
编码器、电机驱动、I2C"]
S3["3. 配置常量
PID参数、滤波系数、频率"]
S4["4. 数据结构
Motor, Car, PIDController, MPU6050Data"]
S5["5. 全局变量"]
S6["6. MPU6050 驱动
init / calibrate / readRaw / updateAngle"]
S7["7. 编码器中断 & 速度计算
ISR(IRAM_ATTR) / calculateSpeed"]
S8["8. PID 控制器
pidInit / pidCompute / pidReset"]
S9["9. 电机控制
setMotor(有符号速度) / moveCar"]
S10["10. WiFi & WebSocket
AP模式 / 命令解析 / 遥测推送"]
S11["11. HTML 页面
PROGMEM raw string literal"]
S12["12. 平衡控制主函数
balanceControl() + 安全保护"]
S13["13. setup()"]
S14["14. loop()"]
S1 --> S2 --> S3 --> S4 --> S5
S5 --> S6 --> S7 --> S8 --> S9
S9 --> S10 --> S11 --> S12
S12 --> S13 --> S14
end
style S6 fill:#3498db,color:#fff
style S8 fill:#e74c3c,color:#fff
style S10 fill:#27ae60,color:#fff
style S12 fill:#8e44ad,color:#fff
```
### 核心数据结构
```c
// 电机
typedef struct {
uint16_t encA, encB; // 编码器引脚
uint16_t pwmPin, in1, in2; // 驱动引脚
uint8_t channel; // LEDC PWM 通道
int16_t speed; // 当前速度 (-255~+255)
volatile long encoderCount; // 编码器脉冲计数 (ISR写入)
} Motor;
// PID 控制器
typedef struct {
float Kp, Ki, Kd; // 三个增益
float setpoint; // 目标值
float integral, lastError; // 内部状态
float outputMin, outputMax; // 输出限幅
float output; // 当前输出
} PIDController;
// IMU 数据
typedef struct {
float accelX, accelY, accelZ; // 加速度 (g)
float gyroX, gyroY, gyroZ; // 角速度 (°/s)
float gyroOffsetX, gyroOffsetY, gyroOffsetZ; // 校准偏移
float pitch; // 滤波后俯仰角
} MPU6050Data;
```
### 定时器架构
系统使用 **标志位驱动** 模式而非在 ISR 中直接执行控制逻辑,因为 I2C 通信不能在中断服务程序中进行:
```mermaid
graph LR
HW["硬件定时器
5ms 周期"] -->|"ISR: 置标志位"| FLAG["controlFlag = true"]
FLAG -->|"loop()检测"| BC["balanceControl()"]
BC --> IMU["I2C 读 MPU6050"]
BC --> PID["PID 计算"]
BC --> MOT["电机输出"]
style HW fill:#e74c3c,color:#fff
style BC fill:#8e44ad,color:#fff
```
### WiFi 遥控协议
通过 WebSocket (端口 81) 进行低延迟双向通信:
**控制命令(客户端 → 小车):**
| 命令 | 格式 | 示例 | 说明 |
| ------- | --------------------------- | --------------------- | ----------------- |
| 前进 | `F:<速度>` | `F:80` | 设置正向目标速度 |
| 后退 | `B:<速度>` | `B:80` | 设置反向目标速度 |
| 左转 | `L:<力度>` | `L:40` | 差速左转 |
| 右转 | `R:<力度>` | `R:40` | 差速右转 |
| 停止 | `S` | `S` | 速度和转向归零 |
| 使能 | `E:1` / `E:0` | `E:1` | 启用/禁用平衡控制 |
| PID调参 | `P:aKp,aKi,aKd,sKp,sKi,sKd` | `P:25,0,0.8,2,0.05,0` | 在线修改PID参数 |
**遥测数据(小车 → 客户端,10Hz JSON):**
```json
{
"pitch": 1.23,
"speed": 45.6,
"aOut": 120.0,
"sOut": 0.50,
"lM": 130,
"rM": 128,
"en": 1
}
```
### 依赖库
| 库名 | 来源 | 用途 |
| -------------------- | -------------- | ------------------- |
| `Wire.h` | ESP32 内置 | I2C 通信(MPU6050) |
| `WiFi.h` | ESP32 内置 | WiFi AP 模式 |
| `WebServer.h` | ESP32 内置 | HTTP 网页服务 |
| `WebSocketsServer.h` | **需手动安装** | WebSocket 双向通信 |
> `WebSocketsServer` 来自 **"WebSockets" by Markus Sattler**,需在 Arduino IDE 的 Library Manager 中搜索安装。
---
## 快速上手
### 1. 环境准备
1. 安装 [Arduino IDE](https://www.arduino.cc/en/software) (2.x 推荐)
2. 添加 ESP32 开发板支持:
- 菜单 `File → Preferences → Additional Board Manager URLs` 添加:
```
https://espressif.github.io/arduino-esp32/package_esp32_index.json
```
- `Tools → Board Manager` 搜索安装 `esp32`
3. 安装 WebSockets 库:
- `Tools → Manage Libraries` 搜索 `WebSockets`,安装 **Markus Sattler** 版本
### 2. 编译上传
1. 选择开发板:`Tools → Board → ESP32S3 Dev Module`
2. 选择端口:`Tools → Port → /dev/cu.usbmodemXXXX`(或对应串口)
3. 点击上传
### 3. 首次启动
1. 上传完成后打开串口监视器(115200 波特率)
2. 看到 `Calibrating MPU6050... Keep robot still!` 时**保持小车静止**
3. 校准完成后串口输出 `Self-Balancing Robot Ready`
4. 手机/电脑连接 WiFi:
- **SSID**:`BalanceBot`
- **密码**:`12345678`
5. 浏览器打开 `http://192.168.4.1`
6. 在网页上点击 **Enable** 启动平衡控制
### 4. 初始化流程
```mermaid
graph TD
START["上电"] --> SERIAL["串口初始化
115200"]
SERIAL --> WIFI["WiFi AP 启动
BalanceBot"]
WIFI --> WS["WebServer +
WebSocket 就绪"]
WS --> MPU_INIT["MPU6050 初始化
唤醒 / 设量程 / DLPF"]
MPU_INIT --> CAL["陀螺仪校准
500次采样取平均
⚠️ 保持静止"]
CAL --> MOT_INIT["电机 & 编码器初始化
挂载中断"]
MOT_INIT --> PID_INIT["PID 控制器初始化
角度环 + 速度环"]
PID_INIT --> TIMER["启动硬件定时器
5ms / 200Hz"]
TIMER --> READY["系统就绪
等待 Enable 指令"]
style CAL fill:#e74c3c,color:#fff
style READY fill:#27ae60,color:#fff
```
---
## 调测方法
### 阶段一:验证 MPU6050 姿态输出
**目标**:确认 pitch 角度输出正确,前倾为正、后倾为负(或反之,取决于安装方向)。
**方法**:
1. 上传代码后打开 Arduino Serial Plotter(`Tools → Serial Plotter`)
2. 串口输出中的 `Pitch:xx.x` 即为当前角度
3. 手持小车前后倾斜,观察角度变化是否连续、无跳变
4. 如果轴方向不对,修改 `mpuUpdateAngle()` 中的 `gyroY` 为 `gyroX`(取决于 MPU6050 安装朝向)
**预期**:
- 静止竖直放置:pitch 接近 0°
- 前倾 45°:pitch 约 45°
- 后倾 45°:pitch 约 -45°
- 快速晃动后静止:角度迅速回归正确值(无明显漂移)
### 阶段二:验证编码器
**目标**:确认编码器计数随车轮转动正确变化。
**方法**:
1. 观察串口输出中 `L:xxx R:xxx` 的电机速度值
2. 用手拨动车轮,确认数值变化
3. 正转和反转应产生相反符号
### 阶段三:PID 调参
这是最关键也最耗时的步骤。推荐使用网页界面的 **PID TUNING** 面板实时调整。
#### 调参流程
```mermaid
graph TD
A["第1步: 只调角度环"] --> A1["先设 A.Kp=15, A.Kd=0"]
A1 --> A2{"手持小车
Enable 平衡"}
A2 --> A3{"倾斜时电机
反应方向正确?"}
A3 -->|"反了"| A4["交换电机接线
或取反 pitch"]
A3 -->|"正确"| A5["逐步增大 A.Kp"]
A5 --> A6{"车体振荡?"}
A6 -->|"是"| A7["略降 A.Kp
增大 A.Kd"]
A6 -->|"否,反应慢"| A5
A7 --> A8["角度环调好
能基本站立"]
A8 --> B["第2步: 加入速度环"]
B --> B1["设 S.Kp=1, S.Ki=0.01"]
B1 --> B2{"小车漂移?"}
B2 -->|"缓慢漂走"| B3["增大 S.Ki"]
B2 -->|"不漂移"| B4["速度环OK"]
B3 --> B2
B4 --> C["第3步: 精调"]
C --> C1["在网页上实时微调
观察数据曲线"]
style A fill:#e74c3c,color:#fff
style B fill:#2980b9,color:#fff
style C fill:#27ae60,color:#fff
```
#### 调参速查表
| 现象 | 原因 | 调整 |
| ------------------- | ------------ | --------- |
| 高频快速振荡 | 角度 Kp 过大 | 降低 A.Kp |
| 倒下不纠偏 | 角度 Kp 过小 | 增大 A.Kp |
| 低频缓慢摇摆 | 角度 Kd 不足 | 增大 A.Kd |
| 高频抖动(嗡嗡声) | 角度 Kd 过大 | 降低 A.Kd |
| 持续向一个方向漂移 | 速度 Ki 不足 | 增大 S.Ki |
| 刹车后大幅前冲/后退 | 速度 Kp 过大 | 降低 S.Kp |
| 长时间才停稳 | 速度 Kp 不足 | 增大 S.Kp |
### 阶段四:WiFi 遥控测试
1. 确认平衡稳定后,在网页上按方向键测试前进/后退/转向
2. 按住按钮时运动,松开自动回到静止平衡
3. 调整网页 JS 中的速度值(默认前后 80、转向 40)适配你的小车
### 安全保护机制
系统内置以下安全措施:
- **倾角保护**:pitch 超过 ±45° 时自动切断电机并重置 PID,防止失控
- **使能开关**:网页 Enable/Disable 按钮控制平衡是否激活
- **启动时禁用**:上电后默认不启动平衡,需手动 Enable
- **死区补偿**:低 PWM 值自动提升到 25,防止电机嗡鸣不转
---
## 参数参考
### 可调常量一览
| 常量名 | 默认值 | 位置 | 说明 |
| ---------------------- | ---------- | ------------- | ------------------------------ |
| `COMP_FILTER_ALPHA` | 0.98 | 代码常量 | 互补滤波系数,越大越信任陀螺仪 |
| `CONTROL_PERIOD_US` | 5000 (5ms) | 代码常量 | 角度环控制周期 |
| `SPEED_LOOP_DIVIDER` | 4 | 代码常量 | 速度环分频(50Hz) |
| `MOTOR_DEAD_ZONE` | 25 | 代码常量 | 电机最小有效 PWM |
| `ANGLE_KP` | 25.0 | 代码/网页可调 | 角度环比例增益 |
| `ANGLE_KI` | 0.0 | 代码/网页可调 | 角度环积分增益 |
| `ANGLE_KD` | 0.8 | 代码/网页可调 | 角度环微分增益 |
| `SPEED_KP` | 2.0 | 代码/网页可调 | 速度环比例增益 |
| `SPEED_KI` | 0.05 | 代码/网页可调 | 速度环积分增益 |
| `SPEED_KD` | 0.0 | 代码/网页可调 | 速度环微分增益 |
| `SAFETY_TILT_LIMIT` | 45° | 代码常量 | 安全倾角上限 |
| `ANGLE_SETPOINT_LIMIT` | 8° | 代码常量 | 速度环输出角度限幅 |
### 运行频率
| 任务 | 频率 | 说明 |
| -------------- | ------ | ----------------- |
| 角度环 PID | 200 Hz | 硬件定时器驱动 |
| 速度环 PID | 50 Hz | 每4个角度环周期 |
| WebSocket 遥测 | 10 Hz | loop() 中定时发送 |
| 串口调试输出 | 2 Hz | loop() 中定时打印 |
---
## 常见问题
**Q: 上电后小车不动?**
A: 默认 `balanceEnabled = false`,需通过网页点击 Enable 或发送 WebSocket 命令 `E:1`。
**Q: 电机转向反了(倾斜时往错误方向纠偏)?**
A: 两种解决办法:
1. 交换电机接线(IN1/IN2 互换)
2. 在 `mpuUpdateAngle()` 中将 pitch 取反
**Q: 角度数据在 0 附近跳动很大?**
A: 检查 MPU6050 的 I2C 连接是否牢固,确认 SCL/SDA 没有接反。必要时降低 I2C 速率到 100kHz。
**Q: 校准后 pitch 不为 0?**
A: 校准时小车必须绝对静止。如果机械结构导致 MPU6050 有固定偏移,可在 `mpuUpdateAngle()` 后加一个固定偏移补偿。
**Q: WiFi 连不上?**
A: 确认手机/电脑连接的是 `BalanceBot` 热点(密码 `12345678`),访问 `http://192.168.4.1`。部分手机会因"无互联网"自动断开,需在 WiFi 设置中关闭"自动切换到移动数据"。
**Q: 编译报错找不到 WebSocketsServer.h?**
A: 在 Arduino IDE 中 `Tools → Manage Libraries`,搜索 `WebSockets`,安装 **Markus Sattler** 的版本。