/* * VMC虚拟模型控制器实现 * * 本文件实现了轮腿机器人的VMC (Virtual Model Control) 虚拟模型控制算法 * 主要功能包括: * 1. 五连杆机构的正逆运动学解算 * 2. 虚拟力到关节力矩的映射 * 3. 地面接触检测 * 4. 等效摆动杆模型转换 * * 参考文献: * - 韭菜的菜 知乎: 平衡步兵控制系统设计 * - 上交轮腿电控开源方案 (2023) */ #include "vmc.h" #include /* Private typedef ---------------------------------------------------------- */ /* Private define ----------------------------------------------------------- */ #define VMC_EPSILON (1e-6f) // 数值计算精度 #define VMC_MAX_ITER (10) // 最大迭代次数 /* Private macro ------------------------------------------------------------ */ /** * @brief 限制数值范围 */ #define VMC_CLAMP(val, min, max) ((val) < (min) ? (min) : ((val) > (max) ? (max) : (val))) /** * @brief 安全开方 */ #define VMC_SAFE_SQRT(x) (((x) > 0) ? sqrtf(x) : 0.0f) /* Private variables -------------------------------------------------------- */ /* Private function prototypes ---------------------------------------------- */ static int8_t VMC_ValidateParams(const VMC_Param_t *param); static void VMC_UpdateKinematics(VMC_t *vmc, float phi1, float phi4); static int8_t VMC_SolveClosedLoop(VMC_t *vmc); static float VMC_ComputeNumericDerivative(float current, float previous, float dt); /* Exported functions ------------------------------------------------------- */ /** * @brief 初始化VMC控制器 */ int8_t VMC_Init(VMC_t *vmc, const VMC_Param_t *param, float sample_freq) { if (vmc == NULL || param == NULL || sample_freq <= 0) { return -1; } // 复制参数 memcpy(&vmc->param, param, sizeof(VMC_Param_t)); // 设置控制周期 vmc->dt = 1.0f / sample_freq; // 重置状态 VMC_Reset(vmc); vmc->initialized = true; return 0; } /** * @brief VMC五连杆正解算 * * 通过髋关节角度和机体姿态,计算足端位置和等效摆动杆参数 * * 坐标系定义: * - x轴: 机体前进方向为正 * - y轴: 竖直向下为正 * - 角度: 顺时针为正 */ int8_t VMC_ForwardSolve(VMC_t *vmc, float phi1, float phi4, float body_pitch, float body_pitch_rate) { if (vmc == NULL || !vmc->initialized) { return -1; } body_pitch = -body_pitch; // 机体俯仰角取反 VMC_Leg_t *leg = &vmc->leg; // 保存历史值 leg->last_phi0 = leg->phi0; leg->last_L0 = leg->L0; leg->last_d_L0 = leg->d_L0; leg->last_d_theta = leg->d_theta; // 更新关节角度 leg->phi1 = phi1; leg->phi4 = phi4; // 更新运动学状态 VMC_UpdateKinematics(vmc, phi1, phi4); // 求解闭环运动学 if (VMC_SolveClosedLoop(vmc) != 0) { return -1; } // 计算足端坐标 leg->foot_x = leg->XC - vmc->param.hip_length / 2.0f; leg->foot_y = leg->YC; // 计算等效摆动杆参数 leg->L0 = VMC_SAFE_SQRT(leg->foot_x * leg->foot_x + leg->foot_y * leg->foot_y); leg->phi0 = atan2f(leg->foot_y, leg->foot_x); // 计算等效摆动杆角度(相对于机体坐标系) leg->alpha = VMC_PI_2 - leg->phi0; leg->theta = VMC_PI - (VMC_PI_2 + body_pitch - leg->phi0); // 角度归一化 VMC_ANGLE_NORMALIZE(leg->theta); VMC_ANGLE_NORMALIZE(leg->phi0); // 计算角速度和长度变化率 leg->d_phi0 = VMC_ComputeNumericDerivative(leg->phi0, leg->last_phi0, vmc->dt); leg->d_alpha = -leg->d_phi0; leg->d_theta = body_pitch_rate + leg->d_phi0; leg->d_L0 = VMC_ComputeNumericDerivative(leg->L0, leg->last_L0, vmc->dt); // 计算角加速度 leg->dd_theta = VMC_ComputeNumericDerivative(leg->d_theta, leg->last_d_theta, vmc->dt); return 0; } /** * @brief VMC五连杆逆解算(力矩分配) * * 根据期望的虚拟力和力矩,通过雅可比矩阵计算关节力矩 */ int8_t VMC_InverseSolve(VMC_t *vmc, float F_virtual, float T_virtual) { if (vmc == NULL || !vmc->initialized) { return -1; } // 保存虚拟力和力矩 vmc->leg.F_virtual = F_virtual; vmc->leg.T_virtual = -T_virtual; // 计算雅可比矩阵 if (VMC_ComputeJacobian(vmc) != 0) { return -1; } VMC_Leg_t *leg = &vmc->leg; // 通过雅可比转置计算关节力矩 // tau = J^T * F_virtual leg->tau_hip_rear = leg->J11 * vmc->leg.F_virtual + leg->J12 * vmc->leg.T_virtual; leg->tau_hip_front = leg->J21 * vmc->leg.F_virtual + leg->J22 * vmc->leg.T_virtual; return 0; } /** * @brief 地面接触检测 * * 基于虚拟力和腿部状态估计地面法向力 */ float VMC_GroundContactDetection(VMC_t *vmc) { if (vmc == NULL || !vmc->initialized) { return 0.0f; } VMC_Leg_t *leg = &vmc->leg; // 计算地面法向力 // Fn = F0*cos(theta) + Tp*sin(theta)/L0 + mg leg->Fn = leg->F_virtual * cosf(leg->theta) + leg->T_virtual * sinf(leg->theta) / leg->L0 + vmc->param.wheel_mass * 9.8f; // 添加轮子重力 // 地面接触判断 leg->is_ground_contact = (leg->Fn > 10.0f); // 10N阈值 return leg->Fn; } /** * @brief 获取足端位置 */ int8_t VMC_GetFootPosition(const VMC_t *vmc, float *x, float *y) { if (vmc == NULL || !vmc->initialized || x == NULL || y == NULL) { return -1; } *x = vmc->leg.foot_x; *y = vmc->leg.foot_y; return 0; } /** * @brief 获取等效摆动杆参数 */ int8_t VMC_GetVirtualLegState(const VMC_t *vmc, float *length, float *angle, float *d_length, float *d_angle) { if (vmc == NULL || !vmc->initialized) { return -1; } if (length) *length = vmc->leg.L0; if (angle) *angle = vmc->leg.theta; if (d_length) *d_length = vmc->leg.d_L0; if (d_angle) *d_angle = vmc->leg.d_theta; return 0; } /** * @brief 获取关节输出力矩 */ int8_t VMC_GetJointTorques(const VMC_t *vmc, float *tau_front, float *tau_rear) { if (vmc == NULL || !vmc->initialized || tau_front == NULL || tau_rear == NULL) { return -1; } *tau_front = vmc->leg.tau_hip_front; *tau_rear = vmc->leg.tau_hip_rear; return 0; } /** * @brief 重置VMC控制器状态 */ void VMC_Reset(VMC_t *vmc) { if (vmc == NULL) { return; } // 清零腿部状态 memset(&vmc->leg, 0, sizeof(VMC_Leg_t)); // 设置初始值 vmc->leg.L0 = 0.15f; // 默认腿长15cm vmc->leg.theta = 0.0f; } /** * @brief 设置虚拟力和力矩 */ void VMC_SetVirtualForces(VMC_t *vmc, float F_virtual, float T_virtual) { if (vmc == NULL || !vmc->initialized) { return; } vmc->leg.F_virtual = F_virtual; vmc->leg.T_virtual = T_virtual; } /** * @brief 计算雅可比矩阵 * * 根据当前关节配置计算从虚拟力到关节力矩的雅可比矩阵 */ int8_t VMC_ComputeJacobian(VMC_t *vmc) { if (vmc == NULL || !vmc->initialized) { return -1; } VMC_Leg_t *leg = &vmc->leg; // 检查分母不为零 float sin_diff = sinf(leg->phi3 - leg->phi2); if (fabsf(sin_diff) < VMC_EPSILON) { return -1; // 奇异配置 } // 计算雅可比矩阵元素 // J11: 后髋关节到支撑力的雅可比 leg->J11 = (vmc->param.leg_1 * sinf(leg->phi0 - leg->phi3) * sinf(leg->phi1 - leg->phi2)) / sin_diff; // J12: 后髋关节到摆动力矩的雅可比 leg->J12 = (vmc->param.leg_1 * cosf(leg->phi0 - leg->phi3) * sinf(leg->phi1 - leg->phi2)) / (leg->L0 * sin_diff); // J21: 前髋关节到支撑力的雅可比 leg->J21 = (vmc->param.leg_4 * sinf(leg->phi0 - leg->phi2) * sinf(leg->phi3 - leg->phi4)) / sin_diff; // J22: 前髋关节到摆动力矩的雅可比 leg->J22 = (vmc->param.leg_4 * cosf(leg->phi0 - leg->phi2) * sinf(leg->phi3 - leg->phi4)) / (leg->L0 * sin_diff); return 0; } /* Private functions -------------------------------------------------------- */ /** * @brief 验证VMC参数有效性 */ static int8_t VMC_ValidateParams(const VMC_Param_t *param) { if (param->hip_length <= 0 || param->leg_1 <= 0 || param->leg_2 <= 0 || param->leg_3 <= 0 || param->leg_4 <= 0 || param->wheel_radius <= 0) { return -1; } // 检查腿部几何约束 if (param->leg_2 + param->leg_3 <= param->leg_1 + param->leg_4) { return -1; // 不满足闭环几何约束 } return 0; } /** * @brief 更新基本运动学参数 */ static void VMC_UpdateKinematics(VMC_t *vmc, float phi1, float phi4) { VMC_Leg_t *leg = &vmc->leg; // 计算关键点坐标 // 点B (后髋关节末端) leg->XB = vmc->param.leg_1 * cosf(phi1); leg->YB = vmc->param.leg_1 * sinf(phi1); // 点D (前髋关节末端) leg->XD = vmc->param.hip_length + vmc->param.leg_4 * cosf(phi4); leg->YD = vmc->param.leg_4 * sinf(phi4); // 计算BD连杆长度 float dx = leg->XD - leg->XB; float dy = leg->YD - leg->YB; leg->lBD = VMC_SAFE_SQRT(dx * dx + dy * dy); } /** * @brief 求解闭环运动学方程 * * 根据两个髋关节角度,求解中间关节角度 */ static int8_t VMC_SolveClosedLoop(VMC_t *vmc) { VMC_Leg_t *leg = &vmc->leg; // 使用余弦定理求解phi2 leg->A0 = 2 * vmc->param.leg_2 * (leg->XD - leg->XB); leg->B0 = 2 * vmc->param.leg_2 * (leg->YD - leg->YB); leg->C0 = vmc->param.leg_2 * vmc->param.leg_2 + leg->lBD * leg->lBD - vmc->param.leg_3 * vmc->param.leg_3; // 检查判别式 float discriminant = leg->A0 * leg->A0 + leg->B0 * leg->B0 - leg->C0 * leg->C0; if (discriminant < 0) { return -1; // 无解 } float sqrt_discriminant = VMC_SAFE_SQRT(discriminant); // 计算phi2 (选择合适的解) leg->phi2 = 2 * atan2f(leg->B0 + sqrt_discriminant, leg->A0 + leg->C0); // 计算phi3 leg->phi3 = atan2f(leg->YB - leg->YD + vmc->param.leg_2 * sinf(leg->phi2), leg->XB - leg->XD + vmc->param.leg_2 * cosf(leg->phi2)); // 计算足端坐标点C leg->XC = leg->XB + vmc->param.leg_2 * cosf(leg->phi2); leg->YC = leg->YB + vmc->param.leg_2 * sinf(leg->phi2); return 0; } /** * @brief 计算数值微分 */ static float VMC_ComputeNumericDerivative(float current, float previous, float dt) { if (dt <= 0) { return 0.0f; } return (current - previous) / dt; }