☰
把档位写进代码:SmartKnob 开源触觉反馈旋钮完整上手指南
2026/10/30 2:22:54 网站建设 项目流程

把档位写进代码:SmartKnob 开源触觉反馈旋钮完整上手指南

【免费下载链接】smartknobHaptic input knob with software-defined endstops and virtual detents项目地址: https://gitcode.com/gh_mirrors/smar/smartknob

SmartKnob 是一个软件定义止动点(software-defined endstops)与虚拟凹轮(virtual detents)的开源智能旋钮项目:你按到的每一格卡位、每一端阻力、每一次按压回弹,都可以通过代码在运行时重新定义。本文带你走完硬件选型、固件烧录、串口连接全流程,并给出可以直接抄的调参起点。

它解决什么问题:旋钮手感不再是物理定死的

普通旋钮的"手感"由机械结构决定:齿轮数、弹簧力矩、限位塑料块,出厂即固定。而 SmartKnob 把手感变成了一组可写的参数。

原理一句话:磁编码器实时读出轴的角度,PID 控制器根据角度算出该施加多少反向扭矩,电机(无刷云台 BLDC)通过 FOC 力矩控制把扭矩"顶"回你的手指。于是:

  • 旋钮有多少格 → 由min_position/max_position决定
  • 每格多宽 → 由position_width_radians决定
  • 每格"咔哒"多强 → 由detent_strength_unit决定
  • 两端多硬 → 由endstop_strength_unit决定
  • 还能通过detent_positions只在部分档位"磁吸",其余位置顺滑通过

也就是说,同一颗旋钮,下一秒就可以从"10 格细档"变成"3 格粗档",或干脆变成无级旋转。这就是"软件定义触觉"。

五分钟跑起来:两条路径看到第一格卡位

结论先说:完整体验用View 板(自研 PCB,带 240x240 圆屏),快速上手可以用社区的NanoFOC DevKit++(选nanofoc编译环境即可,固件开箱可用)。

第一步:获取源码并编译

git clone https://gitcode.com/gh_mirrors/smar/smartknob cd smartknob pio run -e view # 或 pio run -e nanofoc pio run -e view -t upload

第二步:校准。上电后固件会要求一次电机校准,测量极对数、零位电角度偏移和转向,结果存为MotorCalibration持久配置。预期效果:校准完成后旋钮可被电机平稳托住,不再自由乱转。如何验证:用手轻拨旋钮,松开后它应缓慢回到最近的档位中心而不是停在原地。

第三步:连上你的电脑验证。项目自带 Python 示例,进入 software/python/ 后按目录内 Pipfile 配置环境,运行 simple_example.py。按下回车发送一份配置:

config.min_position = 0 config.max_position = 5 config.position_width_radians = math.radians(10) config.detent_strength_unit = 1

预期效果:旋钮立刻变成 6 档(0~5),每档占 10°,有明显的咔哒感。如何验证:拧动旋钮,终端会按current_position0→5 变化打印 State 消息,屏幕(如装配了 LCD)同步显示当前档位和文字。

⚠️ 官方 README 明确提示:这不是"即插即用"项目。View 板涉及小间距 SMD 焊接(推荐 stencil + 回流或热风),动手前建议先读一遍原理图。

硬件并不复杂,但选型有坑

结论先说:整套 BOM 不神秘,真正的坑只有一个——电机的齿槽转矩(cogging)。

核心部件一览:

部件型号关键参数在系统里的角色
电机32mm 转子空心轴云台 BLDC32mm 转子 / 15mm 总高 / 5.9mm 空心轴 / 内置径向磁铁力矩输出 + 自带编码器磁铁
磁编码器MT6701(MagnTek)SSI/I2C/ABZ,SSI 带 CRC角度测量,推荐
电机驱动TMC6300-LA2–11V 输入,1.2A RMS,3x3mm QFN六路 FOC PWM
主控ESP32-PICO-V3-02(Lilygo T-Micro32 Plus)921600 波特率串口运行固件
显示GC9A01 圆屏240x240 SPI实时显示参数
灯环SK6812-SIDE-A x8侧发光环绕旋钮

电机为什么这么讲究?你希望"关掉凹轮时旋钮完全顺滑",这就要求电机断电时也不该有位置吸附。但几乎所有便宜云台电机的转子在断电时都会"跳格"——这就是齿槽转矩。它会直接干扰甚至压过你的虚拟凹轮,尤其是低强度或细密档位。README 的 FAQ 直言:社区测过的便宜云台电机"基本全都不行",推荐的这颗(32mm 空心轴款)是当时唯一称得上"合格"的现货。

编码器三选一:

编码器评价主要问题
MT6701推荐,噪声小、SSI 带 CRC、响应快无低功耗模式,美国分销商难买
TLV493D常见,适合原型(有 breakout 板)噪声大需滤波;内部 ADC 有已知锁死缺陷,固件里专门写了检测与复位逻辑
AS5600便宜易买噪声更大,强磁体下需 8–10mm 气隙防饱和

PCB 下单注意两点(来自 electronics/ 目录下的 KiCad 工程):主控板用白色阻焊层(反射 RGB 灯环的光)、板厚1.2mm而非默认 1.6mm;屏幕板必须 1.2mm(丝印上的 0.6mm 是 VHB 胶厚度,别混淆)。

固件内部:一个虚拟凹轮是怎么算出来的

固件是 FreeRTOS 多任务架构,入口在 main.cpp:显示任务与串口接口任务跑在 core 0,电机控制任务独占 core 1,任务间用队列通信。

核心在 motor_task.cpp 的主循环,每 1ms 一次做四件事:

  1. loopFOC()完成磁场定向控制
  2. 检查命令队列(校准 / 新配置 / 触觉播放)
  3. 判断是否该切换到下一个档位——这是虚拟凹轮的灵魂:
if (angle_to_detent_center > snap_point_radians_decrease) { current_detent_center += config.position_width_radians; current_position--; }

旋钮偏出当前档位中心超过"吸合点"(snap_point,典型 0.5–1.5,构成迟滞),档位中心就向前平移一个position_width_radians,位置计数加一。之后 PID 的比例项以"离档位中心多远"为输入,输出把旋钮拉回中心的扭矩——卡位感就是这样凭空造出来的。

  1. 通过 protobuf 广播当前状态(current_position+sub_position_unit)

几个值得知道的设计细节:

  • 保护机制:转速超过 60 rad/s 时直接输出零扭矩,防止"越拧越紧"的正反馈失控
  • D 参数自适应:细档位用小 D 防止放大传感器噪声,粗档位放大 D 制造清脆"咔哒"
  • snap_point_bias:把吸合点偏离中心,做出"小心放会停住、稍用力弹回 0 位"的非对称手感
  • detent_positions:最多声明 5 个"磁吸档",配合滚动发送实现任意多档位的稀疏分布
  • 通信协议:串口 921600 波特率,protobuf 消息 + COBS 帧定界 + CRC32 校验,协议定义见 smartknob.proto;位置用press_nonce/position_nonce做去重与强制刷新

从剪辑时间线到音频混音:它接上软件后能干什么

结论:触觉旋钮的价值在于"用阻力传递信息",所以接入方式决定了体验上限。官方 README 里给了两个最有代表性的演示设想:

  • 视频剪辑时间线:jog 轮滚动时,你能"摸到"片段边界;切到播放速度模式后,旋钮带弹簧回位,在 1x/2x/4x 处吸附
  • 音频混音:给不同通道旋钮设置不同position_width_radians,物理上区分"粗调/细调"

浏览器端接入用 WebSerial,核心封装在 software/js/packages/smartknobjs-webserial/,示例应用见 example-webserial-basic:

const serialPort = await navigator.serial.requestPort({ filters: SmartKnobWebSerial.USB_DEVICE_FILTERS, });

预期效果:连接后调用sendConfig()修改任意参数,旋钮手感即时变化,界面实时收到 State 回显。如何验证:把detentStrengthUnit从 0.5 改成 0,手感应立刻从"10 格"变无级顺滑。

Node.js 端则有 smartknobjs-node,Python 端复用 smartknob_io.py,底层协议实现(COBS 解码、CRC32 校验、250ms 超时重传)都在这几包里,自写集成时值得通读一遍。

调参与避坑:最影响手感的 5 个参数

参数典型范围调大的效果起点建议
position_width_radians3°~30°/档每格更宽、P 项误差更大、手感更"钝"10°(math.radians(10))
detent_strength_unit0~1咔哒更硬,>1 可能不稳定1
endstop_strength_unit0~1两端更"顶手",0 则无阻力但位置仍受限1
snap_point0.5~1.5吸合更"晚",需要拧过更多角度才换档0.7~1.1
snap_point_bias0 起吸合点偏移,做出非对称"可复位"手感0

常见问题对照(来自 README 与源码注释):

  1. 旋转不平滑、有顿挫→ 先确认电机齿槽转矩是否超标;其次检查FOC_LPF低通参数,适当加大平滑
  2. 有可闻/可感的嗡鸣→ 通常是 PID 的 D 项放大了编码器噪声(TLV493D 上尤其明显),换 MT6701 或加滤波
  3. 凹轮切换不灵敏/跳档→snap_point至少 0.5(固件会拒绝更低值并打印日志),同时检查编码器安装气隙
  4. 电机发热→ 调低FOC_VOLTAGE_LIMIT与FOC_PID_LIMIT(默认 limit 为 3,越界时临时抬到 10)
  5. 换过电机/装配→ 重新执行校准,否则零位偏移与转向判断全部失效

🧰 一个实用技巧:固件基于 SimpleFOC,motor.monitor_downsample打开后可用 SimpleFOC Studio 通过串口实时改 PID 参数,比改代码重烧快得多。

资源地图:从这里继续

你想做什么去哪里
看总览、BOM、FAQREADME.md
改/读固件firmware/src/(电机:motor_task.cpp,显示:display_task.cpp,串口:serial/)
改通信协议proto/smartknob.proto + generate_protobuf.py 重新生成 nanopb
画 PCBelectronics/view_base/ 与 electronics/view_screen/(KiCad 工程,附 JLCPCB 导出脚本)
机械结构cad/README.md(Fusion 360 模型索引);需打印 Enclosure、Knob、ScreenPlatform、RotorSpacer、MountBase、BackPlate 共 6 件
写上位机software/python/、software/js/ 两套示例

社区与方向:项目作者在 README 里挂了 Discord 社区入口,遇到问题或想晒作品直接去那边。路线图包括迁移 ESP32-S3 模块(省内存跑 WiFi/MQTT)、显示层迁移 LVGL、Home Assistant 集成。软件/电子/文档为 Apache 2.0,硬件与机械结构为 CC BY 4.0(见 LICENSE.txt),商用二次分发需保留署名。

🎛️ 总结一句:SmartKnob 把"手感"从机械加工变成了软件配置。先跑通pio run -e view和 Python 示例,把五个参数在手上拧出直觉,剩下的集成工作——时间线、混音器、任意你自己的应用——都只是往队列里发一条 Config 消息的事。

【免费下载链接】smartknobHaptic input knob with software-defined endstops and virtual detents项目地址: https://gitcode.com/gh_mirrors/smar/smartknob

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询