YuanHub 源码分析【六】MIT 模式

从MIT协议到机器人舞蹈:解码CAN总线与关节电机的数据对话 本文深入解析了如何通过CAN总线与关节电机实现机器人协同控制,重点探讨了MIT协议的数据编码艺术和STM32 HAL库的CAN配置实战。文章详细介绍了分布式控制架构的优势、浮点数编码原理以及实时数据交换机制,为开发者提供了从协议解码到多关节协同的完整解决方案,最终实现流畅的机器人舞蹈动作。 阅读详情

目录

一、MIT 模式简介

1.1 MIT 简介

1.2 弹簧与阻尼

1.3 Kd 项的效果

二、代码逻辑

2.1 总览

2.2 AppMotionParam 结构体

2.2 AppRun() 系列逻辑

2.2.1 AppRun()

2.2.2 MitModeRun()

2.2.3 MitTrajectoryPlanningHandle()

2.3 ModeRun() 系列逻辑

2.3.1 MitCtlStep()

2.3.2 mit_ctl()

四、仿真分析

4.1 数据结构

4.1.1 MitCtlInput

4.1.2 MitCtlOutput

4.1.3 MotorCtlSmConfig

4.2 仿真分析

4.2.1 仿真概览

4.2.2 Kp 环

4.2.3 Kd 环

4.2.4 乘 kt


一、MIT 模式简介

1.1 MIT 简介

MIT 模式 (Mixed Integrated Torque,混合集成扭矩控制) 在现代四足机器人和柔性机械臂领域,已经成为了一个行业标准级的底层关节控制接口协议。它最早由 MIT Cheetah (麻省理工猎豹) 团队提出、应用并开源,因此在圈内得名。

其计算输出力矩方式如下:

\tau_{out} = K_p (P_{des} - P_{now}) + K_d (V_{des} - V_{now}) + \tau_{ff}

  • K_{p} = 位置比例系数 / 虚拟刚度 (决定虚拟弹簧有多硬)
  • P_{des} = 目标位置
  • P_{now} = 现在的位置
  • K_{d} = 速度阻尼系数 / 虚拟阻尼 (决定减震器有多粘稠,吸收震荡)
  • V_{des} = 目标速度
  • V_{now} = 现在的速度
  • \tau_{ff} = 前馈力矩 (上层动力学模型提前算好的基础力矩,比如用来抵消腿部自身的重力或惯性力)

TIM 作为给机器人所使用的控制方式去掉了 Ki 积分项,相当于允许速度、位置和力矩产生当前值与目标值的误差,从而形成控制的刚性。

1.2 弹簧与阻尼

MIT 的理想控制是把电机仿真成一根泡在粘稠硅油里的弹簧。

    Kp 变成了刚度 (模拟弹簧):

    • 它模拟的是一根虚拟弹簧。你可以把它想象成在机器狗的实际位置和目标位置之间,拉了一根弹簧。Kp 越大,弹簧越硬。位置误差就是弹簧被拉伸的长度,产生的力矩就是弹簧的拉力。

    Kd 变成了阻尼 (模拟硅油):

    • 它模拟的是一个虚拟减震器 (就像泡在粘稠的油里)。它不看位置差,只看速度差。Kd 越大,对抗速度变化的能力越强,主要用来吸收弹簧产生的震荡能量,防止机器狗的腿像装了弹簧一样一直抖个不停。

    其弹簧有经典的胡克定律:

    F=-kx

    • F = 恢复力 (N)
    • k = 弹簧硬度系数 越大弹簧越硬 (N/m)
    • x = 弹簧拉伸位移量 (m)

    其中恢复力是 MIT 中 Kp 项的输出值;k 就是 Kp 常数;x 是目标位置和实际位置的差。

    硅油有粘性阻尼定律:

    F_d=-cv

    • Fd = 阻尼力 (N)
    • c = 阻尼系数 代表粘稠度 (N·s/m)
    • v = 速度 (m/s)

    其中阻尼力是 MIT 中 Kd 项的输出值;c 就是 Kd 常数;v 是目标速度和实际速度的差。

    至此,我们得到了 TIM 真正想仿真的东西:

    MIT_{out} = F+F_d

    • MITout = MIT 计算出的力矩
    • F = 恢复力 (N)
    • Fd =  阻尼力 (N)

    1.3 Kd 项的效果

    我们知道位置的微分就是速度,对速度做差变成了天然的微分项。

    在 Kp 项 (位置比例系数 / 虚拟刚度) 不变的情况下,调整 Kd (速度阻尼系数 / 虚拟阻尼) 的效果如下:

    在阻尼系数为 1 的时候,弹簧反复回弹:

    而在阻尼系数为 6 的时候,回弹周期明显减少:

    二、代码逻辑

    2.1 总览

    2.2 AppMotionParam 结构体

    下面的结构描述了 MIT 中所需要的变量:

    typedef struct
    {
        int64_t Target_position; //位置目标值
        float Profile_velocity; //轮廓速度
        float Profile_acceleration; //轮廓加速度
        float Profile_deceleration; //轮廓减速度
        float Quick_stop_deceleration; //快速停止减速度
        int32_t Motion_profile_type; //运动轮廓模式
        int64_t Home_offset; //回零偏移
        int8_t Homing_method; //回零方法
        float Target_velocity; //速度目标值
        float Target_torque; //力矩目标值
        float Torque_slope; //力矩上升斜率
        float Encoder_calibration_speed; //编码器校准速度
        float MIT_feedforward_torque; //MIT前馈力矩
        int64_t MIT_target_position; //MIT目标位置
        float MIT_max_current; //MIT最大输出电流
        float MIT_target_velocity; //MIT目标速度
        float MIT_kp; //位置刚度
        float MIT_kd; //速度阻尼系数
        uint8_t Interp_time_value; //插值时间基数
        int8_t Interp_time_index; //插值时间指数
        float Homing_speed_search_for_switch; //回零搜索开关速度
        float Homing_speed_search_for_zero; //回零搜索零点速度
        float Homing_acceleration; //回零加速度
    }AppMotionParam;

    2.2 AppRun() 系列逻辑

    2.2.1 AppRun()

    在 AppRun() 中选择合适的启动模式程序,通过函数指针方式调用,所有模式的初始化、运行、启动、停止函数等均在这个表格内:

    // 应用程序列表
    static const AppTable app_table[APP_NUM] =
    {
        {APP_PP_MODE, PpModeInit, PpModeStart, PpModeRun, PpModeStop, APP_TYPE_PDS},
        {APP_PV_MODE, PvModeInit, PvModeStart, PvModeRun, PvModeStop, APP_TYPE_PDS},
        {APP_PT_MODE, PtModeInit, PtModeStart, PtModeRun, PtModeStop, APP_TYPE_PDS},
        {APP_MIT_MODE, MitModeInit, MitModeStart, MitModeRun, MitModeStop, APP_TYPE_PDS},
        {APP_HM_MODE, HomingModeInit, HomingModeStart, HomingModeRun, HomingModeStop, APP_TYPE_PDS},
        {APP_CSP_MODE, CspModeInit, CspModeStart, CspModeRun, CspModeStop, APP_TYPE_PDS},
        {APP_CSV_MODE, CsvModeInit, CsvModeStart, CsvModeRun, CsvModeStop, APP_TYPE_PDS},
        {APP_CST_MODE, CstModeInit, CstModeStart, CstModeRun, CstModeStop, APP_TYPE_PDS},
        {APP_DIR_ID_MODE, IdDirModeInit, IdDirModeStart, IdDirModeRun, IdDirModeStop, APP_TYPE_PDS},
        {APP_ELEC_ANGLE_ID_MODE, IdElecAngleModeInit, IdElecAngleModeStart, IdElecAngleModeRun, IdElecAngleModeStop, APP_TYPE_PDS},
        {APP_ELEC_ID_MODE, IdElecModeInit, IdElecModeStart, IdElecModeRun, IdElecModeStop, APP_TYPE_PDS},
        {APP_MEC_ID_MODE, IdMecModeInit, IdMecModeStart, IdMecModeRun, IdMecModeStop, APP_TYPE_PDS},
        {APP_POLE_PAIRS_ID_MODE, IdPolePairsModeInit, IdPolePairsModeStart, IdPolePairsModeRun, IdPolePairsModeStop, APP_TYPE_PDS},
        {APP_TQ_FC_ID_MODE, IdTqFcModeInit, IdTqFcModeStart, IdTqFcModeRun, IdTqFcModeStop, APP_TYPE_PDS},
        {APP_NULL, NULL, NULL, NULL, NULL}, // 用于表示空闲状态
    };

    AppRun() 从中根据配置信息查找合适的函数进行调用:AppRun() 从中根据配置信息查找合适的函数进行调用:

    // 运行应用程序
    void AppRun(uint8_t hw_ready_state)
    {
        // 遍历应用程序列表
        for (int i = 0; i < APP_NUM; i++)
        {
            if (kAppInfo[i].enable)
            {
    
                // 启动应用程序
                if (kAppInfo[i].app_state == APP_STA_READY)
                {
    
                    if (kAppInfo[i].app_ptr->start != NULL)
                    {
                        kAppInfo[i].app_result = kAppInfo[i].app_ptr->start();
                        if (kAppInfo[i].app_result == APP_RET_RUNNING)
                        { // start处于等待状态
                            kAppInfo[i].app_state = APP_STA_READY;
                        }
                        else if (kAppInfo[i].app_result == APP_RET_SUCCESS)
                        { // start成功,进入run状态
                            kAppInfo[i].app_state = APP_STA_RUNNING;
                        }
                        else if (kAppInfo[i].app_result == APP_RET_FAIL)
                        { // start失败,进入错误状态
                            kAppInfo[i].app_state = APP_STA_ERROR;
                        }
                    }
                }
    
                // 运行应用程序
                if (kAppInfo[i].app_state == APP_STA_RUNNING)
                {
                    if (kAppInfo[i].app_ptr->run != NULL)
                    {
                        // 回读PWM准备好状态,准备好则运行APP 否则等待处于 READY 状态
                        if (hw_ready_state)
                        {
                            kAppInfo[i].app_result = kAppInfo[i].app_ptr->run();
                            if (kAppInfo[i].app_result == APP_RET_RUNNING)
                            {
                                kAppInfo[i].app_state = APP_STA_RUNNING;
                            }
                            else if (kAppInfo[i].app_result == APP_RET_SUCCESS)
                            {
                                kAppInfo[i].app_state = APP_STA_SUCCESS;
                            }
                            else if (kAppInfo[i].app_result == APP_RET_FAIL)
                            {
                                kAppInfo[i].app_state = APP_STA_ERROR;
                            }
                        }
                        else
                        {
                            kAppInfo[i].app_state = APP_STA_READY;
                        }
                    }
                }
    
                // 停止应用程序
                if ((kAppInfo[i].app_state == APP_STA_SUCCESS) || (kAppInfo[i].app_state == APP_STA_ERROR))
                {
                    if (kAppInfo[i].app_ptr->stop != NULL)
                    {
                        kAppInfo[i].app_ptr->stop();
                    }
                    kAppInfo[i].enable = false;           // 禁用应用程序
                    kAppInfo[i].app_state = APP_STA_IDLE; // 设置应用程序状态为空闲
                }
            }
        }
    }

    2.2.2 MitModeRun()

    MitModeRun() 是 MIT 控制模式的周期执行主函数。它根据上位机下发的控制指令、制动器状态和急停信号,决定电机当前应处于正常运行、暂停 (锁止) 还是紧急停车状态,并负责将负载端的控制参数 (位置、速度、扭矩、Kp、Kd) 换算为电机端的实际执行参数。

    AppResult MitModeRun()
    {
        int64_t pos_tar_p_add = 0;
    
        kMitMode.now_Controlword = (APP_CONTROL_WORD)get_app_Controlword();
    
        if (kMitMode.now_Controlword == APP_CTRL_ENABLE)
        {
            if (!get_app_Emergency_brake_requested())
            {
                if (kMitMode.pre_Controlword == APP_CTRL_DISABLE)
                {
                    // 重新使能后初始化规划器
                    kMitMode.traj.iq_max_A = get_app_MIT_max_current();
                    MitTrajectoryPlanningInit(&kMitMode.traj);
                }
    
                // 规划速度为0的情况:1. 暂停指令生效 2. 抱闸状态不为松闸
                if (get_app_Halt_running_cmd() == true || get_app_Brake_state() != BRAKE_STATE_RELEASED)
                {
                    kMitMode.traj.pos_tar_p = get_app_Motor_position_actual_value();
                    kMitMode.traj.speed_tar_p_s = 0;
                }
                else
                {
                    kMitMode.pos_tar_p_add = (float)(get_app_MIT_target_position() - get_app_Position_actual_value()) *
                                             get_app_Reduction_ratio() * get_app_P_load_2_motor();
                    kMitMode.traj.pos_tar_p = kMitMode.pos_tar_p_add + get_app_Motor_position_actual_value();
    
                    kMitMode.traj.speed_tar_p_s = get_app_MIT_target_velocity() * get_app_Motor_rpm_2_pps() *
                                                  get_app_Reduction_ratio();
                }
    
                kMitMode.traj.iq_max_A = get_app_MIT_max_current();
                kMitMode.traj.tq_set_NM = get_app_MIT_feedforward_torque() * get_app_Reduction_ratio_inv(); // 负载端转矩  转化 为电机端
                kMitMode.traj.kp_pos_NM_rad = get_app_MIT_kp() * get_app_Reduction_ratio_inv();             // 转化为电机端增益
                kMitMode.traj.kd_spd_NM_rad_s = get_app_MIT_kd() * get_app_Reduction_ratio_inv();           // 转化为电机端增益
            }
            else
            {
                set_app_Controlword(APP_CTRL_EMERGENCY_BRAKE); // 强制进入紧急停车(QuickStop)状态
                kMitMode.now_Controlword = APP_CTRL_EMERGENCY_BRAKE;
            }
        }
    
        // 紧急停车(QuickStop)处理
        if (kMitMode.now_Controlword == APP_CTRL_EMERGENCY_BRAKE)
        {
            kMitMode.traj.pos_tar_p = get_app_Motor_position_actual_value();
            kMitMode.traj.speed_tar_p_s = 0; // 如果处于急停状态,规划速度为0
    
            kMitMode.emergency_brake_mode = get_app_Quick_stop_option_code();
            // 在急停后失能电机模式下,检测到零速后电机失能
            if (kMitMode.emergency_brake_mode <= EMERGENCY_BRAKE_MODE_VOLTAGE_LIMIT)
            {
                kMitMode.check_status_val = (CheckStatusVal_t)app_get_check_status_val();
                if (kMitMode.emergency_brake_mode == EMERGENCY_BRAKE_MODE_DISABLED ||
                    kMitMode.check_status_val.bits.velocity_zero == true)
                {
                    set_app_Controlword(APP_CTRL_DISABLE);
                    kMitMode.now_Controlword = APP_CTRL_DISABLE;
                }
            }
        }
    
        if (kMitMode.now_Controlword != APP_CTRL_DISABLE)
        {
            MitTrajectoryPlanningHandle(&kMitMode.traj);
        }
    
        kMitMode.pre_Controlword = kMitMode.now_Controlword;
    
        return APP_RET_RUNNING;
    }

    2.2.3 MitTrajectoryPlanningHandle()

    MIT 模式不许用轨迹规划,直接透传即可:

    // MIT模式轨迹规划器处理函数
    void MitTrajectoryPlanningHandle(MIT_TRAJECTORY_DATA *mit_traj_data)
    {
        axis->mit_ctl_input.pos_tar_p = mit_traj_data->pos_tar_p;
        axis->mit_ctl_input.speed_tar_p_s = mit_traj_data->speed_tar_p_s;
        axis->mit_ctl_input.tq_set_NM = mit_traj_data->tq_set_NM;
        axis->mit_ctl_input.iq_max_A = mit_traj_data->iq_max_A;
    
        axis->mit_ctl_config.kp_pos_NM_rad = mit_traj_data->kp_pos_NM_rad;
        axis->mit_ctl_config.kd_spd_NM_rad_s = mit_traj_data->kd_spd_NM_rad_s;
    }

    2.3 ModeRun() 系列逻辑

    2.3.1 MitCtlStep()

    本函数负责在 MIT 控制模式下,获取实时的传感器反馈 (位置、速度),将其喂给 MIT 控制算法 (PD 阻抗 + 前馈),并将计算出的目标电流送入底层的电流环(FOC),同时附加了一些工业级驱动器必备的平滑和补偿策略。

    // MIT模式
    static void MitCtlStep(Axis *const axis, AxisDw *const axis_dw)
    {
       // axis->mit_ctl_pd_input.tq_set_A  用户指令设定
       // axis->mit_ctl_pd_input.pos_tar_p  用户指令设定
       // axis->mit_ctl_pd_input.speed_tar_rad_s  用户指令设定
    
       axis->mit_ctl_input.pos_now_p = axis->motor_pos_sensor_output.enc_sum_p;   // 获取电机端当前绝对位置
       axis->mit_ctl_input.speed_now_rad_s = axis->speed_obs_pll_output.ev_rad_s; // 速度观测器输出
    
       mit_ctl(&axis->mit_ctl_input, &axis->mit_ctl_config,
               &axis->mit_ctl_output);
    
       axis->pos_speed_ctl_output.iq_tar_A = axis->mit_ctl_output.iq_tar_A; // 设置电流环目标电流
    
       // 转矩脉动与摩擦补偿
       TqFcComStep(axis, axis_dw);
    
       // 指令电流滤波
       CommandCurrentFilter(axis, axis_dw);
    }

    2.3.2 mit_ctl()

    由 Simulink 模型生成的 MIT 计算函数:

    /* Output and update for referenced model: 'mit_ctl' */
    void mit_ctl(const MitCtlInput *rtu_input, const MitCtlConfig *rtu_config,
                 MitCtlOutput *rty_output)
    {
        real32_T rtb_Divide;
    
        /* Product: '<S1>/Divide' incorporates:
         *  DataTypeConversion: '<S1>/Data Type Conversion'
         *  Gain: '<S3>/Gain1'
         *  Gain: '<S4>/Gain1'
         *  Product: '<S1>/Product'
         *  Product: '<S1>/Product1'
         *  Product: '<S3>/Product'
         *  Product: '<S4>/Product'
         *  Sum: '<S1>/Add'
         *  Sum: '<S1>/Add1'
         *  Sum: '<S1>/Add2'
         */
    
        ...
    }

    四、仿真分析

    4.1 数据结构

    4.1.1 MitCtlInput

    typedef struct
    {
        /* 前馈力矩电流 */
        real32_T tq_set_NM;
    
        /* 目标位置 */
        int64_T pos_tar_p;
    
        /* 当前位置 */
        int64_T pos_now_p;
    
        /* q轴最大输出电流 */
        real32_T iq_max_A;
    
        /* 目标速度 */
        real32_T speed_tar_p_s;
    
        /* 当前速度 */
        real32_T speed_now_rad_s;
    }
    
    MitCtlInput;

    4.1.2 MitCtlOutput

    typedef struct
    {
        /* 目标q轴电流 */
        real32_T iq_tar_A;
    }
    
    MitCtlOutput;
    

    4.1.3 MotorCtlSmConfig

    typedef struct
    {
        /* 运行模式 */
        int8_T mode;
    
        /* 欠压保护阈值 */
        real32_T under_voltage_protection_V;
    
        /* 过压保护阈值 */
        real32_T over_voltage_protection_V;
    
        /* 过速保护阈值 */
        real32_T over_speed_protection_rad_s;
    
        /* 欠温保护阈值 */
        real32_T under_temperature_protection_d;
    
        /* 过温保护阈值 */
        real32_T over_temperature_protection_d;
    
        /* 位置跟随误差保护阈值 */
        int32_T position_following_error_protection;
    
        /* 保护生效 */
        uint32_T error_enable;
    
        /* 过流保护阈值 */
        real32_T over_current_protection_A;
    }
    
    MotorCtlSmConfig;

    4.2 仿真分析

    4.2.1 仿真概览

    Mit_ctl 模型如下:

    MIT 模式的建模比较简单,如同公式一样,将 Kp、Kd 和前馈扭矩相加。

    最后求和 Add1 模块将三个来源 tq_set (前馈扭矩) + P 项 + D 项相加,得到总需求扭矩。扭矩转电流:Divide 模块将总扭矩除以电机扭矩常数 kt,计算出理想的 q 轴电流 Iq:

    4.2.2 Kp 环

    将编码器误差相减,除总一圈的编码器的值,再乘 2π,得到 rad 值,最后乘上速度阻尼系数:

    \tau_{kp} = \frac{(pos_{tar} - pos_{now})}{enc_line} \times 2\pi \times K_p

    • pos_tar - pos_now = 位置误差
    • enc_line = 编码器每转总脉冲数
    • Kp = 位置刚度 (Nm/rad)

    至此,我们得到 Kp 输出的力矩值。

    4.2.3 Kd 环

    \tau_{kd} = \left(\frac{speed_{tar}}{enc_{line}} \times 2\pi - \omega_{now}\right) \times K_d

    • speed_tar = 目标速度 (pulse/s)
    • enc_line = 编码器每转总脉冲数
    • ω_now = 当前速度 (rad/s)
    • Kd = 速度阻尼 (Nm·s/rad)

    4.2.4 乘 kt

    最后把这三项乘以 Kt 转矩常数 (Nm/A) 得到我们的输出 iq 电流值

    基于MATLAB与达妙电机MIT模式的动态轨迹追踪实践 本文详细介绍了基于MATLAB与达妙电机MIT模式的动态轨迹追踪实践,包括环境配置、MIT控制模式解析、动态轨迹生成与追踪、数据可视化及参数调优技巧。通过实际案例和代码示例,帮助开发者快速掌握曲线追踪技术,提升控制精度和效率。 阅读详情

    相关推荐

    达妙电机MIT模式调参实战:如何从‘曲线偏离’到‘完美追踪’正弦波?

    本文详细解析了达妙电机MIT模式调参实战,从曲线偏离到完美追踪正弦波的方法论。通过MATLAB实时曲线分析,深入探讨位置环(kp)和速度环(kd)的双重调节技巧,帮助工程师优化参数组合,提升电机控制精度和稳定性。

    weixin_29053073的博客 736

    MIT四足机器人Cheetah 3控制方案理解笔记(2)——Convex Mpc身体姿态控制

    在“MIT四足机器人Cheetah 3控制方案理解笔记(1)”中,主要简单的总结了下一般情况下腿足机器人的摆动腿控制方法以及Cheetah 3的基于集中质量模型的平衡控制器。此外,MIT还在Cheetah 3以及Mini Cheetah上采用了Convex MPC平衡控制器,在其2018年论文《Dynamic Locomotion in the MIT Cheetah 3 Through Convex Model-Predictive Control》中,Cheetah 3通过Convex MPC平衡控制器

    Wincoding的博客 7807

    直观理解-梯度下降及MIT自适应控制律

    具体解释了什么是梯度,以及梯度如何应用于MIT自适应控制律,为后续基于梯度法的模型参考自适应控制做铺垫,如果可以理解的,可以自动跳过。

    weixin_38342580的博客 2046

    机器人 - 关于MIT电机模式控制

    Mixed Integrated Torque为一种混合控制模式,在同一帧CAN数据里包含位置、速度、扭矩三类的闭环指令。驱动器里面把位置环、速度环、前馈扭矩相加,得到一个参考电流,然后再交给电流环完成精准扭矩输出。

    qq_39546358的博客 7174

    【伺服电机控制】MIT无刷电机FOC驱动程序设计

    系列文章目录 提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加 TODO:写完再整理 文章目录系列文章目录前言一、FOC主程序分析1、主程序步骤2、主程序代码二、中断程序分析1.can接收中断主要的任务(1)驱动特殊指令(优先级最高)【用于非串口设置电机模式】(2)CAN接口指令(优先级最低)(3)把电机收到的信号返回到主设备,说明电机有响应代码2.串口接收中断主要的任务设置电机的模式(1)【27】电机复位状态+接收8位数据清零(2)【REST_MODE】:电机还在菜单栏上,在这里进行电机

    qq_35635374的博客 9382

    【四足机器人项目实战】MIT四足机器人力控电机优劣分析及解决办法

    系列文章目录 提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加 TODO:写完再整理 文章目录系列文章目录前言一、mit力控电机方案二、弊端三、优点四、解决办法设计相关看专栏其他博客 前言 认知有限,望大家多多包涵,有什么问题也希望能够与大家多交流,共同成长! 本文先对XXX做个简单的介绍,具体内容后续再更,其他模块可以参考去我其他文章 提示:以下是本篇文章正文内容 一、mit力控电机方案 MITCheetahMini驱动器采用的电机是类似于公版电机T-MotorU8的型号

    qq_35635374的博客 6092

    MIT电机驱动文档

    电机驱动器配备两个在正常运行时使用的连接器:一个用于供电的XT30接口,以及一个用于CAN总线通信的Harwin Datamate L-Tek高可靠性接口。在电路板顶部设有JST-SH接口,包含编程引脚和用于配置驱动器的串口。必须使用预充电电路(推荐阻值10-100Ω),或通过可调电源缓慢升压。CAN接口的型号为M80-8420342,匹配接口型号为M80-8990305。通过921600波特率(8数据位,1停止位,无校验)的串口终端进行配置。大量负功(如制动),必须连接能吸收能量的电源(如蓄电池)

    u011878611的博客 3781

    【四足机器人项目实战】MIT无刷电机FOC硬件电路设计

    系列文章目录 提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加 TODO:写完再整理 文章目录系列文章目录前言一、MCU最小系统--STM32F446RE1.usb串口通讯连接口+程序下载连接口2.MCU电源滤波二、磁编码器角度传感器三、三相驱动桥四、CAN通讯收发器电路五、温度ADC检测六、电源滤波及分压电路1、DC电源输入端2、DC电源滤波电路3、DC电源分压电路4、三相绕线接线端总结参考资料 前言 认知有限,望大家多多包涵,有什么问题也希望能够与大家多交流,共同成长! 本文先对

    qq_35635374的博客 7068

    matlab测试达妙电机MIT模式曲线追踪能力

    测试达妙电机的曲线追踪能力

    2301_79345492的博客 2082

    python语言下使用opencv接口cv2.VideoCapture()接口调用海康机器人工业相机

    文章目录 目录 文章目录 前言 一、opencv接口介绍 二、调用海康机器人工业相机 1.使用海康机器人工业相机的前提 2.调用海康机器人工业相机进行显示 3.调用海康机器人工业相机进行存图 4.调用海康机器人工业相机存视频 5.调用海康机器人工业相机进行简单图像处理 总结 前言 提示:这里可以添加本文要记录的大概内容: 例如:随着人工智能的不断发展,机器学习这门技术也越来越重要,很多人都开启了学习机器学习,本文就介绍了机器学习的基础内容。 提示:以下是本篇文章正文内容,下.

    qq_39570716的博客 1万+

    基于Xilinx Artix7 的FPGA高级应用 (二):千兆以太网通信(实战篇)第一期

    本项目是基于Xlinx Artix7 XC7A35T芯片 以太网芯片选用的是RTL8211EG PHY芯片 MAC 和PHY接口标准是GMII 开发工具是vivado 2018.3 我们先来复习一下以太网的基本通信原理: 当我们的计算机需要与局域网中另外的设备进行通信的时候,需要将数据传输到网卡进行编码,然后传输到线上,经过另一个设备的网卡解码成该设备能读取的数据。 关于以太网协议的知识在原理篇中已经讲过,不再重复,下面直接看接收代码: 首先注意我们的MAC和PHY通信选择的是GMII接口标准,发送数据一

    工大技术底层人员的博客 4034

    利用CAN总线控制关节电机

    电机向主控发送反馈帧,反馈点击状态,电机位置,速度,力矩以及电机温度和电机参数。那我的代码举例,比如我关节电机向我发送的数据的ID是0x000,我向电机发送的ID是0x001,那么我只要把数据帧的内容规定和采集好,就可以实现双方的交流了。我的理解是,FOC程序本身非常复杂且消耗算力(我有亲自写过FOC的算法程序,想了解的可以看看我发布的前几篇博客),如果用一块单片机同时控制多个内部没有集成FOC主控的关节电机,意味着一块单片机要同时控制多路FOC,这样会导致单片机算不过来,每个电机的控制都差强人意。

    2401_88493720的博客 3832

    PackML工业标准解析:统一机器状态管理与数据交换的实践指南

    在工业自动化与智能制造领域,实现设备间的互联互通与高效协同是核心挑战。这背后依赖于一套标准化的机器行为与数据交换规范,其原理在于通过定义统一的状态模型与接口协议,消除不同厂商设备间的“语言”隔阂。这项技术的核心价值在于大幅降低系统集成复杂度、提升运维效率与生产透明度,是构建数字化工厂的基石。其典型应用场景包括包装机械、离散制造产线等需要多设备协同的领域。本文聚焦的PackML(包装机械语言)标准,正是这一理念的杰出实践,它通过一套严谨的**状态机模型**和**数据标签(PackTags)** 体系,为机器提

    weixin_30070339的博客 403

    YuanHub 源码分析【二】全局变量与调试

    本文介绍了YuanHub项目中Simulink控制器模型的结构体参数体系,重点解析了电流环、位置速度环等核心控制模块的参数配置。主要内容包括: Simulink模型采用模块化设计,每个功能对应独立模型文件,包含input/config/output三个结构体接口 详细说明了电流环控制结构体(CurrentCtlConfig/Input/Output)的参数定义,包括PI增益、死区补偿等关键参数 介绍了位置速度环、编码器、速度观测器等模块的结构体配置 阐述了伺服系统的参数数据库体系,包含指令层、反馈层、配置层

    nullccc的博客 497

    01-SA8155 QNX 虚拟机Hypervisor

    Hypervisor运行在Host OS操作系统上,其他操作系统均在Hypervisor上运行。:Hypervisor直接运行在硬件上,操作系统在Hypervisor之上运行。终止(关闭):echo control 0 dev/vmm。路径:qnx_ap/AMSS/platform/vm/resources/关闭/终止 qvm(GVM):slay -s SIGQUIT qvm。", //host PVM --- 主机。不直接操作/dev/vmm 实现关闭,启动vm。

    静虚待令,有物混成 1万+

    USRP B210 运行gnuradio时 FPGA固件版本冲突错误解决方法

    事情起源于昨天拿到USRP后开始调试,结果整了一个下午都没解决这个问题。 昨天晚上自己思考了一下,觉得还是版本问题。 首先,uhd_find_devices和uhd_usrp_probe均可以正常运行,没有报错,固件正常加载,序列号也可看到。 teray@teray-X550JK:~$ uhd_find_devices [INFO] [UHD] linux; GNU C++ version 7.5...

    斯哈研究所 4244

    嵌入式开发笔记:伺服电机四大控制模式深度剖析——从原理到调试实战

    本文深入解析了伺服电机的四种核心控制模式:轮廓位置模式(PP)、轮廓速度模式(PV)、轮廓力矩模式(PT)和MIT混合模式。首先介绍了伺服系统的三环控制结构(位置环、速度环、电流环),随后详细分析了每种模式的工作原理、DS402协议中的关键参数对象、调试方法和常见问题解决方案。特别对MIT混合模式进行了重点讲解,该模式创新性地将位置、速度、扭矩三环指令集成在一帧数据中,适用于机器人关节等需要同时兼顾定位精度和力控柔性的应用场景。文章还提供了各模式的典型应用示例和实用调试建议,为嵌入式开发者提供了从理论到实践

    qq_43091070的博客 919

    思科模拟器Cisco Packet Tracer交换机的管理配置

    一、交换机的管理方式:带内管理和带外管理 通过交换机的 Console 端口管理交换机属于带外管理;这种管理方式不占用交换机的网络端口,第一次配置交换机必须利用 Console 端口进行配置。 通过 Telnet、拨号等方式属于带内管理。 二、控制台配置思科交换机 1.新建拓扑图 新建拓扑,选择console线连接交换机和PC,一端接交换机的console口,一端接PC的RS 232 口(...

    qq_37992321的博客 2万+

    达妙电机CAN通信及实验

    了解报文格式,能够发送和接收报文,实现CAN通信

    doudou2weiwei的博客 7508

    汽车BMS技术分享及其HIL测试方案

    在全球碳中和目标的战略驱动下,新能源汽车产业正以指数级速度重塑交通出行格局。动力电池作为电动汽车的"心脏",其性能与安全性不仅直接决定了车辆的续航里程、使用寿命等关键指标,更深刻影响着消费者对电动汽车的市场接受度。而电池管理系统(Battery Management System, BMS)是动力电池安全运行与性能释放的核心技术支撑。

    Speedgoat的博客 2687

    【红外技术】DDE算法:HE在红外图像可视化上的应用(附源码)

    并且数据一般是以 8 的整数倍的位宽在电子系统中应用,因此大多数的显示器的显示位宽为 8bit,共 256 个灰阶。为了使红外图像可视化,因此需要一种“数据压缩”技术,还需要既可能的不丢失原始图像信息。HE后的图像直方图分布比较均匀离散,图像对比度好。但是HE算法只是适用某些图像,它是将灰度集中区域进行拉伸得到,也会放大该集中区域的噪声,它是一种“不公平”的映射。以下是红外细节增强算法的演示,很明显右边的图像,展现了更多的图像细节,对于小目标的观测非常友好。文章已于2019-12-02修改。

    sunwindl91181的博客 911

    电机PID,串级PID,MIT的PD、tor调参的经验之谈

    本文介绍了电机三环控制系统的调节方法,核心原则是由内向外依次调节电流环、速度环和位置环。电流环通常采用P控制,速度环使用PI控制,位置环只需P控制。调节时要逐步增大参数,观察响应情况,避免超调和震荡。微分控制(D)通常不推荐使用,但在特殊情况下可谨慎添加。对于RM电机等已内置电流环控制的设备,只需调节速度和位置环。文章还提到MIT控制和摩擦补偿的方法,并建议通过上位机绘制曲线来辅助调试。最后作者表示这些是个人经验,欢迎指正。

    2401_86684687的博客 503

    Statistics and Machine Learning Toolbox

    Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

    从理论到实践:MIT-6.824课程中10个最重要的分布式系统设计模式

    **分布式系统设计**是当今云计算和大数据时代的核心技术,而MIT-6.824课程作为全球最著名的分布式系统课程,为我们提供了从理论到实践的完整学习路径。本文将深入解析这门课程中10个最重要的分布式系统设计模式,帮助你掌握构建可靠、可扩展分布式系统的核心技能。 ## 🚀 什么是MIT-6.824课程? MIT-6.824是麻省理工学院计算机科学系的经典课程,专注于分布式系统的设计与实现。课程

    gitblog_00609的博客 714

    迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

    迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

    visio安装包

    Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

    LINUX环境下用命令行控制MIT电机

    我的硬件环境: 1台工控机,买的淘宝的典籍工控机,买来以后自己用U盘改成了UBUNTU 16操作系统 1台深圳智擎的MIT电机 1个24V稳压电源。我用的是中信的ZXD2400可调稳压电源,没有使用便宜的明伟开关电源。我觉得用24V的明伟开关电源足够了 1个USB2CAN模块。 这个是最关键的,必须买一个支持高速的USB2CAN模块,一般的低速模块是不能用的 这个模块最高支持1Mkps的速度,这个速度也是MIT电机所需要的。 实拍图如下: 蓝灯已亮,说明电机已经使能(国内不同牌子的MIT电机,使能灯的颜

    qq467480903的博客 2656

    深度学习工具包 DeepLearningToolbox

    深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

    上一篇: YuanHub 源码分析【四】位置速度
    下一篇: 伺服控制的负载转矩扰动观测器 (TL DOB) 简谈
    崔佳宇_
    崔佳宇_ 优质创作者: 嵌入式与硬件开发技术领域 优质创作者: 嵌入式与硬件开发技术领域
    博客等级 码龄10年 1万+粉丝 173原创
    评论 1
    成就一亿技术人!
    拼手气红包6.0元
    还能输入1000个字符
     
     条评论被折叠 查看
    添加红包

    请填写红包祝福语或标题

    个

    红包个数最小为10个

    元

    红包金额最低5元

    当前余额3.43元 前往充值 >
    需支付:10.00元
    成就一亿技术人!
    领取后你会自动成为博主和红包主的粉丝 规则
    hope_wisdom
    发出的红包
    实付元
    使用余额支付
    点击重新获取
    扫码支付
    钱包余额 0

    抵扣说明:

    1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
    2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

    余额充值