(11-4-02)完整人形机器人的设计与实现案例:机器人跳跃

11.5.2 机器人跳跃

文件OpenLoong-Dyn-Control/demo/jump_mpc.cpp实现了双足机器人跳跃控制的仿真功能,集成了UI控制、数据接口、运动学动力学求解器、MPC力控制器及PVT关节控制器。通过主循环执行仿真步骤,更新传感器数据与机器人状态,分阶段(准备、起跳、上升、下降、着陆)实现跳跃控制逻辑,利用MPC计算力指令并转换为关节力矩,结合PVT控制输出力矩,同时记录了仿真时间、电机状态、位姿等数据,完成了机器人跳跃全过程的仿真与控制。

(1)下面代码的功能是定义全局控制时间步长常量,创建MuJoCo模型加载的错误信息缓存数组,加载指定路径下的XML场景模型文件并编译,同时基于加载的模型创建MuJoCo仿真数据结构,为后续机器人跳跃仿真搭建基础的仿真模型和数据载体。

python 复制代码
// 全局常量定义
const double dt = 0.001;  // 控制时间步长(秒)

// MuJoCo模型加载与编译
char error[1000] = "无法加载二进制模型";  // 模型加载错误信息缓存
mjModel *mj_model = mj_loadXML("../models/scene.xml", 0, error, 1000);  // 加载XML场景模型
mjData *mj_data = mj_makeData(mj_model);                                // 创建MuJoCo仿真数据结构

(2)下面代码的功能是初始化机器人跳跃仿真所需的各类核心功能模块,包括MuJoCo可视化UI控制器、MuJoCo数据交互接口、机器人运动学动力学求解器、MPC力控制器、PVT关节控制器、机器人状态数据总线和数据日志记录器;获取机器人关节总自由度并定义双腿关节力矩指令矩阵;完成GLFW可视化库初始化、机器人刚体视角跟踪启用及仿真演示窗口创建,为仿真运行提供组件支撑。

python 复制代码
int main(int argc, char **argv) {
    // ===================== 初始化各类核心模块 =====================
    // UI控制器(用于MuJoCo可视化)
    UIctr uiController(mj_model, mj_data);   
    // MuJoCo数据接口(传感器/执行器数据交互)
    MJ_Interface mj_interface(mj_model, mj_data); 
    // 机器人运动学/动力学求解器(加载URDF模型)
    Pin_KinDyn kinDynSolver("../models/AzureLoong.urdf"); 
    // MPC力控制器(输入控制时间步)
    MPC mpc_force(dt);  
    // PVT关节控制器(输入仿真步长+关节配置文件)
    PVT_Ctr pvtCtr(mj_model->opt.timestep, "../common/joint_ctrl_config.json");
    // 机器人状态数据总线(输入关节自由度)
    DataBus RobotState(kinDynSolver.model_nv); 
    // 数据日志记录器(指定日志文件路径)
    DataLogger logger("../record/datalog.log"); 

    // 获取机器人关节总自由度(减去浮动基6个自由度)
    int model_nv = kinDynSolver.model_nv;
    // 关节力矩指令(12维,对应双腿关节)
    Eigen::Matrix<double, 12, 1> Uje;

    // ===================== 初始化GLFW可视化窗口 =====================
    uiController.iniGLFW();                          // 初始化GLFW库
    uiController.enableTracking();                   // 启用机器人第1个刚体的视角跟踪
    // 创建仿真窗口(参数:窗口名、是否保存视频;注:保存视频时15秒原始文件可达2.5GB)
    uiController.createWindow("机器人跳跃演示", false);

(3)下面代码的功能是配置数据日志记录的各类关键参数项,涵盖仿真时间、电机状态、机器人位姿、足端位置/力指令等维度;初始化机器人足端和手部在机体坐标系下的期望位姿(位置、欧拉角、旋转矩阵),求解腿和手关节的逆运动学;初始化跳跃控制核心参数,包括起跳时间、准备时长、跳跃状态机、站立/跳跃高度等,为分阶段跳跃控制做好参数和数据记录准备。

python 复制代码
    // ===================== 初始化数据日志项 =====================
    logger.addIterm("simTime", 1);                  // 仿真时间(1维)
    logger.addIterm("motor_pos_des", model_nv - 6); // 电机期望位置(关节自由度-6)
    logger.addIterm("motor_pos_cur", model_nv - 6); // 电机当前位置
    logger.addIterm("motor_vel_cur", model_nv - 6); // 电机当前速度
    logger.addIterm("motor_tor_des", model_nv - 6); // 电机期望扭矩
    logger.addIterm("motor_tor_out", model_nv - 6); // 电机输出扭矩
    logger.addIterm("rpyVal", 3);                   // 机器人基姿滚转/俯仰/偏航(3维)
    logger.addIterm("gpsVal", 3);                   // 机器人基位置(3维)
    logger.addIterm("fe_l_pos_L_des", 3);           // 左脚端期望位置(机体坐标系)
    logger.addIterm("fe_r_pos_L_des", 3);           // 右脚端期望位置(机体坐标系)
    logger.addIterm("fe_l_pos_W", 3);               // 左脚端当前位置(世界坐标系)
    logger.addIterm("fe_r_pos_W", 3);               // 右脚端当前位置(世界坐标系)
    logger.addIterm("Ufe", 12);                     // 足端力/力矩指令(12维)
    logger.finishItermAdding();                     // 完成日志项添加

    // ===================== 初始化足端/手部期望位姿 =====================
    // 足端期望位置(世界坐标系)
    Eigen::Vector3d fe_l_pos_W_des, fe_r_pos_W_des;
    // 左脚端期望位置(机体坐标系,初始值)
    Eigen::Vector3d fe_l_pos_L_des = {-0.018, 0.113, -1.01};
    // 右脚端期望位置(机体坐标系,初始值)
    Eigen::Vector3d fe_r_pos_L_des = {-0.018, -0.116, -1.01};
    // 左脚端期望欧拉角(机体坐标系)
    Eigen::Vector3d fe_l_eul_L_des = {-0.000, -0.008, -0.000};
    // 右脚端期望欧拉角(机体坐标系)
    Eigen::Vector3d fe_r_eul_L_des = {0.000, -0.008, 0.000};
    // 欧拉角转旋转矩阵(左脚端期望姿态)
    Eigen::Matrix3d fe_l_rot_des = eul2Rot(fe_l_eul_L_des(0), fe_l_eul_L_des(1), fe_l_eul_L_des(2));
    // 欧拉角转旋转矩阵(右脚端期望姿态)
    Eigen::Matrix3d fe_r_rot_des = eul2Rot(fe_r_eul_L_des(0), fe_r_eul_L_des(1), fe_r_eul_L_des(2));

    // 足端基准位置(机体坐标系)
    Eigen::Vector3d fe_l_pos_L_base, fe_r_pos_L_base;

    // 左手端期望位置(机体坐标系)
    Eigen::Vector3d hd_l_pos_L_des = {-0.02, 0.32, -0.159};
    // 右手端期望位置(机体坐标系)
    Eigen::Vector3d hd_r_pos_L_des = {-0.02, -0.32, -0.159};
    // 左手端期望欧拉角(机体坐标系)
    Eigen::Vector3d hd_l_eul_L_des = {-1.253, 0.122, -1.732};
    // 右手端期望欧拉角(机体坐标系)
    Eigen::Vector3d hd_r_eul_L_des = {1.253, 0.122, 1.732};
    // 欧拉角转旋转矩阵(左手端期望姿态)
    Eigen::Matrix3d hd_l_rot_des = eul2Rot(hd_l_eul_L_des(0), hd_l_eul_L_des(1), hd_l_eul_L_des(2));
    // 欧拉角转旋转矩阵(右手端期望姿态)
    Eigen::Matrix3d hd_r_rot_des = eul2Rot(hd_r_eul_L_des(0), hd_r_eul_L_des(1), hd_r_eul_L_des(2));

    // 求解腿关节逆运动学(输入足端期望姿态/位置)
    auto resLeg = kinDynSolver.computeInK_Leg(fe_l_rot_des, fe_l_pos_L_des, fe_r_rot_des, fe_r_pos_L_des);
    // 求解手关节逆运动学(输入手端期望姿态/位置)
    auto resHand = kinDynSolver.computeInK_Hand(hd_l_rot_des, hd_l_pos_L_des, hd_r_rot_des, hd_r_pos_L_des);

    // ===================== 跳跃控制参数初始化 =====================
    double startJumpingTime = 8.5;  // 起跳开始时间(秒)
    double prepareTime = 3;         // 准备阶段时长(秒)
    uint16_t jump_state = 0;        // 跳跃状态机:0-起跳 3-上升 4-下降 5-落地
    double stand_z = -0.8;          // 站立时足端z轴位置(机体坐标系)
    double jump_z = 0.2;            // 跳跃高度(米)
    double count = 0.0;             // 落地阶段计时

    mjtNum simstart = mj_data->time;    // 仿真起始时间
    double simTime = mj_data->time;     // 当前仿真时间
    double simEndTime = 13;             // 仿真结束时间

(4)下面代码的功能是实现机器人跳跃仿真的主循环逻辑,外层循环保证可视化窗口未关闭时持续运行,内层循环控制60Hz可视化刷新率并推进MuJoCo仿真步;先更新传感器数据、计算运动学/动力学信息及足端接触力,再按时间和状态机分阶段执行跳跃控制(准备、起跳准备、起跳加速、上升、下降、落地),完成MPC力控指令转关节扭矩、PVT关节控制计算及扭矩指令下发至MuJoCo,同时记录每一步的关键仿真数据,是跳跃控制的核心执行逻辑。

python 复制代码
    // ===================== 主仿真循环 =====================
    while (!glfwWindowShouldClose(uiController.window)) {
        simstart = mj_data->time;
        // 保证60Hz的可视化刷新率(内部按MuJoCo步长推进仿真)
        while (mj_data->time - simstart < 1.0 / 60.0 && uiController.runSim) {
            mj_step(mj_model, mj_data);  // 推进MuJoCo仿真一步
            simTime = mj_data->time;     // 更新当前仿真时间

            // --------------------- 传感器数据更新 ---------------------
            mj_interface.updateSensorValues();  // 读取MuJoCo传感器数据
            mj_interface.dataBusWrite(RobotState); // 将传感器数据写入数据总线

            // --------------------- 运动学/动力学计算 ---------------------
            kinDynSolver.dataBusRead(RobotState); // 从数据总线读取机器人状态
            kinDynSolver.computeJ_dJ();           // 计算雅克比矩阵及雅克比导数
            kinDynSolver.computeDyn();            // 计算动力学(惯性矩阵/科氏力等)
            kinDynSolver.dataBusWrite(RobotState); // 将计算结果写入数据总线

            // 构建站立时的雅克比矩阵(12维:双腿各6维)
            Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic> Jac_stand(12, 12);
            Jac_stand.setZero();
            Jac_stand.block(0, 0, 6, 12) = RobotState.J_l.block(0, model_nv-12, 6, 12);
            Jac_stand.block(6, 0, 6, 12) = RobotState.J_r.block(0, model_nv-12, 6, 12);

            // 提取关节扭矩(排除浮动基6个自由度)
            Eigen::VectorXd torJoint;
            torJoint = Eigen::VectorXd::Zero(model_nv-6);
            for (int i = 0; i < model_nv-6; i++) {
                torJoint[i] = RobotState.motors_tor_cur[i];
            }

            // 估计左右足端接触力(基于动力学和雅克比矩阵)
            Eigen::Vector<double,6> FLest, FRest;
            Eigen::VectorXd tauAll;
            tauAll = Eigen::VectorXd::Zero(model_nv);
            tauAll.block(6,0,model_nv-6,1) = torJoint;
            FLest = -pseudoInv_SVD(RobotState.J_l * RobotState.dyn_M.inverse() * RobotState.J_l.transpose()) 
                    * (RobotState.J_l * RobotState.dyn_M.inverse() * (tauAll - RobotState.dyn_Non) + RobotState.dJ_l * RobotState.dq);
            FRest = -pseudoInv_SVD(RobotState.J_r * RobotState.dyn_M.inverse() * RobotState.J_r.transpose()) 
                    * (RobotState.J_r * RobotState.dyn_M.inverse() * (tauAll - RobotState.dyn_Non) + RobotState.dJ_r * RobotState.dq);

            // --------------------- 分阶段控制逻辑 ---------------------
            if (simTime <= prepareTime) {
                // 阶段1:准备阶段(前3秒)- 保持初始位姿
                fe_l_pos_L_des = RobotState.fe_l_pos_L;  // 左脚端跟随当前位置
                fe_r_pos_L_des = RobotState.fe_r_pos_L;  // 右脚端跟随当前位置
                // 足端期望位置(世界坐标系)= 基姿旋转 * 机体坐标系位置
                fe_l_pos_W_des = RobotState.base_rot * fe_l_pos_L_des;
                fe_r_pos_W_des = RobotState.base_rot * fe_r_pos_L_des;
                // 设置电机期望位置(逆解结果)、速度/扭矩为0
                RobotState.motors_pos_des = eigen2std(resLeg.jointPosRes + resHand.jointPosRes);
                RobotState.motors_vel_des.assign(model_nv - 6, 0);
                RobotState.motors_tor_des.assign(model_nv - 6, 0);

            } else if (simTime < startJumpingTime && simTime > prepareTime) {
                // 阶段2:起跳准备(3~8.5秒)- 足端z轴下降到站立位置
                fe_l_pos_L_des(2) = Ramp(fe_l_pos_L_des(2), stand_z, 0.1 * dt);
                fe_r_pos_L_des(2) = Ramp(fe_r_pos_L_des(2), stand_z, 0.1 * dt);

                // 重新求解腿/手逆运动学
                auto resLeg = kinDynSolver.computeInK_Leg(fe_l_rot_des, fe_l_pos_L_des, fe_r_rot_des, fe_r_pos_L_des);
                auto resHand = kinDynSolver.computeInK_Hand(hd_l_rot_des, hd_l_pos_L_des, hd_r_rot_des, hd_r_pos_L_des);

                // 记录站立时的基准位置
                RobotState.base_pos_stand = RobotState.base_pos;
                RobotState.pfeW_stand.block<3, 1>(0, 0) = fe_l_pos_W_des;
                RobotState.pfeW_stand.block<3, 1>(3, 0) = fe_r_pos_W_des;

                // 设置电机期望位置,速度/扭矩为0
                RobotState.motors_pos_des = eigen2std(resLeg.jointPosRes + resHand.jointPosRes);
                RobotState.motors_vel_des.assign(model_nv - 6, 0);
                RobotState.motors_tor_des.assign(model_nv - 6, 0);

                // 跟踪当前基姿欧拉角/位置/角速度/线速度
                for (int j = 0; j < 3; j++) RobotState.js_eul_des(j) = RobotState.base_rpy(j);
                for (int j = 0; j < 3; j++) RobotState.js_pos_des(j) = RobotState.base_pos(j);
                for (int j = 0; j < 3; j++) RobotState.js_omega_des(j) = RobotState.base_omega_W(j);
                for (int j = 0; j < 3; j++) RobotState.js_vel_des(j) = RobotState.dq(j);
                RobotState.legState = DataBus::DSt;  // 设置腿部状态为"站立"

            } else if (simTime >= startJumpingTime) {
                // 阶段3:跳跃阶段(8.5秒后)- 状态机控制
                // 计算期望跳跃速度(z轴:基于自由落体公式 v=√(2gh))
                double jump_vel_des[3] = {0.0, 0.0, sqrt(2.0 * 9.8 * jump_z)};
                // 起跳加速度时间(达到期望速度的时间)
                double jump_acc_t = 2.0 * (0.9 + stand_z) / (jump_vel_des[2]);
                // 起跳期望欧拉角(小角度俯仰)
                double jump_eul_des[3] = {0.0, 0.0 / 180.0 * 3.1415926, 0.0};

                if (jump_state == 0) {
                    // 子状态0:起跳加速
                    mpc_force.enable();  // 启用MPC力控制
                    // 设置MPC权重矩阵(状态权重L/输入权重K)
                    Eigen::Matrix<double, 1, nx> L_diag;
                    Eigen::Matrix<double, 1, nu> K_diag;
                    L_diag << 50.0, 50.0, 1.0,   // 欧拉角权重
                              50.0, 50.0, 200.0, // CoM位置权重
                              0.1, 0.1 , 0.1,    // 角速度权重
                              0.01, 0.1, 20.0;   // CoM速度权重
                    K_diag << 1.0, 1.0, 0.1,     // 左脚力权重
                              10.0, 10.0, 10.0,  // 左脚力矩权重
                              1.0, 1.0, 0.1,     // 右脚力权重
                              10.0, 10.0, 10.0, 1.0; // 右脚力矩权重
                    mpc_force.set_weight(1e-6, L_diag, K_diag);

                    // 设置期望欧拉角(起跳姿态)
                    RobotState.js_eul_des(0) = jump_eul_des[0];
                    RobotState.js_eul_des(1) = jump_eul_des[1];
                    RobotState.js_eul_des(2) = jump_eul_des[2];

                    // 斜坡函数逼近期望跳跃速度
                    RobotState.js_vel_des(0) = Ramp(RobotState.js_vel_des(0), jump_vel_des[0],
                                                    fabs(jump_vel_des[0] / (jump_acc_t) * dt));
                    RobotState.js_vel_des(1) = 0.0;
                    RobotState.js_vel_des(2) = Ramp(RobotState.js_vel_des(2), jump_vel_des[2],
                                                    fabs(jump_vel_des[2] / jump_acc_t * dt));
                    // 积分更新期望位置
                    RobotState.js_pos_des(2) = RobotState.js_pos_des(2) + RobotState.js_vel_des(2) * dt;

                    // 足端位置跟随当前基姿
                    fe_l_pos_L_des = RobotState.base_rot * RobotState.fe_l_pos_L;
                    fe_r_pos_L_des = RobotState.base_rot * RobotState.fe_r_pos_L;

                    // 加速时间到,切换到上升阶段
                    if (simTime > startJumpingTime + jump_acc_t) {
                        jump_state = 3;
                        mpc_force.disable(); // 关闭MPC
                        // 记录当前足端位置作为基准
                        RobotState.pfeW0.block<3, 1>(0, 0) = fe_l_pos_W_des;
                        RobotState.pfeW0.block<3, 1>(3, 0) = fe_r_pos_W_des;
                    }

                } else if (jump_state == 3) {
                    // 子状态3:上升阶段 - 足端z轴回落至站立位置
                    mpc_force.disable(); // 关闭MPC
                    // 斜坡函数回落足端z轴位置
                    fe_l_pos_W_des[2] = Ramp(fe_l_pos_W_des[2], stand_z, 5.0 * dt);
                    fe_r_pos_W_des[2] = Ramp(fe_r_pos_W_des[2], stand_z, 5.0 * dt);

                    // 世界坐标系转机体坐标系
                    fe_l_pos_L_des = RobotState.base_rot.inverse() * fe_l_pos_W_des;
                    fe_r_pos_L_des = RobotState.base_rot.inverse() * fe_r_pos_W_des;

                    // 逆运动学求解(补偿基姿俯仰角)
                    auto resLeg = kinDynSolver.computeInK_Leg(fe_l_rot_des, fe_l_pos_L_des, fe_r_rot_des, fe_r_pos_L_des);
                    auto resHand = kinDynSolver.computeInK_Hand(hd_l_rot_des, hd_l_pos_L_des, hd_r_rot_des, hd_r_pos_L_des);
                    Eigen::Matrix<double, 31, 1> IKRes;
                    IKRes = resLeg.jointPosRes + resHand.jointPosRes;
                    IKRes(model_nv-6 - 8) += RobotState.base_rpy(1); // 补偿左腿关节
                    IKRes(model_nv-6 - 2) += RobotState.base_rpy(1); // 补偿右腿关节

                    // 上升速度降低到阈值,切换到下降阶段
                    if (RobotState.dq(2) < 0.1) {
                        jump_state = 4;
                        RobotState.pfeW0.block<3, 1>(0, 0) = RobotState.base_rot * RobotState.fe_l_pos_L;
                        RobotState.pfeW0.block<3, 1>(3, 0) = RobotState.base_rot * RobotState.fe_r_pos_L;
                    }

                    // 启用PVT位置/速度控制,设置电机指令
                    pvtCtr.enablePV();
                    RobotState.motors_pos_des = eigen2std(IKRes);
                    RobotState.motors_vel_des.assign(model_nv - 6, 0);
                    RobotState.motors_tor_des.assign(model_nv - 6, 0);

                } else if (jump_state == 4) {
                    // 子状态4:下降阶段 - 足端x轴前伸,准备落地
                    mpc_force.disable(); // 关闭MPC
                    // 足端x轴前伸0.2米
                    fe_l_pos_W_des[0] = Ramp(fe_l_pos_W_des[0], RobotState.pfeW0[0] + 0.2, fabs(10.0 * dt));
                    fe_r_pos_W_des[0] = Ramp(fe_r_pos_W_des[0], RobotState.pfeW0[3] + 0.2, fabs(10.0 * dt));

                    // 世界坐标系转机体坐标系(仅更新x轴)
                    Eigen::Vector3d fe_l_pos_L_des_tmp, fe_r_pos_L_des_tmp;
                    fe_l_pos_L_des_tmp = RobotState.base_rot.inverse() * fe_l_pos_W_des;
                    fe_r_pos_L_des_tmp = RobotState.base_rot.inverse() * fe_r_pos_W_des;
                    fe_l_pos_L_des[0] = fe_l_pos_L_des_tmp[0];
                    fe_r_pos_L_des[0] = fe_r_pos_L_des_tmp[0];

                    // 逆运动学求解(补偿基姿俯仰角)
                    auto resLeg = kinDynSolver.computeInK_Leg(fe_l_rot_des, fe_l_pos_L_des, fe_r_rot_des, fe_r_pos_L_des);
                    auto resHand = kinDynSolver.computeInK_Hand(hd_l_rot_des, hd_l_pos_L_des, hd_r_rot_des, hd_r_pos_L_des);
                    Eigen::MatrixXd IKRes;
                    IKRes = resLeg.jointPosRes + resHand.jointPosRes;
                    IKRes(model_nv-6 - 8) += RobotState.base_rpy(1);
                    IKRes(model_nv-6 - 2) += RobotState.base_rpy(1);

                    // 足端接触力超过阈值,切换到落地阶段
                    if (FLest(2) > 1000 && FRest(2) > 1000) {
                        jump_state = 5;
                        RobotState.pfeW0.block<3, 1>(0, 0) = RobotState.base_rot * RobotState.fe_l_pos_L;
                        RobotState.pfeW0.block<3, 1>(3, 0) = RobotState.base_rot * RobotState.fe_r_pos_L;
                        RobotState.js_pos_des(0) = RobotState.base_pos(0);
                        RobotState.js_pos_des(1) = RobotState.base_pos(1);
                        RobotState.js_pos_des(2) = RobotState.base_pos(2);
                    }

                    // 启用PVT控制,设置电机指令
                    pvtCtr.enablePV();
                    RobotState.motors_pos_des = eigen2std(IKRes);
                    RobotState.motors_vel_des.assign(model_nv - 6, 0);
                    RobotState.motors_tor_des.assign(model_nv - 6, 0);

                } else if (jump_state == 5) {
                    // 子状态5:落地阶段 - MPC控制保持平衡
                    mpc_force.enable(); // 启用MPC
                    // 重新设置MPC权重(落地阶段更关注位置/力控制)
                    Eigen::Matrix<double, 1, nx> L_diag;
                    Eigen::Matrix<double, 1, nu> K_diag;
                    L_diag << 2.0, 10.0, 1.0,     // 欧拉角权重
                              100.0, 100.0, 200.0, // CoM位置权重
                              1e-4, 1e-4, 1e-4,    // 角速度权重
                              0.5, 0.01, 0.5;      // CoM速度权重
                    K_diag << 0.1, 0.1, 0.1,       // 左脚力权重
                              0.01, 0.01, 1.0,     // 左脚力矩权重
                              0.1, 0.1, 0.1,       // 右脚力权重
                              0.01, 0.01, 1.0, 1.0;// 右脚力矩权重
                    mpc_force.set_weight(1e-6, L_diag, K_diag);

                    // 落地阶段期望位置调整,姿态/速度归零
                    double tt = 0.4;
                    RobotState.js_pos_des(2) = Ramp(RobotState.js_pos_des(2), 1.08, 0.1 * dt);
                    RobotState.js_eul_des.setZero();
                    RobotState.js_omega_des.setZero();
                    RobotState.js_vel_des.setZero();

                    // 落地计时
                    if (count < tt / dt) count = count + 1.0;
                }
            }

            // --------------------- MPC力控转关节扭矩 ---------------------
            mpc_force.dataBusRead(RobotState);  // 读取机器人状态到MPC
            mpc_force.cal();                    // 执行MPC计算
            mpc_force.dataBusWrite(RobotState); // 将MPC输出写入数据总线

            if (mpc_force.get_ENA()) {
                // MPC启用时:足端力指令转关节扭矩
                Uje.setZero();
                Uje = Jac_stand.transpose() * (-1.0) * RobotState.fe_react_tau_cmd.block<nu - 1, 1>(0, 0);

                // 关节扭矩限幅(保护电机)
                double jTor_max[6] = {400.0, 100.0, 400.0, 400.0, 80.0, 20.0};
                double jTor_min[6] = {-400.0, -100.0, -400.0, -400.0, -80.0, -20.0};
                for (int i = 0; i < 6; i++) {
                    Limit(Uje(i), jTor_max[i], jTor_min[i]);    // 左腿扭矩限幅
                    Limit(Uje(i + 6), jTor_max[i], jTor_min[i]);// 右腿扭矩限幅
                }

                // 禁用对应关节的PVT位置控制,直接下发MPC扭矩指令
                for (int i = 0; i < 12; i++) {
                    pvtCtr.disablePV(model_nv-6-12 + i);
                    RobotState.motors_tor_des[model_nv-6-12 + i] = Uje(i);
                }
            } else {
                // MPC禁用时:足端力指令归零
                RobotState.fe_react_tau_cmd.setZero();
            }

            // --------------------- PVT关节控制器 ---------------------
            pvtCtr.dataBusRead(RobotState); // 读取期望/当前关节状态
            if (simTime <= startJumpingTime) {
                // 起跳前:低增益PVT控制(防止震荡)
                pvtCtr.calMotorsPVT(110.0 / 1000.0 / 180.0 * 3.1415);
            } else {
                // 跳跃阶段:默认增益PVT控制
                pvtCtr.calMotorsPVT();
            }
            pvtCtr.dataBusWrite(RobotState); // 将PVT输出扭矩写入数据总线

            // --------------------- 执行器指令下发 ---------------------
            mj_interface.setMotorsTorque(RobotState.motors_tor_out); // 扭矩指令下发到MuJoCo

            // --------------------- 数据日志记录 ---------------------
            logger.startNewLine();                  // 新建日志行
            logger.recItermData("simTime", simTime);
            logger.recItermData("motor_pos_des", RobotState.motors_pos_des);
            logger.recItermData("motor_pos_cur", RobotState.motors_pos_cur);
            logger.recItermData("motor_vel_cur", RobotState.motors_vel_cur);
            logger.recItermData("motor_tor_des", RobotState.motors_tor_des);
            logger.recItermData("motor_tor_out", RobotState.motors_tor_out);
            logger.recItermData("rpyVal", RobotState.rpy);
            logger.recItermData("gpsVal", RobotState.base_pos);
            logger.recItermData("fe_l_pos_L_des", fe_l_pos_L_des);
            logger.recItermData("fe_r_pos_L_des", fe_r_pos_L_des);
            logger.recItermData("fe_l_pos_W", RobotState.fe_l_pos_W);
            logger.recItermData("fe_r_pos_W", RobotState.fe_r_pos_W);
            logger.recItermData("Ufe", RobotState.fe_react_tau_cmd.block<nu - 1, 1>(nu * 0, 0));
            logger.finishLine(); // 结束当前日志行
        }

        // 仿真时间到达结束时间,退出循环
        if (mj_data->time >= simEndTime) break;

        // 更新可视化场景
        uiController.updateScene();
    }
相关推荐
m0_603888712 小时前
MPA Multimodal Prototype Augmentation for Few-Shot Learning
人工智能·深度学习·ai·原型模式·论文速览
程序员猫哥_2 小时前
静态网站AI自动生成正在改变什么?2026开发效率新趋势解析
人工智能
edisao2 小时前
第一章:L-704 的 0.00% 偏差
前端·数据库·人工智能
sww_10262 小时前
Spring AI 可观测性实战
java·人工智能·spring
xiaoye-duck2 小时前
深入解析 STL 优先级队列:从原理到实战
c++·算法·stl
蜕变的小白2 小时前
数据结构:排序算法与哈希表
数据结构·算法·哈希算法
坐在地上想成仙2 小时前
AI工具汇总
人工智能
_OP_CHEN2 小时前
【算法基础篇】(六十一)SG 函数通关指南:博弈论通用解法,从原理到实战秒杀各类 ICG 游戏
算法·蓝桥杯·c/c++·博弈论·acm/icpc·sg函数·有向图游戏
IT 行者2 小时前
打造你的家庭 AI 助手(四):企业微信 AI 助手接入你的 OpenClaw
人工智能·企业微信