mirror of
https://github.com/m5stack/StackChan.git
synced 2026-04-28 03:22:39 +00:00
5001b7081b
* add firmware source code
150 lines
3.9 KiB
C++
150 lines
3.9 KiB
C++
/*
|
|
* SPDX-FileCopyrightText: 2026 M5Stack Technology CO LTD
|
|
*
|
|
* SPDX-License-Identifier: MIT
|
|
*/
|
|
#include "servo.h"
|
|
#include <hal/hal.h>
|
|
|
|
using namespace uitk;
|
|
|
|
namespace stackchan::motion {
|
|
|
|
static SpringOptions_t _default_spring_options = {
|
|
.stiffness = 170.0,
|
|
.damping = 26.0,
|
|
|
|
.mass = 1.0,
|
|
.velocity = 0.0,
|
|
|
|
.restSpeed = 0.1,
|
|
.restDelta = 0.1,
|
|
|
|
.duration = 0.0,
|
|
.bounce = 0.0,
|
|
.visualDuration = 0.0,
|
|
};
|
|
|
|
void Servo::init()
|
|
{
|
|
apply_default_spring_options();
|
|
|
|
_angle_anim.teleport(getCurrentAngle());
|
|
update();
|
|
|
|
setTorqueEnabled(false);
|
|
}
|
|
|
|
void Servo::update()
|
|
{
|
|
// Update at 50Hz
|
|
if (GetHAL().millis() - _last_tick < 20) {
|
|
return;
|
|
}
|
|
_last_tick = GetHAL().millis();
|
|
|
|
// Apply animation
|
|
if (!_angle_anim.done()) {
|
|
_angle_anim.update();
|
|
set_angle_impl(static_cast<int>(_angle_anim.directValue()));
|
|
}
|
|
|
|
// Snap to target angle when animation ends
|
|
else if (_snap_to_target_on_rest) {
|
|
_snap_to_target_on_rest = false;
|
|
set_angle_impl(_angle_anim.end);
|
|
}
|
|
|
|
// Auto release torque on rest
|
|
else if (_auto_torque_release_enabled && !isMoving()) {
|
|
if (GetHAL().millis() - _last_torque_check_tick > 200) {
|
|
if (getTorqueEnabled()) {
|
|
setTorqueEnabled(false);
|
|
}
|
|
_last_torque_check_tick = GetHAL().millis();
|
|
}
|
|
}
|
|
}
|
|
|
|
void Servo::move(int angle)
|
|
{
|
|
apply_default_spring_options();
|
|
update_angle_anim_target(angle);
|
|
}
|
|
|
|
void Servo::moveWithSpringParams(int angle, float stiffness, float damping)
|
|
{
|
|
_angle_anim.springOptions().visualDuration = 0.0f; // Disable timing override
|
|
_angle_anim.springOptions().stiffness = stiffness;
|
|
_angle_anim.springOptions().damping = damping;
|
|
|
|
update_angle_anim_target(angle);
|
|
}
|
|
|
|
void Servo::moveWithSpeed(int angle, int speed)
|
|
{
|
|
auto spring_options = map_speed_to_spring_options(speed);
|
|
moveWithSpringParams(angle, spring_options.stiffness, spring_options.damping);
|
|
}
|
|
|
|
int Servo::getCurrentAngle()
|
|
{
|
|
return _angle_anim.directValue();
|
|
}
|
|
|
|
void Servo::apply_default_spring_options()
|
|
{
|
|
auto& options = _angle_anim.springOptions();
|
|
options.visualDuration = 0.0f; // Disable timing override
|
|
options.stiffness = _default_spring_options.stiffness;
|
|
options.damping = _default_spring_options.damping;
|
|
}
|
|
|
|
void Servo::update_angle_anim_target(int angle)
|
|
{
|
|
if (_auto_angle_sync_enabled) {
|
|
_angle_anim.teleport(getCurrentAngle()); // Use current angle as start
|
|
}
|
|
_angle_anim = angle; // Apply new target
|
|
_snap_to_target_on_rest = true;
|
|
}
|
|
|
|
uitk::SpringOptions_t Servo::map_speed_to_spring_options(int speed)
|
|
{
|
|
speed = uitk::clamp(speed, 0, 1000);
|
|
|
|
// 1. 计算 Stiffness (刚度)
|
|
// 使用二次方映射: k = k_min + (speed/1000)^2 * k_range
|
|
// 当 speed=500 时,k 约为 10 + 0.25 * 640 = 170
|
|
float k_min = 10.0f;
|
|
float k_max = 650.0f;
|
|
float normalizedSpeed = speed / 1000.0f;
|
|
float stiffness = k_min + (normalizedSpeed * normalizedSpeed) * (k_max - k_min);
|
|
|
|
// 2. 计算 Damping (阻尼)
|
|
// 为了保持临界阻尼(无过冲,最快稳定),公式为 d = 2 * sqrt(m * k)
|
|
// 如果想要带一点点弹性感(bounce),可以将系数从 2.0 降到 1.5~1.8
|
|
float mass = 1.0f;
|
|
float damping = 2.0f * sqrtf(mass * stiffness);
|
|
|
|
// 3. 构造选项
|
|
uitk::SpringOptions_t options = _default_spring_options;
|
|
options.stiffness = stiffness;
|
|
options.damping = damping;
|
|
options.mass = mass;
|
|
|
|
// 4. 动态调整静止阈值
|
|
// 高速时阈值大一点可以防止由于离散计算导致的微小抖动
|
|
if (speed > 800) {
|
|
options.restDelta = 0.5f;
|
|
options.restSpeed = 0.5f;
|
|
} else {
|
|
options.restDelta = 0.1f;
|
|
options.restSpeed = 0.1f;
|
|
}
|
|
|
|
return options;
|
|
}
|
|
|
|
} // namespace stackchan::motion
|