rm_balance/User/module/balance_chassis.c
2025-10-05 17:41:45 +08:00

355 lines
12 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Balance Chassis Module
*/
/* Includes ----------------------------------------------------------------- */
#include "module/balance_chassis.h"
#include "bsp/can.h"
#include "bsp/time.h"
#include "component/user_math.h"
#include "device/motor_lk.h"
#include "module/config.h"
#include <string.h>
#include <math.h>
/* Private typedef ---------------------------------------------------------- */
/* Private define ----------------------------------------------------------- */
#define CAN_CMD_ENABLE_ID 121 // 使能命令ID
#define CAN_CMD_JOINT_ID 122 // 关节控制命令ID
#define CAN_CMD_WHEEL_LEFT_ID 0x128 // 左轮控制命令ID
#define CAN_CMD_WHEEL_RIGHT_ID 0x129 // 右轮控制命令ID
#define CAN_FEEDBACK_JOINT_BASE_ID 124 // 关节反馈基础ID (124-127)
#define CAN_FEEDBACK_WHEEL_LEFT_ID 130 // 左轮反馈ID
#define CAN_FEEDBACK_WHEEL_RIGHT_ID 131 // 右轮反馈ID
/* Private macro ------------------------------------------------------------ */
#define CMD_TIMEOUT_MS 50 // 50ms超时时间允许控制频率在10-20Hz之间
/* Private variables -------------------------------------------------------- */
static bool joint_command_received = false;
static bool wheel_command_received[2] = {false, false};
// 超时管理 - 防止控制频率不同导致的控制/relax交替
static uint32_t joint_last_cmd_time = 0;
static uint32_t wheel_last_cmd_time[2] = {0, 0};
/* Private function --------------------------------------------------------- */
/**
* @brief 检查并处理CAN控制命令
* @param chassis 底盘实例
* @return 成功返回DEVICE_OK
*/
static int8_t Chassis_ProcessCANCommands(Chassis_t *chassis) {
BSP_CAN_Message_t rx_msg;
joint_command_received = false;
wheel_command_received[0] = false;
wheel_command_received[1] = false;
// 检查ID 121 - 使能4个关节电机
if (BSP_CAN_GetMessage(BSP_CAN_1, CAN_CMD_ENABLE_ID, &rx_msg, BSP_CAN_TIMEOUT_IMMEDIATE) == BSP_OK) {
joint_command_received = true;
for (int i = 0; i < 4; i++) {
MOTOR_LZ_Enable(&chassis->param.joint_param[i]);
}
}
// 检查ID 122 - 运控模式控制4个关节电机
if (BSP_CAN_GetMessage(BSP_CAN_1, CAN_CMD_JOINT_ID, &rx_msg, BSP_CAN_TIMEOUT_IMMEDIATE) == BSP_OK) {
joint_command_received = true;
joint_last_cmd_time = BSP_TIME_Get_ms(); // 更新关节命令时间戳
// 8字节数据分别是4个电机的力矩 (每个电机2字节有符号整数精度0.01 Nm)
for (int i = 0; i < 4; i++) {
int16_t torque_raw;
memcpy(&torque_raw, &rx_msg.data[i * 2], sizeof(int16_t));
float torque = (float)torque_raw / 100.0f; // 转换为浮点数力矩值
chassis->output.joint[i] = torque;
// 使用运控模式控制电机只设置力矩其他参数为0
MOTOR_LZ_MotionParam_t motion_param = {
.target_angle = 0.0f,
.target_velocity = 0.0f,
.kp = 0.0f,
.kd = 0.0f,
.torque = torque
};
MOTOR_LZ_MotionControl(&chassis->param.joint_param[i], &motion_param);
}
}
BSP_TIME_Delay_us(500); // 确保CAN总线有足够时间处理消息
// 检查ID 128 - 左轮控制命令 (与电机发送格式一致)
if (BSP_CAN_GetMessage(BSP_CAN_1, CAN_CMD_WHEEL_LEFT_ID, &rx_msg, BSP_CAN_TIMEOUT_IMMEDIATE) == BSP_OK) {
wheel_command_received[0] = true;
wheel_last_cmd_time[0] = BSP_TIME_Get_ms(); // 更新左轮命令时间戳
float left_out = (float)((int16_t)(rx_msg.data[4] | (rx_msg.data[5] << 8))) / 2048.0f;
chassis->output.wheel[0] = left_out;
MOTOR_LK_SetOutput(&chassis->param.wheel_param[0], left_out);
}
// 检查ID 129 - 右轮控制命令 (与电机发送格式一致)
if (BSP_CAN_GetMessage(BSP_CAN_1, CAN_CMD_WHEEL_RIGHT_ID, &rx_msg, BSP_CAN_TIMEOUT_IMMEDIATE) == BSP_OK) {
wheel_command_received[1] = true;
wheel_last_cmd_time[1] = BSP_TIME_Get_ms(); // 更新右轮命令时间戳
float right_out = (float)((int16_t)(rx_msg.data[4] | (rx_msg.data[5] << 8))) / 2048.0f;
chassis->output.wheel[1] = right_out;
MOTOR_LK_SetOutput(&chassis->param.wheel_param[1], right_out);
}
BSP_TIME_Delay_us(500);
return DEVICE_OK;
}
/**
* @brief 发送关节电机反馈数据
* @param chassis 底盘实例
* @return 成功返回DEVICE_OK
*/
static int8_t Chassis_SendJointFeedback(Chassis_t *chassis) {
// 发送4个关节电机的反馈数据ID分别为124、125、126、127
for (int i = 0; i < 4; i++) {
MOTOR_LZ_t* motor = MOTOR_LZ_GetMotor(&chassis->param.joint_param[i]);
if (motor != NULL) {
BSP_CAN_StdDataFrame_t motor_frame = {
.id = CAN_FEEDBACK_JOINT_BASE_ID + i, // ID: 124, 125, 126, 127
.dlc = 8,
.data = {0}
};
// 数据重构:转矩电流(2字节) + 位置(3字节) + 速度(3字节) = 8字节
// 转矩电流 - 转换为16位整数 (精度0.01 Nm)
int16_t torque_int = (int16_t)(motor->lz_feedback.current_torque * 100);
memcpy(&motor_frame.data[0], &torque_int, sizeof(int16_t));
// 位置 - 转换为24位整数使用3字节 (精度0.0001 rad)
int32_t angle_int = (int32_t)(motor->lz_feedback.current_angle * 10000) & 0xFFFFFF;
motor_frame.data[2] = (angle_int >> 16) & 0xFF;
motor_frame.data[3] = (angle_int >> 8) & 0xFF;
motor_frame.data[4] = angle_int & 0xFF;
// 速度 - 转换为24位整数使用3字节 (精度0.001 rad/s)
int32_t velocity_int = (int32_t)(motor->lz_feedback.current_velocity * 1000) & 0xFFFFFF;
motor_frame.data[5] = (velocity_int >> 16) & 0xFF;
motor_frame.data[6] = (velocity_int >> 8) & 0xFF;
motor_frame.data[7] = velocity_int & 0xFF;
BSP_CAN_TransmitStdDataFrame(BSP_CAN_1, &motor_frame);
}
}
return DEVICE_OK;
}
/**
* @brief 发送轮子电机反馈数据
* @param chassis 底盘实例
* @return 成功返回DEVICE_OK
*/
static int8_t Chassis_SendWheelFeedback(Chassis_t *chassis) {
// 发送2个轮子电机的反馈数据ID分别为130、131
for (int i = 0; i < 2; i++) {
MOTOR_LK_t* motor = MOTOR_LK_GetMotor(&chassis->param.wheel_param[i]);
if (motor != NULL) {
BSP_CAN_StdDataFrame_t wheel_frame = {
.id = CAN_FEEDBACK_WHEEL_LEFT_ID + i, // ID: 130, 131
.dlc = 8,
.data = {0}
};
// 使用LK电机的标准反馈格式
// 角度(2字节) + 速度(2字节) + 力矩电流(2字节) + 编码器(2字节)
// 角度 - 转换为16位整数 (精度0.01度)
int16_t angle_int = (int16_t)(motor->motor.feedback.rotor_abs_angle * 180.0f / M_PI * 100);
wheel_frame.data[0] = (angle_int >> 8) & 0xFF;
wheel_frame.data[1] = angle_int & 0xFF;
// 速度 - 转换为16位整数 (精度1dps)
int16_t speed_int = (int16_t)(motor->motor.feedback.rotor_speed);
wheel_frame.data[2] = (speed_int >> 8) & 0xFF;
wheel_frame.data[3] = speed_int & 0xFF;
// 力矩电流 - 转换为16位整数
int16_t current_int = (int16_t)(motor->motor.feedback.torque_current);
wheel_frame.data[4] = (current_int >> 8) & 0xFF;
wheel_frame.data[5] = current_int & 0xFF;
// 编码器值 - 直接使用角度值作为编码器
uint16_t encoder = (uint16_t)(motor->motor.feedback.rotor_abs_angle / (2 * M_PI) * 65535);
wheel_frame.data[6] = (encoder >> 8) & 0xFF;
wheel_frame.data[7] = encoder & 0xFF;
BSP_CAN_TransmitStdDataFrame(BSP_CAN_1, &wheel_frame);
}
}
return DEVICE_OK;
}
/* Exported functions ------------------------------------------------------- */
/**
* @brief 底盘初始化
* @param chassis 底盘实例
* @param param 底盘参数
* @return 成功返回DEVICE_OK
*/
int8_t Chassis_Init(Chassis_t *chassis, Chassis_Param_t *param) {
if (chassis == NULL || param == NULL) {
return DEVICE_ERR_NULL;
}
// 复制参数
memcpy(&chassis->param, param, sizeof(Chassis_Param_t));
// 初始化CAN
BSP_CAN_Init();
// 初始化电机驱动
MOTOR_LZ_Init();
// 注册关节电机
for (int i = 0; i < 4; i++) {
if (MOTOR_LZ_Register(&chassis->param.joint_param[i]) != DEVICE_OK) {
return DEVICE_ERR;
}
}
// 注册轮子电机
for (int i = 0; i < 2; i++) {
if (MOTOR_LK_Register(&chassis->param.wheel_param[i]) != DEVICE_OK) {
return DEVICE_ERR;
}
}
// 注册CAN接收ID
BSP_CAN_RegisterId(BSP_CAN_1, CAN_CMD_ENABLE_ID, 0); // 使能命令
BSP_CAN_RegisterId(BSP_CAN_1, CAN_CMD_JOINT_ID, 0); // 关节控制命令
BSP_CAN_RegisterId(BSP_CAN_1, CAN_CMD_WHEEL_LEFT_ID, 0); // 左轮控制命令
BSP_CAN_RegisterId(BSP_CAN_1, CAN_CMD_WHEEL_RIGHT_ID, 0); // 右轮控制命令
return DEVICE_OK;
}
/**
* @brief 更新底盘数据
* @param chassis 底盘实例
* @return 成功返回DEVICE_OK
*/
int8_t Chassis_Update(Chassis_t *chassis) {
if (chassis == NULL) {
return DEVICE_ERR_NULL;
}
// 更新所有电机数据
MOTOR_LZ_UpdateAll();
MOTOR_LK_UpdateAll();
// 更新反馈数据
for (int i = 0; i < 4; i++) {
MOTOR_LZ_t* joint_motor = MOTOR_LZ_GetMotor(&chassis->param.joint_param[i]);
if (joint_motor != NULL) {
chassis->data.joint[i] = joint_motor->motor.feedback;
}
}
for (int i = 0; i < 2; i++) {
MOTOR_LK_t* wheel_motor = MOTOR_LK_GetMotor(&chassis->param.wheel_param[i]);
if (wheel_motor != NULL) {
chassis->data.wheel[i] = wheel_motor->motor.feedback;
}
}
return DEVICE_OK;
}
/**
* @brief 使能底盘
* @param chassis 底盘实例
* @return 成功返回DEVICE_OK
*/
int8_t Chassis_Enable(Chassis_t *chassis) {
if (chassis == NULL) {
return DEVICE_ERR_NULL;
}
// 使能关节电机
for (int i = 0; i < 4; i++) {
MOTOR_LZ_Enable(&chassis->param.joint_param[i]);
}
// 开启轮子电机
for (int i = 0; i < 2; i++) {
MOTOR_LK_MotorOn(&chassis->param.wheel_param[i]);
}
return DEVICE_OK;
}
/**
* @brief 底盘松弛
* @param chassis 底盘实例
* @return 成功返回DEVICE_OK
*/
int8_t Chassis_Relax(Chassis_t *chassis) {
if (chassis == NULL) {
return DEVICE_ERR_NULL;
}
// 关节电机松弛
for (int i = 0; i < 4; i++) {
MOTOR_LZ_Relax(&chassis->param.joint_param[i]);
}
// 轮子电机松弛
for (int i = 0; i < 2; i++) {
MOTOR_LK_Relax(&chassis->param.wheel_param[i]);
}
return DEVICE_OK;
}
/**
* @brief 底盘控制处理
* @param chassis 底盘实例
* @return 成功返回DEVICE_OK
*/
int8_t Chassis_Ctrl(Chassis_t *chassis) {
if (chassis == NULL) {
return DEVICE_ERR_NULL;
}
// 处理CAN控制命令
Chassis_ProcessCANCommands(chassis);
uint32_t current_time = BSP_TIME_Get_ms();
// 关节电机超时检查 - 只有在超时时才执行relax
if (!joint_command_received &&
(current_time - joint_last_cmd_time) > CMD_TIMEOUT_MS) {
for (int i = 0; i < 4; i++) {
MOTOR_LZ_Relax(&chassis->param.joint_param[i]);
chassis->output.joint[i] = 0.0f; // 松弛时输出力矩设为0
}
}
// 轮子电机超时检查 - 只有在超时时才执行relax
for (int i = 0; i < 2; i++) {
if (!wheel_command_received[i] &&
(current_time - wheel_last_cmd_time[i]) > CMD_TIMEOUT_MS) {
MOTOR_LK_Relax(&chassis->param.wheel_param[i]);
chassis->output.wheel[i] = 0.0f; // 松弛时输出设为0
}
}
// 发送反馈数据
Chassis_SendJointFeedback(chassis);
Chassis_SendWheelFeedback(chassis);
return DEVICE_OK;
}