平衡控制有了,但是一坨狗屎
This commit is contained in:
parent
07fd423f20
commit
318287223c
4
.vscode/settings.json
vendored
4
.vscode/settings.json
vendored
@ -8,6 +8,8 @@
|
|||||||
"kinematics.h": "c",
|
"kinematics.h": "c",
|
||||||
"bezier_curve.h": "c",
|
"bezier_curve.h": "c",
|
||||||
"queue": "c",
|
"queue": "c",
|
||||||
"stack": "c"
|
"stack": "c",
|
||||||
|
"buzzer.h": "c",
|
||||||
|
"user_task.h": "c"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -158,32 +158,22 @@
|
|||||||
<Ww>
|
<Ww>
|
||||||
<count>0</count>
|
<count>0</count>
|
||||||
<WinNumber>1</WinNumber>
|
<WinNumber>1</WinNumber>
|
||||||
<ItemText>dr16</ItemText>
|
<ItemText>chassis</ItemText>
|
||||||
</Ww>
|
</Ww>
|
||||||
<Ww>
|
<Ww>
|
||||||
<count>1</count>
|
<count>1</count>
|
||||||
<WinNumber>1</WinNumber>
|
<WinNumber>1</WinNumber>
|
||||||
<ItemText>chassis</ItemText>
|
<ItemText>go_feedback</ItemText>
|
||||||
</Ww>
|
</Ww>
|
||||||
<Ww>
|
<Ww>
|
||||||
<count>2</count>
|
<count>2</count>
|
||||||
<WinNumber>1</WinNumber>
|
<WinNumber>1</WinNumber>
|
||||||
<ItemText>n100</ItemText>
|
<ItemText>go</ItemText>
|
||||||
</Ww>
|
</Ww>
|
||||||
<Ww>
|
<Ww>
|
||||||
<count>3</count>
|
<count>3</count>
|
||||||
<WinNumber>1</WinNumber>
|
<WinNumber>1</WinNumber>
|
||||||
<ItemText>go_feedback</ItemText>
|
<ItemText>n100_cali</ItemText>
|
||||||
</Ww>
|
|
||||||
<Ww>
|
|
||||||
<count>4</count>
|
|
||||||
<WinNumber>1</WinNumber>
|
|
||||||
<ItemText>param_default</ItemText>
|
|
||||||
</Ww>
|
|
||||||
<Ww>
|
|
||||||
<count>5</count>
|
|
||||||
<WinNumber>1</WinNumber>
|
|
||||||
<ItemText>chassis_cmd</ItemText>
|
|
||||||
</Ww>
|
</Ww>
|
||||||
</WatchWindow1>
|
</WatchWindow1>
|
||||||
<Tracepoint>
|
<Tracepoint>
|
||||||
|
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#include <main.h>
|
#include <main.h>
|
||||||
#include <tim.h>
|
#include <tim.h>
|
||||||
|
#include "bsp/delay.h"
|
||||||
|
|
||||||
/* Private define ----------------------------------------------------------- */
|
/* Private define ----------------------------------------------------------- */
|
||||||
/* Private macro ------------------------------------------------------------ */
|
/* Private macro ------------------------------------------------------------ */
|
||||||
@ -10,6 +11,9 @@
|
|||||||
/* Private variables -------------------------------------------------------- */
|
/* Private variables -------------------------------------------------------- */
|
||||||
/* Private function -------------------------------------------------------- */
|
/* Private function -------------------------------------------------------- */
|
||||||
/* Exported functions ------------------------------------------------------- */
|
/* Exported functions ------------------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int8_t BSP_Buzzer_Start(void) {
|
int8_t BSP_Buzzer_Start(void) {
|
||||||
if (HAL_TIM_PWM_Start(&htim12, TIM_CHANNEL_2) == HAL_OK) return BSP_OK;
|
if (HAL_TIM_PWM_Start(&htim12, TIM_CHANNEL_2) == HAL_OK) return BSP_OK;
|
||||||
return BSP_ERR;
|
return BSP_ERR;
|
||||||
@ -30,6 +34,22 @@ int8_t BSP_Buzzer_Set(float freq, float duty_cycle) {
|
|||||||
return BSP_OK;
|
return BSP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int8_t BSP_Buzzer_Set_Note(BSP_Buzzer_Note_t note, float delay_sec) {
|
||||||
|
if (note <= 0 || delay_sec < 0.0f) return BSP_ERR;
|
||||||
|
|
||||||
|
uint32_t timer_clk = HAL_RCC_GetPCLK1Freq(); // 具体时钟频率请根据你的芯片和配置调整
|
||||||
|
uint32_t prescaler = htim12.Init.Prescaler + 1;
|
||||||
|
uint32_t period = (uint32_t)((float)timer_clk / (prescaler * note)) - 1;
|
||||||
|
|
||||||
|
__HAL_TIM_SET_AUTORELOAD(&htim12, period);
|
||||||
|
|
||||||
|
uint32_t pulse = (uint32_t)(0.5f * (float)(period + 1)); // 设置50%的占空比
|
||||||
|
__HAL_TIM_SET_COMPARE(&htim12, TIM_CHANNEL_2, pulse);
|
||||||
|
|
||||||
|
BSP_Delay((uint32_t)(delay_sec * 1000.0f)); // 延时指定秒数
|
||||||
|
return BSP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
int8_t BSP_Buzzer_Stop(void) {
|
int8_t BSP_Buzzer_Stop(void) {
|
||||||
if (HAL_TIM_PWM_Stop(&htim12, TIM_CHANNEL_2) == HAL_OK) return BSP_OK;
|
if (HAL_TIM_PWM_Stop(&htim12, TIM_CHANNEL_2) == HAL_OK) return BSP_OK;
|
||||||
return BSP_ERR;
|
return BSP_ERR;
|
||||||
|
@ -13,8 +13,43 @@ extern "C" {
|
|||||||
/* Exported macro ----------------------------------------------------------- */
|
/* Exported macro ----------------------------------------------------------- */
|
||||||
/* Exported types ----------------------------------------------------------- */
|
/* Exported types ----------------------------------------------------------- */
|
||||||
/* Exported functions prototypes -------------------------------------------- */
|
/* Exported functions prototypes -------------------------------------------- */
|
||||||
|
/*预设音符列表*/
|
||||||
|
typedef enum {
|
||||||
|
BSP_BUZZER_NOTE_C4 = 261, // C4音符频率
|
||||||
|
BSP_BUZZER_NOTE_D4 = 294, // D4音符频率
|
||||||
|
BSP_BUZZER_NOTE_E4 = 329, // E4音符频率
|
||||||
|
BSP_BUZZER_NOTE_F4 = 349, // F4音符频率
|
||||||
|
BSP_BUZZER_NOTE_G4 = 392, // G4音符频率
|
||||||
|
BSP_BUZZER_NOTE_A4 = 440, // A4音符频率
|
||||||
|
BSP_BUZZER_NOTE_B4 = 494, // B4音符频率
|
||||||
|
BSP_BUZZER_NOTE_C5 = 523, // C5音符频率
|
||||||
|
BSP_BUZZER_NOTE_D5 = 587, // D5音符频率
|
||||||
|
BSP_BUZZER_NOTE_E5 = 659, // E5音符频率
|
||||||
|
BSP_BUZZER_NOTE_F5 = 698, // F5音符频率
|
||||||
|
BSP_BUZZER_NOTE_G5 = 784, // G5音符频率
|
||||||
|
BSP_BUZZER_NOTE_A5 = 880, // A5音符频率
|
||||||
|
BSP_BUZZER_NOTE_B5 = 988, // B5音符频率
|
||||||
|
BSP_BUZZER_NOTE_C6 = 1047, // C6音符频率
|
||||||
|
BSP_BUZZER_NOTE_D6 = 1175, // D6音符频率
|
||||||
|
BSP_BUZZER_NOTE_E6 = 1319, // E6音符频率
|
||||||
|
BSP_BUZZER_NOTE_F6 = 1397, // F6音符频率
|
||||||
|
BSP_BUZZER_NOTE_G6 = 1568, // G6音符频率
|
||||||
|
BSP_BUZZER_NOTE_A6 = 1760, // A6音符频率
|
||||||
|
BSP_BUZZER_NOTE_B6 = 1976, // B6音符频率
|
||||||
|
BSP_BUZZER_NOTE_C7 = 2093, // C7音符频率
|
||||||
|
BSP_BUZZER_NOTE_D7 = 2349, // D7音符频率
|
||||||
|
BSP_BUZZER_NOTE_E7 = 2637, // E7音符频率
|
||||||
|
BSP_BUZZER_NOTE_F7 = 2794, // F7音符频率
|
||||||
|
BSP_BUZZER_NOTE_G7 = 3136, // G7音符频率
|
||||||
|
BSP_BUZZER_NOTE_A7 = 3520, // A7音符频率
|
||||||
|
BSP_BUZZER_NOTE_B7 = 3951, // B7音符频率
|
||||||
|
BSP_BUZZER_NOTE_C8 = 4186 // C8音符频率
|
||||||
|
} BSP_Buzzer_Note_t;
|
||||||
|
|
||||||
|
|
||||||
int8_t BSP_Buzzer_Start(void);
|
int8_t BSP_Buzzer_Start(void);
|
||||||
int8_t BSP_Buzzer_Set(float freq, float duty_cycle);
|
int8_t BSP_Buzzer_Set(float freq, float duty_cycle);
|
||||||
|
int8_t BSP_Buzzer_Set_Note(BSP_Buzzer_Note_t note, float delay_sec);
|
||||||
int8_t BSP_Buzzer_Stop(void);
|
int8_t BSP_Buzzer_Stop(void);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
@ -99,7 +99,7 @@ static void CMD_RcLogic(const CMD_RC_t *rc, CMD_t *cmd, float dt_sec) {
|
|||||||
cmd->chassis.ctrl_vec.vy = rc->ch_l_x;
|
cmd->chassis.ctrl_vec.vy = rc->ch_l_x;
|
||||||
cmd->chassis.ctrl_vec.wz = rc->ch_r_x;
|
cmd->chassis.ctrl_vec.wz = rc->ch_r_x;
|
||||||
cmd->chassis.delta_hight = rc->ch_res;
|
cmd->chassis.delta_hight = rc->ch_res;
|
||||||
|
cmd->chassis.delta_eulr.pit = rc->ch_r_y;
|
||||||
}
|
}
|
||||||
|
|
||||||
// /**
|
// /**
|
||||||
|
@ -38,6 +38,7 @@ typedef struct {
|
|||||||
CMD_ChassisAction_t action; /* 底盘动作 */
|
CMD_ChassisAction_t action; /* 底盘动作 */
|
||||||
float delta_hight; /* 底盘高度,单位:米 */
|
float delta_hight; /* 底盘高度,单位:米 */
|
||||||
MoveVector_t ctrl_vec; /* 底盘控制向量 */
|
MoveVector_t ctrl_vec; /* 底盘控制向量 */
|
||||||
|
AHRS_Eulr_t delta_eulr; /* 云台欧拉角变化量 */
|
||||||
} CMD_ChassisCmd_t;
|
} CMD_ChassisCmd_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
@ -49,31 +49,6 @@ int8_t Kinematics_RealOutput(const Kinematics_JointCMD_t *real, Kinematics_Joint
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Kinematics_ForwardKinematics(const float theta_in[3], const Kinematics_LinkLength_t *leg_params, float foot_out[3], Kinematics_Sign_t *sign) {
|
|
||||||
float l1 = leg_params->hip * sign->hip;
|
|
||||||
float l2 = -leg_params->thigh;
|
|
||||||
float l3 = -leg_params->calf;
|
|
||||||
|
|
||||||
float q1 = theta_in[0];
|
|
||||||
float q2 = theta_in[1] * sign->thigh;
|
|
||||||
float q3 = theta_in[2] * sign->calf;
|
|
||||||
|
|
||||||
float s1 = sinf(q1);
|
|
||||||
float s2 = sinf(q2);
|
|
||||||
float s3 = sinf(q3);
|
|
||||||
|
|
||||||
float c1 = cosf(q1);
|
|
||||||
float c2 = cosf(q2);
|
|
||||||
float c3 = cosf(q3);
|
|
||||||
|
|
||||||
float c23 = c2 * c3 - s2 * s3;
|
|
||||||
float s23 = s2 * c3 + c2 * s3;
|
|
||||||
|
|
||||||
foot_out[0] = l3 * s23 + l2 * s2;
|
|
||||||
foot_out[1] = -l3 * s1 * c23 + l1 * c1 - l2 * c2 * s1;
|
|
||||||
foot_out[2] = l3 * c1 * c23 + l1 * s1 + l2 * c1 * c2;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 单腿逆运动学
|
* @brief 单腿逆运动学
|
||||||
* @param foot_in 足端目标位置 [x, y, z](单位:米)
|
* @param foot_in 足端目标位置 [x, y, z](单位:米)
|
||||||
@ -110,3 +85,38 @@ int8_t Kinematics_InverseKinematics(const float foot_in[3], const Kinematics_Lin
|
|||||||
theta_out[2] = q3 * sign->calf;
|
theta_out[2] = q3 * sign->calf;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Kinematics_ForwardKinematics(const float theta_in[3], const Kinematics_LinkLength_t *leg_params, float foot_out[3], Kinematics_Sign_t *sign) {
|
||||||
|
float l1 = leg_params->hip * sign->hip;
|
||||||
|
float l2 = -leg_params->thigh;
|
||||||
|
float l3 = -leg_params->calf;
|
||||||
|
|
||||||
|
float q1 = theta_in[0];
|
||||||
|
float q2 = theta_in[1] * sign->thigh;
|
||||||
|
float q3 = theta_in[2] * sign->calf;
|
||||||
|
|
||||||
|
float s1 = sinf(q1);
|
||||||
|
float s2 = sinf(q2);
|
||||||
|
float s3 = sinf(q3);
|
||||||
|
|
||||||
|
float c1 = cosf(q1);
|
||||||
|
float c2 = cosf(q2);
|
||||||
|
float c3 = cosf(q3);
|
||||||
|
|
||||||
|
float c23 = c2 * c3 - s2 * s3;
|
||||||
|
float s23 = s2 * c3 + c2 * s3;
|
||||||
|
|
||||||
|
float px = l3 * s23 + l2 * s2;
|
||||||
|
float py = -l3 * s1 * c23 + l1 * c1 - l2 * c2 * s1;
|
||||||
|
float pz = l3 * c1 * c23 + l1 * s1 + l2 * c1 * c2;
|
||||||
|
|
||||||
|
// 配合逆运动学的符号处理
|
||||||
|
if (sign->hip != sign->thigh) {
|
||||||
|
foot_out[1] = py;
|
||||||
|
} else {
|
||||||
|
foot_out[1] = -py;
|
||||||
|
}
|
||||||
|
foot_out[0] = px;
|
||||||
|
foot_out[2] = pz;
|
||||||
|
}
|
@ -9,19 +9,45 @@
|
|||||||
|
|
||||||
#include "component/user_math.h"
|
#include "component/user_math.h"
|
||||||
|
|
||||||
int8_t Limit_ChassicOutput(const float feedback_pos ,float *out_pos, float max_speed, float max_angle, float min_angle){
|
int8_t Limit_ChassicOutput(const float feedback_pos ,float *out_pos,float *out_troque, float max_speed, float max_angle, float min_angle, float max_torque) {
|
||||||
|
|
||||||
// 限制位置变化速度
|
// 限制位置变化速度
|
||||||
if (*out_pos - feedback_pos > max_speed) {
|
// if (*out_pos - feedback_pos > max_speed) {
|
||||||
*out_pos = feedback_pos + max_speed;
|
// *out_pos = feedback_pos + max_speed;
|
||||||
} else if (*out_pos - feedback_pos < -max_speed) {
|
// } else if (*out_pos - feedback_pos < -max_speed) {
|
||||||
*out_pos = feedback_pos - max_speed;
|
// *out_pos = feedback_pos - max_speed;
|
||||||
|
// }
|
||||||
|
if (fabsf(*out_pos - feedback_pos) > max_speed) {
|
||||||
|
if (*out_pos > feedback_pos) {
|
||||||
|
*out_pos = feedback_pos + max_speed;
|
||||||
|
} else {
|
||||||
|
*out_pos = feedback_pos - max_speed;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 限制角度范围
|
// 限制角度范围
|
||||||
if (*out_pos > max_angle) {
|
if (*out_pos > max_angle) {
|
||||||
*out_pos = max_angle;
|
*out_pos = max_angle;
|
||||||
} else if (*out_pos < min_angle) {
|
} else if (*out_pos < min_angle) {
|
||||||
*out_pos = min_angle;
|
*out_pos = min_angle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*限制前馈力矩*/
|
||||||
|
// if (fabsf(*out_pos) > max_torque) {
|
||||||
|
// if (*out_pos > 0) {
|
||||||
|
// *out_pos = max_torque;
|
||||||
|
// } else {
|
||||||
|
// *out_pos = -max_torque;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
if (fabsf(*out_troque) > max_torque) {
|
||||||
|
if (*out_troque > 0) {
|
||||||
|
*out_troque = max_torque;
|
||||||
|
} else {
|
||||||
|
*out_troque = -max_torque;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
return 0; // 成功
|
return 0; // 成功
|
||||||
}
|
}
|
@ -11,8 +11,8 @@ extern "C" {
|
|||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
int8_t Limit_ChassicOutput(const float feedback_pos ,float *out_pos,
|
int8_t Limit_ChassicOutput(const float feedback_pos ,float *out_pos, float *out_torque,
|
||||||
float max_speed, float max_angle, float min_angle);
|
float max_speed, float max_angle, float min_angle, float max_torque);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -4,6 +4,10 @@
|
|||||||
|
|
||||||
#include "path.h"
|
#include "path.h"
|
||||||
|
|
||||||
|
float Path_Straight1d(float start, float end, float t) {
|
||||||
|
return start + (end - start) * t;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 生成一条直线段的路径点
|
* @brief 生成一条直线段的路径点
|
||||||
* @param start 起始点
|
* @param start 起始点
|
||||||
|
@ -11,6 +11,9 @@ extern "C" {
|
|||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
|
||||||
|
float Path_Straight1d(float start, float end, float t);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 生成一条直线段的路径点
|
* @brief 生成一条直线段的路径点
|
||||||
* @param start 起始点
|
* @param start 起始点
|
||||||
|
@ -133,7 +133,7 @@ bool N100_WaitDmaCplt(void) {
|
|||||||
SIGNAL_N100_NEW_DATA);
|
SIGNAL_N100_NEW_DATA);
|
||||||
}
|
}
|
||||||
|
|
||||||
int8_t N100_ParseData (N100_t *n100) {
|
int8_t N100_ParseData (N100_t *n100, N100_Cali_t *cali) {
|
||||||
if (n100 == NULL) return DEVICE_ERR_NULL;
|
if (n100 == NULL) return DEVICE_ERR_NULL;
|
||||||
// 先校验原始数据
|
// 先校验原始数据
|
||||||
if (!N100_Verify(rxbuf)) {
|
if (!N100_Verify(rxbuf)) {
|
||||||
@ -144,7 +144,9 @@ int8_t N100_ParseData (N100_t *n100) {
|
|||||||
n100->eulr = n100->rx_raw.data.eulr;
|
n100->eulr = n100->rx_raw.data.eulr;
|
||||||
n100->gyro = n100->rx_raw.data.gyro;
|
n100->gyro = n100->rx_raw.data.gyro;
|
||||||
n100->quat = n100->rx_raw.data.quat;
|
n100->quat = n100->rx_raw.data.quat;
|
||||||
|
n100->eulr.rol += cali->offset_x; /* 偏航角校准 */
|
||||||
|
n100->eulr.pit += cali->offset_y; /* 俯仰角校准 */
|
||||||
|
n100->eulr.yaw += cali->offset_z; /* 翻滚角校准 */
|
||||||
return DEVICE_OK;
|
return DEVICE_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,6 +37,11 @@ typedef struct {
|
|||||||
uint8_t end;
|
uint8_t end;
|
||||||
} N100_AHRS_t;
|
} N100_AHRS_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
float offset_x;
|
||||||
|
float offset_y;
|
||||||
|
float offset_z;
|
||||||
|
} N100_Cali_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
osThreadId_t thread_alert;
|
osThreadId_t thread_alert;
|
||||||
@ -52,7 +57,7 @@ int8_t N100_Restart(void);
|
|||||||
|
|
||||||
int8_t N100_StartReceiving(N100_t *n100);
|
int8_t N100_StartReceiving(N100_t *n100);
|
||||||
bool N100_WaitDmaCplt(void);
|
bool N100_WaitDmaCplt(void);
|
||||||
int8_t N100_ParseData (N100_t *n100);
|
int8_t N100_ParseData (N100_t *n100, N100_Cali_t *cali);
|
||||||
int8_t N100_HandleOffline(N100_t *n100);
|
int8_t N100_HandleOffline(N100_t *n100);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
@ -15,9 +15,10 @@
|
|||||||
/* Private typedef ---------------------------------------------------------- */
|
/* Private typedef ---------------------------------------------------------- */
|
||||||
/* Private define ----------------------------------------------------------- */
|
/* Private define ----------------------------------------------------------- */
|
||||||
|
|
||||||
#define CHASSIS_DEFAULT_HEIGHT (0.2f) /* 底盘默认高度,单位:米 */
|
#define CHASSIS_DEFAULT_HEIGHT (0.22f) /* 底盘默认高度,单位:米 */
|
||||||
|
|
||||||
#define CHASSIS_MAX_SPEED (0.03f) /*调试用速度限幅*/
|
#define CHASSIS_MAX_SPEED (0.02f) /*调试用速度限幅*/
|
||||||
|
#define CHASSIS_MAX_TORQUE (1.0f) /*调试用力矩限幅*/
|
||||||
/* Private macro ------------------------------------------------------------ */
|
/* Private macro ------------------------------------------------------------ */
|
||||||
|
|
||||||
|
|
||||||
@ -43,13 +44,14 @@ static Kinematics_JointCMD_t position_mode = {
|
|||||||
.T = 0.0f, /* 零力矩 */
|
.T = 0.0f, /* 零力矩 */
|
||||||
.W = 0.0f, /* 零速度 */
|
.W = 0.0f, /* 零速度 */
|
||||||
.Pos = 0.0f, /* 零位置 */
|
.Pos = 0.0f, /* 零位置 */
|
||||||
.K_P = 3.0f, /* 刚度系数 */
|
.K_P = 1.2f, /* 刚度系数 */
|
||||||
.K_W = 0.2f, /* 速度系数 */
|
.K_W = 0.05f, /* 速度系数 */
|
||||||
};
|
};
|
||||||
|
|
||||||
// static uint8_t chassis_mode_states = 0;
|
// static uint8_t chassis_mode_states = 0;
|
||||||
// static uint8_t chassis_action_states = 0;
|
// static uint8_t chassis_action_states = 0;
|
||||||
static uint8_t stage = 0;
|
static uint8_t stage = 0;
|
||||||
|
static float T = 0.0f; /* 记录当前时间 */
|
||||||
/* Private function -------------------------------------------------------- */
|
/* Private function -------------------------------------------------------- */
|
||||||
/**
|
/**
|
||||||
* \brief 设置底盘运行模式
|
* \brief 设置底盘运行模式
|
||||||
@ -62,19 +64,26 @@ static uint8_t stage = 0;
|
|||||||
static int8_t Chassis_SetMode(Chassis_t *c, CMD_ChassisCmd_t *c_cmd) {
|
static int8_t Chassis_SetMode(Chassis_t *c, CMD_ChassisCmd_t *c_cmd) {
|
||||||
if (c == NULL) return CHASSIS_ERR_NULL; /* 主结构体不能为空 */
|
if (c == NULL) return CHASSIS_ERR_NULL; /* 主结构体不能为空 */
|
||||||
if (c_cmd == NULL) return CHASSIS_ERR_NULL; /* 控制指令不能为空 */
|
if (c_cmd == NULL) return CHASSIS_ERR_NULL; /* 控制指令不能为空 */
|
||||||
|
if (c->mode == c_cmd->mode && c->action == c_cmd->action) {
|
||||||
|
return CHASSIS_OK; /* 如果当前模式和动作与要设置的相同,直接返回 */
|
||||||
|
}
|
||||||
|
stage = 0;
|
||||||
|
c->mode = c_cmd->mode;
|
||||||
|
c->action = c_cmd->action; /* 更新底盘模式和动作 */
|
||||||
|
c->time = 0.0f; /* 重置时间 */
|
||||||
|
c->eulr_setpoint.pit = 0;
|
||||||
|
c->eulr_setpoint.rol = 0.0f; /* 重置期望的俯仰角和翻滚角 */
|
||||||
|
c->eulr_setpoint.yaw = c->eulr_imu.yaw; /* 重置期望的偏航角为当前IMU偏航角 */
|
||||||
|
if (c->mode != CHASSIS_MODE_POSITION) {
|
||||||
|
c->height = CHASSIS_DEFAULT_HEIGHT; /* 重置底盘高度 */
|
||||||
|
}
|
||||||
|
for (uint8_t i = 0; i < GO_MOTOR_NUM; i++) {
|
||||||
|
PID_Reset(c->pid.motor_id + i);
|
||||||
|
LowPassFilter2p_Reset(c->filter.in + i, 0.0f);
|
||||||
|
LowPassFilter2p_Reset(c->filter.out + i, 0.0f);
|
||||||
|
}
|
||||||
|
PID_Reset(&c->pid.blance); /* 重置平衡PID */
|
||||||
|
|
||||||
if (c->mode != c_cmd->mode) { /* 如果当前模式和要设置的模式不同 */
|
|
||||||
c->mode = c_cmd->mode; /* 更新底盘模式 */
|
|
||||||
stage = 0; /* 重置阶段 */
|
|
||||||
c->time = 0.0f; /* 重置时间 */
|
|
||||||
c->height = CHASSIS_DEFAULT_HEIGHT; /* 重置底盘高度 */
|
|
||||||
}
|
|
||||||
if (c->action != c_cmd->action) { /* 如果当前动作和要设置的动作不同 */
|
|
||||||
c->action = c_cmd->action; /* 更新底盘动作 */
|
|
||||||
stage = 0; /* 重置阶段 */
|
|
||||||
c->time = 0.0f; /* 重置时间 */
|
|
||||||
c->height = CHASSIS_DEFAULT_HEIGHT; /* 重置底盘高度 */
|
|
||||||
}
|
|
||||||
return CHASSIS_OK;
|
return CHASSIS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -97,15 +106,27 @@ int8_t Chassis_Init(Chassis_t *c, const Chassis_Params_t *param,
|
|||||||
c->mode = CHASSIS_MODE_RELAX; /* 设置上电后底盘默认模式 */
|
c->mode = CHASSIS_MODE_RELAX; /* 设置上电后底盘默认模式 */
|
||||||
c->height = CHASSIS_DEFAULT_HEIGHT; /* 设置底盘默认高度为0.2米 */
|
c->height = CHASSIS_DEFAULT_HEIGHT; /* 设置底盘默认高度为0.2米 */
|
||||||
|
|
||||||
c->pid.motor_id = BSP_Malloc(GO_MOTOR_MODE_NUM * sizeof(*c->pid.motor_id));
|
c->pid.motor_id = BSP_Malloc(GO_MOTOR_NUM * sizeof(*c->pid.motor_id));
|
||||||
if (c->pid.motor_id == NULL) goto error;
|
if (c->pid.motor_id == NULL) goto error;
|
||||||
|
|
||||||
c->filter.in = BSP_Malloc(GO_MOTOR_MODE_NUM * sizeof(*c->filter.in));
|
c->filter.in = BSP_Malloc(GO_MOTOR_NUM * sizeof(*c->filter.in));
|
||||||
if (c->filter.in == NULL) goto error;
|
if (c->filter.in == NULL) goto error;
|
||||||
|
|
||||||
c->filter.out = BSP_Malloc(GO_MOTOR_MODE_NUM * sizeof(*c->filter.out));
|
c->filter.out = BSP_Malloc(GO_MOTOR_NUM * sizeof(*c->filter.out));
|
||||||
if (c->filter.out == NULL) goto error;
|
if (c->filter.out == NULL) goto error;
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < GO_MOTOR_NUM; i++) {
|
||||||
|
PID_Init(c->pid.motor_id + i, KPID_MODE_NO_D, target_freq,
|
||||||
|
&(c->param->torque_pid_param));
|
||||||
|
|
||||||
|
LowPassFilter2p_Init(c->filter.in + i, target_freq,
|
||||||
|
c->param->low_pass_cutoff_freq.in);
|
||||||
|
LowPassFilter2p_Init(c->filter.out + i, target_freq,
|
||||||
|
c->param->low_pass_cutoff_freq.out);
|
||||||
|
}
|
||||||
|
PID_Init(&c->pid.blance, KPID_MODE_NO_D, target_freq,
|
||||||
|
&(c->param->blance_pid_param));
|
||||||
|
|
||||||
return CHASSIS_OK; /* 返回成功 */
|
return CHASSIS_OK; /* 返回成功 */
|
||||||
|
|
||||||
error:
|
error:
|
||||||
@ -132,6 +153,15 @@ int8_t Chassis_UpdateFeedback(Chassis_t *c, const GO_ChassisFeedback_t *go){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int8_t Chassis_UpdateImu(Chassis_t *c, const AHRS_Eulr_t *eulr){
|
||||||
|
if (c == NULL) return CHASSIS_ERR_NULL; /* 主结构体不能为空 */
|
||||||
|
if (eulr == NULL) return CHASSIS_ERR_NULL; /* IMU数据不能为空 */
|
||||||
|
|
||||||
|
c->eulr_imu.yaw = eulr->yaw;
|
||||||
|
c->eulr_imu.pit = eulr->pit;
|
||||||
|
c->eulr_imu.rol = eulr->rol;
|
||||||
|
}
|
||||||
|
|
||||||
int8_t Chassis_Control(Chassis_t *c, const CMD_ChassisCmd_t *c_cmd, uint32_t now){
|
int8_t Chassis_Control(Chassis_t *c, const CMD_ChassisCmd_t *c_cmd, uint32_t now){
|
||||||
/* 底盘数据和控制指令结构体不能为空 */
|
/* 底盘数据和控制指令结构体不能为空 */
|
||||||
if (c == NULL) return CHASSIS_ERR_NULL;
|
if (c == NULL) return CHASSIS_ERR_NULL;
|
||||||
@ -159,10 +189,21 @@ int8_t Chassis_Control(Chassis_t *c, const CMD_ChassisCmd_t *c_cmd, uint32_t now
|
|||||||
c->action = CHASSIS_ACTION_NONE; /* 清除动作状态 */
|
c->action = CHASSIS_ACTION_NONE; /* 清除动作状态 */
|
||||||
c->time = 0.0f; /* 重置时间 */
|
c->time = 0.0f; /* 重置时间 */
|
||||||
break;
|
break;
|
||||||
case CHASSIS_MODE_POSITION:{
|
case CHASSIS_MODE_FOLLOW:{
|
||||||
for (uint8_t i = 0; i < GO_MOTOR_NUM; i++) {
|
for (uint8_t i = 0; i < GO_MOTOR_NUM; i++) {
|
||||||
c->output.id[i] = position_mode; /* 设置位置模式 */}
|
c->output.id[i] = position_mode; /* 设置位置模式 */
|
||||||
|
}
|
||||||
|
c->delta_eulr.pit = PID_Calc(&c->pid.blance, 0.0f, c->eulr_imu.pit, 0.0f, c->dt);
|
||||||
|
c->eulr_setpoint.pit += c->delta_eulr.pit; /* 更新期望的俯仰角 */
|
||||||
|
//限制c->eulr_setpoint.pit在-0.1f到0.1f之间
|
||||||
|
if (c->eulr_setpoint.pit > 0.05f) {
|
||||||
|
c->eulr_setpoint.pit = 0.05f;
|
||||||
|
}
|
||||||
|
else if (c->eulr_setpoint.pit < -0.05f) {
|
||||||
|
c->eulr_setpoint.pit = -0.05f;
|
||||||
|
}
|
||||||
|
// c->delta_eulr.pit = 0;
|
||||||
|
/* 计算俯仰角变化量 */
|
||||||
switch (c->action) {
|
switch (c->action) {
|
||||||
case CHASSIS_ACTION_NONE:{
|
case CHASSIS_ACTION_NONE:{
|
||||||
for (uint8_t i = 0; i < GO_MOTOR_NUM; i++) {
|
for (uint8_t i = 0; i < GO_MOTOR_NUM; i++) {
|
||||||
@ -175,110 +216,107 @@ int8_t Chassis_Control(Chassis_t *c, const CMD_ChassisCmd_t *c_cmd, uint32_t now
|
|||||||
|
|
||||||
case CHASSIS_ACTION_STAND:{
|
case CHASSIS_ACTION_STAND:{
|
||||||
c->height = c->height + c_cmd->delta_hight * c->dt; /* 更新底盘高度 */
|
c->height = c->height + c_cmd->delta_hight * c->dt; /* 更新底盘高度 */
|
||||||
|
float pitch_correction = PID_Calc(&c->pid.blance, 0.0f, c->eulr_imu.pit, 0.0f, c->dt);
|
||||||
for (uint8_t i = 0; i < GO_MOTOR_NUM/3; i++) {
|
for (uint8_t i = 0; i < GO_MOTOR_NUM/3; i++) {
|
||||||
if (i % 2 == 0) { /* 左前腿和右后腿 */
|
if (c->eulr_setpoint.pit > 0.0f) {
|
||||||
float target_pose[3] = {0.0, -0.0861, -c->height};
|
if (i == 0) { /* 左前腿 */
|
||||||
float angle_pose[3];
|
float target_pose[3] = {-0.015, -0.0861, -c->height - c->eulr_setpoint.pit};
|
||||||
Kinematics_InverseKinematics(target_pose, &c->param->mech_param.length, angle_pose, &c->param->mech_param.sign.leg[i]);
|
float angle_pose[3];
|
||||||
c->output.id[i * 3].Pos = angle_pose[0]; /* 左前腿髋关节 */
|
Kinematics_InverseKinematics(target_pose, &c->param->mech_param.length, angle_pose, &c->param->mech_param.sign.leg[i]);
|
||||||
c->output.id[i * 3 + 1].Pos = angle_pose[1]; /* 左前腿大腿 */
|
c->output.id[i * 3].Pos = angle_pose[0]; /* 左前腿髋关节 */
|
||||||
c->output.id[i * 3 + 2].Pos = angle_pose[2]; /* 左前腿小腿 */
|
c->output.id[i * 3 + 1].Pos = angle_pose[1]; /* 左前腿大腿 */
|
||||||
|
c->output.id[i * 3 + 2].Pos = angle_pose[2]; /* 左前腿小腿 */
|
||||||
|
} else if (i == 1) { /* 右前腿 */
|
||||||
|
float target_pose[3] = {-0.015, 0.0861, -c->height - c->eulr_setpoint.pit}; /* 右前腿和左后腿 */
|
||||||
|
float angle_pose[3];
|
||||||
|
Kinematics_InverseKinematics(target_pose, &c->param->mech_param.length, angle_pose, &c->param->mech_param.sign.leg[i]);
|
||||||
|
c->output.id[i * 3].Pos = angle_pose[0]; /* 右前腿髋关节 */
|
||||||
|
c->output.id[i * 3 + 1].Pos = angle_pose[1]; /* 右前腿大腿 */
|
||||||
|
c->output.id[i * 3 + 2].Pos = angle_pose[2]; /* 右前腿小腿 */
|
||||||
|
} else if (i == 2) { /* 左后腿 */
|
||||||
|
float target_pose[3] = {-0.015, -0.0861, -c->height + c->eulr_setpoint.pit}; /* 左前腿和右后腿 */
|
||||||
|
float angle_pose[3];
|
||||||
|
Kinematics_InverseKinematics(target_pose, &c->param->mech_param.length, angle_pose, &c->param->mech_param.sign.leg[i]);
|
||||||
|
c->output.id[i * 3].Pos = angle_pose[0]; /* 左前腿髋关节 */
|
||||||
|
c->output.id[i * 3 + 1].Pos = angle_pose[1]; /* 左前腿大腿 */
|
||||||
|
c->output.id[i * 3 + 2].Pos = angle_pose[2]; /* 左前腿小腿 */
|
||||||
|
} else if (i == 3) { /* 右后腿 */
|
||||||
|
float target_pose[3] = {-0.015, 0.0861, -c->height + c->eulr_setpoint.pit}; /* 右前腿和左后腿 */
|
||||||
|
float angle_pose[3];
|
||||||
|
Kinematics_InverseKinematics(target_pose, &c->param->mech_param.length, angle_pose, &c->param->mech_param.sign.leg[i]);
|
||||||
|
c->output.id[i * 3].Pos = angle_pose[0]; /* 右前腿髋关节 */
|
||||||
|
c->output.id[i * 3 + 1].Pos = angle_pose[1]; /* 右前腿大腿 */
|
||||||
|
c->output.id[i * 3 + 2].Pos = angle_pose[2]; /* 右前腿小腿 */
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
float target_pose[3] = {0.0, 0.0861, -c->height}; /* 右前腿和左后腿 */
|
if (i == 0) { /* 左前腿 */
|
||||||
float angle_pose[3];
|
float target_pose[3] = {-0.015, -0.0861, -c->height - c->eulr_setpoint.pit};
|
||||||
Kinematics_InverseKinematics(target_pose, &c->param->mech_param.length, angle_pose, &c->param->mech_param.sign.leg[i]);
|
float angle_pose[3];
|
||||||
c->output.id[i * 3].Pos = angle_pose[0]; /* 右前腿髋关节 */
|
Kinematics_InverseKinematics(target_pose, &c->param->mech_param.length, angle_pose, &c->param->mech_param.sign.leg[i]);
|
||||||
c->output.id[i * 3 + 1].Pos = angle_pose[1]; /* 右前腿大腿 */
|
c->output.id[i * 3].Pos = angle_pose[0]; /* 左前腿髋关节 */
|
||||||
c->output.id[i * 3 + 2].Pos = angle_pose[2]; /* 右前腿小腿 */
|
c->output.id[i * 3 + 1].Pos = angle_pose[1]; /* 左前腿大腿 */
|
||||||
|
c->output.id[i * 3 + 2].Pos = angle_pose[2]; /* 左前腿小腿 */
|
||||||
|
} else if (i == 1) { /* 右前腿 */
|
||||||
|
float target_pose[3] = {-0.015, 0.0861, -c->height - c->eulr_setpoint.pit}; /* 右前腿和左后腿 */
|
||||||
|
float angle_pose[3];
|
||||||
|
Kinematics_InverseKinematics(target_pose, &c->param->mech_param.length, angle_pose, &c->param->mech_param.sign.leg[i]);
|
||||||
|
c->output.id[i * 3].Pos = angle_pose[0]; /* 右前腿髋关节 */
|
||||||
|
c->output.id[i * 3 + 1].Pos = angle_pose[1]; /* 右前腿大腿 */
|
||||||
|
c->output.id[i * 3 + 2].Pos = angle_pose[2]; /* 右前腿小腿 */
|
||||||
|
} else if (i == 2) { /* 左后腿 */
|
||||||
|
float target_pose[3] = {-0.015, -0.0861, -c->height + c->eulr_setpoint.pit};
|
||||||
|
float angle_pose[3];
|
||||||
|
Kinematics_InverseKinematics(target_pose, &c->param->mech_param.length, angle_pose, &c->param->mech_param.sign.leg[i]);
|
||||||
|
c->output.id[i * 3].Pos = angle_pose[0]; /* 左前腿髋关节 */
|
||||||
|
c->output.id[i * 3 + 1].Pos = angle_pose[1]; /* 左前腿大腿 */
|
||||||
|
c->output.id[i * 3 + 2].Pos = angle_pose[2]; /* 左前腿小腿 */
|
||||||
|
} else if (i == 3) { /* 右后腿 */
|
||||||
|
float target_pose[3] = {-0.015, 0.0861, -c->height + c->eulr_setpoint.pit}; /* 右前腿和左后腿 */
|
||||||
|
float angle_pose[3];
|
||||||
|
Kinematics_InverseKinematics(target_pose, &c->param->mech_param.length, angle_pose, &c->param->mech_param.sign.leg[i]);
|
||||||
|
c->output.id[i * 3].Pos = angle_pose[0]; /* 右前腿髋关节 */
|
||||||
|
c->output.id[i * 3 + 1].Pos = angle_pose[1]; /* 右前腿大腿 */
|
||||||
|
c->output.id[i * 3 + 2].Pos = angle_pose[2]; /* 右前腿小腿 */
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
// break;
|
||||||
} /* 站立动作 */
|
if (c_cmd->ctrl_vec.vx == 0.0f && c_cmd->ctrl_vec.vy == 0.0f && c_cmd->ctrl_vec.wz == 0.0f) {
|
||||||
|
|
||||||
case CHASSIS_ACTION_WALK:{
|
|
||||||
c->height = c->height + c_cmd->delta_hight * c->dt;
|
|
||||||
float T = 2.0f; // 步态周期(s)
|
|
||||||
float swing_height = 0.15f; // 摆动腿抬高高度
|
|
||||||
float stride_x = c_cmd->ctrl_vec.vx * T; // x方向步幅
|
|
||||||
float stride_y = c_cmd->ctrl_vec.vy * T; // y方向步幅
|
|
||||||
float wz = c_cmd->ctrl_vec.wz; // 旋转速度
|
|
||||||
|
|
||||||
uint8_t swing_leg = (uint8_t)(c->time / (T / 4)) % 4; // 当前摆动腿编号
|
|
||||||
float t_phase = fmodf(c->time, T / 4) / (T / 4); // 当前腿相内归一化时间
|
|
||||||
|
|
||||||
for (uint8_t leg = 0; leg < 4; leg++) {
|
|
||||||
float target_pose[3];
|
|
||||||
float base_y = (leg % 2 == 0) ? -0.0861f : 0.0861f;
|
|
||||||
float base_x = 0.0f;
|
|
||||||
|
|
||||||
if (leg == swing_leg) {
|
|
||||||
// 摆动腿:贝塞尔轨迹,抬高并前移
|
|
||||||
float start[3] = {base_x - stride_x/2, base_y - stride_y/2, -c->height};
|
|
||||||
float mid1[3] = {base_x, base_y, -c->height + swing_height};
|
|
||||||
float mid2[3] = {base_x, base_y, -c->height + swing_height};
|
|
||||||
float end[3] = {base_x + stride_x/2, base_y + stride_y/2, -c->height};
|
|
||||||
Path_Bezier3d(start, mid1, mid2, end, t_phase, target_pose);
|
|
||||||
} else {
|
|
||||||
// 支撑腿:3/4周期内走完整步幅的1/3
|
|
||||||
// 计算支撑腿在支撑相内的归一化时间
|
|
||||||
uint8_t support_leg_index = (leg + 4 - swing_leg) % 4;
|
|
||||||
float support_phase = fmodf(c->time + (T / 4) * support_leg_index, T) / (T * 3.0f / 4.0f);
|
|
||||||
if (support_phase > 1.0f) support_phase = 1.0f;
|
|
||||||
|
|
||||||
// 支撑腿分三段,每段1/3步幅
|
|
||||||
float seg = support_phase * 3.0f; // [0,3)
|
|
||||||
int seg_idx = (int)seg; // 0,1,2
|
|
||||||
float seg_phase = seg - seg_idx; // [0,1)
|
|
||||||
|
|
||||||
// 每段起止点
|
|
||||||
float seg_start_x = base_x + stride_x/2 - stride_x * seg_idx / 3.0f;
|
|
||||||
float seg_start_y = base_y + stride_y/2 - stride_y * seg_idx / 3.0f;
|
|
||||||
float seg_end_x = base_x + stride_x/2 - stride_x * (seg_idx+1) / 3.0f;
|
|
||||||
float seg_end_y = base_y + stride_y/2 - stride_y * (seg_idx+1) / 3.0f;
|
|
||||||
|
|
||||||
Path_straight3d(
|
|
||||||
(float[3]){seg_start_x, seg_start_y, -c->height},
|
|
||||||
(float[3]){seg_end_x, seg_end_y, -c->height},
|
|
||||||
seg_phase, target_pose
|
|
||||||
);
|
|
||||||
}
|
|
||||||
float angle_pose[3];
|
|
||||||
Kinematics_InverseKinematics(target_pose, &c->param->mech_param.length, angle_pose, &c->param->mech_param.sign.leg[leg]);
|
|
||||||
c->output.id[leg * 3 + 0].Pos = angle_pose[0];
|
|
||||||
c->output.id[leg * 3 + 1].Pos = angle_pose[1];
|
|
||||||
c->output.id[leg * 3 + 2].Pos = angle_pose[2];
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
} /* 站立动作 */
|
||||||
|
|
||||||
|
|
||||||
case CHASSIS_ACTION_TROT:{
|
case CHASSIS_ACTION_TROT:{
|
||||||
|
c->height = c->height + c_cmd->delta_hight * c->dt;
|
||||||
|
|
||||||
float T = 0.6f; // 步态周期(s)
|
float T = 0.6f; // 步态周期(s)
|
||||||
if (c->time > T) {
|
if (c->time > T) {
|
||||||
c->time -= T; // 保持时间在0-T之间
|
c->time -= T; // 保持时间在0-T之间
|
||||||
}
|
}
|
||||||
float stride_x = c_cmd->ctrl_vec.vx / 10.0f;
|
float stride_x = c_cmd->ctrl_vec.vx / 10.0f;
|
||||||
float stride_y = c_cmd->ctrl_vec.vy / 10.0f;
|
float stride_y = c_cmd->ctrl_vec.vy / 10.0f;
|
||||||
float swing_height = 0.15f; // 摆动腿抬高高度
|
float swing_height = 0.12f; // 摆动腿抬高高度
|
||||||
|
|
||||||
// 对角腿分组:0-3一组,1-2一组
|
// 对角腿分组:0-3一组,1-2一组
|
||||||
for (uint8_t leg = 0; leg < 4; leg++) {
|
for (uint8_t leg = 0; leg < 4; leg++) {
|
||||||
float t_leg = fmodf(c->time + ((leg == 0 || leg == 3) ? 0.0f : T/2), T) / T; // 对角腿相差半周期
|
float t_leg = fmodf(c->time + ((leg == 0 || leg == 3) ? 0.0f : T/2), T) / T; // 对角腿相差半周期
|
||||||
float base_y = (leg % 2 == 0) ? -0.0861f : 0.0861f;
|
float base_y = (leg % 2 == 0) ? -0.0861f : 0.0861f;
|
||||||
|
float wz = (leg < 2) ? c_cmd->ctrl_vec.wz/5 : -c_cmd->ctrl_vec.wz/5; // 前两条腿正向,后两条腿反向
|
||||||
float target_pose[3];
|
float target_pose[3];
|
||||||
if (t_leg < 0.5f) {
|
if (t_leg < 0.5f) {
|
||||||
// 摆动相,贝塞尔插值
|
// 摆动相,贝塞尔插值
|
||||||
float t_bezier = t_leg / 0.5f;
|
float t_bezier = t_leg / 0.5f;
|
||||||
float start[3] = {-stride_x, base_y - stride_y, -c->height};
|
float start[3] = {-stride_x -0.015, base_y - stride_y - wz, -c->height};
|
||||||
float mid1[3] = {0, base_y, -c->height + swing_height};
|
float mid1[3] = {-0.015 - stride_x/2, base_y, -c->height + swing_height};
|
||||||
float mid2[3] = {0, base_y, -c->height + swing_height};
|
float mid2[3] = {-0.015 + stride_x/2, base_y, -c->height + swing_height};
|
||||||
float end[3] = {stride_x, base_y + stride_y, -c->height};
|
float end[3] = {stride_x -0.015, base_y + stride_y + wz, -c->height};
|
||||||
Path_Bezier3d(start, mid1, mid2, end, t_bezier, target_pose);
|
Path_Bezier3d(start, mid1, mid2, end, t_bezier, target_pose);
|
||||||
} else {
|
} else {
|
||||||
// 支撑相,首尾相连,起点为上一个摆动相终点,终点为下一个摆动相起点
|
// 支撑相,首尾相连,起点为上一个摆动相终点,终点为下一个摆动相起点
|
||||||
float t_line = (t_leg - 0.5f) / 0.5f;
|
float t_line = (t_leg - 0.5f) / 0.5f;
|
||||||
float start[3] = {stride_x, base_y + stride_y, -c->height}; // 摆动相终点
|
float start[3] = {stride_x-0.015, base_y + stride_y + wz, -c->height}; // 摆动相终点
|
||||||
float end[3] = {-stride_x, base_y - stride_y, -c->height}; // 下一个摆动相起点
|
float end[3] = {-stride_x-0.015, base_y - stride_y - wz, -c->height}; // 下一个摆动相起点
|
||||||
Path_straight3d(start, end, t_line, target_pose);
|
Path_straight3d(start, end, t_line, target_pose);
|
||||||
}
|
}
|
||||||
float angle_pose[3];
|
float angle_pose[3];
|
||||||
@ -292,16 +330,176 @@ int8_t Chassis_Control(Chassis_t *c, const CMD_ChassisCmd_t *c_cmd, uint32_t now
|
|||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
for (uint8_t i = 0; i < GO_MOTOR_NUM; i++) {
|
||||||
|
/*PID计算力矩*/
|
||||||
|
c->output.id[i].T = PID_Calc(c->pid.motor_id, c->output.id[i].Pos, c->feedback.id[i].Pos, 0.0f, c->dt);
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case CHASSIS_MODE_POSITION:{
|
||||||
|
/*设置pd参数为位控参数,获取遥控器控制命令*/
|
||||||
|
for (uint8_t i = 0; i < GO_MOTOR_NUM; i++) {
|
||||||
|
c->output.id[i] = position_mode;} /* 设置位置模式 */
|
||||||
|
c->height = c->height + c_cmd->delta_hight * c->dt; /* 更新底盘高度 */
|
||||||
|
c->eulr_setpoint.pit += c_cmd->delta_eulr.pit* c->dt; /* 更新期望的俯仰角 */
|
||||||
|
c->eulr_setpoint.rol = 0.0f; /* 期望的俯仰角和翻滚角 */
|
||||||
|
c->eulr_setpoint.yaw += c_cmd->delta_eulr.yaw * c->dt; /* 期望的偏航角 */
|
||||||
|
|
||||||
|
/*pitch轴软件限位*/
|
||||||
|
if (c->eulr_setpoint.pit > 0.2f) {
|
||||||
|
c->eulr_setpoint.pit = 0.2f; /* 限制俯仰角在-0.05到0.05之间 */
|
||||||
|
} else if (c->eulr_setpoint.pit < -0.2f) {
|
||||||
|
c->eulr_setpoint.pit = -0.2f; /* 限制俯仰角在-0.05到0.05之间 */
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (c->action)
|
||||||
|
{
|
||||||
|
case CHASSIS_ACTION_NONE:{
|
||||||
|
for (uint8_t i = 0; i < GO_MOTOR_NUM; i++) {
|
||||||
|
c->output.id[i].Pos = c->feedback.id[i].Pos;}
|
||||||
|
c->mode = CHASSIS_MODE_POSITION;
|
||||||
|
c->action = CHASSIS_ACTION_NONE;
|
||||||
|
c->time = 0.0f; /* 重置时间 */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CHASSIS_ACTION_STAND:{
|
||||||
|
switch (stage) {
|
||||||
|
/* 重置时间记录当前位子 */
|
||||||
|
case 0:{
|
||||||
|
c->time = 0.0f; /* 重置时间 */
|
||||||
|
T = 0.2f; /* 设置站立动作的时间周期 */
|
||||||
|
for (uint8_t i = 0; i < GO_MOTOR_NUM/3; i++) {
|
||||||
|
c->leg_pos.start_pos.leg[i].x = c->feedback.id[i * 3].Pos; /* 四条腿基坐标x轴位置 */
|
||||||
|
c->leg_pos.start_pos.leg[i].y = c->feedback.id[i * 3 + 1].Pos;
|
||||||
|
c->leg_pos.start_pos.leg[i].z = c->feedback.id[i * 3 + 2].Pos;
|
||||||
|
|
||||||
|
c->leg_pos.end_pos.leg[i].x = -0.015f; /* 四条腿基坐标x轴位置 */
|
||||||
|
c->leg_pos.end_pos.leg[i].y = (i % 2 == 0) ? -c->param->mech_param.length.hip : c->param->mech_param.length.hip; /* 左前腿和右后腿y轴位置,右前腿和左后腿y轴位置 */
|
||||||
|
c->leg_pos.end_pos.leg[i].z = -c->height; /* 四条腿基坐标z轴位置 */
|
||||||
|
float angle_pose[3];
|
||||||
|
Kinematics_InverseKinematics(&c->leg_pos.end_pos.leg[i].x, &c->param->mech_param.length, angle_pose, &c->param->mech_param.sign.leg[i]);
|
||||||
|
c->leg_pos.end_pos.leg[i].x = angle_pose[0]; /* 髋关节 */
|
||||||
|
c->leg_pos.end_pos.leg[i].y = angle_pose[1]; /* 大腿 */
|
||||||
|
c->leg_pos.end_pos.leg[i].z = angle_pose[2]; /* 小腿 */
|
||||||
|
|
||||||
|
/*设置四腿基坐标*/
|
||||||
|
c->foot_base[i].x = -0.015f; /* 四条腿基坐标x轴位置 */
|
||||||
|
c->foot_base[i].y = (i % 2 == 0) ? -c->param->mech_param.length.hip : c->param->mech_param.length.hip; /* 左前腿和右后腿y轴位置,右前腿和左后腿y轴位置 */
|
||||||
|
c->foot_base[i].z = -c->height;
|
||||||
|
|
||||||
|
}
|
||||||
|
stage ++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 1:{
|
||||||
|
/*在周期内对12个关节完成一次直线插补*/
|
||||||
|
for (uint8_t i = 0; i < GO_MOTOR_NUM/3; i++) {
|
||||||
|
float t = c->time / T; /* 计算当前时间在周期中的比例 */
|
||||||
|
c->output.id[i * 3].Pos = Path_Straight1d(c->leg_pos.start_pos.leg[i].x, c->leg_pos.end_pos.leg[i].x, t); /* 髋关节 */
|
||||||
|
c->output.id[i * 3 + 1].Pos = Path_Straight1d(c->leg_pos.start_pos.leg[i].y, c->leg_pos.end_pos.leg[i].y, t); /* 大腿 */
|
||||||
|
c->output.id[i * 3 + 2].Pos = Path_Straight1d(c->leg_pos.start_pos.leg[i].z, c->leg_pos.end_pos.leg[i].z, t); /* 小腿 */
|
||||||
|
}
|
||||||
|
if (c->time >= T) {
|
||||||
|
stage ++;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 2:{
|
||||||
|
/*计算pit和rol的平衡pid*/
|
||||||
|
float pitch_correction = PID_Calc(&c->pid.blance, c->eulr_setpoint.pit, c->eulr_imu.pit, 0.0f, c->dt);
|
||||||
|
float roll_correction = PID_Calc(&c->pid.blance, c->eulr_setpoint.rol, c->eulr_imu.rol, 0.0f, c->dt);
|
||||||
|
|
||||||
|
/*计算为了补偿平衡的腿长*/
|
||||||
|
c->foot_base[0].z = -c->height - pitch_correction - roll_correction + c->delta_eulr.pit; /* 左前腿 */
|
||||||
|
c->foot_base[1].z = -c->height - pitch_correction + roll_correction + c->delta_eulr.pit; /* 右前腿 */
|
||||||
|
c->foot_base[2].z = -c->height + pitch_correction - roll_correction - c->delta_eulr.pit; /* 左后腿 */
|
||||||
|
c->foot_base[3].z = -c->height + pitch_correction + roll_correction - c->delta_eulr.pit; /* 右后腿 */
|
||||||
|
float target_pose[3];
|
||||||
|
float angle_pose[3];
|
||||||
|
/*将足端坐标转换为关节角度*/
|
||||||
|
for (uint8_t i = 0; i < GO_MOTOR_NUM/3; i++) {
|
||||||
|
target_pose[0] = c->foot_base[i].x; /* 髋关节 */
|
||||||
|
target_pose[1] = c->foot_base[i].y; /* 大腿 */
|
||||||
|
target_pose[2] = c->foot_base[i].z;
|
||||||
|
Kinematics_InverseKinematics(target_pose, &c->param->mech_param.length, angle_pose, &c->param->mech_param.sign.leg[i]);
|
||||||
|
c->output.id[i * 3].Pos = angle_pose[0]; /* 髋关节 */
|
||||||
|
c->output.id[i * 3 + 1].Pos = angle_pose[1]; /* 大腿 */
|
||||||
|
c->output.id[i * 3 + 2].Pos = angle_pose[2]; /* 小腿 */
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (c_cmd->ctrl_vec.vx == 0.0f && c_cmd->ctrl_vec.vy == 0.0f && c_cmd->ctrl_vec.wz == 0.0f){
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} /* 站立动作 */
|
||||||
|
|
||||||
|
case CHASSIS_ACTION_TROT:{
|
||||||
|
if( c->action == CHASSIS_ACTION_TROT){
|
||||||
|
float pitch_correction = PID_Calc(&c->pid.blance, c->eulr_setpoint.pit, c->eulr_imu.pit, 0.0f, c->dt);
|
||||||
|
float roll_correction = PID_Calc(&c->pid.blance, c->eulr_setpoint.rol, c->eulr_imu.rol, 0.0f, c->dt);
|
||||||
|
|
||||||
|
/*计算为了补偿平衡的腿长*/
|
||||||
|
c->foot_base[0].z = -c->height - pitch_correction - roll_correction; /* 左前腿 */
|
||||||
|
c->foot_base[1].z = -c->height - pitch_correction + roll_correction; /* 右前腿 */
|
||||||
|
c->foot_base[2].z = -c->height + pitch_correction - roll_correction; /* 左后腿 */
|
||||||
|
c->foot_base[3].z = -c->height + pitch_correction + roll_correction; /* 右后腿 */
|
||||||
|
}
|
||||||
|
T = 0.6f; /* 设置trot动作的时间周期 */
|
||||||
|
if (c->time > T) {
|
||||||
|
c->time -= T; // 保持时间在0-T之间
|
||||||
|
}
|
||||||
|
float stride_x = c_cmd->ctrl_vec.vx / 10.0f; /* 步幅x轴 */
|
||||||
|
float stride_y = c_cmd->ctrl_vec.vy / 10.0f; /* 步幅y轴 */
|
||||||
|
float swing_height = 0.12f; // 摆动腿默认抬高高度
|
||||||
|
|
||||||
|
// 对角腿分组:0-3一组,1-2一组
|
||||||
|
for (uint8_t leg = 0; leg < 4; leg++) {
|
||||||
|
float t_leg = fmodf(c->time + ((leg == 0 || leg == 3) ? 0.0f : T/2), T) / T; // 对角腿相差半周期
|
||||||
|
float wz = (leg < 2) ? c_cmd->ctrl_vec.wz/5 : -c_cmd->ctrl_vec.wz/5; // 前两条腿正向,后两条腿反向
|
||||||
|
float target_pose[3];
|
||||||
|
if (t_leg < 0.5f) {
|
||||||
|
// 摆动相,贝塞尔插值
|
||||||
|
float t_bezier = t_leg / 0.5f;
|
||||||
|
float start[3] = {c->foot_base[leg].x -stride_x, c->foot_base[leg].y - stride_y - wz, c->foot_base[leg].z};
|
||||||
|
float mid1[3] = {c->foot_base[leg].x - stride_x/2, c->foot_base[leg].y, c->foot_base[leg].z + swing_height};
|
||||||
|
float mid2[3] = {c->foot_base[leg].x + stride_x/2, c->foot_base[leg].y, c->foot_base[leg].z + swing_height};
|
||||||
|
float end[3] = {c->foot_base[leg].x +stride_x , c->foot_base[leg].y + stride_y + wz, c->foot_base[leg].z};
|
||||||
|
Path_Bezier3d(start, mid1, mid2, end, t_bezier, target_pose);
|
||||||
|
} else {
|
||||||
|
// 支撑相,首尾相连,起点为上一个摆动相终点,终点为下一个摆动相起点
|
||||||
|
float t_line = (t_leg - 0.5f) / 0.5f;
|
||||||
|
float start[3] = {stride_x+c->foot_base[leg].x, c->foot_base[leg].y + stride_y + wz, c->foot_base[leg].z}; // 摆动相终点
|
||||||
|
float end[3] = {-stride_x+c->foot_base[leg].x, c->foot_base[leg].y - stride_y - wz, c->foot_base[leg].z}; // 下一个摆动相起点
|
||||||
|
Path_straight3d(start, end, t_line, target_pose);
|
||||||
|
}
|
||||||
|
float angle_pose[3];
|
||||||
|
Kinematics_InverseKinematics(target_pose, &c->param->mech_param.length, angle_pose, &c->param->mech_param.sign.leg[leg]);
|
||||||
|
c->output.id[leg * 3 + 0].Pos = angle_pose[0];
|
||||||
|
c->output.id[leg * 3 + 1].Pos = angle_pose[1];
|
||||||
|
c->output.id[leg * 3 + 2].Pos = angle_pose[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
} /* trot动作 */
|
||||||
|
}
|
||||||
|
for (uint8_t i = 0; i < GO_MOTOR_NUM; i++) {
|
||||||
|
/*PID计算力矩*/
|
||||||
|
c->output.id[i].T = PID_Calc(c->pid.motor_id, c->output.id[i].Pos, c->feedback.id[i].Pos, 0.0f, c->dt);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
for (uint8_t i = 0; i < GO_MOTOR_NUM; i++) {
|
for (uint8_t i = 0; i < GO_MOTOR_NUM; i++) {
|
||||||
/* 限制输出 */
|
|
||||||
Limit_ChassicOutput(c->feedback.id[i].Pos, &c->output.id[i].Pos,
|
Limit_ChassicOutput(c->feedback.id[i].Pos, &c->output.id[i].Pos, &c->output.id[i].T,
|
||||||
c->param->mech_param.ratio.id[i] * CHASSIS_MAX_SPEED,
|
c->param->mech_param.ratio.id[i] * CHASSIS_MAX_SPEED,
|
||||||
c->param->mech_param.limit.max.id[i],
|
c->param->mech_param.limit.max.id[i],
|
||||||
c->param->mech_param.limit.min.id[i]);
|
c->param->mech_param.limit.min.id[i],
|
||||||
|
c->param->mech_param.ratio.id[i] * CHASSIS_MAX_TORQUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -38,24 +38,6 @@ typedef enum {
|
|||||||
IMU_ERROR,
|
IMU_ERROR,
|
||||||
} Chassis_ImuType_t;
|
} Chassis_ImuType_t;
|
||||||
|
|
||||||
// typedef union {
|
|
||||||
// float id[GO_MOTOR_NUM]; /* 零点角度,单位:弧度 */
|
|
||||||
// struct {
|
|
||||||
// float lf_hip; /* 左前腿髋关节零点 */
|
|
||||||
// float lf_thigh; /* 左前腿大腿零点 */
|
|
||||||
// float lf_calf; /* 左前腿小腿零点 */
|
|
||||||
// float rf_hip; /* 右前腿髋关节零点 */
|
|
||||||
// float rf_thigh; /* 右前腿大腿零点 */
|
|
||||||
// float rf_calf; /* 右前腿小腿零点 */
|
|
||||||
// float lr_hip; /* 左后腿髋关节零点 */
|
|
||||||
// float lr_thigh; /* 左后腿大腿零点 */
|
|
||||||
// float lr_calf; /* 左后腿小腿零点 */
|
|
||||||
// float rr_hip; /* 右后腿髋关节零点 */
|
|
||||||
// float rr_thigh; /* 右后腿大腿零点 */
|
|
||||||
// float rr_calf; /* 右后腿小腿零点 */
|
|
||||||
// } named;
|
|
||||||
// } Chassis_MotorZeroPoint_t;
|
|
||||||
|
|
||||||
typedef union {
|
typedef union {
|
||||||
float id[GO_MOTOR_NUM];
|
float id[GO_MOTOR_NUM];
|
||||||
struct{
|
struct{
|
||||||
@ -74,9 +56,14 @@ typedef union {
|
|||||||
} named;
|
} named;
|
||||||
} joint_params;
|
} joint_params;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
float x;
|
||||||
|
float y;
|
||||||
|
float z;
|
||||||
|
} joint_pos;
|
||||||
typedef struct {
|
typedef struct {
|
||||||
joint_params min;
|
joint_params min;
|
||||||
joint_params max; /* 关节的最小和最大角度,单位:弧度 */
|
joint_params max;
|
||||||
} joint_limits;
|
} joint_limits;
|
||||||
|
|
||||||
typedef union {
|
typedef union {
|
||||||
@ -97,6 +84,7 @@ typedef struct{
|
|||||||
|
|
||||||
joint_params ratio; /* 关节减速比 */
|
joint_params ratio; /* 关节减速比 */
|
||||||
joint_limits limit; /* 关节的最小和最大角度,单位:弧度 */
|
joint_limits limit; /* 关节的最小和最大角度,单位:弧度 */
|
||||||
|
|
||||||
Kinematics_LinkLength_t length; /* 关节长度,单位:米 */
|
Kinematics_LinkLength_t length; /* 关节长度,单位:米 */
|
||||||
Kinematics_LegOffset_t leg_offset; /* 关节偏移,单位:米 */
|
Kinematics_LegOffset_t leg_offset; /* 关节偏移,单位:米 */
|
||||||
Kinematics_DirectionSign_t sign; /* 关节侧向标志(左前/左后为-1,右前/右后为1) */
|
Kinematics_DirectionSign_t sign; /* 关节侧向标志(左前/左后为-1,右前/右后为1) */
|
||||||
@ -110,7 +98,7 @@ typedef struct {
|
|||||||
Chassis_Mech_Params_t mech_param; /* 机械参数 */
|
Chassis_Mech_Params_t mech_param; /* 机械参数 */
|
||||||
|
|
||||||
KPID_Params_t torque_pid_param; /* 力矩矩控制PID的参数 */
|
KPID_Params_t torque_pid_param; /* 力矩矩控制PID的参数 */
|
||||||
|
KPID_Params_t blance_pid_param; /* 平衡PID的参数 */
|
||||||
/* 低通滤波器截止频率 */
|
/* 低通滤波器截止频率 */
|
||||||
struct {
|
struct {
|
||||||
float in; /* 输入 */
|
float in; /* 输入 */
|
||||||
@ -133,18 +121,45 @@ typedef struct {
|
|||||||
GO_ChassisFeedback_t feedback; /* 底盘反馈信息 */
|
GO_ChassisFeedback_t feedback; /* 底盘反馈信息 */
|
||||||
GO_ChassisCMD_t output;
|
GO_ChassisCMD_t output;
|
||||||
float height; /* 底盘高度,单位:米 */
|
float height; /* 底盘高度,单位:米 */
|
||||||
|
|
||||||
|
struct {
|
||||||
|
float x;
|
||||||
|
float y;
|
||||||
|
float z;
|
||||||
|
} foot_base[GO_MOTOR_NUM/3]; /* 四个足端的位置,单位:米 */
|
||||||
|
|
||||||
|
struct{
|
||||||
|
struct{
|
||||||
|
joint_pos leg[GO_MOTOR_NUM/3]; /* 四条腿的关节位置,单位:米 */
|
||||||
|
}start_pos;
|
||||||
|
struct{
|
||||||
|
joint_pos leg[GO_MOTOR_NUM/3]; /* 四条腿的关节位置,单位:米 */
|
||||||
|
}mid1_pos;
|
||||||
|
struct{
|
||||||
|
joint_pos leg[GO_MOTOR_NUM/3]; /* 四条腿的关节位置,单位:米 */
|
||||||
|
}mid2_pos;
|
||||||
|
struct{
|
||||||
|
joint_pos leg[GO_MOTOR_NUM/3]; /* 四条腿的关节位置,单位:米 */
|
||||||
|
}end_pos;
|
||||||
|
} leg_pos; /* 四条腿的关节位置,单位:米 */
|
||||||
|
|
||||||
/* 模块通用 */
|
/* 模块通用 */
|
||||||
CMD_ChassisMode_t mode; /* 底盘模式 */
|
CMD_ChassisMode_t mode; /* 底盘模式 */
|
||||||
|
|
||||||
CMD_ChassisAction_t action; /* 底盘模式 */
|
CMD_ChassisAction_t action; /* 底盘模式 */
|
||||||
|
|
||||||
/* 底盘设计 */
|
AHRS_Eulr_t eulr_imu; /* 欧拉角,单位:弧度 */
|
||||||
int8_t num_joint; /* 关节数量 */
|
|
||||||
|
|
||||||
|
AHRS_Eulr_t delta_eulr; /* 欧拉角变化量,单位:弧度 */
|
||||||
|
|
||||||
|
AHRS_Eulr_t eulr_setpoint; /* 期望的欧拉角,单位:弧度 */
|
||||||
|
|
||||||
MoveVector_t move_vec; /* 底盘实际的运动向量 */
|
MoveVector_t move_vec; /* 底盘实际的运动向量 */
|
||||||
|
|
||||||
/* 反馈控制用的PID */
|
/* 反馈控制用的PID */
|
||||||
struct {
|
struct {
|
||||||
KPID_t *motor_id; /* 控制轮子电机用的PID的动态数组 */
|
KPID_t *motor_id; /* 控制轮子电机用的PID的动态数组 */
|
||||||
|
KPID_t blance; /* 平衡PID */
|
||||||
} pid;
|
} pid;
|
||||||
|
|
||||||
/* 滤波器 */
|
/* 滤波器 */
|
||||||
@ -178,6 +193,14 @@ int8_t Chassis_Init(Chassis_t *c, const Chassis_Params_t *param,float target_fre
|
|||||||
*/
|
*/
|
||||||
int8_t Chassis_UpdateFeedback(Chassis_t *c, const GO_ChassisFeedback_t *go);
|
int8_t Chassis_UpdateFeedback(Chassis_t *c, const GO_ChassisFeedback_t *go);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 更新底盘的IMU信息
|
||||||
|
* @param c 包含底盘数据的结构体
|
||||||
|
* @param eulr 包含IMU欧拉角的结构体
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
int8_t Chassis_UpdateImu(Chassis_t *c, const AHRS_Eulr_t *eulr);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief 运行底盘控制逻辑
|
* \brief 运行底盘控制逻辑
|
||||||
*
|
*
|
||||||
|
@ -30,13 +30,24 @@ Config_t param_default = {
|
|||||||
.type = CHASSIS_TYPE_QUADRUPED,
|
.type = CHASSIS_TYPE_QUADRUPED,
|
||||||
|
|
||||||
.torque_pid_param = {
|
.torque_pid_param = {
|
||||||
.k = 1.0f, /* 控制器增益 */
|
.k = 5.0f, /* 控制器增益 */
|
||||||
.p = 1.0f, /* 比例项增益 */
|
.p = 20.0f, /* 比例项增益 */
|
||||||
.i = 0.0f, /* 积分项增益 */
|
.i = 1.0f, /* 积分项增益 */
|
||||||
.d = 0.0f, /* 微分项增益 */
|
.d = 0.0f, /* 微分项增益 */
|
||||||
.i_limit = 0.0f, /* 积分项上限 */
|
.i_limit = 100.0f, /* 积分项上限 */
|
||||||
.out_limit = 0.0f, /* 输出绝对值限制 */
|
.out_limit = 100.0f, /* 输出绝对值限制 */
|
||||||
.d_cutoff_freq = 10.0f, /* D项低通截止频率 */
|
.d_cutoff_freq = -1.0f, /* D项低通截止频率 */
|
||||||
|
.range = -1.0f, /* 计算循环误差时使用,大于0时启用 */
|
||||||
|
},
|
||||||
|
|
||||||
|
.blance_pid_param = {
|
||||||
|
.k = 1.00f, /* 控制器增益 */
|
||||||
|
.p = 0.08f, /* 比例项增益 */
|
||||||
|
.i = 0.08f, /* 积分项增益 */
|
||||||
|
.d = 0.0f, /* 微分项增益 */
|
||||||
|
.i_limit = 0.05f, /* 积分项上限 */
|
||||||
|
.out_limit = 0.1f, /* 输出绝对值限制 */
|
||||||
|
.d_cutoff_freq = -1.0f, /* D项低通截止频率 */
|
||||||
.range = -1.0f, /* 计算循环误差时使用,大于0时启用 */
|
.range = -1.0f, /* 计算循环误差时使用,大于0时启用 */
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -91,19 +102,19 @@ Config_t param_default = {
|
|||||||
.named = {
|
.named = {
|
||||||
.lf_hip = 0.02f, /* 左前腿髋关节零点角度,单位:弧度 */
|
.lf_hip = 0.02f, /* 左前腿髋关节零点角度,单位:弧度 */
|
||||||
.lf_thigh = -3.17f, /* 左前腿大腿零点角度,单位:弧度 */
|
.lf_thigh = -3.17f, /* 左前腿大腿零点角度,单位:弧度 */
|
||||||
.lf_calf = 2.84f - JOINT_CALF_OFFSET, /* 左前腿小腿零点角度,单位:弧度 */
|
.lf_calf = 0.72f - JOINT_CALF_OFFSET, /* 左前腿小腿零点角度,单位:弧度 */
|
||||||
|
|
||||||
.rf_hip = 5.37f, /* 右前腿髋关节零点角度,单位:弧度 */
|
.rf_hip = 5.37f, /* 右前腿髋关节零点角度,单位:弧度 */
|
||||||
.rf_thigh = 9.38f, /* 右前腿大腿零点角度,单位:弧度 */
|
.rf_thigh = 9.38f, /* 右前腿大腿零点角度,单位:弧度 */
|
||||||
.rf_calf = 4.45f + JOINT_CALF_OFFSET, /* 右前腿小腿零点角度,单位:弧度 */
|
.rf_calf = 4.96f + JOINT_CALF_OFFSET, /* 右前腿小腿零点角度,单位:弧度 */
|
||||||
|
|
||||||
.lr_hip = 4.70f, /* 左后腿髋关节零点角度,单位:弧度 */
|
.lr_hip = 4.5f, /* 左后腿髋关节零点角度,单位:弧度 */
|
||||||
.lr_thigh = -4.11f, /* 左后腿大腿零点角度,单位:弧度 */
|
.lr_thigh = -3.2f, /* 左后腿大腿零点角度,单位:弧度 */
|
||||||
.lr_calf = 2.56f - JOINT_CALF_OFFSET, /* 左后腿小腿零点角度,单位:弧度 */
|
.lr_calf = 1.73f - JOINT_CALF_OFFSET, /* 左后腿小腿零点角度,单位:弧度 */
|
||||||
|
|
||||||
.rr_hip = 2.85f, /* 右后腿髋关节零点角度,单位:弧度 */
|
.rr_hip = 2.7f, /* 右后腿髋关节零点角度,单位:弧度 */
|
||||||
.rr_thigh = 10.81f, /* 右后腿大腿零点角度,单位:弧度 */
|
.rr_thigh = 10.58f, /* 右后腿大腿零点角度,单位:弧度 */
|
||||||
.rr_calf = 3.59f + JOINT_CALF_OFFSET, /* 右后腿小腿零点角度,单位:弧度 */
|
.rr_calf = 3.58f + JOINT_CALF_OFFSET, /* 右后腿小腿零点角度,单位:弧度 */
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -26,7 +26,12 @@ N100_t n100;
|
|||||||
BMI088_Cali_t bmi088_cali = {
|
BMI088_Cali_t bmi088_cali = {
|
||||||
.gyro_offset = {0.0f, 0.0f, 0.0f},
|
.gyro_offset = {0.0f, 0.0f, 0.0f},
|
||||||
}; /* BMI088校准数据 */
|
}; /* BMI088校准数据 */
|
||||||
AHRS_Magn_t mage = {0.0f, 0.0f, 0.0f}; /* 磁力计数据,未使用 */
|
N100_Cali_t n100_cali = {
|
||||||
|
.offset_x = 0.0f,
|
||||||
|
.offset_y = 0.0f,
|
||||||
|
.offset_z = 0.0f,
|
||||||
|
};
|
||||||
|
AHRS_Magn_t mage = {-0.01f, 0.025f, 0.0f}; /* 磁力计数据,未使用 */
|
||||||
AHRS_t gimbal_ahrs;
|
AHRS_t gimbal_ahrs;
|
||||||
AHRS_Eulr_t eulr_to_send;
|
AHRS_Eulr_t eulr_to_send;
|
||||||
|
|
||||||
@ -109,14 +114,15 @@ void Task_atti_esti(void *argument) {
|
|||||||
N100_StartReceiving(&n100);
|
N100_StartReceiving(&n100);
|
||||||
if (N100_WaitDmaCplt()) {
|
if (N100_WaitDmaCplt()) {
|
||||||
osKernelLock();
|
osKernelLock();
|
||||||
N100_ParseData(&n100);
|
N100_ParseData(&n100, &n100_cali); /* 解析N100接收到的数据 */
|
||||||
osKernelUnlock();
|
osKernelUnlock();
|
||||||
} else {
|
} else {
|
||||||
N100_HandleOffline(&n100);
|
N100_HandleOffline(&n100);
|
||||||
}
|
}
|
||||||
osMessageQueueReset(task_runtime.msgq.body.eulr_imu); /* 重置消息队列 */
|
osMessageQueueReset(task_runtime.msgq.body.eulr_imu); /* 重置消息队列 */
|
||||||
osMessageQueuePut(task_runtime.msgq.body.eulr_imu, &n100.eulr, 0, 0); /* 将欧拉角数据放入消息队列 */
|
osMessageQueuePut(task_runtime.msgq.body.eulr_imu, &n100.eulr, 0, 0); /* 将欧拉角数据放入消息队列 */
|
||||||
|
osMessageQueueReset(task_runtime.msgq.chassis.eulr_imu); /* 重置消息队列 */
|
||||||
|
osMessageQueuePut(task_runtime.msgq.chassis.eulr_imu, &n100.eulr, 0, 0); /* 将欧拉角数据放入消息队列 */
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
/* Private variables -------------------------------------------------------- */
|
/* Private variables -------------------------------------------------------- */
|
||||||
/* USER STRUCT BEGIN*/
|
/* USER STRUCT BEGIN*/
|
||||||
Chassis_t chassis;
|
Chassis_t chassis;
|
||||||
|
AHRS_Eulr_t chassis_eulr; /* 底盘IMU欧拉角数据 */
|
||||||
GO_ChassisFeedback_t chassis_feedback; /* 底盘反馈数据 */
|
GO_ChassisFeedback_t chassis_feedback; /* 底盘反馈数据 */
|
||||||
GO_ChassisCMD_t chassis_output; /* 底盘输出数据 */
|
GO_ChassisCMD_t chassis_output; /* 底盘输出数据 */
|
||||||
CMD_ChassisCmd_t chassis_cmd; /* 底盘控制命令 */
|
CMD_ChassisCmd_t chassis_cmd; /* 底盘控制命令 */
|
||||||
@ -43,6 +44,9 @@ void Task_ctrl_chassis(void *argument) {
|
|||||||
if(osMessageQueueGet(task_runtime.msgq.chassis.feefback, &chassis_feedback, NULL, 0) == osOK) { /* 从消息队列中获取底盘反馈数据 */
|
if(osMessageQueueGet(task_runtime.msgq.chassis.feefback, &chassis_feedback, NULL, 0) == osOK) { /* 从消息队列中获取底盘反馈数据 */
|
||||||
Chassis_UpdateFeedback(&chassis, &chassis_feedback); /* 更新底盘反馈数据 */
|
Chassis_UpdateFeedback(&chassis, &chassis_feedback); /* 更新底盘反馈数据 */
|
||||||
}
|
}
|
||||||
|
if(osMessageQueueGet(task_runtime.msgq.chassis.eulr_imu, &chassis_eulr, NULL, 0) == osOK) { /* 从消息队列中获取IMU欧拉角数据 */
|
||||||
|
Chassis_UpdateImu(&chassis, &chassis_eulr); /* 更新底盘IMU数据 */
|
||||||
|
}
|
||||||
osMessageQueueGet(task_runtime.msgq.cmd.chassis, &chassis_cmd, NULL, 0);
|
osMessageQueueGet(task_runtime.msgq.cmd.chassis, &chassis_cmd, NULL, 0);
|
||||||
|
|
||||||
osKernelLock();
|
osKernelLock();
|
||||||
|
@ -46,6 +46,7 @@ void Task_Init(void *argument) {
|
|||||||
task_runtime.msgq.chassis.feefback = osMessageQueueNew(2u, sizeof(GO_ChassisFeedback_t), NULL);
|
task_runtime.msgq.chassis.feefback = osMessageQueueNew(2u, sizeof(GO_ChassisFeedback_t), NULL);
|
||||||
task_runtime.msgq.chassis.output = osMessageQueueNew(2u, sizeof(GO_ChassisCMD_t), NULL);
|
task_runtime.msgq.chassis.output = osMessageQueueNew(2u, sizeof(GO_ChassisCMD_t), NULL);
|
||||||
task_runtime.msgq.body.eulr_imu = osMessageQueueNew(2u, sizeof(AHRS_Eulr_t), NULL);
|
task_runtime.msgq.body.eulr_imu = osMessageQueueNew(2u, sizeof(AHRS_Eulr_t), NULL);
|
||||||
|
task_runtime.msgq.chassis.eulr_imu = osMessageQueueNew(2u, sizeof(AHRS_Eulr_t), NULL);
|
||||||
/* USER MESSAGE END */
|
/* USER MESSAGE END */
|
||||||
|
|
||||||
osKernelUnlock(); // 解锁内核
|
osKernelUnlock(); // 解锁内核
|
||||||
|
@ -6,8 +6,7 @@
|
|||||||
/* Includes ----------------------------------------------------------------- */
|
/* Includes ----------------------------------------------------------------- */
|
||||||
#include "task/user_task.h"
|
#include "task/user_task.h"
|
||||||
/* USER INCLUDE BEGIN*/
|
/* USER INCLUDE BEGIN*/
|
||||||
#include "bsp/uart.h"
|
#include "bsp/buzzer.h"
|
||||||
#include "gom_protocol.h"
|
|
||||||
/* USER INCLUDE END*/
|
/* USER INCLUDE END*/
|
||||||
|
|
||||||
/* Private typedef ---------------------------------------------------------- */
|
/* Private typedef ---------------------------------------------------------- */
|
||||||
@ -15,10 +14,8 @@
|
|||||||
/* Private macro ------------------------------------------------------------ */
|
/* Private macro ------------------------------------------------------------ */
|
||||||
/* Private variables -------------------------------------------------------- */
|
/* Private variables -------------------------------------------------------- */
|
||||||
/* USER STRUCT BEGIN*/
|
/* USER STRUCT BEGIN*/
|
||||||
MotorCmd_t mcmd = {0};
|
// const BSP_Buzzer_Note_t music_notes[] = { BSP_BUZZER_NOTE_A6, BSP_BUZZER_NOTE_G6, BSP_BUZZER_NOTE_F5, BSP_BUZZER_NOTE_G6, BSP_BUZZER_NOTE_G5, BSP_BUZZER_NOTE_G6, BSP_BUZZER_NOTE_F4, BSP_BUZZER_NOTE_D4, BSP_BUZZER_NOTE_C6, BSP_BUZZER_NOTE_C4, 0, BSP_BUZZER_NOTE_F5, BSP_BUZZER_NOTE_G5, BSP_BUZZER_NOTE_C7, BSP_BUZZER_NOTE_C6, 0, BSP_BUZZER_NOTE_D6, BSP_BUZZER_NOTE_B6, BSP_BUZZER_NOTE_C4, BSP_BUZZER_NOTE_B6, BSP_BUZZER_NOTE_G6, BSP_BUZZER_NOTE_A6, BSP_BUZZER_NOTE_F4, BSP_BUZZER_NOTE_C4, BSP_BUZZER_NOTE_A6, 0, BSP_BUZZER_NOTE_C5, BSP_BUZZER_NOTE_D6, BSP_BUZZER_NOTE_C6, 0, BSP_BUZZER_NOTE_C4, BSP_BUZZER_NOTE_E4, BSP_BUZZER_NOTE_C6, BSP_BUZZER_NOTE_B6, BSP_BUZZER_NOTE_A6, BSP_BUZZER_NOTE_E6, BSP_BUZZER_NOTE_A6, BSP_BUZZER_NOTE_E4, BSP_BUZZER_NOTE_G4, BSP_BUZZER_NOTE_C4, BSP_BUZZER_NOTE_F6, BSP_BUZZER_NOTE_C4, 0 };
|
||||||
MotorData_t data = {0};
|
// uint32_t index = 0; /* 音符索引 */
|
||||||
|
|
||||||
|
|
||||||
/* USER STRUCT END*/
|
/* USER STRUCT END*/
|
||||||
|
|
||||||
/* Private function --------------------------------------------------------- */
|
/* Private function --------------------------------------------------------- */
|
||||||
@ -33,28 +30,20 @@ void Task_test(void *argument) {
|
|||||||
osDelay(TEST_INIT_DELAY); /* 延时一段时间再开启任务 */
|
osDelay(TEST_INIT_DELAY); /* 延时一段时间再开启任务 */
|
||||||
|
|
||||||
/* USER CODE INIT BEGIN*/
|
/* USER CODE INIT BEGIN*/
|
||||||
mcmd.id = 0;
|
BSP_Buzzer_Start(); /* 启动蜂鸣器 */
|
||||||
mcmd.mode = 1;
|
BSP_Buzzer_Set_Note(BSP_BUZZER_NOTE_C4, 0.5f); /* 设置蜂鸣器音符为C4,延时0.5秒 */
|
||||||
mcmd.K_P = 0;
|
BSP_Buzzer_Set_Note(BSP_BUZZER_NOTE_E4, 0.5f); /* 设置蜂鸣器音符为E4,延时0.5秒 */
|
||||||
mcmd.K_W = 0;
|
BSP_Buzzer_Set_Note(BSP_BUZZER_NOTE_G4, 0.5f); /* 设置蜂鸣器音符为G4,延时0.5秒 */
|
||||||
mcmd.Pos = 0;
|
BSP_Buzzer_Stop(); /* 停止蜂鸣器 */
|
||||||
mcmd.W = 0;
|
// BSP_Buzzer_Set(2000,0.5f);
|
||||||
mcmd.T = 0;
|
|
||||||
/* USER CODE INIT END*/
|
/* USER CODE INIT END*/
|
||||||
|
|
||||||
uint32_t tick = osKernelGetTickCount(); /* 控制任务运行频率的计时 */
|
uint32_t tick = osKernelGetTickCount(); /* 控制任务运行频率的计时 */
|
||||||
while (1) {
|
while (1) {
|
||||||
tick += delay_tick; /* 计算下一个唤醒时刻 */
|
tick += delay_tick; /* 计算下一个唤醒时刻 */
|
||||||
/* USER CODE BEGIN */
|
/* USER CODE BEGIN */
|
||||||
|
// BSP_Buzzer_Set_Note(music_notes[index], 0.02f); /* 设置蜂鸣器音符 */
|
||||||
// modify_data(&mcmd);
|
// index++;
|
||||||
|
|
||||||
// HAL_UART_Transmit(BSP_UART_GetHandle(BSP_UART_LEFT_LEG), (uint8_t *)&mcmd.motor_send_data, sizeof(mcmd.motor_send_data), 1);
|
|
||||||
|
|
||||||
// HAL_UART_Receive(BSP_UART_GetHandle(BSP_UART_LEFT_LEG), (uint8_t *)&data.motor_recv_data, sizeof(data.motor_recv_data), 1);
|
|
||||||
|
|
||||||
// extract_data(&data);
|
|
||||||
|
|
||||||
|
|
||||||
/* USER CODE END */
|
/* USER CODE END */
|
||||||
osDelayUntil(tick); /* 运行结束,等待下一次唤醒 */
|
osDelayUntil(tick); /* 运行结束,等待下一次唤醒 */
|
||||||
|
@ -13,7 +13,7 @@ extern "C" {
|
|||||||
/* USER INCLUDE END */
|
/* USER INCLUDE END */
|
||||||
/* Exported constants ------------------------------------------------------- */
|
/* Exported constants ------------------------------------------------------- */
|
||||||
/* 任务运行频率 */
|
/* 任务运行频率 */
|
||||||
#define TEST_FREQ (1000)
|
#define TEST_FREQ (50)
|
||||||
#define MONITOR_FREQ (100)
|
#define MONITOR_FREQ (100)
|
||||||
#define CMD_FREQ (500)
|
#define CMD_FREQ (500)
|
||||||
#define CTRL_LEG_FREQ (800)
|
#define CTRL_LEG_FREQ (800)
|
||||||
@ -67,6 +67,7 @@ typedef struct {
|
|||||||
struct {
|
struct {
|
||||||
osMessageQueueId_t feefback;
|
osMessageQueueId_t feefback;
|
||||||
osMessageQueueId_t output;
|
osMessageQueueId_t output;
|
||||||
|
osMessageQueueId_t eulr_imu;
|
||||||
}chassis; /* 底盘控制消息队列 */
|
}chassis; /* 底盘控制消息队列 */
|
||||||
} msgq;
|
} msgq;
|
||||||
/* USER MESSAGE END */
|
/* USER MESSAGE END */
|
||||||
|
BIN
Utils/1111.mp3
Normal file
BIN
Utils/1111.mp3
Normal file
Binary file not shown.
4
Utils/222.c
Normal file
4
Utils/222.c
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
// 自动生成的音符数组,每0.2秒检测一次主旋律
|
||||||
|
const BSP_Buzzer_Note_t music_notes[] = { BSP_BUZZER_NOTE_A6, BSP_BUZZER_NOTE_G6, BSP_BUZZER_NOTE_F5, BSP_BUZZER_NOTE_G6, BSP_BUZZER_NOTE_G5, BSP_BUZZER_NOTE_G6, BSP_BUZZER_NOTE_F4, BSP_BUZZER_NOTE_D4, BSP_BUZZER_NOTE_C6, BSP_BUZZER_NOTE_C4, 0, BSP_BUZZER_NOTE_F5, BSP_BUZZER_NOTE_G5, BSP_BUZZER_NOTE_C7, BSP_BUZZER_NOTE_C6, 0, BSP_BUZZER_NOTE_D6, BSP_BUZZER_NOTE_B6, BSP_BUZZER_NOTE_C4, BSP_BUZZER_NOTE_B6, BSP_BUZZER_NOTE_G6, BSP_BUZZER_NOTE_A6, BSP_BUZZER_NOTE_F4, BSP_BUZZER_NOTE_C4, BSP_BUZZER_NOTE_A6, 0, BSP_BUZZER_NOTE_C5, BSP_BUZZER_NOTE_D6, BSP_BUZZER_NOTE_C6, 0, BSP_BUZZER_NOTE_C4, BSP_BUZZER_NOTE_E4, BSP_BUZZER_NOTE_C6, BSP_BUZZER_NOTE_B6, BSP_BUZZER_NOTE_A6, BSP_BUZZER_NOTE_E6, BSP_BUZZER_NOTE_A6, BSP_BUZZER_NOTE_E4, BSP_BUZZER_NOTE_G4, BSP_BUZZER_NOTE_C4, BSP_BUZZER_NOTE_F6, BSP_BUZZER_NOTE_C4, 0 };
|
||||||
|
const float music_notes_durations[] = { 0.20f, 0.20f, 0.20f, 0.20f, 0.20f, 0.20f, 0.20f, 0.20f, 0.20f, 0.20f, 0.20f, 0.20f, 0.20f, 0.20f, 0.20f, 0.20f, 0.20f, 0.20f, 0.20f, 0.20f, 0.20f, 0.80f, 0.20f, 0.20f, 0.20f, 0.60f, 0.20f, 0.20f, 0.20f, 0.20f, 0.20f, 0.20f, 0.20f, 0.20f, 0.40f, 0.20f, 0.20f, 0.20f, 0.20f, 0.20f, 0.20f, 0.40f, 0.80f };
|
||||||
|
const unsigned int music_notes_len = 43;
|
BIN
Utils/222.mp3
Normal file
BIN
Utils/222.mp3
Normal file
Binary file not shown.
@ -1,78 +1,177 @@
|
|||||||
# test_kinematics.py
|
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
from mpl_toolkits.mplot3d import Axes3D
|
||||||
|
|
||||||
def forward_kinematics(theta, link, side_sign):
|
# 定义机械参数
|
||||||
l1 = link['hip'] * side_sign
|
leg_params = {
|
||||||
l2 = -link['thigh']
|
'hip': 0.1, # 髋关节长度
|
||||||
l3 = -link['calf']
|
'thigh': 0.3, # 大腿长度
|
||||||
|
'calf': 0.3 # 小腿长度
|
||||||
|
}
|
||||||
|
|
||||||
|
# 定义关节方向符号 (与C代码中一致)
|
||||||
|
signs = {
|
||||||
|
'left_front': {'hip': 1, 'thigh': 1, 'calf': -1},
|
||||||
|
'right_front': {'hip': -1, 'thigh': -1, 'calf': 1},
|
||||||
|
'left_rear': {'hip': -1, 'thigh': 1, 'calf': -1},
|
||||||
|
'right_rear': {'hip': 1, 'thigh': -1, 'calf': 1}
|
||||||
|
}
|
||||||
|
|
||||||
|
def forward_kinematics(theta, leg_params, sign):
|
||||||
|
"""正运动学"""
|
||||||
q1, q2, q3 = theta
|
q1, q2, q3 = theta
|
||||||
|
q2 = q2 * sign['thigh']
|
||||||
|
q3 = q3 * sign['calf']
|
||||||
|
|
||||||
|
l1 = leg_params['hip'] * sign['hip']
|
||||||
|
l2 = leg_params['thigh']
|
||||||
|
l3 = leg_params['calf']
|
||||||
|
|
||||||
|
# 计算足端位置
|
||||||
|
x = l2 * np.cos(q2) + l3 * np.cos(q2 + q3)
|
||||||
|
y = l1 * np.cos(q1) + np.sin(q1) * (l2 * np.sin(q2) + l3 * np.sin(q2 + q3))
|
||||||
|
z = l1 * np.sin(q1) - np.cos(q1) * (l2 * np.sin(q2) + l3 * np.sin(q2 + q3))
|
||||||
|
|
||||||
|
foot_pos = np.array([x, -y if sign['hip'] == sign['thigh'] else y, z])
|
||||||
|
return foot_pos
|
||||||
|
|
||||||
s1, s2, s3 = np.sin([q1, q2, q3])
|
def inverse_kinematics(foot_pos, leg_params, sign):
|
||||||
c1, c2, c3 = np.cos([q1, q2, q3])
|
"""逆运动学"""
|
||||||
|
px = foot_pos[0]
|
||||||
c23 = c2 * c3 - s2 * s3
|
py = -foot_pos[1] if sign['hip'] == sign['thigh'] else foot_pos[1]
|
||||||
s23 = s2 * c3 + c2 * s3
|
pz = foot_pos[2]
|
||||||
|
|
||||||
x = l3 * s23 + l2 * s2
|
b2y = leg_params['hip'] * sign['hip']
|
||||||
y = -l3 * s1 * c23 + l1 * c1 - l2 * c2 * s1
|
b3z = -leg_params['thigh']
|
||||||
z = l3 * c1 * c23 + l1 * s1 + l2 * c1 * c2
|
b4z = -leg_params['calf']
|
||||||
return np.array([x, y, z])
|
a = leg_params['hip']
|
||||||
|
c = np.sqrt(px**2 + py**2 + pz**2)
|
||||||
def q1_ik(py, pz, l1):
|
b = np.sqrt(c**2 - a**2)
|
||||||
L = np.sqrt(py*py + pz*pz - l1*l1)
|
|
||||||
return np.arctan2(pz*l1 + py*L, py*l1 - pz*L)
|
# 检查可达性
|
||||||
|
|
||||||
def q3_ik(b3z, b4z, b):
|
|
||||||
temp = (b3z**2 + b4z**2 - b**2) / (2.0 * np.abs(b3z * b4z))
|
|
||||||
temp = np.clip(temp, -1.0, 1.0)
|
|
||||||
q3 = np.arccos(temp)
|
|
||||||
return -(np.pi - q3)
|
|
||||||
|
|
||||||
def q2_ik(q1, q3, px, py, pz, b3z, b4z):
|
|
||||||
a1 = py * np.sin(q1) - pz * np.cos(q1)
|
|
||||||
a2 = px
|
|
||||||
m1 = b4z * np.sin(q3)
|
|
||||||
m2 = b3z + b4z * np.cos(q3)
|
|
||||||
return np.arctan2(m1 * a1 + m2 * a2, m1 * a2 - m2 * a1)
|
|
||||||
|
|
||||||
def inverse_kinematics(foot, link, side_sign):
|
|
||||||
px, py, pz = foot
|
|
||||||
b2y = link['hip'] * side_sign
|
|
||||||
b3z = -link['thigh']
|
|
||||||
b4z = -link['calf']
|
|
||||||
a = link['hip']
|
|
||||||
c = np.sqrt(px*px + py*py + pz*pz)
|
|
||||||
b = np.sqrt(c*c - a*a)
|
|
||||||
if np.isnan(b) or b > abs(b3z) + abs(b4z):
|
if np.isnan(b) or b > abs(b3z) + abs(b4z):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def q1_ik(py, pz, l1):
|
||||||
|
L = np.sqrt(py**2 + pz**2 - l1**2)
|
||||||
|
return np.arctan2(pz*l1 + py*L, py*l1 - pz*L)
|
||||||
|
|
||||||
|
def q3_ik(b3z, b4z, b):
|
||||||
|
temp = (b3z**2 + b4z**2 - b**2) / (2.0 * abs(b3z * b4z))
|
||||||
|
temp = np.clip(temp, -1.0, 1.0)
|
||||||
|
q3 = np.arccos(temp)
|
||||||
|
return -(np.pi - q3)
|
||||||
|
|
||||||
|
def q2_ik(q1, q3, px, py, pz, b3z, b4z):
|
||||||
|
a1 = py * np.sin(q1) - pz * np.cos(q1)
|
||||||
|
a2 = px
|
||||||
|
m1 = b4z * np.sin(q3)
|
||||||
|
m2 = b3z + b4z * np.cos(q3)
|
||||||
|
return np.arctan2(m1*a1 + m2*a2, m1*a2 - m2*a1)
|
||||||
|
|
||||||
q1 = q1_ik(py, pz, b2y)
|
q1 = q1_ik(py, pz, b2y)
|
||||||
q3 = q3_ik(b3z, b4z, b)
|
q3 = q3_ik(b3z, b4z, b)
|
||||||
q2 = q2_ik(q1, q3, px, py, pz, b3z, b4z)
|
q2 = q2_ik(q1, q3, px, py, pz, b3z, b4z)
|
||||||
return np.array([q1, q2, q3])
|
|
||||||
|
return np.array([q1, q2 * sign['thigh'], q3 * sign['calf']])
|
||||||
|
|
||||||
# ...existing code...
|
def test_kinematics(leg_sign, test_name):
|
||||||
|
print(f"\nTesting {test_name} leg...")
|
||||||
|
|
||||||
|
# 生成随机关节角度
|
||||||
|
np.random.seed(42)
|
||||||
|
test_angles = np.random.uniform(-np.pi/2, np.pi/2, (10, 3))
|
||||||
|
|
||||||
|
errors = []
|
||||||
|
|
||||||
|
for angles in test_angles:
|
||||||
|
# 正运动学计算足端位置
|
||||||
|
foot_pos = forward_kinematics(angles, leg_params, leg_sign)
|
||||||
|
|
||||||
|
# 逆运动学计算关节角度
|
||||||
|
calculated_angles = inverse_kinematics(foot_pos, leg_params, leg_sign)
|
||||||
|
|
||||||
|
if calculated_angles is None:
|
||||||
|
print(f"Unreachable position for angles: {angles}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
# 计算误差
|
||||||
|
error = np.max(np.abs(angles - calculated_angles))
|
||||||
|
errors.append(error)
|
||||||
|
|
||||||
|
print(f"Original angles: {angles}")
|
||||||
|
print(f"Calculated angles: {calculated_angles}")
|
||||||
|
print(f"Error: {error:.6f} rad\n")
|
||||||
|
|
||||||
|
avg_error = np.mean(errors)
|
||||||
|
max_error = np.max(errors)
|
||||||
|
print(f"Average error: {avg_error:.6f} rad")
|
||||||
|
print(f"Maximum error: {max_error:.6f} rad")
|
||||||
|
|
||||||
|
return avg_error, max_error
|
||||||
|
|
||||||
def test():
|
# 测试所有四种腿型
|
||||||
link = {'hip': 0.05, 'thigh': 0.2, 'calf': 0.2}
|
results = {}
|
||||||
side_sign = 1 # 左前腿
|
for leg_name, leg_sign in signs.items():
|
||||||
|
avg_err, max_err = test_kinematics(leg_sign, leg_name)
|
||||||
|
results[leg_name] = (avg_err, max_err)
|
||||||
|
|
||||||
# 输入一个目标点
|
# 打印汇总结果
|
||||||
foot_target = np.array([0.0, 0.06, -0.2])
|
print("\nSummary of results:")
|
||||||
print("目标足端位置:", foot_target)
|
for leg_name, (avg_err, max_err) in results.items():
|
||||||
|
print(f"{leg_name}: avg error={avg_err:.6f} rad, max error={max_err:.6f} rad")
|
||||||
|
|
||||||
# 逆运动学求解关节角度
|
# 可视化一个例子
|
||||||
theta = inverse_kinematics(foot_target, link, side_sign)
|
fig = plt.figure(figsize=(10, 8))
|
||||||
if theta is None:
|
ax = fig.add_subplot(111, projection='3d')
|
||||||
print("目标点不可达!")
|
|
||||||
return
|
|
||||||
print("逆运动学解(关节角度,单位:弧度):", theta)
|
|
||||||
print("逆运动学解(关节角度,单位:度):", np.degrees(theta))
|
|
||||||
|
|
||||||
# 正运动学验算
|
# 选择左前腿作为示例
|
||||||
foot_check = forward_kinematics(theta, link, side_sign)
|
leg_sign = signs['left_front']
|
||||||
print("正运动学验算足端位置:", foot_check)
|
angles = np.array([0.5, -0.3, 0.7]) # 髋关节、大腿、小腿角度
|
||||||
print("位置误差:", np.abs(foot_target - foot_check))
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
# 计算正运动学
|
||||||
test()
|
foot_pos = forward_kinematics(angles, leg_params, leg_sign)
|
||||||
|
|
||||||
|
# 绘制
|
||||||
|
l1 = leg_params['hip'] * leg_sign['hip']
|
||||||
|
l2 = leg_params['thigh']
|
||||||
|
l3 = leg_params['calf']
|
||||||
|
|
||||||
|
q1, q2, q3 = angles
|
||||||
|
q2 = q2 * leg_sign['thigh']
|
||||||
|
q3 = q3 * leg_sign['calf']
|
||||||
|
|
||||||
|
# 计算各关节位置
|
||||||
|
hip_pos = np.array([0, 0, 0])
|
||||||
|
knee_pos = np.array([
|
||||||
|
l2 * np.cos(q2),
|
||||||
|
l1 * np.cos(q1) + np.sin(q1) * l2 * np.sin(q2),
|
||||||
|
l1 * np.sin(q1) - np.cos(q1) * l2 * np.sin(q2)
|
||||||
|
])
|
||||||
|
foot_pos = np.array([
|
||||||
|
l2 * np.cos(q2) + l3 * np.cos(q2 + q3),
|
||||||
|
l1 * np.cos(q1) + np.sin(q1) * (l2 * np.sin(q2) + l3 * np.sin(q2 + q3)),
|
||||||
|
l1 * np.sin(q1) - np.cos(q1) * (l2 * np.sin(q2) + l3 * np.sin(q2 + q3))
|
||||||
|
])
|
||||||
|
|
||||||
|
# 调整y坐标符号
|
||||||
|
if leg_sign['hip'] != leg_sign['thigh']:
|
||||||
|
knee_pos[1] = -knee_pos[1]
|
||||||
|
foot_pos[1] = -foot_pos[1]
|
||||||
|
|
||||||
|
# 绘制连杆
|
||||||
|
ax.plot([hip_pos[0], knee_pos[0]], [hip_pos[1], knee_pos[1]], [hip_pos[2], knee_pos[2]], 'b-', linewidth=3)
|
||||||
|
ax.plot([knee_pos[0], foot_pos[0]], [knee_pos[1], foot_pos[1]], [knee_pos[2], foot_pos[2]], 'r-', linewidth=3)
|
||||||
|
|
||||||
|
# 绘制关节
|
||||||
|
ax.scatter(hip_pos[0], hip_pos[1], hip_pos[2], c='k', s=100, label='Hip')
|
||||||
|
ax.scatter(knee_pos[0], knee_pos[1], knee_pos[2], c='k', s=100, label='Knee')
|
||||||
|
ax.scatter(foot_pos[0], foot_pos[1], foot_pos[2], c='g', s=100, label='Foot')
|
||||||
|
|
||||||
|
ax.set_xlabel('X (m)')
|
||||||
|
ax.set_ylabel('Y (m)')
|
||||||
|
ax.set_zlabel('Z (m)')
|
||||||
|
ax.set_title('Leg Kinematics Example (Left Front Leg)')
|
||||||
|
ax.legend()
|
||||||
|
plt.tight_layout()
|
||||||
|
plt.show()
|
65
Utils/music.py
Normal file
65
Utils/music.py
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
from pydub import AudioSegment
|
||||||
|
import numpy as np
|
||||||
|
import os
|
||||||
|
import librosa
|
||||||
|
|
||||||
|
# 音符频率表(与buzzer.h一致)
|
||||||
|
NOTE_FREQS = [
|
||||||
|
261, 294, 329, 349, 392, 440, 494, 523, 587, 659, 698, 784, 880, 988, 1047, 1175, 1319, 1397, 1568, 1760, 1976, 2093, 2349, 2637, 2794, 3136, 3520, 3951, 4186
|
||||||
|
]
|
||||||
|
NOTE_NAMES = [
|
||||||
|
"BSP_BUZZER_NOTE_C4", "BSP_BUZZER_NOTE_D4", "BSP_BUZZER_NOTE_E4", "BSP_BUZZER_NOTE_F4", "BSP_BUZZER_NOTE_G4", "BSP_BUZZER_NOTE_A4", "BSP_BUZZER_NOTE_B4",
|
||||||
|
"BSP_BUZZER_NOTE_C5", "BSP_BUZZER_NOTE_D5", "BSP_BUZZER_NOTE_E5", "BSP_BUZZER_NOTE_F5", "BSP_BUZZER_NOTE_G5", "BSP_BUZZER_NOTE_A5", "BSP_BUZZER_NOTE_B5",
|
||||||
|
"BSP_BUZZER_NOTE_C6", "BSP_BUZZER_NOTE_D6", "BSP_BUZZER_NOTE_E6", "BSP_BUZZER_NOTE_F6", "BSP_BUZZER_NOTE_G6", "BSP_BUZZER_NOTE_A6", "BSP_BUZZER_NOTE_B6",
|
||||||
|
"BSP_BUZZER_NOTE_C7", "BSP_BUZZER_NOTE_D7", "BSP_BUZZER_NOTE_E7", "BSP_BUZZER_NOTE_F7", "BSP_BUZZER_NOTE_G7", "BSP_BUZZER_NOTE_A7", "BSP_BUZZER_NOTE_B7",
|
||||||
|
"BSP_BUZZER_NOTE_C8"
|
||||||
|
]
|
||||||
|
|
||||||
|
def freq_to_note_name(freq):
|
||||||
|
# 找到最接近的音符
|
||||||
|
idx = np.argmin(np.abs(np.array(NOTE_FREQS) - freq))
|
||||||
|
return NOTE_NAMES[idx], NOTE_FREQS[idx]
|
||||||
|
|
||||||
|
def mp3_to_note_array(mp3_path, c_array_name="music_notes", out_path=None, interval_sec=0.02):
|
||||||
|
y, sr = librosa.load(mp3_path, sr=None, mono=True)
|
||||||
|
hop_length = int(sr * interval_sec)
|
||||||
|
pitches, magnitudes = librosa.piptrack(y=y, sr=sr, hop_length=hop_length)
|
||||||
|
notes = []
|
||||||
|
durations = []
|
||||||
|
last_note = None
|
||||||
|
for t in range(pitches.shape[1]):
|
||||||
|
index = magnitudes[:, t].argmax()
|
||||||
|
freq = pitches[index, t]
|
||||||
|
if freq > 0:
|
||||||
|
note_name, note_freq = freq_to_note_name(freq)
|
||||||
|
else:
|
||||||
|
note_name = "0"
|
||||||
|
if note_name == last_note and notes:
|
||||||
|
durations[-1] += interval_sec
|
||||||
|
else:
|
||||||
|
notes.append(note_name)
|
||||||
|
durations.append(interval_sec)
|
||||||
|
last_note = note_name
|
||||||
|
|
||||||
|
# 生成C数组字符串
|
||||||
|
notes_str = ", ".join(notes)
|
||||||
|
durations_str = ", ".join([f"{d:.2f}f" for d in durations])
|
||||||
|
c_code = f"// 自动生成的音符数组,每{interval_sec}秒检测一次主旋律\n"
|
||||||
|
c_code += f"const BSP_Buzzer_Note_t {c_array_name}[] = {{ {notes_str} }};\n"
|
||||||
|
c_code += f"const float {c_array_name}_durations[] = {{ {durations_str} }};\n"
|
||||||
|
c_code += f"const unsigned int {c_array_name}_len = {len(notes)};\n"
|
||||||
|
|
||||||
|
# 自动生成输出路径
|
||||||
|
if out_path is None:
|
||||||
|
base, _ = os.path.splitext(mp3_path)
|
||||||
|
out_path = base + ".c"
|
||||||
|
|
||||||
|
# 写入C文件
|
||||||
|
with open(out_path, "w") as f:
|
||||||
|
f.write(c_code)
|
||||||
|
print(f"音符数组已写入: {out_path}")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
mp3_path = "/Users/lvzucheng/Documents/R/CM_DOG/Utils/222.mp3" # 你的mp3文件路径
|
||||||
|
c_array_name = "music_notes" # C数组名
|
||||||
|
mp3_to_note_array(mp3_path, c_array_name, interval_sec=0.2)
|
3
Utils/tldhc.c
Normal file
3
Utils/tldhc.c
Normal file
File diff suppressed because one or more lines are too long
BIN
Utils/tldhc.mp3
Normal file
BIN
Utils/tldhc.mp3
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user