- 物联网
- 嵌入式
- 驱动开发
- 硬件开发
【免费下载链接】esp-iot-solution
Espressif IoT Library. IoT Device Drivers, Documentations and Solutions.
FOC Knob 是 ESP-IoT-Solution 中基于磁场定向控制(FOC)电机实现"触觉反馈旋钮"交互的组件,它通过无刷直流(BLDC)电机输出阻尼、卡位(detent)、限位等可编程力矩,让传统旋钮具备多种手感模式。本文以 foc_knob 组件文档 为核心骨架,结合组件源码与 FOC_knob_example 完整示例,带你掌握 7 种默认旋钮模式、参数定制方法、完整 API 调用方式以及基于 ESP32-S3-Motor-LCDkit 的从零构建流程。
FOC Knob 是什么
FOC Knob 是一个提供丰富 API 的组件,允许开发者定义多种工作模式(Modes of Operation),并灵活选用不同类型的 BLDC 电机,来实现具有触觉反馈(Haptic Feedback)的旋钮。其核心思想是:用电机代替机械阻尼结构——通过实时读取电机轴角度与速度,计算当前应施加的力矩,让旋钮在不同角度区间表现出"无阻尼顺滑"、"细密卡位"、"强卡位"、"两端限位"或"开关式"等不同手感。
组件默认模式定义在foc_knob_default.c文件中,用户可以直接使用这些默认模式,也可以按需修改参数以获得不同响应。组件的公开接口声明位于 foc_knob.h,C 语言实现位于 foc_knob.c。
支持的旋钮模式
默认情况下,组件在 foc_knob_default.c 中预定义了 7 种模式,对应模式编号与含义如下:
| Mode | 描述 |
|---|---|
| 0. unbound no detents | 无边界,细顺滑旋转,无卡位点 |
| 1. Unbounded Fine detents | 无边界,细顺滑旋转,细密卡位点 |
| 2. Unbounded Strong detents | 无边界,细顺滑旋转,明显的卡位点 |
| 3. Bounded No detents | 限定在特定范围(0-360)内,细顺滑旋转,无卡位点 |
| 4. Bounded Fine detents | 限定范围内,较粗的旋转,细卡位点 |
| 5. Bounded Strong detents | 限定范围内,较粗的旋转,强卡位点 |
| 6. On/Off strong detents | 限定范围内,带明显停止点的 On/Off 开关式功能 |
模式编号的枚举定义在 foc_knob_default.h 中,从MOTOR_UNBOUND_NO_DETENTS到MOTOR_ON_OFF_STRONG_DETENTS,并以MOTOR_MAX_MODES作为边界哨兵。
参数结构与自定义模式
每种模式都由一组参数描述。用户可以在下面的结构体中修改数值,以按需定制旋钮功能(该结构体定义见 foc_knob.h):
typedef struct { int32_t num_positions; // 位置数量 int32_t position; // 当前位置 float position_width_radians; // 每个位置的宽度(弧度) float detent_strength_unit; // 正常旋转时的卡位强度 float endstop_strength_unit; // 到达端点时的卡位强度 float snap_point; // 每个位置的吸附点 const char *descriptor; // 描述 } foc_knob_param_t;各字段的语义与用法如下:
num_positions:位置总数。取0或负值表示"无边界"(unbounded)模式,电机可无限旋转;取正值表示"有边界"(bounded)模式,旋钮被限制在0 ~ num_positions-1个位置区间内。position:该模式的初始位置(index)。position_width_radians:单个位置对应的角度宽度,单位为弧度。该值直接决定每个卡位之间的角度间距,例如5 * PI / 180表示每 5° 一个卡位。detent_strength_unit:正常旋转过程中,将旋钮"拉回"卡位中心所施加的力矩强度(与 P 系数成正比,见下文力矩计算)。endstop_strength_unit:到达边界时阻止继续旋转的力矩强度。snap_point:吸附点阈值系数,决定角度偏离卡位中心多大比例时会"跳"到下一个卡位。descriptor:人类可读的描述字符串,可通过foc_knob_get_state()读取。
默认模式的参数取值
foc_knob_default.c 中 7 种默认模式的完整参数如下,可作为自定义模式的参考基线:
static const foc_knob_param_t unbound_no_detents[] = { {0, 0, 1 * PI / 180, 1, 0, 1.1, "Unbounded\nNo detents"}, }; static const foc_knob_param_t unbound_fine_detents[] = { {0, 0, 5 * PI / 180, 0.8, 0, 1.1, "Unbounded\nFine detents"}, }; static const foc_knob_param_t unbound_coarse_detents[] = { {0, 0, 10 * PI / 180, 1.2, 0, 1.1, "Unbounded\nStrong detents"}, }; static const foc_knob_param_t bound_no_detents[] = { {360, 180, 1 * PI / 180, 0.3, 1, 1.1, "Bounded\nNo detents"}, }; static const foc_knob_param_t bound_fine_dents[] = { {32, 0, 5 * PI / 180, 0.6, 1, 1.1, "Bounded\nFine detents"}, }; static const foc_knob_param_t bound_coarse_dents[] = { {13, 0, 10 * PI / 180, 1.1, 1, 0.55, "Bounded\nStrong detents"}, }; static const foc_knob_param_t on_off_strong_dents[] = { { 2, 0, 60 * PI / 180, 1, 1, 0.55, "On/Off strong detents"}, };这些参数数组通过default_foc_knob_param_lst[]指针数组与模式编号一一对应,DEFAULT_PARAM_LIST_NUM由数组大小推导得出(见 foc_knob_default.c)。从默认值可以观察到设计规律:
- 无边界模式
num_positions = 0,因此不存在限位;有边界模式则根据position_width_radians反推位置数(如 360 位置 × 1°/位)。 endstop_strength_unit只在有边界模式中非零,作为边界"软墙"强度。- 强卡位模式(如
bound_coarse_dents)的snap_point更低(0.55),吸附更灵敏,手感更干脆。
组件 API 详解
组件的完整 API 均声明在 foc_knob.h,核心接口如下。
创建与销毁
foc_knob_handle_t foc_knob_create(const foc_knob_config_t *config); esp_err_t foc_knob_delete(foc_knob_handle_t handle);foc_knob_create()要求传入非空的config且必须提供 PID 回调函数pid_cb,否则返回 NULL(见 foc_knob.c)。配置结构体为:
typedef struct { foc_knob_param_t const **param_lists; /*!< 模式参数列表,若为 NULL 则使用默认列表 */ uint16_t param_list_num; /*!< 模式数量 */ float max_torque_out_limit; /*!< 最大输出力矩限制(越界时) */ float max_torque; /*!< 最大力矩限制 */ foc_knob_pid_cb_t pid_cb; /*!< PID 回调函数 */ } foc_knob_config_t;当param_lists为 NULL 时,组件会打印日志并自动使用default_foc_knob_param_lst与DEFAULT_PARAM_LIST_NUM。内部会为每个模式分别分配一份位置记录(position[]),并创建互斥量保证线程安全。
运行与模式切换
float foc_knob_run(foc_knob_handle_t handle, float shaft_velocity, float shaft_angle); esp_err_t foc_knob_change_mode(foc_knob_handle_t handle, uint16_t mode);foc_knob_run()是每个控制周期必须调用一次的核心函数,输入当前轴速度(rad/s)与轴角度(rad),返回应施加到电机上的力矩(torque)。示例应用在motor_task中以约 1 ms 周期调用(见 main.cpp)。foc_knob_change_mode()用于运行中动态切换模式;若mode超出模式数量或对应模式参数为 NULL,返回ESP_ERR_INVALID_ARG。
事件回调
组件通过事件机制通知应用层"发生了什么",事件枚举与注册接口如下:
typedef enum { FOC_KNOB_INC = 0, /*!< 位置增加 */ FOC_KNOB_DEC, /*!< 位置减少 */ FOC_KNOB_H_LIM, /*!< 计数达到最大上限 */ FOC_KNOB_L_LIM, /*!< 计数达到最小下限 */ FOC_KNOB_ANGLE_CHANGE, /*!< 角度变化 */ FOC_KNOB_EVENT_MAX, /*!< 事件总数(哨兵) */ } foc_knob_event_t; esp_err_t foc_knob_register_cb(foc_knob_handle_t handle, foc_knob_event_t event, foc_knob_cb_t cb, void *usr_data); esp_err_t foc_knob_unregister_cb(foc_knob_handle_t handle, foc_knob_event_t event);回调类型为void (*)(void *foc_knob_handle, void *user_data)。示例中通过foc_knob_inc_cb/foc_knob_dec_cb读取状态、foc_knob_h_lim_cb/foc_knob_l_lim_cb打印到达边界信息(见 main.cpp)。
状态与位置查询
esp_err_t foc_knob_get_state(foc_knob_handle_t handle, foc_knob_state_t *state); esp_err_t foc_knob_get_event(foc_knob_handle_t handle, foc_knob_event_t *event); esp_err_t foc_knob_get_current_mode_position(foc_knob_handle_t handle, int32_t *position); esp_err_t foc_knob_set_currect_mode_position(foc_knob_handle_t handle, int32_t position); esp_err_t foc_knob_get_position(foc_knob_handle_t handle, uint16_t mode, int32_t *position); esp_err_t foc_knob_set_position(foc_knob_handle_t handle, uint16_t mode, int32_t position);其中foc_knob_state_t是对当前模式的实时快照,包含angle_to_detent_center(当前角度与卡位中心的偏差)、position_width_radians、num_positions、position与descriptor。注意set_position系列接口会同时将角度偏差清零并重置中心校准标志center_adjusted,使旋钮从新位置重新校准(见 foc_knob.c)。
底层力矩计算原理
foc_knob_run()的内部流程(见 foc_knob.c)可分解为五个关键步骤,理解这些步骤有助于调出自定义手感:
- 中心校准:首次运行(或模式切换后)把当前角度记录为卡位中心
current_detent_center。 - 静止漂移修正:通过对轴速度做 EWMA(指数加权移动平均)滤波判断旋钮是否静止;静止超过 500 ms 且角度偏差小于 5° 时,缓慢地把卡位中心向当前角度靠拢,避免传感器零点漂移导致的手感偏移。
- 吸附点(Snap Point)判定:当角度偏差超过
position_width_radians * snap_point时,判定越过卡位边界,位置position相应增减并触发FOC_KNOB_DEC/FOC_KNOB_INC事件;到达边界位置时额外触发FOC_KNOB_L_LIM/FOC_KNOB_H_LIM。 - 死区处理:将角度偏差钳制在卡位宽度 20%(且不超过 1°)的死区内,避免在卡位中心附近因传感器噪声产生抖动。
- PID 力矩输出:通过用户提供的
pid_cb(P, D, limit, error)计算最终力矩。其中越界时P使用endstop_strength_unit * 4、限制为max_torque_out_limit,否则使用detent_strength_unit * 4、限制为max_torque;D系数则按卡位宽度在 3°~8° 之间做分段线性缩放——细卡位需要较大 D 系数以产生清晰的"咔哒"感,粗卡位则降低 D 系数以避免放大力矩噪声。当轴速度超过CONFIG_FOC_KNOB_MAX_VELOCITY时直接输出 0 力矩,防止高速旋转时力矩失控。
示例应用中的motor_pid_cb把 P/D/limit 直接注入 SimpleFOC 的motor.PID_velocity并返回其输出(见 main.cpp),实现"力矩环由 FOC Knob 组件调控、FOC 底层由 SimpleFOC 驱动"的分层架构。
硬件要求
本示例专为ESP32-S3-Motor-LCDkit开发板设计,配套硬件如下:
- BLDC 电机:兼容 5V 2804 型号无刷电机(示例使用 7 对极电机,见 main.cpp)。
- 位置传感器:适合使用基于霍尔效应的传感器,如 MT6701 或 AS5600。示例通过 SPI 接口连接 MT6701(SCLK=GPIO12、MISO=GPIO13、CS=GPIO11,见 main.cpp)。
示例还使用按键(GPIO0)切换模式:每次按下,模式从MOTOR_UNBOUND_NO_DETENTS循环递增到下一个模式,并触发一次短暂的"电机抖动"反馈(motor_shake_func),见 main.cpp。
从零构建与烧录
以 ESP32-S3 为目标芯片,完整构建流程如下(详见 示例 README):
第 1 步:设置目标芯片
idf.py set-target esp32s3第 2 步:编译工程
idf.py build第 3 步:烧录并启动 IDF Monitor
idf.py flash monitor工程默认配置位于 sdkconfig.defaults,已启用 240 MHz CPU 主频并将 FreeRTOS tick 设为 1000 Hz(1 ms 节拍),与motor_task的 1 ms 控制周期相匹配。示例应用同时依赖esp-simplefoc(电机 FOC 驱动)与iot_button(模式切换按键)两个组件,可在 main/CMakeLists.txt 与组件清单中查看依赖关系。
可调配置项
组件通过 Kconfig 提供一项全局配置(见 Kconfig):
CONFIG_FOC_KNOB_MAX_VELOCITY取值范围1 ~ 100,默认60。该值表示旋钮的最大允许轴速度(rad/s),超过该速度时组件输出零力矩。Kconfig 帮助信息特别提示:最大速度设得越小,旋钮被甩动后的回弹速度也越小,手感更柔和,适合调整旋钮的"惯性"表现。可通过idf.py menuconfig进入 "FOC Knob" 菜单修改。
小结
FOC Knob 组件把"旋钮手感"抽象成了foc_knob_param_t参数与 7 种内置模式的组合:num_positions决定有界/无界,position_width_radians决定卡位密度,detent_strength_unit与endstop_strength_unit分别决定旋转手感和边界硬度,snap_point决定吸附灵敏度。借助foc_knob_run()的周期调用与事件回调机制,开发者无需关心 FOC 底层细节,即可在 ESP32-S3 上快速实现音量旋钮、菜单选择器、温控拨盘、开关切换等各类触觉反馈交互界面。如需进一步理解实现细节,建议通读 foc_knob.c 的力矩计算主循环,并结合 main.cpp 观察完整的应用集成方式。
- 物联网
- 嵌入式
- 驱动开发
- 硬件开发
【免费下载链接】esp-iot-solution
Espressif IoT Library. IoT Device Drivers, Documentations and Solutions.
相关推荐
IOPaint 低内存模式实战:4GB 显存也能流畅跑 Stable Diffusion
IOPaint 低内存模式实战:4GB 显存也能流畅跑 Stable Diffusion 深夜赶工,一台只有 4GB 显存的老笔记本上打开 IOPaint,点下
人工智能AI 应用计算机视觉图像处理媒体生成后端ESP32-S3 FOC 触觉旋钮示例解析:基于 esp-iot-solution 的 foc_knob_example 实现无刷电机旋钮交互
ESP32 S3 FOC 触觉旋钮示例解析:基于 esp iot solution 的 foc_knob_example 实现无刷电机旋钮交互 导读 foc_k
物联网嵌入式驱动开发硬件开发【亲测免费】 X-Knob 力反馈智能旋钮项目搭建指南
X Knob 力反馈智能旋钮项目搭建指南 目录 1. 项目的目录结构及介绍 Directory Structure Overview 该项目遵循清晰的组织架构来
智能硬件嵌入式智能家居硬件开发
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考