Files
4wheel4turn/app/app_turntable.c
2025-10-21 19:14:21 +08:00

404 lines
14 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.

#include "app_config.h"
#include "app_dependence.h"
#include "interface.h"
#include "app_turntable.h"
#include "app_pid.h"
#include "app_param_manage.h"
#include "app_frm_monitor.h"
#include "app_frm_signal.h"
#include "app_frm_timer.h"
#include "drive_rs04.h"
#include "sdrv_vic.h"
#include <math.h>
PID_t turnable_speed_pid;
PID_t turnable_position_pid;
TurnableData turnable_data = {0};
/**
* @brief 将笛卡尔坐标 (x,y,z) 转换为球坐标 (r,θ,φ)
*
* @param x X轴坐标值单位
* @param y Y轴坐标值单位
* @param z Z轴坐标值单位
* @param[out] out 输出球坐标结构体指针
* @return bool 转换是否成功:
* - true: 转换成功
* - false: 输入无效包含NaN/INF或out为NULL
*
* @note 特殊输入处理:
* 1. 如果输入包含 NaN 或无穷大,返回 false
* 2. 当 r < 1e-10 时视为原点,设置 out=(0, 0, 0)
* 3. 在Z轴附近 (|z/r| ≈ 1) 时自动截断到 [-1,1] 保证数值稳定性
*
* @warning 使用要求:
* - 必须检查返回值不能直接使用out内容
* - out指针必须指向有效内存
*
* @example 正确用法:
* SphericalCoordinate sph;
* if (cartesianToSpherical(1.0f, 0.0f, 0.0f, &sph)) {
* // 使用sph...
* }
*/
uint8_t cartesianToSpherical(float x, float y, float z, SphericalCoordinate* out)
{
// 参数有效性检查(防御性编程)
if (!out || !isfinite(x) || !isfinite(y) || !isfinite(z)) {
return 0;
}
// 计算径向距离 r = √(x² + y² + z²)
out->r = sqrtf(x * x + y * y + z * z);
// 原点判定(使用容差避免浮点误差)
if (out->r < 1e-10f) {
out->theta = 0.0f;
out->phi = 0.0f;
return 1;
}
// 极角 θ = acos(z/r) 的数值稳定性处理
float z_over_r = z / out->r;
if (z_over_r > 1.0f) z_over_r = 1.0f; // 处理上溢出
if (z_over_r < -1.0f) z_over_r = -1.0f; // 处理下溢出
out->theta = acosf(z_over_r);
// 方位角 φ = atan2(y, x)
out->phi = atan2f(y, x);
return 1;
}
// 计算CRC8校验多项式 x^8 + 1简单的累加和
uint8_t encoder_calculate_crc(const uint8_t* data, uint8_t length)
{
uint8_t crc = 0x00;
for (int i = 0; i < length; i++) {
crc += data[i];
}
return crc;
}
static void setTurnableMotorOutput()
{
turnable_data.out_pitch_motor_ampere = constrain(turnable_data.out_pitch_motor_ampere, -RS02_ANGULAR_VELOCITY_MAX, RS02_ANGULAR_VELOCITY_MAX);
turnable_data.out_left_motor_ampere = constrain(turnable_data.out_left_motor_ampere, -RS02_ANGULAR_VELOCITY_MAX, RS02_ANGULAR_VELOCITY_MAX);
setMotorWrite(MASTER_CANID, PITCH_MOTOR_CANID, &un_sdo_output1, LIMIT_SPEED_INDEX,turnable_data.out_pitch_motor_ampere);
setMotorWrite(MASTER_CANID, PITCH_MOTOR_CANID, &un_sdo_output4, LOC_REF_INDEX,turnable_data.desired_pitch_position);
setMotorWrite(MASTER_CANID, RIGHT_MOTOR_CANID, &un_sdo_output2, SPD_REF,turnable_data.out_left_motor_ampere);
un_can_debug_output.bit_data.set_left_out = (uint16_t)(int16_t)(turnable_data.out_left_motor_ampere_limit * 100);
un_can_debug_output.bit_data.set_right_out = (uint16_t)(int16_t)(turnable_data.out_right_motor_ampere_limit*100);
publishMessage(&un_sdo_output1, 1);
publishMessage(&un_sdo_output2, 1);
publishMessage(&un_sdo_output4, 1);
}
// 转台
static void turnableProcess(void *signal_id)
{
// if((turnable_data.current_state == POWER_WORKING))//高压上电才运行
// {
switch(turnable_data.turnable_state)//先发送切换模式以及电机失能,后面直接使能 最后发送数据
{
case 0:
timerStart(&turnable_data.turnable_timer, 1000, 1); // 启动定时器1s
turnable_data.turnable_state = 1;
break;
case 1:
if (!turnable_data.turnable_timer.active)// 1s定时
{
turnable_data.turnable_state = 2;
}
else
{
turnable_data.turnable_state = 1;
}
break;
case 2://模式设置
if(turnable_data.turnable_cnt >= 5)//发送5次
{
turnable_data.turnable_cnt = 0;
turnable_data.turnable_state = 3;
}
else
{
turnable_data.turnable_cnt ++;
turnable_data.turnable_state = 2;
setMotorMode(MASTER_CANID, PITCH_MOTOR_CANID, &un_sdo_output1, POSITION_MODE_CSP);
setMotorMode(MASTER_CANID, RIGHT_MOTOR_CANID, &un_sdo_output2, VELOCITY_MODE);
setMotorWrite(MASTER_CANID, RIGHT_MOTOR_CANID, &un_sdo_output5, LIMIT_CUR,5); //设置最大电流为5A
publishMessage(&un_sdo_output1, 1);
publishMessage(&un_sdo_output2, 1);
publishMessage(&un_sdo_output5, 1);
}
break;
//------------------------------------------------------------------------------
case 3:
if(turnable_data.turnable_cnt >= 5)//发送5次
{
turnable_data.turnable_cnt = 0;
turnable_data.turnable_state = 4;
}
else
{
turnable_data.turnable_cnt ++;
turnable_data.turnable_state = 3;
motorEnable(MASTER_CANID, PITCH_MOTOR_CANID, &un_sdo_output1);
motorEnable(MASTER_CANID, RIGHT_MOTOR_CANID, &un_sdo_output2);
publishMessage(&un_sdo_output1, 1);
publishMessage(&un_sdo_output2, 1);
}
break;
case 4:
turnable_data.turnable_cnt = 0;
turnable_data.turnable_state = 4;
setTurnableMotorOutput();//输出函数
break;
default:break;
}
}
void turnableParametersInit(void *signal_id)
{
(void)signal_id; // 标记变量为已使用,避免编译器警告
if(0 == un_right_intput.rx_can_id.bits.mode_state)//判断状态是否为复位,如果复位就重新使能
{
motorEnable(MASTER_CANID, RIGHT_MOTOR_CANID, &un_sdo_output3);
publishMessage(&un_sdo_output3, 1);
}
if(0 == un_pitch_intput.rx_can_id.bits.mode_state)//判断状态是否为复位,如果复位就重新使能
{
motorEnable(MASTER_CANID, PITCH_MOTOR_CANID, &un_sdo_output3);
publishMessage(&un_sdo_output3, 1);
}
turnable_data.desired_horizontal_speed = getParam("turn_sp");
turnable_data.desired_pitch_speed = getParam("pit_sp");
turnable_data.min_pitch_postion = getParam("minYpos"); //俯仰位置最小限制值
turnable_data.max_pitch_postion = getParam("maxYpos"); //俯仰位置最大限制值
printf( "turnable left A %f\n",turnable_data.out_left_motor_ampere);
printf( "turnable right A %f\n",turnable_data.out_right_motor_ampere);
printf( "turnable pitch A %f\n",turnable_data.out_pitch_motor_ampere);
// printf( "desired speed %f\n",turnable_data.desired_speed);
printf( "speed %f\n",turnable_data.speed);
printf( "turnable state %d\n",turnable_data.turnable_state);
timerStart(&turnable_data.turnable_timer1,1000,1);//100ms调用一次
}
static void dataRequest(void *signal_id)
{
str_magnetic_encoder.magnetic_data = ENCODER_HEADER;
publishMessage(&str_magnetic_encoder, 1);
timerStart(&turnable_data.turnable_timer3,100,1);//100ms调用一次
}
// 差速输入处理函数
static void turnableInput(void *signal_id)
{
if(signal_id == &power_data)//电机上电
{
turnable_data.current_state = power_data.current_state;
}
else if(signal_id == &un_computer_turnable_Input)
{
turnable_data.position_x = (float)( SWAP_ENDIAN_32(un_computer_turnable_Input.bit_data.position_x) );
turnable_data.position_y = (float)( SWAP_ENDIAN_32(un_computer_turnable_Input.bit_data.position_y) );
turnable_data.position_z = (float)( SWAP_ENDIAN_32(un_computer_turnable_Input.bit_data.position_z) );
}
else if(signal_id == &un_encoder_data_input)
{
if( un_encoder_data_input.arr[sizeof(un_encoder_data_input)-1] == encoder_calculate_crc(&un_encoder_data_input.arr[0], sizeof(un_encoder_data_input)-1) )//CRC校验
{
turnable_data.horizontal_position = (float)(un_encoder_data_input.bit_data.abs_value)*2.0f*PI/ENCODER_MAX_COUNTS;//将数据转换为实际角度
}
}
else if ( (signal_id == &un_remote_control_input) && (1 == un_remote_control_input.bit_data.enable) )// 遥控器断线,不更新数据
{
// diff_data.remote_emergency_stop = !(uint8_t)un_remote_control_input.bit_data.switch_b;
// diff_data.mode = un_remote_control_input.bit_data.switch_c =
float x_axis_temp = (float)(un_remote_control_input.bit_data.x_axis) - REMOTE_ZERO;
if( ( x_axis_temp > 50 ) || ( x_axis_temp < -50 ) )
{
turnable_data.out_left_motor_ampere = 0.02*(x_axis_temp);//计算电流
turnable_data.out_right_motor_ampere = turnable_data.out_left_motor_ampere;
}
else
{
turnable_data.out_left_motor_ampere = 0;//计算电流
turnable_data.out_right_motor_ampere = turnable_data.out_left_motor_ampere;
}
x_axis_temp = (float)(un_remote_control_input.bit_data.y_axis) - REMOTE_ZERO;
if(x_axis_temp > 50) //根据Y轴数据来定义
{
turnable_data.out_pitch_motor_ampere = 0.01*fabs(x_axis_temp);
turnable_data.desired_pitch_position = turnable_data.max_pitch_postion;
}
else if(x_axis_temp < -50)
{
turnable_data.out_pitch_motor_ampere = 0.01*fabs(x_axis_temp);
turnable_data.desired_pitch_position = turnable_data.min_pitch_postion;
}
else
{
turnable_data.out_pitch_motor_ampere = 0;
}
}
else if(signal_id == &un_pitch_intput)
{
turnable_data.pitch_position = convertPhysical( SWAP_ENDIAN_16(un_right_intput.rx_can_data.bit_data.current_angle),-RS02_ANGULAR_VELOCITY_MAX,RS02_ANGULAR_VELOCITY_MAX,MOTOR_ANGLE_DEADZONE );
}
else{}
turnable_data.right_motor_speed = convertPhysical( SWAP_ENDIAN_16(un_right_intput.rx_can_data.bit_data.current_velocity),-RS02_ANGULAR_VELOCITY_MAX,RS02_ANGULAR_VELOCITY_MAX,MOTOR_VELOCITY_DEADZONE );
turnable_data.speed = (turnable_data.right_motor_speed + turnable_data.left_motor_speed)/2.0f;
if ( (power_data.current_state == POWER_STANDBY) || (power_data.current_state == POWER_SLEEP) )//这几种状态可以转转台
{
turnable_data.turnable_state = 0;//清空状态。保证每次上电都初始化
}
else
{
turnableProcess(signal_id);//处理映射
}
// timerStart(&turnable_data.turnable_timer2,100,1);//100ms调用一次
}
void turnableInit()
{
// 初始化速度 PID 控制器
initializePid(&turnable_speed_pid, PID_MODE_DERIVATIVE_CALC, 0.0001f);
// // 设置速度 PID 控制器的参数
// setPidParameters(&turnable_speed_pid,
// getParam("spd_kp"),
// getParam("spd_ki"),
// getParam("spd_kd"),
// getParam("spd_il"),
// getParam("spd_ol")
// );
//目标参数初始化
str_magnetic_encoder.ip[0] = 192;
str_magnetic_encoder.ip[1] = 168;
str_magnetic_encoder.ip[2] = 17;
str_magnetic_encoder.ip[3] = 33;
str_magnetic_encoder.port = 2011;
subscribe(&un_remote_control_input, turnableInput);
subscribe(&un_computer_turnable_Input, turnableInput);
subscribe(&un_pitch_intput, turnableInput);
timerInit(&turnable_data.turnable_timer);
timerInit(&turnable_data.turnable_timer1);
// timerInit(&turnable_data.turnable_timer2);
// subscribe(&turnable_data.turnable_timer2, turnableInput);
//
// timerStart(&turnable_data.turnable_timer2,100,1);//100ms调用一次
subscribe(&turnable_data.turnable_timer1, turnableParametersInit);
timerStart(&turnable_data.turnable_timer1,1000,1);//100ms调用一次
timerInit(&turnable_data.turnable_timer3);
subscribe(&turnable_data.turnable_timer3, dataRequest);
timerStart(&turnable_data.turnable_timer3,100,1);//100ms调用一次
subscribe(&un_encoder_data_input, turnableInput);
turnable_data.turnable_state = 0;
un_right_intput.rx_can_data.bit_data.current_velocity = ZERO_VAULE;
un_right_intput.rx_can_data.bit_data.current_angle = ZERO_VAULE;
un_right_intput.rx_can_data.bit_data.current_torque = ZERO_VAULE;
un_pitch_intput.rx_can_data.bit_data.current_velocity = ZERO_VAULE;
un_pitch_intput.rx_can_data.bit_data.current_angle = ZERO_VAULE;
un_pitch_intput.rx_can_data.bit_data.current_torque = ZERO_VAULE;
printf( "turnable: initial OK %d\n",getCurrentTime());
}