355 lines
12 KiB
C
355 lines
12 KiB
C
/*
|
||
* @file virtual_chassis.c
|
||
* @brief 虚拟底盘设备驱动实现 - 控制端
|
||
* @details 作为控制端,发送控制命令到底盘,接收底盘反馈数据
|
||
*/
|
||
|
||
/* Includes ----------------------------------------------------------------- */
|
||
#include "device/virtual_chassis.h"
|
||
#include "bsp/can.h"
|
||
#include "bsp/time.h"
|
||
#include <string.h>
|
||
#include <math.h>
|
||
|
||
/* Private typedef ---------------------------------------------------------- */
|
||
/* Private define ----------------------------------------------------------- */
|
||
|
||
/* Private macro ------------------------------------------------------------ */
|
||
/* Private variables -------------------------------------------------------- */
|
||
/* Private function prototypes ---------------------------------------------- */
|
||
/**
|
||
* @brief 解析关节电机反馈数据 (ID: 124-127)
|
||
* @param chassis 虚拟底盘结构体
|
||
* @param id CAN ID
|
||
* @param data 数据包
|
||
* @return 解析结果
|
||
*/
|
||
static int8_t Virtual_Chassis_DecodeJointMotor(Virtual_Chassis_t *chassis, uint16_t id, uint8_t *data) {
|
||
if (chassis == NULL || data == NULL) {
|
||
return DEVICE_ERR_NULL;
|
||
}
|
||
|
||
int motor_idx = id - chassis->param.motors.joint_feedback_base_id;
|
||
if (motor_idx < 0 || motor_idx >= 4) {
|
||
return DEVICE_ERR;
|
||
}
|
||
|
||
// 解析关节电机反馈数据:转矩电流(2字节) + 位置(3字节) + 速度(3字节)
|
||
|
||
// 1. 解析转矩电流 - 前2字节,16位有符号整数 (精度0.01 Nm)
|
||
int16_t torque_int;
|
||
memcpy(&torque_int, &data[0], sizeof(int16_t));
|
||
chassis->data.joint[motor_idx].torque_current = (float)torque_int / 100.0f;
|
||
|
||
// 2. 解析位置 - 第3-5字节,24位有符号整数 (精度0.0001 rad)
|
||
int32_t angle_int = 0;
|
||
angle_int |= ((int32_t)data[2]) << 16;
|
||
angle_int |= ((int32_t)data[3]) << 8;
|
||
angle_int |= ((int32_t)data[4]);
|
||
|
||
// 符号扩展(24位转32位)
|
||
if (angle_int & 0x800000) {
|
||
angle_int |= 0xFF000000;
|
||
}
|
||
chassis->data.joint[motor_idx].rotor_abs_angle = (float)angle_int / 10000.0f;
|
||
|
||
// 3. 解析速度 - 第6-8字节,24位有符号整数 (精度0.001 rad/s)
|
||
int32_t velocity_int = 0;
|
||
velocity_int |= ((int32_t)data[5]) << 16;
|
||
velocity_int |= ((int32_t)data[6]) << 8;
|
||
velocity_int |= ((int32_t)data[7]);
|
||
|
||
// 符号扩展(24位转32位)
|
||
if (velocity_int & 0x800000) {
|
||
velocity_int |= 0xFF000000;
|
||
}
|
||
chassis->data.joint[motor_idx].rotor_speed = (float)velocity_int / 1000.0f;
|
||
|
||
chassis->data.joint[motor_idx].temp = 0.0f;
|
||
|
||
return DEVICE_OK;
|
||
}
|
||
|
||
/**
|
||
* @brief 解析轮子电机反馈数据 (ID: 130-131)
|
||
* @param chassis 虚拟底盘结构体
|
||
* @param id CAN ID
|
||
* @param data 数据包
|
||
* @return 解析结果
|
||
*/
|
||
static int8_t Virtual_Chassis_DecodeWheelMotor(Virtual_Chassis_t *chassis, uint16_t id, uint8_t *data) {
|
||
if (chassis == NULL || data == NULL) {
|
||
return DEVICE_ERR_NULL;
|
||
}
|
||
|
||
int wheel_idx;
|
||
if (id == chassis->param.motors.wheel_left_feedback_id) {
|
||
wheel_idx = 0; // 左轮
|
||
} else if (id == chassis->param.motors.wheel_right_feedback_id) {
|
||
wheel_idx = 1; // 右轮
|
||
} else {
|
||
return DEVICE_ERR;
|
||
}
|
||
|
||
// 解析轮子电机反馈数据:角度(2字节) + 速度(2字节) + 力矩电流(2字节) + 编码器(2字节)
|
||
|
||
// 角度 - 精度0.01度,需转换为弧度
|
||
int16_t angle_int;
|
||
angle_int = (data[0] << 8) | data[1];
|
||
chassis->data.wheel[wheel_idx].rotor_abs_angle = (float)angle_int / 100.0f * M_PI / 180.0f;
|
||
|
||
// 速度 - 精度1dps,直接赋值
|
||
int16_t speed_int;
|
||
speed_int = (data[2] << 8) | data[3];
|
||
chassis->data.wheel[wheel_idx].rotor_speed = (float)speed_int;
|
||
|
||
// 力矩电流
|
||
int16_t current_int;
|
||
current_int = (data[4] << 8) | data[5];
|
||
chassis->data.wheel[wheel_idx].torque_current = (float)current_int;
|
||
|
||
// 编码器值(暂不使用)
|
||
chassis->data.wheel[wheel_idx].temp = 0.0f;
|
||
|
||
return DEVICE_OK;
|
||
}
|
||
|
||
/**
|
||
* @brief 解析IMU数据
|
||
* @param chassis 虚拟底盘结构体
|
||
* @param id CAN ID
|
||
* @param data 数据包
|
||
* @return 解析结果
|
||
*/
|
||
static int8_t Virtual_Chassis_DecodeIMU(Virtual_Chassis_t *chassis, uint16_t id, uint8_t *data) {
|
||
if (chassis == NULL || data == NULL) {
|
||
return DEVICE_ERR_NULL;
|
||
}
|
||
|
||
if (id == chassis->param.imu.accl_id) { // 加速度计数据 - x/y/z 各2字节,精度0.01g
|
||
int16_t x, y, z;
|
||
memcpy(&x, &data[0], 2);
|
||
memcpy(&y, &data[2], 2);
|
||
memcpy(&z, &data[4], 2);
|
||
chassis->data.imu.accl.x = x / 100.0f;
|
||
chassis->data.imu.accl.y = y / 100.0f;
|
||
chassis->data.imu.accl.z = z / 100.0f;
|
||
}
|
||
else if (id == chassis->param.imu.gyro_id) { // 陀螺仪数据 - x/y/z 各2字节,精度0.01°/s
|
||
int16_t x, y, z;
|
||
memcpy(&x, &data[0], 2);
|
||
memcpy(&y, &data[2], 2);
|
||
memcpy(&z, &data[4], 2);
|
||
// 注意:底盘发送的是精度0.01°/s,而不是0.1°/s,所以除以100而不是10
|
||
chassis->data.imu.gyro.x = (x / 100.0f) * M_PI / 180.0f; // °/s -> rad/s
|
||
chassis->data.imu.gyro.y = (y / 100.0f) * M_PI / 180.0f;
|
||
chassis->data.imu.gyro.z = (z / 100.0f) * M_PI / 180.0f;
|
||
}
|
||
else if (id == chassis->param.imu.euler_id) { // 欧拉角数据 - yaw/pitch/roll 各2字节,精度0.01°
|
||
int16_t yaw, pit, rol;
|
||
memcpy(&yaw, &data[0], 2);
|
||
memcpy(&pit, &data[2], 2);
|
||
memcpy(&rol, &data[4], 2);
|
||
chassis->data.imu.euler.yaw = (yaw / 100.0f) * M_PI / 180.0f; // ° -> rad
|
||
chassis->data.imu.euler.pit = (pit / 100.0f) * M_PI / 180.0f;
|
||
chassis->data.imu.euler.rol = (rol / 100.0f) * M_PI / 180.0f;
|
||
}
|
||
else if (id == chassis->param.imu.quat_id) { // 四元数数据 - q0/q1/q2/q3 各2字节,精度1/32000
|
||
int16_t q0, q1, q2, q3;
|
||
memcpy(&q0, &data[0], 2);
|
||
memcpy(&q1, &data[2], 2);
|
||
memcpy(&q2, &data[4], 2);
|
||
memcpy(&q3, &data[6], 2);
|
||
chassis->data.imu.quat.q0 = q0 / 32000.0f;
|
||
chassis->data.imu.quat.q1 = q1 / 32000.0f;
|
||
chassis->data.imu.quat.q2 = q2 / 32000.0f;
|
||
chassis->data.imu.quat.q3 = q3 / 32000.0f;
|
||
}
|
||
else {
|
||
return DEVICE_ERR;
|
||
}
|
||
|
||
return DEVICE_OK;
|
||
}
|
||
|
||
/* Exported functions ------------------------------------------------------- */
|
||
|
||
/**
|
||
* @brief 初始化虚拟底盘设备
|
||
*/
|
||
int8_t Virtual_Chassis_Init(Virtual_Chassis_t *chassis, Virtual_Chassis_Param_t *param) {
|
||
if (chassis == NULL || param == NULL) {
|
||
return DEVICE_ERR_NULL;
|
||
}
|
||
|
||
// 复制参数配置
|
||
chassis->param = *param;
|
||
|
||
// 初始化设备头部
|
||
chassis->header.online = false;
|
||
chassis->header.last_online_time = 0;
|
||
|
||
// 初始化数据
|
||
memset(&chassis->data, 0, sizeof(Virtual_Chassis_Feedback_t));
|
||
memset(&chassis->output, 0, sizeof(Virtual_Chassis_Output_t));
|
||
|
||
BSP_CAN_Init();
|
||
|
||
// 注册关节电机反馈ID (124-127)
|
||
for (int i = 0; i < 4; i++) {
|
||
BSP_CAN_RegisterId(chassis->param.motors.can, chassis->param.motors.joint_feedback_base_id + i, 3);
|
||
}
|
||
|
||
// 注册轮子电机反馈ID (130-131)
|
||
BSP_CAN_RegisterId(chassis->param.motors.can, chassis->param.motors.wheel_left_feedback_id, 3);
|
||
BSP_CAN_RegisterId(chassis->param.motors.can, chassis->param.motors.wheel_right_feedback_id, 3);
|
||
|
||
// 注册IMU数据ID (769-772 / 0x301-0x304)
|
||
BSP_CAN_RegisterId(chassis->param.imu.can, chassis->param.imu.accl_id, 3);
|
||
BSP_CAN_RegisterId(chassis->param.imu.can, chassis->param.imu.gyro_id, 3);
|
||
BSP_CAN_RegisterId(chassis->param.imu.can, chassis->param.imu.euler_id, 3);
|
||
BSP_CAN_RegisterId(chassis->param.imu.can, chassis->param.imu.quat_id, 3);
|
||
|
||
// 设置设备在线状态
|
||
chassis->header.online = true;
|
||
chassis->header.last_online_time = BSP_TIME_Get_us();
|
||
|
||
return DEVICE_OK;
|
||
}
|
||
|
||
|
||
/**
|
||
* @brief 更新虚拟底盘数据(接收底盘反馈数据)
|
||
*/
|
||
int8_t Virtual_Chassis_Update(Virtual_Chassis_t *chassis) {
|
||
if (chassis == NULL) {
|
||
return DEVICE_ERR_NULL;
|
||
}
|
||
|
||
BSP_CAN_Message_t msg;
|
||
|
||
// 接收IMU数据
|
||
if (BSP_CAN_GetMessage(chassis->param.imu.can, chassis->param.imu.accl_id, &msg, BSP_CAN_TIMEOUT_IMMEDIATE) == BSP_OK) {
|
||
Virtual_Chassis_DecodeIMU(chassis, chassis->param.imu.accl_id, msg.data);
|
||
}
|
||
if (BSP_CAN_GetMessage(chassis->param.imu.can, chassis->param.imu.gyro_id, &msg, BSP_CAN_TIMEOUT_IMMEDIATE) == BSP_OK) {
|
||
Virtual_Chassis_DecodeIMU(chassis, chassis->param.imu.gyro_id, msg.data);
|
||
}
|
||
if (BSP_CAN_GetMessage(chassis->param.imu.can, chassis->param.imu.euler_id, &msg, BSP_CAN_TIMEOUT_IMMEDIATE) == BSP_OK) {
|
||
Virtual_Chassis_DecodeIMU(chassis, chassis->param.imu.euler_id, msg.data);
|
||
}
|
||
if (BSP_CAN_GetMessage(chassis->param.imu.can, chassis->param.imu.quat_id, &msg, BSP_CAN_TIMEOUT_IMMEDIATE) == BSP_OK) {
|
||
Virtual_Chassis_DecodeIMU(chassis, chassis->param.imu.quat_id, msg.data);
|
||
}
|
||
|
||
// 接收关节电机反馈数据 (124-127)
|
||
for (int i = 0; i < 4; i++) {
|
||
uint16_t joint_id = chassis->param.motors.joint_feedback_base_id + i;
|
||
if (BSP_CAN_GetMessage(chassis->param.motors.can, joint_id, &msg, BSP_CAN_TIMEOUT_IMMEDIATE) == BSP_OK) {
|
||
Virtual_Chassis_DecodeJointMotor(chassis, joint_id, msg.data);
|
||
}
|
||
}
|
||
|
||
// 接收轮子电机反馈数据 (130-131)
|
||
if (BSP_CAN_GetMessage(chassis->param.motors.can, chassis->param.motors.wheel_left_feedback_id, &msg, BSP_CAN_TIMEOUT_IMMEDIATE) == BSP_OK) {
|
||
Virtual_Chassis_DecodeWheelMotor(chassis, chassis->param.motors.wheel_left_feedback_id, msg.data);
|
||
}
|
||
if (BSP_CAN_GetMessage(chassis->param.motors.can, chassis->param.motors.wheel_right_feedback_id, &msg, BSP_CAN_TIMEOUT_IMMEDIATE) == BSP_OK) {
|
||
Virtual_Chassis_DecodeWheelMotor(chassis, chassis->param.motors.wheel_right_feedback_id, msg.data);
|
||
}
|
||
|
||
return DEVICE_OK;
|
||
}
|
||
|
||
|
||
/**
|
||
* @brief 发送电机使能命令
|
||
*/
|
||
int8_t Virtual_Chassis_Enable(Virtual_Chassis_t *chassis) {
|
||
if (chassis == NULL) {
|
||
return DEVICE_ERR_NULL;
|
||
}
|
||
|
||
BSP_CAN_StdDataFrame_t enable_frame = {
|
||
.id = chassis->param.motors.enable_id,
|
||
.dlc = 0,
|
||
.data = {0}
|
||
};
|
||
|
||
chassis->output.enable_joints = true;
|
||
|
||
if (BSP_CAN_TransmitStdDataFrame(chassis->param.motors.can, &enable_frame) == BSP_OK) {
|
||
return DEVICE_OK;
|
||
}
|
||
|
||
return DEVICE_ERR;
|
||
}
|
||
|
||
/**
|
||
* @brief 发送关节电机力矩控制命令
|
||
*/
|
||
int8_t Virtual_Chassis_SendJointTorque(Virtual_Chassis_t *chassis, float torques[4]) {
|
||
if (chassis == NULL || torques == NULL) {
|
||
return DEVICE_ERR_NULL;
|
||
}
|
||
|
||
BSP_CAN_StdDataFrame_t torque_frame = {
|
||
.id = chassis->param.motors.joint_cmd_id,
|
||
.dlc = 8,
|
||
.data = {0}
|
||
};
|
||
|
||
// 8字节数据分别是4个电机的力矩 (每个电机2字节,有符号整数,精度0.01 Nm)
|
||
for (int i = 0; i < 4; i++) {
|
||
int16_t torque_raw = (int16_t)(torques[i] * 100.0f);
|
||
memcpy(&torque_frame.data[i * 2], &torque_raw, sizeof(int16_t));
|
||
chassis->output.joint_torque[i] = torques[i];
|
||
}
|
||
|
||
return BSP_CAN_TransmitStdDataFrame(chassis->param.motors.can, &torque_frame) == BSP_OK ? DEVICE_OK : DEVICE_ERR;
|
||
}
|
||
|
||
/**
|
||
* @brief 发送轮子电机控制命令
|
||
*/
|
||
int8_t Virtual_Chassis_SendWheelCommand(Virtual_Chassis_t *chassis, uint8_t left_cmd[8], uint8_t right_cmd[8]) {
|
||
if (chassis == NULL || left_cmd == NULL || right_cmd == NULL) {
|
||
return DEVICE_ERR_NULL;
|
||
}
|
||
|
||
// 发送左轮控制命令
|
||
BSP_CAN_StdDataFrame_t left_frame = {
|
||
.id = chassis->param.motors.wheel_left_id,
|
||
.dlc = 8,
|
||
};
|
||
memcpy(left_frame.data, left_cmd, 8);
|
||
memcpy(chassis->output.wheel_left_cmd, left_cmd, 8);
|
||
|
||
// 发送右轮控制命令
|
||
BSP_CAN_StdDataFrame_t right_frame = {
|
||
.id = chassis->param.motors.wheel_right_id,
|
||
.dlc = 8,
|
||
};
|
||
memcpy(right_frame.data, right_cmd, 8);
|
||
memcpy(chassis->output.wheel_right_cmd, right_cmd, 8);
|
||
|
||
int8_t result1 = BSP_CAN_TransmitStdDataFrame(chassis->param.motors.can, &left_frame);
|
||
int8_t result2 = BSP_CAN_TransmitStdDataFrame(chassis->param.motors.can, &right_frame);
|
||
|
||
return (result1 == BSP_OK && result2 == BSP_OK) ? DEVICE_OK : DEVICE_ERR;
|
||
}
|
||
|
||
/**
|
||
* @brief 检查虚拟底盘是否在线
|
||
*/
|
||
bool Virtual_Chassis_IsOnline(Virtual_Chassis_t *chassis) {
|
||
if (chassis == NULL) {
|
||
return false;
|
||
}
|
||
|
||
return chassis->header.online;
|
||
}
|
||
|
||
|
||
/* Private functions -------------------------------------------------------- */
|