复刻 B 站世博同学(穆世博)开源的桌面级双轮腿机器人:ESP32 主控,SimpleFOC 以力矩模式驱动两颗轮毂无刷电机,两只 STS3032 总线舵机调节腿高,连上它的热点就能在浏览器里遥控。本文顺着开源资料拆开机械件、四块电路板和控制代码,讲清自平衡怎样拆成四个比例项、腿高与跳跃怎样换算到舵机,并整理上电流程、舵机标定、WiFi 设置和复刻时要留意的细节。
视频演示
视频最后一个镜头,键盘前面并排摆着三样东西。粉色外壳的墨水屏,黄色的马里奥问号砖,还有一台银色机身的小机器人,两只轮子着地,两条腿撑着,自己立在桌垫上。

前两样以前写过。第三样是新来的,桌面轮足机器人。
它的脑洞不是我的。原作者是 B 站的世博同学(穆世博),2023 年 3 月发了第一版演示,后来把结构、电路板和代码整套开源到 GitHub,开源贡献者里还有李育锋。我做的是复刻,照着资料打板、打印、焊接、装配、编译,让它在我的桌面上站起来。
复刻的好处是脑洞现成。焊锡、螺丝和调参,还得自己来。
轮子负责跑,腿负责高低
轮腿这个形态,大块头早就有了。视频开头放了几段大型轮腿机器人的画面,在林地和室内跑来跑去。
思路很直接。平地上靠轮子,跑得快也省力;腿用来调高低、找平衡,必要时还能蹦一下。代价是它和平衡车一样,天生站不住,得靠控制时时刻刻扶着。
世博同学把这套东西缩到了桌面上。轮胎直径 25 mm,网页上的机身高度滑块从 32 mm 拉到 85 mm。个头小,该有的却一样不少,两颗轮毂无刷电机、两只总线舵机、一块 ESP32 主控,外加编码器和 IMU。
开源仓库分成四个目录。1.RobotModel 放机械结构,2.Hardware 放电路板,3.Software 放代码和依赖库,4.Docs 放说明文档和接线图。下面就顺着这几个目录,一层一层拆开看。
机械件,三种做法加一堆外购件
自己加工的零件,按工艺分三类。
| 做法 | 零件 | 仓库里的文件 |
|---|---|---|
| 3D 打印,README 推荐尼龙粉末烧结 | 大腿、小腿、大腿盖板、轮毂、立柱盖,PCB 盖板可选 | Parts-Manufactured/1.3DPrint 下的 STL |
| CNC | 电机座(6061 铝)、机身底座(钢) | Parts-Manufactured/2.CNC 下的 STP 和螺纹孔说明 |
| 板材切割 | 碳纤维面板 | Parts-Manufactured/3.CarbonFiberPanels 下的 DWG 和 PDF |

买的零件列在 Parts-Purchased/BOM.xlsx 里,表格附了购买链接。主要的几样如下。
| 零件 | 规格 | 数量 |
|---|---|---|
| 腿部舵机 | STS3032 总线舵机 | 2 |
| 轮毂电机 | BOM 只给了购买链接 | 2 |
| 编码器磁铁 | 直径 4 mm,厚 1 mm | 2 |
| 电池 | 1100 mAh | 1 |
| 充电器 | 平衡锂电充电器 | 1 |
| 轮胎 | 直径 25 mm,宽 10 mm | 2 |
| 轴承 | 3×6×2、4×7×2.5 | 各 4 |
螺丝、螺母、铜柱和销钉也都在表里。另外还要 3 根 GH1.25 4PIN 双头连接线,推荐长度 15 cm,需要单独买。
装配时有两处容易忽略。编码器磁铁要用 502 胶水粘在电机转轴的端面上;部分轴和轴承压进去的时候,可能要借一把平口钳。
四块板子,各管一摊
电路板一共四张,都用嘉立创 EDA 画。仓库里每张板都有 PCB 源文件、Gerber 和 BOM,其中三张还附了原理图 PDF。
| 板子 | 核心器件 | 负责什么 |
|---|---|---|
| 主控板 | ESP32、两颗 L6234PD013TR、CH340C | 跑控制和网页,驱动两颗轮毂电机,USB 串口下载 |
| 编码器板 | AS5600 | 每个轮子一块,用 I2C 读电机转角 |
| IMU 板 | MPU6050 模块 | 测机身姿态,和右侧编码器共用一路 I2C |
| 舵机调试板 | 两颗单路三态缓冲器 | 把串口的两根线合成一根信号线,分时收发 |


L6234PD013TR 是两路轮毂电机的驱动芯片。README 专门提醒过,淘宝上这颗假货不少,买的时候留个心眼。
电源也有一句提醒。板上的正负极丝印已经修正,用手册里推荐的电池直接插就行;换别家电池,先认清正负端再插。

调试的时候,引脚表比原理图好查。下面这些都写在 wl_pro_robot.ino 的开头。
| 功能 | 引脚 |
|---|---|
| 电机 1 驱动 | PWM 32、33、25,使能 22 |
| 电机 2 驱动 | PWM 26、27、14,使能 12 |
| 编码器 1 | I2C0,SDA 19,SCL 18 |
| 编码器 2 和 MPU6050 | I2C1,SDA 23,SCL 5 |
| 腿部总线舵机 | Serial2,1 Mbps |
| 电池电压检测 | GPIO35 |
| 电量指示灯 | GPIO13 |
站稳,靠四个比例项
SimpleFOC 在这台机器人里只干一件事,把轮毂电机变成听话的力矩源。两颗电机都设成力矩模式,力矩用电压控制;平衡、前进和转向全在主循环里算,算完再把力矩交给它。
loop() 每转一圈,依次做电压检测、处理网页消息、更新 IMU、算平衡、算转向、控制腿,最后给两颗电机下目标。
motor1.target = (-0.5)*(LQR_u + YAW_output);
motor2.target = (-0.5)*(LQR_u - YAW_output);
LQR_u 是两只轮子共同的平衡力矩,YAW_output 是转向量,一边加、一边减,机身就拧过去了。
源码在读轮子位移和速度时也取了负值,旁边的注释写得很实在。
//给负值是因为按照当前的电机接线,正转矩会向后转
硬件就是这样,电机线换个相序,符号就得跟着翻。
平衡力矩叫 LQR,代码里却没有现场求增益的过程。作者在注释里把公式写了出来,再拆成四个只开比例项的控制器,方便边跑边调。
//LQR平衡算式,实际使用中为便于调参,讲算式分解为4个P控制,采用PIDController方法在commander中实时调试
//QR_u = LQR_k1*(LQR_angle - angle_zeropoint) + LQR_k2*LQR_gyro + LQR_k3*(LQR_distance - distance_zeropoint) + LQR_k4*LQR_speed;
| 比例项 | 输入 | 默认 P | 串口命令 |
|---|---|---|---|
| 角度 | 俯仰角减去平衡零点 | 1 | A |
| 角速度 | 俯仰角速度 | 0.06 | B |
| 位移 | 轮子位移减去位移零点 | 0.5 | C |
| 速度 | 轮速减去摇杆给的速度 | 0.7 | D |

串口波特率 115200,接的是 SimpleFOC 的 Commander,每条命令以换行结尾。发 AP 回读角度项当前的 P,发 AP1.2 就把它改成 1.2。A 到 L 一共 12 个字母,还管着转向、横滚、滤波和零点补偿,调参不用反复烧录。
光有四个比例项,它能站,但还不够好用。真正有意思的是源码里那几段「运动细节优化处理」。
- 推着摇杆前后走时,位移零点一直跟着轮子跑,免得位移项把它往回拽。
- 摇杆松开,等轮速降到 0.5 以下,再把当前位置记成新零点,原地停车。
- 被人用手快速推开,轮速超过 15,同样重置零点,不跟你较劲。
- 轮速一次跳变超过 10,或者超过 50,或者正在跳跃,就当轮子离地了,只保留角度和角速度两项。
- 静止时再串一个 PI 补小力矩的非线性,同时慢慢挪动平衡零点,让它自己找到真实的重心。
- 机身越高,速度项的 P 越小,高度低于 50 mm 用 0.7,低于 64 mm 用 0.6,再往上用 0.5。
摔倒也有处理。俯仰角超过 25°,判定失控,两轮力矩直接清零;扶回 10° 以内,再等 200 个循环才恢复出力。网页上没勾 Robot Go! 的时候,力矩同样是零。
两条腿,一个高度
腿由两只 STS3032 总线舵机驱动。左边的 ID 是 1,右边是 2,腿完全蹲到机械限位的位置标定为 2048,配置用飞特的调试软件。
网页上拖动高度滑块,固件把毫米换算成舵机位置。
Position[0] = 2048 + 12 + 8.4*(wrobot.height-32) - leg_position_add;
Position[1] = 2048 - 12 - 8.4*(wrobot.height-32) - leg_position_add;
两只舵机镜像安装,所以一个加、一个减,高度每加 1 mm,舵机位置变 8.4 步。leg_position_add 来自 IMU 的横滚角,两边减同一个数,镜像之后就成了一条腿伸、一条腿缩,机身被找平。位置最后还会被夹在 2110 到 2510、1586 到 1986 之间,防止舵机拧过头。
跳跃藏在 Jump 按钮里。按下发 jump,松开发 stop,固件等的就是这一下松手。检测到之后,舵机的速度和加速度参数都写成 0,腿直接伸到 80 mm 对应的位置;大约 30 个循环后收回 40 mm,计数到 200 才允许下一跳。跳跃这段时间,平衡控制只保留角度和角速度。

连上热点,在浏览器里开
ESP32 自己开热点、起网页服务器。页面就写在 basic_web.h 里,跟着固件存在 Flash 中;控制指令通过 81 端口的 WebSocket,以 JSON 发回机器人。
上电流程按 README 走。
- 电池的 XH2.54 插头插到主控板后侧接口,拨开关,红灯亮表示电源接通。
- 两只轮子依次微动,这是在做 FOC 初始化,随后腿开始动。
- 电量充足时,主控板上的蓝灯会亮;初始化结束还没亮,先去充电。
- 按一下主控板上的 EN 键重启。
- 手机或电脑连上机器人的热点,浏览器打开
192.168.1.11。 - 用手把机器人扶正,轮子轻轻碰到地面,勾选 Robot Go!,它就自己站住了,之后用摇杆控制。
初始化时代码还会校准陀螺仪零偏,这段时间让它静止放着更稳妥。

热点名和密码有个小出入。README 写的是以 WL 开头、密码为小写的 WiFi 名;仓库里的 wifi.cpp 写的是下面这两行。自己编译烧录的话,以代码为准。
const char *AP_SSID = "WLROBOT";
const char *AP_PSW = "12345678";
代码里也写了连家里路由器的 STA 模式,但 setup() 里那行 set_sta_wifi() 是注释掉的,默认只开热点。想切过去,要改 setup(),填好 sta_ssid 和 sta_password,再从串口看分到的 IP。网页的 WebSocket 地址取的是当前页面的主机名,两种模式都能连上。
页面上的控件,比固件用到的多一些。
| 页面控件 | 发出的字段 | 固件现在怎么用 |
|---|---|---|
| Robot Go! | stable |
关掉时两轮力矩清零 |
| 摇杆 | joy_x、joy_y |
前后走和转向 |
| BaseHeight 滑块,32–85 mm | height |
换算成腿部舵机位置 |
| Jump 按钮 | dir |
按下再松开,触发跳跃 |
| Forward、Back、Left、Right | dir |
解析了,主循环没有使用 |
| Roll、LinearVel、AngularVel 滑块 | roll、linear、angular |
解析了,主循环没有使用 |
所以别对着 Forward 按钮较劲,走路请用摇杆。反过来看,这几个空着的字段也是现成的接口,想加功能,从这里接进去最省事。
我的这一台
开工前,我在笔记里开了一页「轮足机器人开发」。原理图和 PCB 在嘉立创 EDA 里过了一遍,结构模型也在三维软件里看过装配关系,然后才打板、打印零件、焊接。焊板子的时候,放大镜也用上了。
代码我没用 Arduino IDE,放进了 PlatformIO 的 esp32dev 环境里编。视频里那一次编译用时 29.32 秒,RAM 用了 15.1%(49608 字节),Flash 用了 67.5%(885089 字节)。中途会刷出一串 _round 重定义的提示,SimpleFOC 和 ESP32 的 Arduino 核心各定义了一次,最后照样生成了固件。
依赖库按 README 的说法,WebSockets 用仓库 3.Software/libraries 里带的那份,其他库可以直接装官方版本。

装好以后,它在桌垫上立住了,也在地砖上开着跑了一段。

许可这件事也说清楚。仓库里的代码文件头带着 MIT 许可声明,版权写的是 Mu Shibo;仓库根目录没有单独的 LICENSE 文件,结构模型和 PCB 也没看到另附的许可说明。自己学习复刻之外的用途,最好先问原作者一声。
回到键盘前面
墨水屏安安静静守着一张图,问号砖等着人去摇。轮足机器人站在旁边,靠轮子一刻不停地小幅修正,才没有倒下。
三个小家伙里,它最不省心,也最像活的。
脑洞是世博同学的,这一台站起来的,是我焊出来的。
参考资料
鸿尘客 Honchic · 把脑洞焊到电路板上