Compare commits

...

5 Commits

Author SHA1 Message Date
383ad03cda 123321 2026-03-20 05:01:32 +08:00
d71dbde8d8 Merge remote-tracking branch 'origin/main' into integrate_arm_chassis_20260320 2026-03-20 04:14:29 +08:00
6309cd5bd2 夹爪 2026-03-20 04:14:00 +08:00
d5933612aa 准备加夹爪 2026-03-20 02:33:10 +08:00
edc200bdf0 添加pose/joint控制 2026-03-20 01:11:33 +08:00
7 changed files with 476 additions and 141 deletions

View File

@ -81,6 +81,7 @@ target_sources(${CMAKE_PROJECT_NAME} PRIVATE
User/module/cmd/cmd_adapter.c
User/module/cmd/cmd_behavior.c
User/module/cmd/cmd_example.c
User/module/gripper_rm.cpp
# User/task sources
User/task/arm_main.cpp
User/task/blink.c

View File

@ -66,6 +66,8 @@ typedef enum {
typedef struct {
bool enable; /* 使能开关 */
bool set_target_as_current; /* true: 将目标位姿同步为当前实际位姿 */
bool gripper_toggle; /* 夹爪状态切换置位(单帧脉冲) */
bool gripper_close; /* 夹爪目标状态true=夹紧, false=松开 */
Arm_CtrlType_t ctrl_type; /* 控制类型 */
Arm6dof_Pose_t target_pose; /* 目标末端位姿(世界坐标系,供调试观察) */
Arm6dof_JointAngles_t target_joints; /* 目标六个关节角度(自定义控制器模式) */

View File

@ -552,7 +552,14 @@ public:
int8_t Control() {
// 更新时间
uint64_t now_us = BSP_TIME_Get_us();
timer_.dt = (now_us - timer_.last_wakeup) / 1000000.0f;
if (timer_.last_wakeup == 0 || now_us <= timer_.last_wakeup) {
timer_.dt = 0.001f;
} else {
timer_.dt = (now_us - timer_.last_wakeup) / 1000000.0f;
// 异常调度抖动保护,避免单帧超大/超小 dt 造成控制突变。
if (timer_.dt < 0.0002f) timer_.dt = 0.0002f;
if (timer_.dt > 0.02f) timer_.dt = 0.02f;
}
timer_.last_wakeup = now_us;
timer_.now = now_us / 1000000.0f;
@ -592,7 +599,23 @@ public:
}
if(control_.mode == ControlMode::JOINT_POSITION) {
// JOINT_POSITION 模式由上层写入目标角,这里做边界校验与状态同步
for (size_t i = 0; i < JOINT_NUM; ++i) {
if (!joints_[i]) {
continue;
}
const float target = joints_[i]->GetTargetAngle();
const auto& p = joints_[i]->GetParams();
if (!std::isfinite(target) || target < p.qmin || target > p.qmax) {
RelaxAllJoints();
control_.state = MotionState::ERROR;
return -1;
}
inverseKinematics_.angles.q[i] = target;
}
inverseKinematics_.valid = true;
traj_.valid = true;
traj_.active = false;
}
// GRAVITY_COMP 模式:保持当前位置 + 重力补偿

View File

@ -3,6 +3,7 @@
*/
#include "cmd.h"
#include "bsp/time.h"
#include <math.h>
#include <stdint.h>
#include <string.h>
@ -16,7 +17,7 @@ static void CMD_RC_BuildChassisCmd(CMD_t *ctx) {
CMD_RCModeMap_t *map = &ctx->config->rc_mode_map;
/* 根据左拨杆位置选择模式 */
switch (ctx->input.rc.sw[1]) {
switch (ctx->input.rc.sw[0]) {
case CMD_SW_UP:
ctx->output.chassis.cmd.mode = map->sw_left_up;
break;
@ -256,74 +257,92 @@ static void CMD_NUC_BuildShootCmd(CMD_t *ctx) {
#if CMD_ENABLE_SRC_RC && CMD_ENABLE_MODULE_ARM
static void CMD_RC_BuildArmCmd(CMD_t *ctx) {
/*
* 姿
*
* SW_L (sw[0]) 使:
* UP /
* MID 使
* DOWN 使
*
* SW_R (sw[1]) :
*
* UP 3 +
* X (RX) X
* Y (RY) Y
* Y (LY) Z
* X (LX) Yaw
*
* MID 姿// +
* X (RX) Yaw
* Y (RY) Pitch
* X (LX) Roll
* Y (LY) Z
*
* DOWN set_target_as_current姿
*
*
* SW_L
* UP relax
* MID SW_R
* DOWN
*
* SW_L=MID SW_R=UP
* LY J2/J3
*
* SW_L=MID SW_R=MID
* LX J1, LY J2, RY J3
*
* SW_L=MID SW_R=DOWN
* RX J4, RY J5, LY J6
*
* SW_L=DOWN
* SW_R=UP
* SW_R=MID
* SW_R=DOWN
*/
ctx->output.arm.cmd.set_target_as_current = false;
if (ctx->input.rc.sw[1] == CMD_SW_DOWN) {
ctx->output.arm.cmd.set_target_as_current = true;
}
ctx->output.arm.cmd.gripper_toggle = false;
switch (ctx->input.rc.sw[0]) {
case CMD_SW_MID:
case CMD_SW_DOWN:
ctx->output.arm.cmd.enable = true;
break;
default:
ctx->output.arm.cmd.enable = false;
goto end;
}
/* 遥控器模式使用笛卡尔位姿累积控制 */
ctx->output.arm.cmd.ctrl_type = ARM_CTRL_REMOTE_CARTESIAN;
/* set_target_as_current 置位时不叠加摇杆增量,由上层负责同步位姿基准 */
if (ctx->output.arm.cmd.set_target_as_current) return;
/* 摇杆速度直接积分到 target_pose坐标系语义由 arm_main 的 control_frame 决定) */
const float pos_scale = 0.3f; /* 末端线速度上限 (m/s) */
const float rot_scale = 1.0f; /* 末端角速度上限 (rad/s) */
/* 按控制类型分别处理增量输入 */
const float joint_scale = 1.0f; /* 关节角速度上限 (rad/s) */
const float dt = ctx->timer.dt;
switch (ctx->input.rc.sw[1]) {
switch (ctx->input.rc.sw[0]) {
case CMD_SW_UP:
/* 位置模式3轴平移 + 偏航 */
ctx->output.arm.cmd.target_pose.x += ctx->input.rc.joy_right.x * pos_scale * dt;
ctx->output.arm.cmd.target_pose.y += ctx->input.rc.joy_right.y * pos_scale * dt;
ctx->output.arm.cmd.target_pose.z += ctx->input.rc.joy_left.y * pos_scale * dt;
ctx->output.arm.cmd.target_pose.yaw += ctx->input.rc.joy_left.x * rot_scale * dt;
ctx->output.arm.cmd.enable = false;
break;
case CMD_SW_MID:
/* 姿态模式:俯仰 + 横滚 + 偏航 + 升降 */
ctx->output.arm.cmd.target_pose.yaw += ctx->input.rc.joy_right.x * rot_scale * dt;
ctx->output.arm.cmd.target_pose.pitch += ctx->input.rc.joy_right.y * rot_scale * dt;
ctx->output.arm.cmd.target_pose.roll += ctx->input.rc.joy_left.x * rot_scale * dt;
ctx->output.arm.cmd.target_pose.z += ctx->input.rc.joy_left.y * pos_scale * dt;
ctx->output.arm.cmd.enable = true;
ctx->output.arm.cmd.ctrl_type = ARM_CTRL_CUSTOM_JOINT;
switch (ctx->input.rc.sw[1]) {
case CMD_SW_UP: {
const float d = ctx->input.rc.joy_left.y * joint_scale * dt;
ctx->output.arm.cmd.target_joints.q[1] += d;
ctx->output.arm.cmd.target_joints.q[2] += d;
break;
}
case CMD_SW_MID:
ctx->output.arm.cmd.target_joints.q[0] += ctx->input.rc.joy_left.x * joint_scale * dt;
ctx->output.arm.cmd.target_joints.q[1] += ctx->input.rc.joy_left.y * joint_scale * dt;
ctx->output.arm.cmd.target_joints.q[2] += ctx->input.rc.joy_right.y * joint_scale * dt;
break;
case CMD_SW_DOWN:
ctx->output.arm.cmd.target_joints.q[3] += ctx->input.rc.joy_right.x * joint_scale * dt;
ctx->output.arm.cmd.target_joints.q[4] += ctx->input.rc.joy_right.y * joint_scale * dt;
ctx->output.arm.cmd.target_joints.q[5] += ctx->input.rc.joy_left.y * joint_scale * dt;
break;
default:
break;
}
break;
case CMD_SW_DOWN: {
// 左拨杆下:只控制夹爪,机械臂保持使能但不注入新的关节目标
ctx->output.arm.cmd.enable = true;
ctx->output.arm.cmd.ctrl_type = ARM_CTRL_CUSTOM_JOINT;
bool has_cmd = false;
bool target_close = ctx->output.arm.cmd.gripper_close;
if (ctx->input.rc.sw[1] == CMD_SW_UP) {
has_cmd = true;
target_close = false;
} else if (ctx->input.rc.sw[1] == CMD_SW_DOWN) {
has_cmd = true;
target_close = true;
}
if (has_cmd && target_close != ctx->output.arm.cmd.gripper_close) {
ctx->output.arm.cmd.gripper_close = target_close;
ctx->output.arm.cmd.gripper_toggle = true;
}
break;
}
default:
ctx->output.arm.cmd.enable = false;
break;
}
end:
return;
}
#endif /* CMD_ENABLE_SRC_RC && CMD_ENABLE_MODULE_ARM */

192
User/module/gripper_rm.cpp Normal file
View File

@ -0,0 +1,192 @@
#include "module/gripper_rm.hpp"
#include <cmath>
#include "task/user_task.h"
namespace arm {
GripperState gripper_state_dbg = GripperState::OPEN_HOLD;
bool gripper_target_close = false;
float gripper_output_dbg = 0.0f;
float gripper_out_open = -0.35f;
float gripper_out_close_fast = 0.55f;
float gripper_out_preload = 0.72f;
float gripper_out_hold = 0.46f;
float gripper_out_boost = 0.64f;
float gripper_contact_current_th = 4200.0f;
float gripper_contact_speed_th = 2.0f;
float gripper_slip_speed_th = 1.6f;
uint16_t gripper_contact_debounce_ms = 18;
uint16_t gripper_preload_ms = 180;
uint16_t gripper_boost_ms = 120;
uint16_t gripper_open_timeout_ms = 1200;
uint16_t gripper_close_timeout_ms = 1500;
static MOTOR_RM_Param_t g_gripper_rm_param = {};
static bool g_gripper_inited = false;
static uint16_t GripperMsToTicks(uint16_t ms) {
const uint32_t freq = ARM_MAIN_FREQ;
uint32_t ticks = ((uint32_t)ms * freq + 999u) / 1000u;
if (ticks == 0u) {
ticks = 1u;
}
return (uint16_t)ticks;
}
static void GripperSetOutput(float out_norm) {
if (out_norm > 1.0f) out_norm = 1.0f;
if (out_norm < -1.0f) out_norm = -1.0f;
gripper_output_dbg = out_norm;
(void)MOTOR_RM_SetOutput(&g_gripper_rm_param, out_norm);
}
static void GripperStateMachineStep(bool target_close) {
static uint16_t state_ticks = 0;
static uint16_t contact_ticks = 0;
static uint16_t slip_ticks = 0;
static uint16_t boost_ticks = 0;
static bool prev_target_close = false;
const bool target_changed = (target_close != prev_target_close);
prev_target_close = target_close;
MOTOR_RM_t* gm = MOTOR_RM_GetMotor(&g_gripper_rm_param);
const bool online = (gm != nullptr) && gm->motor.header.online;
const float fb_speed = online ? gm->feedback.rotor_speed : 0.0f;
const float fb_current = online ? gm->feedback.torque_current : 0.0f;
const bool contact_now = fabsf(fb_current) > gripper_contact_current_th &&
fabsf(fb_speed) < gripper_contact_speed_th;
const bool slip_now = fabsf(fb_speed) > gripper_slip_speed_th;
if (target_changed) {
state_ticks = 0;
contact_ticks = 0;
slip_ticks = 0;
boost_ticks = 0;
gripper_state_dbg = target_close ? GripperState::CLOSING : GripperState::OPENING;
}
if (!online) {
gripper_state_dbg = GripperState::FAULT;
GripperSetOutput(0.0f);
return;
}
++state_ticks;
switch (gripper_state_dbg) {
case GripperState::OPENING:
GripperSetOutput(gripper_out_open);
if (target_close) {
gripper_state_dbg = GripperState::CLOSING;
state_ticks = 0;
contact_ticks = 0;
} else if (state_ticks >= GripperMsToTicks(gripper_open_timeout_ms)) {
gripper_state_dbg = GripperState::OPEN_HOLD;
state_ticks = 0;
}
break;
case GripperState::OPEN_HOLD:
GripperSetOutput(0.0f);
if (target_close) {
gripper_state_dbg = GripperState::CLOSING;
state_ticks = 0;
contact_ticks = 0;
}
break;
case GripperState::CLOSING:
GripperSetOutput(gripper_out_close_fast);
if (!target_close) {
gripper_state_dbg = GripperState::OPENING;
state_ticks = 0;
contact_ticks = 0;
break;
}
contact_ticks = contact_now ? (uint16_t)(contact_ticks + 1u) : 0u;
if (contact_ticks >= GripperMsToTicks(gripper_contact_debounce_ms)) {
gripper_state_dbg = GripperState::PRELOAD;
state_ticks = 0;
} else if (state_ticks >= GripperMsToTicks(gripper_close_timeout_ms)) {
// 稳夹优先:闭合超时后转入保持而不是松开
gripper_state_dbg = GripperState::CLOSE_HOLD;
state_ticks = 0;
}
break;
case GripperState::PRELOAD:
GripperSetOutput(gripper_out_preload);
if (!target_close) {
gripper_state_dbg = GripperState::OPENING;
state_ticks = 0;
} else if (state_ticks >= GripperMsToTicks(gripper_preload_ms)) {
gripper_state_dbg = GripperState::CLOSE_HOLD;
state_ticks = 0;
}
break;
case GripperState::CLOSE_HOLD:
if (!target_close) {
gripper_state_dbg = GripperState::OPENING;
state_ticks = 0;
slip_ticks = 0;
boost_ticks = 0;
break;
}
if (boost_ticks > 0) {
GripperSetOutput(gripper_out_boost);
--boost_ticks;
} else {
GripperSetOutput(gripper_out_hold);
}
// 检测到可能打滑时短时增压,提高稳夹能力
if (slip_now) {
++slip_ticks;
} else {
slip_ticks = 0;
}
if (slip_ticks >= GripperMsToTicks(30)) {
boost_ticks = GripperMsToTicks(gripper_boost_ms);
slip_ticks = 0;
}
break;
case GripperState::FAULT:
default:
GripperSetOutput(0.0f);
if (online) {
gripper_state_dbg = target_close ? GripperState::CLOSING : GripperState::OPENING;
state_ticks = 0;
}
break;
}
}
int8_t Gripper_Init(const MOTOR_RM_Param_t& param) {
g_gripper_rm_param = param;
g_gripper_inited = (MOTOR_RM_Register(&g_gripper_rm_param) == DEVICE_OK);
return g_gripper_inited ? DEVICE_OK : DEVICE_ERR;
}
void Gripper_Step(bool target_close) {
if (!g_gripper_inited) {
return;
}
gripper_target_close = target_close;
(void)MOTOR_RM_Update(&g_gripper_rm_param);
GripperStateMachineStep(target_close);
(void)MOTOR_RM_Ctrl(&g_gripper_rm_param);
}
} // namespace arm

View File

@ -0,0 +1,44 @@
#pragma once
#include <stdint.h>
#include "device/motor_rm.h"
namespace arm {
enum class GripperState : uint8_t {
OPENING = 0,
OPEN_HOLD,
CLOSING,
PRELOAD,
CLOSE_HOLD,
FAULT,
};
// 调试变量与可调参数保留为全局便于Watch实时观察/修改。
extern GripperState gripper_state_dbg;
extern bool gripper_target_close;
extern float gripper_output_dbg;
// 夹爪输出MOTOR_RM_SetOutput 归一化范围:[-1, 1]
extern float gripper_out_open;
extern float gripper_out_close_fast;
extern float gripper_out_preload;
extern float gripper_out_hold;
extern float gripper_out_boost;
// 接触/防滑判据
extern float gripper_contact_current_th;
extern float gripper_contact_speed_th;
extern float gripper_slip_speed_th;
extern uint16_t gripper_contact_debounce_ms;
extern uint16_t gripper_preload_ms;
extern uint16_t gripper_boost_ms;
extern uint16_t gripper_open_timeout_ms;
extern uint16_t gripper_close_timeout_ms;
int8_t Gripper_Init(const MOTOR_RM_Param_t& param);
void Gripper_Step(bool target_close);
} // namespace arm

View File

@ -7,6 +7,7 @@
#include "task/user_task.h"
#include "module/arm_oop.hpp"
#include "module/gripper_rm.hpp"
#include "module/motor_base.hpp"
#include "module/joint.hpp"
#include "bsp/can.h"
@ -66,6 +67,16 @@ RoboticArm* robot_arm = nullptr;
Arm_CMD_t arm_cmd; // 当前机械臂控制命令(含 target_pose / set_target_as_current
static MOTOR_RM_Param_t MakeGripperRMParam() {
MOTOR_RM_Param_t param{};
param.can = BSP_CAN_1;
param.id = 0x201;
param.module = MOTOR_M3508;
param.reverse = false;
param.gear = true;
return param;
}
// ============================================================================
// 测试用调试变量可在调试器Watch窗口直接观察和修改
// ============================================================================
@ -154,10 +165,26 @@ static Arm6dof_Pose_t SyncTargetToFrame(uint8_t frame, const Arm6dof_Pose_t& cur
}
static void SyncCommandTargetFromCurrent() {
Arm6dof_Pose_t sync = SyncTargetToFrame(control_frame, robot_arm->GetEndPose());
if (!robot_arm) {
return;
}
const Arm6dof_Pose_t sync = SyncTargetToFrame(control_frame, robot_arm->GetEndPose());
Arm6dof_JointAngles_t sync_joints = arm_cmd.target_joints;
for (int i = 0; i < 6; ++i) {
if (joints[i]) {
sync_joints.q[i] = joints[i]->GetCurrentAngle();
}
}
arm_cmd.target_pose = sync;
arm_cmd.target_joints = sync_joints;
// 同步上游CMD中的目标值避免下一帧被旧目标覆盖导致跳变。
// 注意这里只同步目标量不修改夹爪toggle等一次性控制位。
if (cmd_for_arm) {
cmd_for_arm->target_pose = sync;
cmd_for_arm->target_joints = sync_joints;
}
}
@ -187,6 +214,12 @@ static void RelaxAllMotors() {
}
}
static void ApplyJointTorques(const float torques[6]) {
for (int i = 0; i < 6; ++i) {
joints[i]->TorqueControl(torques[i]);
}
}
static void MoveBySelectedFrame(const Arm6dof_Pose_t& target) {
switch (control_frame) {
case 2:
@ -219,6 +252,65 @@ static void RunCartesianControl(ControlMode mode) {
traj_progress_dbg = robot_arm->GetTrajProgress();
}
static void RunJointControlFromCmd() {
float target_joints[6];
for (int i = 0; i < 6; ++i) {
target_joints[i] = arm_cmd.target_joints.q[i];
}
robot_arm->SetMode(ControlMode::JOINT_POSITION);
if (robot_arm->MoveJoint(target_joints) != 0) {
// 超限时回收错误并把命令吸附到当前角,避免持续触发错误
robot_arm->ResetError();
SyncCommandTargetFromCurrent();
}
robot_arm->Control();
ik_from_fk_result = robot_arm->GetIkAngles();
traj_progress_dbg = 0.0f;
}
static void RunTestStageControl() {
switch (test_stage) {
case 0: {
// 检测电机正电流旋转方向与urdf规定是否一致
static float rotationDirectionCheck[6] = {0.0f};
ApplyJointTorques(rotationDirectionCheck);
break;
}
case 1:
// 阶段1仅计算不输出。观察 gravity_torques_dbg[0~5] 是否量级合理
RelaxAllMotors();
break;
case 2:
// 阶段2输出重力补偿力矩验证各轴方向与量级
ApplyJointTorques(gravity_torques_dbg);
break;
case 4:
// 笛卡尔空间轨迹规划控制:基于数值逆解(雅可比)
RunCartesianControl(ControlMode::CARTESIAN_POSITION);
break;
case 5:
// 2.5D 解析降维控制(支持关节/笛卡尔两种上层指令)
if (arm_cmd.ctrl_type == ARM_CTRL_CUSTOM_JOINT) {
RunJointControlFromCmd();
} else {
RunCartesianControl(ControlMode::CARTESIAN_ANALYTICAL);
}
break;
case 3:
default:
// 默认:全部六轴 GRAVITY_COMP 模式(位置保持 + 重力补偿前馈)
robot_arm->SetMode(ControlMode::GRAVITY_COMP);
robot_arm->Control();
break;
}
}
extern "C" {
void Task_arm_main(void* argument) {
@ -230,6 +322,7 @@ void Task_arm_main(void* argument) {
BSP_CAN_Init();
MOTOR_LZ_Init(); // 注册LZ电机ID解析器
Gripper_Init(MakeGripperRMParam());
// 初始化运动学模型
Arm6dof_Init(dh_params);
@ -290,7 +383,15 @@ void Task_arm_main(void* argument) {
// 更新机械臂状态读取关节角度、FK、重力补偿计算
robot_arm->Update();
osMessageQueueGet(task_runtime.msgq.arm.cmd, &arm_cmd, NULL, 0);
const osStatus_t arm_cmd_rx = osMessageQueueGet(task_runtime.msgq.arm.cmd, &arm_cmd, NULL, 0);
if (arm_cmd_rx != osOK) {
// 未收到新命令时清理单帧脉冲避免重复消费上一帧toggle类信号
arm_cmd.gripper_toggle = false;
arm_cmd.set_target_as_current = false;
}
// 夹爪两态控制:直接跟随 gripper_close内部状态机负责稳夹优先策略
Gripper_Step(arm_cmd.gripper_close);
robot_arm->Enable(arm_cmd.enable);
@ -306,6 +407,13 @@ void Task_arm_main(void* argument) {
SyncCommandTargetFromCurrent();
}
// 控制类型切换沿:同步目标,避免 pose/joint 切换时命令继承旧目标导致突变
static Arm_CtrlType_t last_ctrl_type = ARM_CTRL_REMOTE_CARTESIAN;
if (arm_cmd.enable && arm_cmd.ctrl_type != last_ctrl_type) {
SyncCommandTargetFromCurrent();
}
last_ctrl_type = arm_cmd.ctrl_type;
// 每帧同步调试观察变量
for (int i = 0; i < 6; ++i) {
@ -318,90 +426,36 @@ void Task_arm_main(void* argument) {
end_pose_mm_dbg = { ep.x * 1000.0f, ep.y * 1000.0f, ep.z * 1000.0f,
ep.roll, ep.pitch, ep.yaw };
switch (test_stage) {
case 0:
//检测电机正电流旋转方向与urdf规定是否一致
static float rotationDirectionCheck[6] = {0.0f};
joints[0]->TorqueControl(rotationDirectionCheck[0]);
joints[1]->TorqueControl(rotationDirectionCheck[1]);
joints[2]->TorqueControl(rotationDirectionCheck[2]);
joints[3]->TorqueControl(rotationDirectionCheck[3]);
joints[4]->TorqueControl(rotationDirectionCheck[4]);
joints[5]->TorqueControl(rotationDirectionCheck[5]);
break;
case 1:
// 阶段1仅计算不输出。观察 gravity_torques_dbg[0~5] 是否量级合理
RelaxAllMotors();
break;
case 2:
// 阶段1测试单个关节力矩输出用于验证力矩计算
// 修改下面的索引来测试不同关节j2=1, j3=2, j5=4
// motors[0]->Relax();
// motors[1]->Relax();
// motors[2]->Relax();
// motors[3]->Relax();
// motors[4]->Relax();
// motors[5]->Relax();
joints[1]->TorqueControl(gravity_torques_dbg[1]); // j2大臂
joints[2]->TorqueControl(gravity_torques_dbg[2]); // j3小臂
joints[4]->TorqueControl(gravity_torques_dbg[4]); // j5腕部
joints[0]->TorqueControl(gravity_torques_dbg[0]);
joints[3]->TorqueControl(gravity_torques_dbg[3]);
joints[5]->TorqueControl(gravity_torques_dbg[5]);
break;
case 3:
default:
// 阶段4全部六轴 GRAVITY_COMP 模式(位置保持 + 重力补偿前馈)
robot_arm->SetMode(ControlMode::GRAVITY_COMP);
robot_arm->Control();
break;
case 4:
// 笛卡尔空间轨迹规划控制:基于数值逆解(雅可比)
RunCartesianControl(ControlMode::CARTESIAN_POSITION);
break;
case 5: {
// =================================================================================
// ★ 2.5D 解析降维控制算法 ★
// 已经集成到底层 arm_oop 的 CARTESIAN_ANALYTICAL 模式中,完全与原轨迹规划流程接轨
// =================================================================================
RunCartesianControl(ControlMode::CARTESIAN_ANALYTICAL); // 启用降维解析数学模式
break;
}
}
RunTestStageControl();
// IK测试以当前角度为初始猜测对 target_pose 求逆运动学
// 在调试器中将 ik_test_enable 置1启用观察 ik_test_result 和 ik_test_ret
if (ik_test_enable) {
ik_test_ret_analytical = robot_arm->InverseKinematicsAnalytical(&arm_cmd.target_pose,
&ik_test_result,
&current_angles);
ik_test_ret_numerical = 0;
ik_test_solver_dbg = -1;
// if (ik_test_enable) {
// ik_test_ret_analytical = robot_arm->InverseKinematicsAnalytical(&arm_cmd.target_pose,
// &ik_test_result,
// &current_angles);
// ik_test_ret_numerical = 0;
// ik_test_solver_dbg = -1;
// 解析解失败时回退到数值IK便于调试观察并区分“无解”与“分支失败”。
if (ik_test_ret_analytical == 0) {
ik_test_ret = 0;
ik_test_solver_dbg = 0;
} else {
ik_test_ret_numerical = Arm6dof_InverseKinematics(&arm_cmd.target_pose,
&current_angles,
&ik_test_result,
0.001f,
50);
if (ik_test_ret_numerical == 0) {
ik_test_ret = 0;
ik_test_solver_dbg = 1;
} else {
ik_test_ret = -1;
ik_test_solver_dbg = -1;
}
}
}
// // 解析解失败时回退到数值IK便于调试观察并区分“无解”与“分支失败”。
// if (ik_test_ret_analytical == 0) {
// ik_test_ret = 0;
// ik_test_solver_dbg = 0;
// } else {
// ik_test_ret_numerical = Arm6dof_InverseKinematics(&arm_cmd.target_pose,
// &current_angles,
// &ik_test_result,
// 0.001f,
// 50);
// if (ik_test_ret_numerical == 0) {
// ik_test_ret = 0;
// ik_test_solver_dbg = 1;
// } else {
// ik_test_ret = -1;
// ik_test_solver_dbg = -1;
// }
// }
// }
osDelayUntil(tick); /* 运行结束,等待下一次唤醒 */
}