// // Created by xtkuang on 2025/7/24. // #include "humanoid_robot.h" #include "motor/ti5_motor/canopen/ti5_motor_canopen_protocol.h" #include "motor/ti5_motor/ti5_motor.h" #include "utils/base/abstract_interpolation.h" using namespace std; using namespace cmvr::device; template HumanoidRobot::HumanoidRobot(const XmlNode &cfg) : AbstractRobot(cfg) { try { id_ = cfg.getAttrString("id"); dof_ = DOF; if (!pathExists(cfg.getAttrString("urdf"))) { throw runtime_error("urdf file does not exist"); } auto rcfg = cmvr::dyn::LoadRobotFromURDF( cfg.getAttrString("urdf"), cfg.getAttrString("baseLink")); m_robot_ = std::make_shared >(rcfg); joint_names_ = splitString(cfg.getAttrString("jointNames"), ","); link_names_ = splitString(cfg.getAttrString("linkNames"), ","); if (joint_names_.size() != dof_) { throw runtime_error("joint names size mismatched with dof"); } m_state_ = m_robot_->MakeState(link_names_, joint_names_); m_cctrl_ = make_shared >(m_robot_); upd_freq_ = cfg.getAttrDefault("updFreq", 500); CSP_buffer_ = make_shared >(cfg.getAttrDefault("bufferSize", 50)); CSV_buffer_ = make_shared >(cfg.getAttrDefault("bufferSize", 50)); CSC_buffer_ = make_shared >(cfg.getAttrDefault("bufferSize", 50)); auto can_cfg = cfg.getChild("CanManger"); auto l_can_cfg = can_cfg.getChild("LeftArmCan"); l_motors_cfg_ = l_can_cfg.getChildren("Motor"); l_can_client_ = std::make_shared(l_can_cfg); l_can_sender_ = std::make_shared >(); l_can_receiver_ = std::make_shared >(); l_message_manager_ = std::make_shared >(); auto r_can_cfg = can_cfg.getChild("RightArmCan"); r_motors_cfg_ = r_can_cfg.getChildren("Motor"); r_can_client_ = std::make_shared(r_can_cfg); r_can_sender_ = std::make_shared >(); r_can_receiver_ = std::make_shared >(); r_message_manager_ = std::make_shared >(); auto waist_can_cfg = can_cfg.getChild("WaistCan"); waist_motors_cfg_ = waist_can_cfg.getChildren("Motor"); waist_can_client_ = std::make_shared(waist_can_cfg); waist_can_sender_ = std::make_shared >(); waist_can_receiver_ = std::make_shared >(); waist_message_manager_ = std::make_shared >(); upd_timer_ = make_shared(); upd_timer_->start(chrono::nanoseconds(1000 / upd_freq_ * 1000), [this] { update_state_(); }); rsm_.store(ROBOT_READY); } catch (exception &e) { LOG(ERROR) << "HumanoidRobot init failed, id=" << id_; throw runtime_error(e.what()); } } template void HumanoidRobot::init() { // 1 === 初始化公共组件 === l_can_client_->init(); r_can_client_->init(); waist_can_client_->init(); auto ret = l_can_sender_->Init(l_can_client_.get(), false); if (ret != ErrorCode::OK) { LOG(ERROR) << "Failed to init can sender."; } ret = r_can_sender_->Init(r_can_client_.get(), false); if (ret != ErrorCode::OK) { LOG(ERROR) << "Failed to init can sender."; } ret = waist_can_sender_->Init(waist_can_client_.get(), false); if (ret != ErrorCode::OK) { LOG(ERROR) << "Failed to init can sender."; } ret = l_can_receiver_->Init(l_can_client_.get(), l_message_manager_.get(), false); if (ret != ErrorCode::OK) { LOG(ERROR) << "Failed to init can receiver."; } ret = r_can_receiver_->Init(r_can_client_.get(), r_message_manager_.get(), false); if (ret != ErrorCode::OK) { LOG(ERROR) << "Failed to init can receiver."; } ret = waist_can_receiver_->Init(waist_can_client_.get(), waist_message_manager_.get(), false); if (ret != ErrorCode::OK) { LOG(ERROR) << "Failed to init can receiver."; } // 2 === 启动通讯 === l_can_client_->start(); ret = l_can_sender_->Start(); if (ret != ErrorCode::OK) { LOG(ERROR) << "Failed to start can sender."; } r_can_client_->start(); ret = r_can_sender_->Start(); if (ret != ErrorCode::OK) { LOG(ERROR) << "Failed to start can sender."; } waist_can_client_->start(); ret = waist_can_sender_->Start(); if (ret != ErrorCode::OK) { LOG(ERROR) << "Failed to start can sender."; } ret = l_can_receiver_->Start(); if (ret != ErrorCode::OK) { LOG(ERROR) << "Failed to start can receiver."; } ret = r_can_receiver_->Start(); if (ret != ErrorCode::OK) { LOG(ERROR) << "Failed to start can receiver."; } ret = waist_can_receiver_->Start(); if (ret != ErrorCode::OK) { LOG(ERROR) << "Failed to start can receiver."; } // 3 == 创建协议 === auto l_canopen_protocol = std::make_shared(l_can_sender_, l_message_manager_); auto r_canopen_protocol = std::make_shared(r_can_sender_, r_message_manager_); auto waist_canopen_protocol = std::make_shared(waist_can_sender_, waist_message_manager_); // 4 === 创建 MotorManager === motor_manager_ = std::make_shared(); // for (const auto& cfg : r_motors_cfg_) { // auto motor = std::make_shared(cfg); // motor->setProtocol(r_canopen_protocol); // motor->init(); // 耗时操作 // motor_manager_->addMotor(motor); // } // // for (const auto& cfg : l_motors_cfg_) { // auto motor = std::make_shared(cfg); // motor->setProtocol(l_canopen_protocol); // motor->init(); // 耗时操作 // motor_manager_->addMotor(motor); // } // 5 === 并行创建电机 === auto left_task = std::async(std::launch::async, [&] { LOG(INFO) << "[Thread " << std::this_thread::get_id() << "] Start initializing LEFT motors..."; for (const auto &cfg: l_motors_cfg_) { auto motor = std::make_shared(cfg); motor->setProtocol(l_canopen_protocol); motor->init(); motor_manager_->addMotor(motor); } }); auto right_task = std::async(std::launch::async, [&] { LOG(INFO) << "[Thread " << std::this_thread::get_id() << "] Start initializing RIGHT motors..."; for (const auto &cfg: r_motors_cfg_) { auto motor = std::make_shared(cfg); motor->setProtocol(r_canopen_protocol); motor->init(); motor_manager_->addMotor(motor); } }); auto waist_task = std::async(std::launch::async, [&] { LOG(INFO) << "[Thread " << std::this_thread::get_id() << "] Start initializing waist motors..."; for (const auto &cfg: waist_motors_cfg_) { auto motor = std::make_shared(cfg); motor->setProtocol(waist_canopen_protocol); motor->init(); motor_manager_->addMotor(motor); } }); // 等待两个线程完成 left_task.get(); right_task.get(); waist_task.get(); rsm_.store(ROBOT_ESTOP); LOG(INFO) << "All motors initialized successfully."; } template void HumanoidRobot::torqueOff() { try { if (rsm_.load() == ROBOT_RUNNING) { throw runtime_error("robot is running"); } if (rsm_.load() != ROBOT_TOROFF) { for (const auto &pair: motor_manager_->motorsMap()) { if (pair.second->jointName() != "WAIST_Y" && pair.second->jointName() != "WAIST_P" ) pair.second->torqueOff(); } rsm_.store(ROBOT_TOROFF); } } catch (std::exception &e) { throw runtime_error(e.what()); } } template HumanoidRobot::~HumanoidRobot() { // TODO: close can interfaces upd_timer_->stop(); std::vector cmd = { {"L_SHOULDER_P", 0.0}, {"L_SHOULDER_R", -1.31873}, {"L_SHOULDER_Y", 0.0}, {"L_ELBOW_R", -0.537621}, {"L_WRIST_P", 0.0}, {"L_WRIST_Y", 0.000183204}, {"L_WRIST_R", 0.0225797}, {"R_SHOULDER_P", -0.0201069}, {"R_SHOULDER_R", 1.46698}, {"R_SHOULDER_Y", 1.45894}, {"R_ELBOW_R", 0.159681}, {"R_WRIST_P", 0.0808349}, {"R_WRIST_Y", -0.138279}, {"R_WRIST_R", -0.243169}, {"WAIST_Y", 0}, {"WAIST_P", 0} }; // this->moveJ(cmd,0.8); this->torqueOff(); } template int HumanoidRobot::getDOF() { return dof_; } template std::vector HumanoidRobot::getJointNames() { return joint_names_; } template std::unordered_map HumanoidRobot::getJointQ() const{ std::unordered_map joint_qs; for (const auto &pair : motor_manager_->motorsMap()) { auto motor = pair.second; joint_qs[motor->jointName()] = motor->getQ(); } return joint_qs; } template void HumanoidRobot::getJointQ(std::unordered_map &joint_qs) const { for (auto &pair : joint_qs) { auto motor = motor_manager_->getMotor(pair.first); if (motor) { pair.second = motor->getQ(); } else { pair.second = 0.0; } } } template std::vector HumanoidRobot::getLinkNames() { return link_names_; } template void HumanoidRobot::getState(RobotState &state) { try { lock_guard lock(exec_mtx_); // TODO: copy m_state_ date into state } catch (exception &e) { throw runtime_error(e.what()); } } template void HumanoidRobot::torqueOn() { eStop(); } template void HumanoidRobot::eStop() { if (rsm_.load() != ROBOT_ESTOP) { CSP_buffer_->clear(); CSV_buffer_->clear(); CSC_buffer_->clear(); for (const auto &pair: motor_manager_->motorsMap()) { pair.second->brake(); } rsm_.store(ROBOT_ESTOP); } } template void HumanoidRobot::moveJ(std::vector &cmd, double vel, double acc) { try { if (rsm_.load() == ROBOT_RUNNING) { flash_cmd_.store(true); eStop(); } if (rsm_.load() == ROBOT_ESTOP || rsm_.load() == ROBOT_READY || rsm_.load() == ROBOT_TOROFF) { rsm_.store(ROBOT_RUNNING); for (const auto &j: cmd) { auto motor = motor_manager_->getMotor(j.joint_name); if (motor != nullptr) { // PPM 模式下 这个实际速度会超30% 左右 motor->setQd(vel); if (motor->getMode() != msgs::RUN_MODE_PROFILE_POSITION) { motor->setMode(msgs::RUN_MODE_PROFILE_POSITION); } motor->setQ(j.rad); } } //3. wait for completion bool completion = true; do { completion = true; for (const auto &j: cmd) { auto motor = motor_manager_->getMotor(j.joint_name); if (motor != nullptr) { if (!motor->reachedTargetQ()) { completion = false; break; } } } // 4. while waiting, check flash_cmd_, if it is true, set it false then exit if (flash_cmd_.load()) { flash_cmd_.store(false); return; } std::this_thread::sleep_for(std::chrono::milliseconds(2)); } while (!completion); rsm_.store(ROBOT_ESTOP); } else { throw runtime_error("rsm invalid"); } } catch (exception &e) { throw runtime_error(e.what()); } } template void HumanoidRobot::calibrateZeroQ(const std::string &joint_name) { auto motor = motor_manager_->getMotor(joint_name); motor->calibrateZeroQ(); } template void HumanoidRobot::moveJ(const std::string &base_link, const std::string &ee_link, msgs::Pose3d pose, double vel, double acc) { try { if (rsm_.load() == ROBOT_RUNNING) { flash_cmd_.store(true); eStop(); } if (rsm_.load() == ROBOT_ESTOP || rsm_.load() == ROBOT_READY || rsm_.load() == ROBOT_TOROFF) { rsm_.store(ROBOT_RUNNING); // update m_state_ Eigen::Vector q_init; auto q_map = getJointQ(); q_init << q_map["L_SHOULDER_P"], q_map["L_SHOULDER_R"], q_map["L_SHOULDER_Y"], q_map["L_ELBOW_R"], q_map["L_WRIST_P"], q_map["L_WRIST_Y"], q_map["L_WRIST_R"], q_map["R_SHOULDER_P"], q_map["R_SHOULDER_R"], q_map["R_SHOULDER_Y"], q_map["R_ELBOW_R"], q_map["R_WRIST_P"], q_map["R_WRIST_Y"], q_map["R_WRIST_R"]; LOG(INFO) << "q_init: " << q_init; m_state_->SetQ(q_init); m_robot_->ComputeForwardKinematics(m_state_); Eigen::Matrix4d T_target = Eigen::Matrix4d::Identity(); T_target.block<3,3>(0,0) = eulerZYXToRotationMatrix(pose.euler().rx(), pose.euler().ry(), pose.euler().rz()); // 输入为弧度 T_target(0,3) = pose.position().x(); T_target(1,3) = pose.position().y(); T_target(2,3) = pose.position().z(); cmvr::ctrl::PoseTarget target; target.T_target = T_target; target.w_posrot = 0.5; target.weight = 1.0; target.link_name = ee_link; // slove ik Eigen::Vector q_cmd; bool ok = m_cctrl_->compute(m_state_, base_link, {target}, 0.002, ctrl::CartesianController::Mode::Position, q_cmd, 10000, 1e-6); if (!ok) { throw runtime_error("solve IK failed"); } std::vector joint_points{ {"R_SHOULDER_P", q_cmd[7]}, {"R_SHOULDER_R", q_cmd[8]}, {"R_SHOULDER_Y", q_cmd[9]}, {"R_ELBOW_R", q_cmd[10]}, {"R_WRIST_P", q_cmd[11]}, {"R_WRIST_Y", q_cmd[12]}, {"R_WRIST_R", q_cmd[13]} }; for (const auto &j: joint_points) { auto motor = motor_manager_->getMotor(j.joint_name); if (motor != nullptr) { // PPM 模式下 这个实际速度会超30% 左右 motor->setQd(vel); if (motor->getMode() != msgs::RUN_MODE_PROFILE_POSITION) { motor->setMode(msgs::RUN_MODE_PROFILE_POSITION); } motor->setQ(j.rad); } } //3. wait for completion bool completion = true; do { completion = true; for (const auto &j: joint_points) { auto motor = motor_manager_->getMotor(j.joint_name); if (motor != nullptr) { if (!motor->reachedTargetQ()) { completion = false; break; } } } // 4. while waiting, check flash_cmd_, if it is true, set it false then exit if (flash_cmd_.load()) { flash_cmd_.store(false); return; } std::this_thread::sleep_for(std::chrono::milliseconds(2)); } while (!completion); rsm_.store(ROBOT_READY); } else { throw runtime_error("rsm invalid"); } } catch (exception &e) { throw runtime_error(e.what()); } } template void HumanoidRobot::moveJ_IK(const std::string &base_link, const std::vector &targets, double vel, double acc) { try { if (rsm_.load() == ROBOT_RUNNING) { flash_cmd_.store(true); eStop(); } if (rsm_.load() == ROBOT_ESTOP || rsm_.load() == ROBOT_READY || rsm_.load() == ROBOT_TOROFF) { rsm_.store(ROBOT_RUNNING); // update m_state_ Eigen::Vector q_init; auto q_map = getJointQ(); q_init << q_map["L_SHOULDER_P"], q_map["L_SHOULDER_R"], q_map["L_SHOULDER_Y"], q_map["L_ELBOW_R"], q_map["L_WRIST_P"], q_map["L_WRIST_Y"], q_map["L_WRIST_R"], q_map["R_SHOULDER_P"], q_map["R_SHOULDER_R"], q_map["R_SHOULDER_Y"], q_map["R_ELBOW_R"], q_map["R_WRIST_P"], q_map["R_WRIST_Y"], q_map["R_WRIST_R"]; LOG(INFO) << "q_init: " << q_init; m_state_->SetQ(q_init); m_robot_->ComputeForwardKinematics(m_state_); // slove ik Eigen::Vector q_cmd; bool ok = m_cctrl_->compute(m_state_, base_link, targets, 0.002, ctrl::CartesianController::Mode::Position, q_cmd, 10000, 1e-6); if (!ok) { throw runtime_error("solve IK failed"); } std::vector joint_points{ {"R_SHOULDER_P", q_cmd[7]}, {"R_SHOULDER_R", q_cmd[8]}, {"R_SHOULDER_Y", q_cmd[9]}, {"R_ELBOW_R", q_cmd[10]}, {"R_WRIST_P", q_cmd[11]}, {"R_WRIST_Y", q_cmd[12]}, {"R_WRIST_R", q_cmd[13]} }; // for (const auto &j: joint_points) { // auto motor = motor_manager_->getMotor(j.joint_name); // if (motor != nullptr) { // // PPM 模式下 这个实际速度会超30% 左右 // motor->setQd(vel); // if (motor->getMode() != msgs::RUN_MODE_PROFILE_POSITION) { // motor->setMode(msgs::RUN_MODE_PROFILE_POSITION); // } // motor->setQ(j.rad); // } // } // // //3. wait for completion // bool completion = true; // do { // completion = true; // for (const auto &j: joint_points) { // auto motor = motor_manager_->getMotor(j.joint_name); // if (motor != nullptr) { // if (!motor->reachedTargetQ()) { // completion = false; // break; // } // } // } // // 4. while waiting, check flash_cmd_, if it is true, set it false then exit // if (flash_cmd_.load()) { // flash_cmd_.store(false); // return; // } // std::this_thread::sleep_for(std::chrono::milliseconds(2)); // } while (!completion); rsm_.store(ROBOT_READY); } else { throw runtime_error("rsm invalid"); } } catch (exception &e) { throw runtime_error(e.what()); } } // template // void HumanoidRobot::moveL(std::string &base_link, std::vector &targets, double vel, double acc) { // try { // if (rsm_.load() == ROBOT_RUNNING) { // flash_cmd_.store(true); // eStop(); // return; // } // if (rsm_.load() == ROBOT_ESTOP || rsm_.load() == ROBOT_READY || rsm_.load() == ROBOT_TOROFF) { // rsm_.store(ROBOT_RUNNING); // // // 获取当前关节状态 // Eigen::Vector q_init; // auto q_map = getJointQ(); // q_init << q_map["L_SHOULDER_P"], q_map["L_SHOULDER_R"], q_map["L_SHOULDER_Y"], q_map["L_ELBOW_R"], // q_map["L_WRIST_P"], q_map["L_WRIST_Y"], q_map["L_WRIST_R"], // q_map["R_SHOULDER_P"], q_map["R_SHOULDER_R"], q_map["R_SHOULDER_Y"], q_map["R_ELBOW_R"], // q_map["R_WRIST_P"], q_map["R_WRIST_Y"], q_map["R_WRIST_R"]; // // LOG(INFO) << "q_init: " << q_init; // m_state_->SetQ(q_init); // m_robot_->ComputeForwardKinematics(m_state_); // // // 获取基座链接索引 // auto base_idx = m_robot_->GetLinkIdx(base_link); // // // // 获取当前末端位姿 - 使用前向运动学计算 // std::vector current_poses; // for (const auto& target : targets) { // auto ee_idx = m_robot_->GetLinkIdx(target.link_name); // // // // 使用正向运动学计算当前位姿 // Eigen::Matrix4d T = m_robot_->GetTransformation(m_state_, base_idx, ee_idx); // current_poses.push_back(T); // // // 打印当前末端执行器的 XYZ 和欧拉角 // if (&target == &targets.front()) { // Eigen::Vector3d position = T.block<3, 1>(0, 3); // Eigen::Matrix3d rotation = T.block<3, 3>(0, 0); // Eigen::Vector3d euler = rotationMatrixToEulerZYX(rotation); // // LOG(INFO) << "Starting point (Initial position): " // << "X: " << position[0] << ", Y: " << position[1] << ", Z: " << position[2]; // LOG(INFO) << "Starting orientation (Euler angles): " // << "RX: " << euler[0] << ", RY: " << euler[1] << ", RZ: " << euler[2]; // } // } // // // 计算最大距离和插值点数 // double max_distance = 0.0; // for (size_t i = 0; i < targets.size(); i++) { // Eigen::Vector3d current_pos = current_poses[i].block<3, 1>(0, 3); // Eigen::Vector3d target_pos = targets[i].T_target.block<3, 1>(0, 3); // double distance = (target_pos - current_pos).norm(); // max_distance = std::max(max_distance, distance); // } // // // 基于速度和距离计算插值点数 // double move_time = max_distance / vel; // int num_points = static_cast(move_time * 100); // 100Hz控制频率 // // // 存储所有插值点的关节角度 // std::vector> joint_trajectory; // joint_trajectory.reserve(num_points + 1); // // // 记录上一次成功的关节角度 // Eigen::Vector last_success_q = q_init; // // // 预先计算所有插值点的逆运动学 // for (int i = 0; i <= num_points; i++) { // if (flash_cmd_.load()) { // flash_cmd_.store(false); // rsm_.store(ROBOT_READY); // return; // } // // double t = static_cast(i) / num_points; // // // 创建插值后的目标 // std::vector interpolated_targets = targets; // for (size_t j = 0; j < targets.size(); j++) { // // 位置线性插值 // Eigen::Vector3d current_pos = current_poses[j].block<3, 1>(0, 3); // Eigen::Vector3d target_pos = targets[j].T_target.block<3, 1>(0, 3); // Eigen::Vector3d interp_pos = current_pos + t * (target_pos - current_pos); // // // 旋转球面线性插值 // Eigen::Matrix3d current_rot_matrix = current_poses[j].block<3, 3>(0, 0); // Eigen::Matrix3d target_rot_matrix = targets[j].T_target.block<3, 3>(0, 0); // Eigen::Quaterniond current_rot(current_rot_matrix); // Eigen::Quaterniond target_rot(target_rot_matrix); // Eigen::Quaterniond interp_rot = current_rot.slerp(t, target_rot); // // // 更新目标位姿 // interpolated_targets[j].T_target.setIdentity(); // interpolated_targets[j].T_target.block<3, 3>(0, 0) = interp_rot.toRotationMatrix(); // interpolated_targets[j].T_target.block<3, 1>(0, 3) = interp_pos; // } // // // 求解逆运动学 // Eigen::Vector q_cmd; // bool ok = m_cctrl_->compute(m_state_, base_link, interpolated_targets, 0.002, // ctrl::CartesianController::Mode::Position, // q_cmd, 10000, 1e-6); // // if (!ok) { // LOG(WARNING) << "IK failed at point " << i << ", using last successful configuration"; // q_cmd = last_success_q; // } else { // last_success_q = q_cmd; // } // // joint_trajectory.push_back(q_cmd); // // // 获取当前末端执行器的位置 (通过正向运动学) // m_state_->SetQ(q_cmd); // m_robot_->ComputeForwardKinematics(m_state_); // // // 获取当前末端执行器的位姿 (变换矩阵 T) // auto ee_idx = m_robot_->GetLinkIdx(targets[0].link_name); // Eigen::Matrix4d T = m_robot_->GetTransformation(m_state_, base_idx, ee_idx); // // // 从变换矩阵中提取 XYZ 坐标 // Eigen::Vector3d end_effector_pos = T.block<3, 1>(0, 3); // // // 打印 IK 解算出的 XYZ 位置 // if (i % 10 == 0) { // 每10个点打印一次,避免日志过多 // LOG(INFO) << "IK solution at point " << i << " : " // << "X: " << end_effector_pos[0] << ", Y: " << end_effector_pos[1] << ", Z: " << end_effector_pos[2]; // } // } // // // 计算每个关节的最大角度变化 // Eigen::Vector max_angle_change = Eigen::Vector::Zero(); // for (int i = 1; i < joint_trajectory.size(); i++) { // Eigen::Vector delta = joint_trajectory[i] - joint_trajectory[i-1]; // for (int j = 0; j < DOF; j++) { // if (std::abs(delta[j]) > std::abs(max_angle_change[j])) { // max_angle_change[j] = delta[j]; // } // } // } // // // 计算每个关节所需的时间比例因子 // Eigen::Vector time_scale_factors = Eigen::Vector::Ones(); // for (int j = 0; j < DOF; j++) { // if (std::abs(max_angle_change[j]) > 1e-6) { // // 根据关节的最大速度和加速度限制计算时间比例因子 // double max_vel = 1.0; // 假设最大角速度 1 rad/s // double max_acc = 2.0; // 假设最大角加速度 2 rad/s² // // double required_time_vel = std::abs(max_angle_change[j]) / max_vel; // double required_time_acc = std::sqrt(std::abs(max_angle_change[j]) / max_acc); // // double required_time = std::max(required_time_vel, required_time_acc); // time_scale_factors[j] = required_time / move_time; // } // } // // // 取最大的时间比例因子作为整体时间缩放因子 // double max_time_scale = time_scale_factors.maxCoeff(); // if (max_time_scale > 1.0) { // // 需要延长运动时间 // move_time *= max_time_scale; // num_points = static_cast(move_time * 100); // LOG(INFO) << "Adjusted move time: " << move_time << " seconds"; // } // // // 执行轨迹 // for (int i = 0; i <= num_points; i++) { // if (flash_cmd_.load()) { // flash_cmd_.store(false); // break; // } // // // 计算当前时间点的索引(考虑时间缩放) // int idx = static_cast(i / max_time_scale); // if (idx >= joint_trajectory.size()) { // idx = joint_trajectory.size() - 1; // } // // Eigen::Vector q_cmd = joint_trajectory[idx]; // // // 发送关节命令 - 控制所有7个关节 // std::vector joint_points{ // {"R_SHOULDER_P", q_cmd[7]}, {"R_SHOULDER_R", q_cmd[8]}, // {"R_SHOULDER_Y", q_cmd[9]}, {"R_ELBOW_R", q_cmd[10]}, // {"R_WRIST_P", q_cmd[11]}, {"R_WRIST_Y", q_cmd[12]}, // {"R_WRIST_R", q_cmd[13]} // }; // // // 计算每个关节的角度变化 // std::vector angle_changes(joint_points.size(), 0.0); // if (i > 0) { // int prev_idx = static_cast((i-1) / max_time_scale); // if (prev_idx >= joint_trajectory.size()) { // prev_idx = joint_trajectory.size() - 1; // } // // Eigen::Vector prev_q = joint_trajectory[prev_idx]; // for (size_t j = 0; j < joint_points.size(); j++) { // angle_changes[j] = std::abs(q_cmd[7 + j] - prev_q[7 + j]); // } // } // // // 设置每个关节的速度和位置 // for (size_t j = 0; j < joint_points.size(); j++) { // auto& joint_point = joint_points[j]; // auto motor = motor_manager_->getMotor(joint_point.joint_name); // if (motor != nullptr) { // // 根据关节的角度变化计算实际速度 // double actual_vel = vel; // if (i > 0) { // actual_vel = angle_changes[j] / (move_time / num_points); // } // // motor->setQd(actual_vel); // if (motor->getMode() != msgs::RUN_MODE_PROFILE_POSITION) { // motor->setMode(msgs::RUN_MODE_PROFILE_POSITION); // } // motor->setQ(joint_point.rad); // } // } // // // 等待一段时间,控制频率 // std::this_thread::sleep_for(std::chrono::milliseconds(10)); // } // // // 等待最终位置到达 - 检查所有关节 // bool completion = true; // do { // completion = true; // for (const auto& name : joint_names_) { // auto motor = motor_manager_->getMotor(name); // if (motor != nullptr && !motor->reachedTargetQ()) { // completion = false; // break; // } // } // if (flash_cmd_.load()) { // flash_cmd_.store(false); // return; // } // std::this_thread::sleep_for(std::chrono::milliseconds(2)); // } while (!completion); // // rsm_.store(ROBOT_READY); // } else { // throw std::runtime_error("rsm invalid"); // } // } catch (std::exception &e) { // rsm_.store(ROBOT_ESTOP); // throw std::runtime_error(e.what()); // } // } template void HumanoidRobot::moveL(std::string &base_link, std::vector &targets, double vel, double acc) { try { if (rsm_.load() == ROBOT_RUNNING) { flash_cmd_.store(true); eStop(); return; } if (rsm_.load() == ROBOT_ESTOP || rsm_.load() == ROBOT_READY || rsm_.load() == ROBOT_TOROFF) { rsm_.store(ROBOT_RUNNING); // 获取当前关节状态 Eigen::Vector q_init; auto q_map = getJointQ(); q_init << q_map["L_SHOULDER_P"], q_map["L_SHOULDER_R"], q_map["L_SHOULDER_Y"], q_map["L_ELBOW_R"], q_map["L_WRIST_P"], q_map["L_WRIST_Y"], q_map["L_WRIST_R"], q_map["R_SHOULDER_P"], q_map["R_SHOULDER_R"], q_map["R_SHOULDER_Y"], q_map["R_ELBOW_R"], q_map["R_WRIST_P"], q_map["R_WRIST_Y"], q_map["R_WRIST_R"]; LOG(INFO) << "q_init: " << q_init; m_state_->SetQ(q_init); m_robot_->ComputeForwardKinematics(m_state_); // 获取基座链接索引 auto base_idx = m_robot_->GetLinkIdx(base_link); // 获取当前末端位姿 - 使用前向运动学计算 std::vector current_poses; for (const auto& target : targets) { auto ee_idx = m_robot_->GetLinkIdx(target.link_name); // 使用正向运动学计算当前位姿 Eigen::Matrix4d T = m_robot_->GetTransformation(m_state_, base_idx, ee_idx); current_poses.push_back(T); // 打印当前末端执行器的 XYZ 和欧拉角 if (&target == &targets.front()) { Eigen::Vector3d position = T.block<3, 1>(0, 3); Eigen::Matrix3d rotation = T.block<3, 3>(0, 0); Eigen::Vector3d euler = rotationMatrixToEulerZYX(rotation); LOG(INFO) << "Starting point (Initial position): " << "X: " << position[0] << ", Y: " << position[1] << ", Z: " << position[2]; LOG(INFO) << "Starting orientation (Euler angles): " << "RX: " << euler[0] << ", RY: " << euler[1] << ", RZ: " << euler[2]; } } // 计算最大距离和插值点数 double max_distance = 0.0; for (size_t i = 0; i < targets.size(); i++) { Eigen::Vector3d current_pos = current_poses[i].block<3, 1>(0, 3); Eigen::Vector3d target_pos = targets[i].T_target.block<3, 1>(0, 3); double distance = (target_pos - current_pos).norm(); max_distance = std::max(max_distance, distance); } // 基于速度和距离计算插值点数 double move_time = max_distance / vel; int num_points = static_cast(move_time * 100); // 100Hz控制频率 // 存储所有插值点的关节角度 std::vector> joint_trajectory; joint_trajectory.reserve(num_points + 1); // 记录上一次成功的关节角度 Eigen::Vector last_success_q = q_init; // 预先计算所有插值点的逆运动学 for (int i = 0; i <= num_points; i++) { if (flash_cmd_.load()) { flash_cmd_.store(false); rsm_.store(ROBOT_READY); return; } double t = static_cast(i) / num_points; // 创建插值后的目标(只做位置插值,旋转保持不变) std::vector interpolated_targets = targets; for (size_t j = 0; j < targets.size(); j++) { // 位置线性插值 Eigen::Vector3d current_pos = current_poses[j].block<3, 1>(0, 3); Eigen::Vector3d target_pos = targets[j].T_target.block<3, 1>(0, 3); Eigen::Vector3d interp_pos = current_pos + t * (target_pos - current_pos); // 保持旋转不变 Eigen::Matrix3d current_rot_matrix = current_poses[j].block<3, 3>(0, 0); interpolated_targets[j].T_target.setIdentity(); interpolated_targets[j].T_target.block<3, 3>(0, 0) = current_rot_matrix; interpolated_targets[j].T_target.block<3, 1>(0, 3) = interp_pos; } // 求解逆运动学 Eigen::Vector q_cmd; bool ok = m_cctrl_->compute(m_state_, base_link, interpolated_targets, 0.002, ctrl::CartesianController::Mode::Position, q_cmd, 10000, 1e-6); if (!ok) { LOG(WARNING) << "IK failed at point " << i << ", using last successful configuration"; q_cmd = last_success_q; } else { last_success_q = q_cmd; } joint_trajectory.push_back(q_cmd); // 获取当前末端执行器的位置 (通过正向运动学) m_state_->SetQ(q_cmd); m_robot_->ComputeForwardKinematics(m_state_); // 获取当前末端执行器的位姿 (变换矩阵 T) auto ee_idx = m_robot_->GetLinkIdx(targets[0].link_name); Eigen::Matrix4d T = m_robot_->GetTransformation(m_state_, base_idx, ee_idx); // 从变换矩阵中提取 XYZ 坐标 Eigen::Vector3d end_effector_pos = T.block<3, 1>(0, 3); // 打印 IK 解算出的 XYZ 位置 if (i % 10 == 0) { // 每10个点打印一次,避免日志过多 LOG(INFO) << "IK solution at point " << i << " : " << "X: " << end_effector_pos[0] << ", Y: " << end_effector_pos[1] << ", Z: " << end_effector_pos[2]; } } // 执行轨迹 for (int i = 0; i <= num_points; i++) { if (flash_cmd_.load()) { flash_cmd_.store(false); break; } // 获取当前时间点的关节角度 Eigen::Vector q_cmd = joint_trajectory[i]; // 发送关节命令 std::vector joint_points{ {"R_SHOULDER_P", q_cmd[7]}, {"R_SHOULDER_R", q_cmd[8]}, {"R_SHOULDER_Y", q_cmd[9]}, {"R_ELBOW_R", q_cmd[10]}, {"R_WRIST_P", q_cmd[11]}, {"R_WRIST_Y", q_cmd[12]}, {"R_WRIST_R", q_cmd[13]} }; // 设置每个关节的速度和位置 for (size_t j = 0; j < joint_points.size(); j++) { auto& joint_point = joint_points[j]; auto motor = motor_manager_->getMotor(joint_point.joint_name); if (motor != nullptr) { motor->setQ(joint_point.rad); } } // 等待一段时间,控制频率 std::this_thread::sleep_for(std::chrono::milliseconds(10)); } // 等待最终位置到达 - 检查所有关节 bool completion = true; do { completion = true; for (const auto& name : joint_names_) { auto motor = motor_manager_->getMotor(name); if (motor != nullptr && !motor->reachedTargetQ()) { completion = false; break; } } if (flash_cmd_.load()) { flash_cmd_.store(false); return; } std::this_thread::sleep_for(std::chrono::milliseconds(2)); } while (!completion); rsm_.store(ROBOT_READY); } else { throw std::runtime_error("rsm invalid"); } } catch (std::exception &e) { rsm_.store(ROBOT_ESTOP); throw std::runtime_error(e.what()); } } template void HumanoidRobot::speedJ(std::string &joint_name, RobotJointIndexDirection dir, double vel, double acc) { try { if (rsm_.load() == ROBOT_RUNNING) { flash_cmd_.store(true); eStop(); } else if (rsm_.load() == ROBOT_ESTOP || rsm_.load() == ROBOT_READY) { rsm_.store(ROBOT_RUNNING); // TODO: // 1. set joint speed and acc // 2. set joint speed by PROFILE VELOCITY MODE (PVM) // rsm_.store(ROBOT_READY); -> should not set rsm_ to ready because motor is running } else { throw runtime_error("rsm invalid"); } } catch (exception &e) { throw runtime_error(e.what()); } } template void HumanoidRobot::speedL(RobotCartesian cart, RobotJointIndexDirection dir, double vel, double acc) { try { if (rsm_.load() == ROBOT_RUNNING) { flash_cmd_.store(true); eStop(); } else if (rsm_.load() == ROBOT_ESTOP || rsm_.load() == ROBOT_READY) { rsm_.store(ROBOT_RUNNING); // TODO: ??? // rsm_.store(ROBOT_READY); -> should not set rsm_ to ready because motor is running } else { throw runtime_error("rsm invalid"); } } catch (exception &e) { throw runtime_error(e.what()); } } template void HumanoidRobot::followJointTrajectory(std::vector > &traj, double dt) { try { if (rsm_.load() == ROBOT_ESTOP || rsm_.load() == ROBOT_READY || rsm_.load() == ROBOT_TOROFF) { auto ok = check_joint_traj_(traj, dt); if (!ok) { throw runtime_error("joint traj invalid"); } rsm_.store(ROBOT_RUNNING); // TODO: need to optimize callback loop for (auto i = 0; i < traj.size(); i++) { if (flash_cmd_.load()) { flash_cmd_.store(false); LOG(INFO) << "followJointTrajectory is canceled"; return; } servoJ(traj[i], dt); this_thread::sleep_for(chrono::milliseconds((int) dt)); } rsm_.store(ROBOT_ESTOP); } else { throw runtime_error("rsm invalid"); } } catch (exception &e) { throw runtime_error(e.what()); } } template void HumanoidRobot::followPoseTrajectory(std::string &base_link, std::vector > &targets, double dt) { try { if (rsm_.load() == ROBOT_RUNNING) { flash_cmd_.store(true); eStop(); } else if (rsm_.load() == ROBOT_ESTOP || rsm_.load() == ROBOT_READY) { rsm_.store(ROBOT_RUNNING); // TODO: // 1. set Timer(dt) // 2. for each timestamp, use Cyclic Synchronous Position (CSP) Mode to set joint position // 3. if flash_cmd_ is set, set it to false and exit // 3. join timer rsm_.store(ROBOT_READY); } else { throw runtime_error("rsm invalid"); } } catch (exception &e) { throw runtime_error(e.what()); } } template void HumanoidRobot::servoJ(std::vector &joints, double dt) { for (const auto &j: joints) { auto motor = motor_manager_->getMotor(j.joint_name); if (motor != nullptr) { if (motor->getMode() != msgs::RUN_MODE_CYCLIC_SYNC_POSITION) { motor->setMode(msgs::RUN_MODE_CYCLIC_SYNC_POSITION); } motor->setQd(j.vel); motor->setQ(j.rad); } } rsm_.store(ROBOT_READY); } template void HumanoidRobot::servoJ(std::vector &joints, double vel, double dt) { for (const auto &j: joints) { auto motor = motor_manager_->getMotor(j.joint_name); if (motor != nullptr) { if (motor->getMode() != msgs::RUN_MODE_CYCLIC_SYNC_POSITION) { motor->setMode(msgs::RUN_MODE_CYCLIC_SYNC_POSITION); } motor->setQd(vel); motor->setQ(j.rad); } } } template void HumanoidRobot::servoJ(const std::string &base_link, const std::string &ee_link, msgs::Pose3d pose, double vel, double acc) { try { // update m_state_ Eigen::Vector q_init; auto q_map = getJointQ(); q_init << q_map["L_SHOULDER_P"], q_map["L_SHOULDER_R"], q_map["L_SHOULDER_Y"], q_map["L_ELBOW_R"], q_map["L_WRIST_P"], q_map["L_WRIST_Y"], q_map["L_WRIST_R"], q_map["R_SHOULDER_P"], q_map["R_SHOULDER_R"], q_map["R_SHOULDER_Y"], q_map["R_ELBOW_R"], q_map["R_WRIST_P"], q_map["R_WRIST_Y"], q_map["R_WRIST_R"]; LOG(INFO) << "q_init: " << q_init; m_state_->SetQ(q_init); m_robot_->ComputeForwardKinematics(m_state_); Eigen::Matrix4d T_target = Eigen::Matrix4d::Identity(); T_target.block<3, 3>(0, 0) = eulerZYXToRotationMatrix(pose.euler().rx(), pose.euler().ry(), pose.euler().rz()); // 输入为弧度 T_target(0, 3) = pose.position().x(); T_target(1, 3) = pose.position().y(); T_target(2, 3) = pose.position().z(); cmvr::ctrl::PoseTarget target; target.T_target = T_target; target.w_posrot = 0.5; target.weight = 1.0; target.link_name = ee_link; // slove ik Eigen::Vector q_cmd; bool ok = m_cctrl_->compute(m_state_, base_link, {target}, 0.002, ctrl::CartesianController::Mode::Position, q_cmd, 10000, 1e-6); if (!ok) { throw runtime_error("solve IK failed"); } std::vector joint_points{ {"R_SHOULDER_P", q_cmd[7]}, {"R_SHOULDER_R", q_cmd[8]}, {"R_SHOULDER_Y", q_cmd[9]}, {"R_ELBOW_R", q_cmd[10]}, {"R_WRIST_P", q_cmd[11]}, {"R_WRIST_Y", q_cmd[12]}, {"R_WRIST_R", q_cmd[13]} }; servoJ(joint_points, vel, 0.1); } catch (exception &e) { throw runtime_error(e.what()); } } template void HumanoidRobot::servoDeltaJ(const std::string &base_link, const std::string &ee_link, msgs::Pose3d delta_pose, double vel, double acc) { try { // 1 : 计算当前位姿 auto cur_pose = fk(base_link, ee_link); // 2 : 计算目标角度 target pos = cur_pose + delta_pose cmvr::msgs::Pose3d target_pose; target_pose.mutable_position()->set_x(cur_pose.position().x() + delta_pose.position().x()); target_pose.mutable_position()->set_y(cur_pose.position().y() + delta_pose.position().y()); target_pose.mutable_position()->set_z(cur_pose.position().z() + delta_pose.position().z()); target_pose.mutable_euler()->set_rx(cur_pose.euler().rx() + delta_pose.euler().rx()); target_pose.mutable_euler()->set_ry(cur_pose.euler().ry() + delta_pose.euler().ry()); target_pose.mutable_euler()->set_rz(cur_pose.euler().rz() + delta_pose.euler().rz()); //3 : servoJ(base_link, ee_link, target_pose, vel, acc); } catch (exception &e) { throw runtime_error(e.what()); } } template void HumanoidRobot::servoL(std::string &base_link, std::vector &targets, double dt) { try { Eigen::Vector q_cmd; bool ok = m_cctrl_->compute(m_state_, base_link, targets, 1, ctrl::CartesianController::Mode::Position, q_cmd, 60, 1e-4); if (!ok) { LOG(WARNING) << "[HumanoidRobot] (servoL): solve IK failed, id=" << id_; throw runtime_error("IK failed"); } std::vector joints(dof_); for (size_t i = 0; i < dof_; i++) { joints[i].joint_name = joint_names_[i]; joints[i].rad = q_cmd[i]; } servoJ(joints, dt); } catch (exception &e) { throw runtime_error(e.what()); } } template bool HumanoidRobot::check_joint_traj_(std::vector > &traj, double dt) { // TODO: to be implemented return true; } template void HumanoidRobot::moveDeltaJ(const std::string &base_link, const std::string &ee_link, msgs::Pose3d delta_pose, double vel, double acc) { try { // 1 : 计算当前位姿 auto cur_pose = fk(base_link, ee_link); // 2 : 计算目标角度 target pos = cur_pose + delta_pose cmvr::msgs::Pose3d target_pose; target_pose.mutable_position()->set_x(cur_pose.position().x() + delta_pose.position().x()); target_pose.mutable_position()->set_y(cur_pose.position().y() + delta_pose.position().y()); target_pose.mutable_position()->set_z(cur_pose.position().z() + delta_pose.position().z()); target_pose.mutable_euler()->set_rx(cur_pose.euler().rx() + delta_pose.euler().rx()); target_pose.mutable_euler()->set_ry(cur_pose.euler().ry() + delta_pose.euler().ry()); target_pose.mutable_euler()->set_rz(cur_pose.euler().rz() + delta_pose.euler().rz()); //3 : moveJ(base_link, ee_link, target_pose, vel, acc); } catch (exception &e) { throw runtime_error(e.what()); } } template void HumanoidRobot::update_state_() { std::lock_guard lock(state_mtx_); // TODO: set m_state_ // m_state_->SetQ(); // m_state_->SetQdot(); // m_state_->SetQddot(); } template Eigen::Matrix3d HumanoidRobot::eulerZYXToRotationMatrix(double rx, double ry, double rz) { Eigen::Matrix3d R_x; R_x << 1, 0, 0, 0, cos(rx), -sin(rx), 0, sin(rx), cos(rx); Eigen::Matrix3d R_y; R_y << cos(ry), 0, sin(ry), 0, 1, 0, -sin(ry), 0, cos(ry); Eigen::Matrix3d R_z; R_z << cos(rz), -sin(rz), 0, sin(rz), cos(rz), 0, 0, 0, 1; return R_x * R_y * R_z; } template Eigen::Vector3d HumanoidRobot::rotationMatrixToEulerZYX(const Eigen::Matrix3d &R) { double rx, ry, rz; // 根据 R = R_x * R_y * R_z // R = | cy*cz -cy*sz sy | // | sx*sy*cz + cx*sz -sx*sy*sz + cx*cz -sx*cy | // | -cx*sy*cz + sx*sz cx*sy*sz + sx*cz cx*cy | // 提取 ry(绕 Y 的角度) ry = std::asin(R(0,2)); // R(0,2) = sin(ry) double cy = std::cos(ry); if (std::abs(cy) > 1e-6) { // 正常情况 rx = std::atan2(-R(1,2), R(2,2)); rz = std::atan2(-R(0,1), R(0,0)); } else { // 万向节锁:cy ≈ 0 rx = 0; // 任意选择 if (ry > 0) { rz = std::atan2(R(1,0), R(1,1)); } else { rz = std::atan2(-R(1,0), R(1,1)); } } return Eigen::Vector3d(rx, ry, rz); } template cmvr::msgs::Pose3d HumanoidRobot::fk(const std::string &base_link, const std::string &ee_link) { cmvr::msgs::Pose3d pose; try { // 获取当前关节角度 Eigen::Vector q; auto q_map = getJointQ(); // 类似 moveJ 中获取关节角度 q << q_map["L_SHOULDER_P"], q_map["L_SHOULDER_R"], q_map["L_SHOULDER_Y"], q_map["L_ELBOW_R"], q_map["L_WRIST_P"], q_map["L_WRIST_Y"], q_map["L_WRIST_R"], q_map["R_SHOULDER_P"], q_map["R_SHOULDER_R"], q_map["R_SHOULDER_Y"], q_map["R_ELBOW_R"], q_map["R_WRIST_P"], q_map["R_WRIST_Y"], q_map["R_WRIST_R"]; // 更新状态并计算前向运动学 m_state_->SetQ(q); m_robot_->ComputeForwardKinematics(m_state_); // 获取基座和末端索引 auto base_idx = m_robot_->GetLinkIdx(base_link); auto ee_idx = m_robot_->GetLinkIdx(ee_link); // 获取变换矩阵 Eigen::Matrix4d T = m_robot_->GetTransformation(m_state_, base_idx, ee_idx); // 填充 Pose3d pose.mutable_position()->set_x(T(0,3)); pose.mutable_position()->set_y(T(1,3)); pose.mutable_position()->set_z(T(2,3)); // 将旋转矩阵转换为欧拉角 Eigen::Matrix3d R = T.block<3,3>(0,0); Eigen::Vector3d euler = rotationMatrixToEulerZYX(R); // 你需要实现或已有此工具函数 pose.mutable_euler()->set_rx(euler(0)); pose.mutable_euler()->set_ry(euler(1)); pose.mutable_euler()->set_rz(euler(2)); } catch (const std::exception &e) { throw std::runtime_error(std::string("FK计算失败: ") + e.what()); } return pose; } void printTrajectoryInfo( const std::vector& trajectory, const std::vector& times, const std::vector& velocities, double total_distance) { std::cout << "\n===================================== 轨迹详细信息 =====================================" << std::endl; std::cout << "总路径长度: " << std::fixed << std::setprecision(6) << total_distance << "m" << std::endl; std::cout << "总运动时间: " << std::fixed << std::setprecision(3) << times.back() << "s" << std::endl; std::cout << "轨迹点总数: " << trajectory.size() << " 个" << std::endl; std::cout << "-----------------------------------------------------------------------------------------" << std::endl; std::cout << std::setw(4) << "序号" << " | " << std::setw(8) << "时间(s)" << " | " << std::setw(10) << "x(m)" << " | " << std::setw(10) << "y(m)" << " | " << std::setw(10) << "z(m)" << " | " << std::setw(12) << "速度(m/s)" << " | " << std::setw(16) << "到起点距离(m)" << std::endl; std::cout << "-----------------------------------------------------------------------------------------" << std::endl; Eigen::Vector3d start_pos(trajectory[0](0,3), trajectory[0](1,3), trajectory[0](2,3)); for (size_t idx = 0; idx < trajectory.size(); ++idx) { const auto& T = trajectory[idx]; Eigen::Vector3d pos(T(0,3), T(1,3), T(2,3)); double dist_from_start = (pos - start_pos).norm(); std::cout << std::setw(4) << idx << " | " << std::fixed << std::setprecision(3) << std::setw(8) << times[idx] << " | " << std::fixed << std::setprecision(6) << std::setw(10) << pos.x() << " | " << std::fixed << std::setprecision(6) << std::setw(10) << pos.y() << " | " << std::fixed << std::setprecision(6) << std::setw(10) << pos.z() << " | " << std::fixed << std::setprecision(6) << std::setw(12) << velocities[idx] << " | " << std::fixed << std::setprecision(6) << std::setw(16) << dist_from_start << std::endl; } std::cout << "=========================================================================================\n" << std::endl; } template void HumanoidRobot::moveDeltaL(const std::string &base_link, const std::string &ee_link, msgs::Pose3d delta_pose, double vel, double acc) { try { Eigen::Vector q_current_for_ik; auto q_map_current = getJointQ(); q_current_for_ik << q_map_current["L_SHOULDER_P"], q_map_current["L_SHOULDER_R"], q_map_current["L_SHOULDER_Y"], q_map_current["L_ELBOW_R"], q_map_current["L_WRIST_P"], q_map_current["L_WRIST_Y"], q_map_current["L_WRIST_R"], q_map_current["R_SHOULDER_P"], q_map_current["R_SHOULDER_R"], q_map_current["R_SHOULDER_Y"], q_map_current["R_ELBOW_R"], q_map_current["R_WRIST_P"], q_map_current["R_WRIST_Y"], q_map_current["R_WRIST_R"]; LOG(INFO) << "q_current_for_ik: " << q_current_for_ik; // 1. 计算末端当前位姿(通过FK) msgs::Pose3d current_pose = fk(base_link, ee_link); LOG(INFO) << current_pose.mutable_position()->x() << " " << current_pose.mutable_position()->y() << " " << current_pose.mutable_position()->z() << " " << current_pose.mutable_euler()->rx() << " " << current_pose.mutable_euler()->ry() << " " << current_pose.mutable_euler()->rz(); // 2. 计算目标位姿 = 当前位姿 + 相对偏移(位置/姿态分别叠加) msgs::Pose3d target_pose; target_pose.mutable_position()->set_x(current_pose.position().x() + delta_pose.position().x()); target_pose.mutable_position()->set_y(current_pose.position().y() + delta_pose.position().y()); target_pose.mutable_position()->set_z(current_pose.position().z() + delta_pose.position().z()); target_pose.mutable_euler()->set_rx(current_pose.euler().rx() + delta_pose.euler().rx()); target_pose.mutable_euler()->set_ry(current_pose.euler().ry() + delta_pose.euler().ry()); target_pose.mutable_euler()->set_rz(current_pose.euler().rz() + delta_pose.euler().rz()); // 3. 调用moveL执行直线运动到目标位姿 moveL(base_link, ee_link, target_pose, vel, acc); } catch (const std::exception &e) { LOG(ERROR) << "moveDeltaL failed: " << e.what(); throw std::runtime_error(std::string("moveDeltaL error: ") + e.what()); } } template void HumanoidRobot::moveL(const std::string &base_link, const std::string &ee_link, msgs::Pose3d target_pose, double vel, double acc) { if (vel <= 0 || acc <= 0) { throw std::runtime_error("moveL: vel and acc must be positive"); } try { const double CONTROL_PERIOD = 1.0 / 50.0; // 控制周期保持不变 msgs::Pose3d current_pose = fk(base_link, ee_link); // 1. 初始化当前和目标位姿矩阵 Eigen::Matrix4d T_current = Eigen::Matrix4d::Identity(); T_current.block<3, 3>(0, 0) = eulerZYXToRotationMatrix( current_pose.euler().rx(), current_pose.euler().ry(), current_pose.euler().rz() ); T_current(0, 3) = current_pose.position().x(); T_current(1, 3) = current_pose.position().y(); T_current(2, 3) = current_pose.position().z(); Eigen::Matrix4d T_target = Eigen::Matrix4d::Identity(); T_target.block<3, 3>(0, 0) = eulerZYXToRotationMatrix( target_pose.euler().rx(), target_pose.euler().ry(), target_pose.euler().rz() ); T_target(0, 3) = target_pose.position().x(); T_target(1, 3) = target_pose.position().y(); T_target(2, 3) = target_pose.position().z(); // 保存起始姿态,确保整个运动过程中姿态保持不变 Eigen::Matrix3d start_orientation = T_current.block<3, 3>(0, 0); // 2. 获取当前关节配置并验证目标可达性 Eigen::Vector q_current; auto q_map_current = getJointQ(); q_current << q_map_current["L_SHOULDER_P"], q_map_current["L_SHOULDER_R"], q_map_current["L_SHOULDER_Y"], q_map_current["L_ELBOW_R"], q_map_current["L_WRIST_P"], q_map_current["L_WRIST_Y"], q_map_current["L_WRIST_R"], q_map_current["R_SHOULDER_P"], q_map_current["R_SHOULDER_R"], q_map_current["R_SHOULDER_Y"], q_map_current["R_ELBOW_R"], q_map_current["R_WRIST_P"], q_map_current["R_WRIST_Y"], q_map_current["R_WRIST_R"]; LOG(INFO) << "Current joint configuration: " << q_current; m_state_->SetQ(q_current); m_robot_->ComputeForwardKinematics(m_state_); // 验证目标点可达性 - 使用起始姿态,确保姿态不变 cmvr::ctrl::PoseTarget target_ik_check; target_ik_check.T_target = T_target; target_ik_check.T_target.block<3, 3>(0, 0) = start_orientation; // 使用起始姿态 target_ik_check.w_posrot = 0.5; target_ik_check.weight = 1.0; target_ik_check.link_name = ee_link; Eigen::Vector q_cmd_check; bool ik_solvable = m_cctrl_->compute(m_state_, base_link, {target_ik_check}, 0.002, ctrl::CartesianController::Mode::Position, q_cmd_check, 10000, 1e-6); if (!ik_solvable) { throw std::runtime_error("moveL: Target pose is unreachable with constant orientation"); } // 3. 计算位置差值(保持姿态不变) Eigen::Vector3d delta_pos = T_target.block<3, 1>(0, 3) - T_current.block<3, 1>(0, 3); double total_distance = delta_pos.norm(); if (total_distance < 1e-6) { LOG(INFO) << "moveL: Target is already reached"; return; } // 4. 基于S曲线速度规划的时间规划 // 计算总时间和插值点数 double move_time = calculateMoveTime(total_distance, vel, acc); size_t num_points = std::max(2ul, static_cast(ceil(move_time / CONTROL_PERIOD))); // 生成时间轴和距离比例 std::vector time_points; std::vector distance_ratios; generateSTrapezoidalProfile(total_distance, vel, acc, move_time, num_points, time_points, distance_ratios); LOG(INFO) << "moveL: Planning trajectory - points=" << num_points << ", total distance=" << total_distance << "m, move time=" << move_time << "s"; // 5. 生成轨迹点(位置线性插值,姿态保持不变) std::vector cartesian_trajectory; for (size_t i = 0; i <= num_points; ++i) { double s = distance_ratios[i]; // 使用S曲线规划的距离比例 Eigen::Matrix4d T_interp = Eigen::Matrix4d::Identity(); T_interp.block<3, 3>(0, 0) = start_orientation; // 保持起始姿态不变 // 仅位置按比例插值 T_interp(0, 3) = T_current(0, 3) + s * delta_pos.x(); T_interp(1, 3) = T_current(1, 3) + s * delta_pos.y(); T_interp(2, 3) = T_current(2, 3) + s * delta_pos.z(); cartesian_trajectory.push_back(T_interp); } // 6. 预先计算所有轨迹点的关节位置 std::vector> joint_positions; joint_positions.push_back(q_current); // 起始位置 // 预先计算所有关节位置 for (size_t i = 1; i < cartesian_trajectory.size(); ++i) { const auto& T_interp = cartesian_trajectory[i]; // 构造当前目标 cmvr::ctrl::PoseTarget current_target; current_target.T_target = T_interp; current_target.link_name = ee_link; current_target.w_posrot = 0.5; current_target.weight = 1.0; // 使用前一点的位置作为初始值求解IK Eigen::Vector q_next; bool ok = m_cctrl_->compute(m_state_, base_link, {current_target}, CONTROL_PERIOD, ctrl::CartesianController::Mode::Position, q_next, 10000, 1e-6); if (!ok) { LOG(WARNING) << "Pre-computation IK failed at point " << i << ", using previous point"; q_next = joint_positions.back(); } joint_positions.push_back(q_next); } // 7. 计算每个点的关节速度 std::vector> joint_velocities; joint_velocities.push_back(Eigen::Vector::Zero()); // 起始速度为零 for (size_t i = 1; i < joint_positions.size(); ++i) { double dt = time_points[i] - time_points[i-1]; Eigen::Vector vel = (joint_positions[i] - joint_positions[i-1]) / dt; joint_velocities.push_back(vel); } // 8. 打印轨迹信息 std::cout << "\n===================================== 轨迹规划信息 =====================================" << std::endl; std::cout << "轨迹点总数: " << cartesian_trajectory.size() << " 个" << std::endl; std::cout << "总路径长度: " << std::fixed << std::setprecision(6) << total_distance << "m" << std::endl; std::cout << "最大速度: " << std::fixed << std::setprecision(6) << vel << "m/s" << std::endl; std::cout << "加速度: " << std::fixed << std::setprecision(6) << acc << "m/s²" << std::endl; std::cout << "总时间: " << std::fixed << std::setprecision(6) << move_time << "s" << std::endl; std::cout << "起点位置: (x=" << T_current(0,3) << ", y=" << T_current(1,3) << ", z=" << T_current(2,3) << ")" << std::endl; std::cout << "终点位置: (x=" << T_target(0,3) << ", y=" << T_target(1,3) << ", z=" << T_target(2,3) << ")" << std::endl; std::cout << "保持姿态不变" << std::endl; std::cout << "-----------------------------------------------------------------------------------------" << std::endl; // 9. 执行轨迹 auto loop_start_time = std::chrono::high_resolution_clock::now(); for (size_t i = 0; i < cartesian_trajectory.size(); ++i) { // 获取当前点的关节位置和速度 Eigen::Vector q_cmd = joint_positions[i]; Eigen::Vector q_vel = joint_velocities[i]; // 更新状态 m_state_->SetQ(q_cmd); m_robot_->ComputeForwardKinematics(m_state_); // 发送关节命令 - 为每个电机单独设置位置和速度 std::vector joint_command; for (size_t j = 0; j < DOF; ++j) { JointPoint jp; jp.joint_name = joint_names_[j]; jp.rad = q_cmd[j]; jp.vel = std::abs(q_vel[j]); // 使用计算出的关节速度 joint_command.push_back(jp); } // 计算当前点应该执行的时间 double expected_time = time_points[i]; // servoJ(joint_command, vel, expected_time); for (const auto &j: joint_command) { auto motor = motor_manager_->getMotor(j.joint_name); if (motor != nullptr) { if (motor->getMode() != msgs::RUN_MODE_CYCLIC_SYNC_POSITION) { motor->setMode(msgs::RUN_MODE_CYCLIC_SYNC_POSITION); } motor->setQd(j.vel); motor->setQ(j.rad); } } // 检查中断 if (flash_cmd_.load()) { flash_cmd_.store(false); LOG(INFO) << "moveL: Interrupted by external command"; return; } // 控制时间节奏 - 使用精确的时间规划 auto expected_time_point = loop_start_time + std::chrono::nanoseconds( static_cast(expected_time * 1e9) ); auto now = std::chrono::high_resolution_clock::now(); if (now < expected_time_point) { std::this_thread::sleep_until(expected_time_point); } else { LOG(WARNING) << "moveL: Behind schedule at point " << i << " by " << std::chrono::duration_cast(now - expected_time_point).count() << "ms"; } } // 最终状态更新 m_state_->SetQ(joint_positions.back()); m_robot_->ComputeForwardKinematics(m_state_); rsm_.store(ROBOT_READY); LOG(INFO) << "moveL: Trajectory completed successfully"; } catch (const std::exception &e) { LOG(ERROR) << "moveL failed: " << e.what(); rsm_.store(ROBOT_ERROR); throw std::runtime_error(std::string("moveL error: ") + e.what()); } } // 辅助函数:计算运动时间 template double HumanoidRobot::calculateMoveTime(double distance, double vel, double acc) { // 计算加速和减速所需的时间和距离 double acc_time = vel / acc; double acc_distance = 0.5 * acc * acc_time * acc_time; // 如果加速距离超过总距离的一半,需要调整最大速度 if (2 * acc_distance > distance) { // 三角形速度曲线:加速然后直接减速 double max_reachable_vel = std::sqrt(acc * distance); return 2 * max_reachable_vel / acc; } else { // 梯形速度曲线:加速-匀速-减速 double constant_distance = distance - 2 * acc_distance; double constant_time = constant_distance / vel; return 2 * acc_time + constant_time; } } // 辅助函数:生成S曲线轨迹规划 template void HumanoidRobot::generateSTrapezoidalProfile(double total_distance, double max_vel, double max_acc, double total_time, size_t num_points, std::vector& time_points, std::vector& distance_ratios) { time_points.clear(); distance_ratios.clear(); // 计算加速和减速阶段的时间 double acc_time = max_vel / max_acc; double acc_distance = 0.5 * max_acc * acc_time * acc_time; // 确定实际的速度曲线形状 if (2 * acc_distance > total_distance) { // 三角形速度曲线 double actual_max_vel = std::sqrt(max_acc * total_distance); acc_time = actual_max_vel / max_acc; acc_distance = 0.5 * max_acc * acc_time * acc_time; double dec_time = acc_time; // 生成时间点和距离比例 for (size_t i = 0; i <= num_points; ++i) { double t = static_cast(i) / num_points * total_time; time_points.push_back(t); if (t <= acc_time) { // 加速阶段 double s = 0.5 * max_acc * t * t; distance_ratios.push_back(s / total_distance); } else { // 减速阶段 double dec_start_time = total_time - dec_time; double dec_elapsed = t - dec_start_time; double s = acc_distance + actual_max_vel * dec_elapsed - 0.5 * max_acc * dec_elapsed * dec_elapsed; distance_ratios.push_back(s / total_distance); } } } else { // 梯形速度曲线 double constant_time = (total_distance - 2 * acc_distance) / max_vel; double dec_time = acc_time; // 生成时间点和距离比例 for (size_t i = 0; i <= num_points; ++i) { double t = static_cast(i) / num_points * total_time; time_points.push_back(t); if (t <= acc_time) { // 加速阶段 double s = 0.5 * max_acc * t * t; distance_ratios.push_back(s / total_distance); } else if (t <= acc_time + constant_time) { // 匀速阶段 double s = acc_distance + max_vel * (t - acc_time); distance_ratios.push_back(s / total_distance); } else { // 减速阶段 double dec_start_time = acc_time + constant_time; double dec_elapsed = t - dec_start_time; double s = acc_distance + max_vel * constant_time + max_vel * dec_elapsed - 0.5 * max_acc * dec_elapsed * dec_elapsed; distance_ratios.push_back(s / total_distance); } } } } template class cmvr::device::HumanoidRobot<7>; template class cmvr::device::HumanoidRobot<14>; template class cmvr::device::HumanoidRobot<20>;