资讯详情

资讯详情

C620电调驱动M3508电机:CAN/PWM链路与PID闭环实战

简介这是一份面向RoboMaster电调调试、嵌入式控制学习与课程设计场景的C620电调Demo程序说明文档围绕M3508电机在PWM与CAN两种模式下的驱动展开适合已具备STM32基础、希望打通主控板、电调与电机联调链路的读者。压缩包仅含1个PDF文件约905KB篇幅集中便于随查随用。文档以STM32F427主控和Keil5、STM32CubeMX、HAL固件库及FreeRTOS为环境背景梳理工程分层、BSP用户代码组织、任务内控制逻辑以及宏定义切换CAN/PWM的方法并说明CubeMX固件库安装、JRE依赖等准备事项。硬件部分覆盖三相线、7-Pin数据线、CAN1与PWM端口、24V供电的连接差异强调CAN与PWM信号线不可同时接入。控制层面还对比CAN电流闭环需读取转速并做PID调节、PWM转速闭环仅需给定脉宽的关键区别并给出按键调速流程与供电注意事项。已有3366人学习可作为电调联调、代码框架理解、控制模式选择与排错思路的参考。1. 两块板子一根线这套 C620 电调 Demo 到底在演示什么电机不转第一反应是代码写错了。带 M3508 的项目里八成问题出在接线或者使能没给对——这份 C620 电调程序说明书最有价值的地方不是它教你怎么驱动电机而是它把「一条控制指令从主控板出发、经过电调、最后变成电机转速」这条链路完整摊开给你看。整套 Demo 跑在一块主控开发板后文就叫 RM 主控板上主控 MCU 是 STM32F427开发环境 Keil5 μVision外设初始化代码由 STM32CubeMX 加 HAL 固件库生成还勾了 FreeRTOS。它同时给了两条控制链路CAN 指令和 PWM 信号用两个宏互斥切换。适合谁适合刚拿到 C620 电调和 M3508 电机、想先跑通一遍再考虑底盘结构的人也适合已经在用这套硬件、但一直靠改别人工程、没搞清楚报文和 PID 各自负责哪一段的人。2. 环境与工程骨架从 CubeMX 配置到 Keil5 能编译的第一版这套代码的骨架不是手写的是 CubeMX 生成的。理解了生成规则你才知道哪些文件能改、哪些一改下次重新生成就会被覆盖。很多人第一次接触这类工程会在main.c里写业务逻辑结果改一次.ioc重生成代码全没了血泪经验。2.1 装 CubeMX 之前先把 Java 环境理清楚STM32CubeMX 本身是 Java 写的双击没反应、闪退、界面白屏十次有八次是 JRE 没装或者版本不对。稳妥的顺序是先确认本机有 Java 运行环境再装 CubeMX 本体。命令行敲一句就能看出来java -version # 能打印出 java version 1.8.0_xxx 之类即可 # 没有输出或提示 command not found先去装 JRE 再回来装完之后再去配固件库。CubeMX 的固件库不是随主程序一起塞进来的需要单独下载 zip 包然后走Help - Install New Libraries - From Local选中 zip 里的包点开。装对了的标志很直观左边器件列表里对应系列那一行版本号的勾选框会变成绿色。这里有个高发翻车点只装了 CubeMX 主程序没装 firmware package。表现是新建工程、选完芯片、配完外设点生成代码的时候报错或者生成出来的工程里Drivers/STM32F4xx_HAL_Driver目录是空的。原因很简单外设驱动源码在 pack 里不在 CubeMX 里。所以安装顺序必须是JRE → CubeMX → 对应系列的 firmware pack缺一不可。2.2 从 .ioc 到 Keil 工程外设配置和生成选项新建工程选 STM32F427 这颗芯片之后需要配的东西其实不多但每一项都直接影响后面能不能跑。按这套 Demo 的需求核心是这几块配置项取值作用RCCHSE 外部晶振提供系统时钟源时钟树按板子晶振频率配到 180MHz决定 CAN、TIM 的实际时基CAN11MbpsNormal 模式走 CAN 控制链路TIM5PWM 输出通道 1走 PWM 控制链路FreeRTOS勾选保留默认任务提供StartDefaultTask跑控制循环ToolchainMDK-ARM V5生成 Keil5 工程时钟树这一块要特别注意CAN 挂在 APB1 上APB1 的时钟决定了 CAN 波特率分频参数的算法。你如果按 168MHz 的经验去算 180MHz 的分频波特率就会偏电调收不到正确帧表现是「程序在发电机一点反应没有」。配完时钟树之后CubeMX 会直接把实际频率标在图上照着这个数去算分频最稳。生成选项里Generate peripheral initialization as a pair of .c/.h files per peripheral建议勾上这样 CAN、TIM 的初始化会被拆到单独的can.c、tim.c里比全塞在main.c里好找。另外Keep User Code when re-generating默认是开的别关关了之后再生成会把你写在USER CODE BEGIN和END之间的东西一并冲掉。2.3 BSP 分组自己写的代码该放哪CubeMX 生成的工程层次大致是这样几层最底下是 ARM core 和 CMSIS往上是 HAL 驱动层再往上是 Middleware这里就是 FreeRTOS最上面才是 User Code。这套 Demo 的做法是在 Keil 工程里新建一个Bsp分组把用户自己的文件放进去和其它自动生成的代码物理隔离。Project/ ├── Core/ │ ├── Inc/ # main.h, FreeRTOSConfig.h, stm32f4xx_it.h │ ├── Src/ # main.c, freertos.c, stm32f4xx_it.c, can.c, tim.c ├── Drivers/ │ ├── CMSIS/ │ └── STM32F4xx_HAL_Driver/ ├── Middlewares/ │ └── Third_Party/FreeRTOS/ └── Bsp/ # 手动添加CubeMX 不管这里 ├── bsp_can.c ├── bsp_can.h ├── pid.c └── pid.hBsp目录是纯手工目录CubeMX 重新生成时不会碰它所以业务代码、驱动封装、PID 这类东西全放这里是这套工程结构最值得抄的一点。加进 Keil 的时候有两个动作不能漏一是右键分组Add Existing Files to Group把.c文件真正加进工程二是在Options for Target - C/C - Include Paths里把Bsp目录加进去。只做第一步不做第二步编译时会报cannot open source input file bsp_can.h这个错看着像文件不存在其实是头文件搜索路径没配。3. 控制链路拆解CAN 报文和 PWM 脉宽各自怎么落到电机上两种控制方式的本质差别不在接线在闭环归属。CAN 模式下电调只做电流闭环转速是你自己在主控里闭的PWM 模式下电调内部就把转速闭掉了主控只管给一个脉宽。搞清楚这一点后面 PID 该不该写、写在哪就不会乱。3.1 CAN 链路波特率、滤波器与 0x200 控制帧C620 走 CAN 的时候主控发出去的控制帧标准 ID 是0x200一帧 8 字节同时携带 4 个电调的目标电流值每个占 2 字节高字节在前。电调回传的反馈帧 ID 是0x201到0x208一一对应 1 到 8 号电调。这个 ID 分配是固定的不是随便定的所以接多个电机时顺序不能乱。初始化部分最关键的是波特率和滤波器。波特率算错一切白搭滤波器配错中断里一帧都收不到。void BSP_CAN_Init(void) { CAN_FilterTypeDef filter; /* 1MbpsAPB1 42MHzPrescaler3BS113TqBS22Tq 42MHz / (1 13 2) / 3 1MHz */ hcan1.Instance CAN1; hcan1.Init.Prescaler 3; hcan1.Init.Mode CAN_MODE_NORMAL; hcan1.Init.SyncJumpWidth CAN_SJW_1TQ; hcan1.Init.TimeSeg1 CAN_BS1_13TQ; hcan1.Init.TimeSeg2 CAN_BS2_2TQ; hcan1.Init.AutoBusOff ENABLE; /* 总线异常自动恢复 */ hcan1.Init.AutoRetransmission ENABLE; hcan1.Init.ReceiveFifoLocked DISABLE; hcan1.Init.TransmitFifoPriority DISABLE; HAL_CAN_Init(hcan1); /* 只放行 0x201~0x208其余 ID 挡在 FIFO0 外面 */ filter.FilterBank 0; filter.FilterMode CAN_FILTERMODE_IDMASK; filter.FilterScale CAN_FILTERSCALE_32BIT; filter.FilterIdHigh 0x201 5; filter.FilterIdLow 0; filter.FilterMaskIdHigh 0x7F8 5; /* 低 3 位不参与比较 */ filter.FilterMaskIdLow 0; filter.FilterFIFOAssignment CAN_FILTER_FIFO0; filter.FilterActivation ENABLE; filter.SlaveStartFilterBank 14; HAL_CAN_ConfigFilter(hcan1, filter); HAL_CAN_Start(hcan1); HAL_CAN_ActivateNotification(hcan1, CAN_IT_RX_FIFO0_MSG_PENDING); }逐段说明一下。Prescaler和TimeSeg1/TimeSeg2一起决定波特率公式是APB1时钟 / (1 BS1 BS2) / Prescaler这里 42MHz 除以 16 再除以 3正好 1MHz也就是 1Mbps。这三个参数必须和时钟树对得上改过时钟树就要重新算。AutoBusOff打开是为了总线短时异常后能自恢复不带这个出一次错就得复位。滤波器那段用的是掩码模式0x7F8的二进制是111 1111 1000意思是除了最低 3 位之外全部要匹配而最低 3 位正好是 1 到 8 号电调的编号所以0x201到0x208都能进来其它 ID 被挡掉。这比在中断里写 if 判断要干净中断负担也小。发送函数把四个电流值拼成一帧void CAN_SendMotorCurrent(int16_t i1, int16_t i2, int16_t i3, int16_t i4) { CAN_TxHeaderTypeDef tx; uint8_t data[8]; uint32_t mailbox; tx.StdId 0x200; /* 1~4 号电调统一收这个 ID */ tx.ExtId 0; tx.IDE CAN_ID_STD; tx.RTR CAN_RTR_DATA; tx.DLC 8; tx.TransmitGlobalTime DISABLE; data[0] (i1 8) 0xFF; data[1] i1 0xFF; data[2] (i2 8) 0xFF; data[3] i2 0xFF; data[4] (i3 8) 0xFF; data[5] i3 0xFF; data[6] (i4 8) 0xFF; data[7] i4 0xFF; HAL_CAN_AddTxMessage(hcan1, tx, data, mailbox); }参数i1到i4是目标电流值量程一般是 -16384 到 16384对应电调的 -20A 到 20A具体换算看电调手册。注意这里用的是int16_t负数右移在 C 里是实现相关的所以拼字节的时候一定要先 0xFF再赋值不然高字节会带符号扩展发出去的电流值就乱了。3.2 反馈帧解析0x201 里的转速怎么取出来反馈帧一帧 8 字节的排布是固定的第 0-1 字节是机械角度第 2-3 字节是转速 rpm第 4-5 字节是实际转矩电流第 6 字节是温度第 7 字节保留。转速是int16_t有正负正转反转靠符号区分。typedef struct { uint16_t angle; int16_t speed; /* rpm有符号 */ int16_t current; uint8_t temp; } MotorFB_t; MotorFB_t motor_fb[4]; void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan) { CAN_RxHeaderTypeDef rx; uint8_t data[8]; if (HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, rx, data) ! HAL_OK) return; if (rx.StdId 0x201 rx.StdId 0x204) { uint8_t idx rx.StdId - 0x201; motor_fb[idx].angle (data[0] 8) | data[1]; motor_fb[idx].speed (int16_t)((data[2] 8) | data[3]); motor_fb[idx].current (int16_t)((data[4] 8) | data[5]); motor_fb[idx].temp data[6]; } }这段有几个细节。speed拼完之后必须强转int16_t否则(data[2] 8) | data[3]在 int 上是正数反转时读出来是一个三万多的值PID 直接算飞。另外angle是 0 到 8191 循环的机械角度做位置环的时候要注意过零点处理做速度环用不上。温度这一字节可以用来做过温保护超过 80 度就降额这个在真车上很有必要。回调函数名字是 HAL 库的固定回调前提是你在HAL_CAN_ActivateNotification里使能了CAN_IT_RX_FIFO0_MSG_PENDING并且stm32f4xx_it.c里的CAN1_RX0_IRQHandler调了HAL_CAN_IRQHandler。这两处漏一个回调永远不会进现象就是「电机能转但 PID 不收敛」因为反馈一直是 0。3.3 PWM 链路TIM5 的时基和比较值怎么算PWM 模式下主控只管往 TIM5 的通道 1 上输出一个固定频率、占空比可变的方波。频率和脉宽的合法范围以电调手册为准常见做法是取 50Hz 到 500Hz 之间示例里用 100Hz周期 10ms脉宽在 1ms 到 2ms 之间变化对应转速范围。时基参数这么算定时器时钟 90MHzAPB1 定时器倍频后想要 100Hz则(Prescaler1) * (ARR1) 900000。取Prescaler 89ARR 9999正好 100Hz。改转速就是改比较寄存器的值/* duty: 0.0 ~ 1.0映射到 CCR 的 0 ~ ARR */ void PWM_SetDuty(TIM_HandleTypeDef *htim, uint32_t ch, float duty) { if (duty 0.0f) duty 0.0f; if (duty 1.0f) duty 1.0f; uint32_t arr __HAL_TIM_GET_AUTORELOAD(htim); __HAL_TIM_SET_COMPARE(htim, ch, (uint32_t)(arr * duty)); } /* 把目标转速映射成脉宽再转成占空比 */ void PWM_SetSpeed(TIM_HandleTypeDef *htim, uint32_t ch, int16_t rpm) { /* 假设 -10000~10000 rpm 对应 1ms~2ms中位 1.5ms 为停转 */ float ms 1.5f (float)rpm / 10000.0f * 0.5f; PWM_SetDuty(htim, ch, ms / 10.0f); /* 周期 10ms */ }这里的映射关系是我按常见做法写的示意-10000~10000直接对应1ms~2ms实际值一定要拿电调说明书核对一遍不同固件版本的脉宽-转速对应表可能不一样照抄容易得到一个「给了信号但转速对不上」的结果。另外 PWM 模式下电调内部已经做了转速闭环主控这边就不要再套一层 PID 了两层闭环串起来参数互相打架调起来非常玄学。3.4 宏开关CAN_CONTROL 与 PWM_CONTROL 的互斥设计两种模式在代码里是用两个宏切开的位置在freertos.c顶部。同一时间只能定义一个#define CAN_CONTROL // #define PWM_CONTROL void StartDefaultTask(void *argument) { int16_t set_speed 0; PID_t motor_pid; BSP_CAN_Init(); PID_Init(motor_pid, 6.0f, 0.05f, 0.0f, 16000.0f, 5000.0f); for (;;) { #ifdef CAN_CONTROL motor_pid.target set_speed; int16_t cur (int16_t)PID_Calc(motor_pid, motor_fb[0].speed); CAN_SendMotorCurrent(cur, 0, 0, 0); #elif defined(PWM_CONTROL) PWM_SetSpeed(htim5, TIM_CHANNEL_1, set_speed); #endif osDelay(10); /* 10ms 控制周期 */ } }用#ifdef / #elif而不是两个独立if是为了编译期就把不用的那条链路裁掉省 flash 也避免运行期误判。这段有个容易忽略的点PWM_SetSpeed用到了htim5如果只定义了CAN_CONTROLCubeMX 那边可以不生成 TIM5 的初始化但tim.c里的句柄还是得留着不然切回 PWM 模式时链接报错。稳妥做法是两条链路的外设都生成只是用宏控制调用硬件上按接线说明接一种就行。4. 速度闭环实战PID 在 StartDefaultTask 里怎么跑起来CAN 模式下电调给你的是电流环你给多少电流它就出多大力矩转速完全靠你自己闭。这一步是整个 Demo 里最像「控制」的部分也是最容易调崩的部分。4.1 两种闭环电流环和转速环差在哪先把这个概念掰开。CAN 模式下0x200帧里的数值是目标电流电调内部用一个很快的电流环去跟踪它跟踪得很准但它不管电机转多快。负载一变同样的电流对应的转速就变了。所以你要在 10ms 的控制周期里读回实际转速和目标转速做差用 PID 算出应该给多少电流——这就是串了一个外环。PWM 模式反过来脉宽直接映射目标转速电调内部把转速闭掉了负载变化时它会自己加电流维持转速。主控这边不需要再算 PID。对比项CAN 控制PWM 控制电调内部闭环电流环转速环主控是否需要 PID需要转速外环不需要反馈数据有0x201~0x208 全量无控制精度高可做力矩分配一般够用接线2-Pin CAN 线3-Pin PWM 线这张表基本决定了你选哪种做底盘要做力矩分配、要做麦轮解算必须用 CAN只是想让电机转起来验证硬件PWM 十分钟就能出结果。4.2 pid.c结构体、初始化与计算函数pid.c这套实现是位置式 PID带积分限幅和输出限幅够用且好懂typedef struct { float kp, ki, kd; float target, measure; float err, last_err, integral; float max_out; /* 输出限幅 */ float max_integral; /* 积分限幅 */ float out; } PID_t; void PID_Init(PID_t *p, float kp, float ki, float kd, float max_out, float max_integral) { p-kp kp; p-ki ki; p-kd kd; p-target 0; p-measure 0; p-err 0; p-last_err 0; p-integral 0; p-max_out max_out; p-max_integral max_integral; p-out 0; } float PID_Calc(PID_t *p, float measure) { p-measure measure; p-err p-target - p-measure; p-integral p-err; /* 积分限幅防止堵转时积分项撑爆导致恢复后猛冲 */ if (p-integral p-max_integral) p-integral p-max_integral; if (p-integral -p-max_integral) p-integral -p-max_integral; p-out p-kp * p-err p-ki * p-integral p-kd * (p-err - p-last_err); p-last_err p-err; /* 输出限幅直接对齐电调的 -16384~16384 量程 */ if (p-out p-max_out) p-out p-max_out; if (p-out -p-max_out) p-out -p-max_out; return p-out; }参数说明kp是比例决定响应快慢给大了会震荡ki是积分用来消稳态误差给大了会超调、恢复慢kd是微分抑制超调用但转速信号本身有噪声微分项容易放大噪声所以示例里直接给了 0先不加。max_out给 16000比 16384 略小一点留点余量防止饱和。max_integral给 5000这个数决定了堵转时积分最多能攒多少给太大堵转几秒后一松手电机会猛冲一下非常吓人。调参顺序我一般这么走先把ki、kd都置 0只加kp从 1 开始往上加加到电机能跟住阶跃目标、略有超调为止然后加ki从 0.01 开始消掉稳态误差kd除非超调很明显否则不加。10ms 一个周期ki的累加是离散的给 1.0 这种数量级基本必炸。4.3 10ms 节拍与按键调速的落地控制周期用osDelay(10)卡在 10ms这个值不是随便定的。太快CAN 总线带宽吃紧而且转速反馈本来就是 1kHz 更新的再快没意义太慢外环响应跟不上人一推负载电机就掉速。10ms 对应 100Hz 的控制频率对转速环来说是够的。按键部分接在 RM 主控板的板载按键上思路很简单每轮循环扫一次键有按下就加减目标转速步进 500rpm 之类。这里有个细节按键要消抖不能一轮循环检测到就加一次osDelay(10)的循环里一次按下会被检测到几十次转速直接冲到上限。常见做法是记一个上一次的电平只在「上次松开、这次按下」的边沿触发int16_t Key_Scan(void) { static uint8_t last 1; uint8_t now HAL_GPIO_ReadPin(KEY_GPIO_Port, KEY_Pin); if (last 1 now 0) { /* 下降沿按下瞬间 */ last now; return 500; /* 每次加 500rpm */ } last now; return 0; }把这个函数接进主循环set_speed Key_Scan();再夹一个上下限按键调速就成立了。别忘了限幅set_speed冲到 30000 的时候 PID 输出会一直顶在饱和值电机疯转。5. 避坑排查接线、编译、跑飞的五类高频翻车这套硬件看着简单实际踩的坑集中在几个固定位置。下面五条是我见过、也自己踩过的按「现象 → 原因 → 解决」写对着查能省不少时间。5.1 接线类两条信号线同时接上现象程序烧进去电机要么不转要么转一下停一下行为完全不受控。原因CAN 信号线和 PWM 信号线同时接到了电调上。C620 两个端口都能收指令同时给信号它自己也不知道听谁的。解决只接一种。走 CAN 就把 PWM 那根 3-Pin 线拔掉走 PWM 就把 CAN 的 2-Pin 线拔掉。这条在接线阶段就要养成习惯插之前先确认用哪种模式。现象电机转起来之后主控板突然挂掉或者电源指示灯闪。原因用了电源箱供电电机减速时反电动势回灌把主控板上做电源保护的二极管击穿了。解决用动力电池供电别图方便拿实验室电源箱顶。这条原文里专门标了注意是有实际代价的。5.2 编译与下载类pack 没装、路径没配现象CubeMX 点生成代码报错或者生成的工程里 HAL 驱动目录空空如也。原因只装了 CubeMX 主程序没装对应系列的 firmware package。解决Help - Install New Libraries - From Local把 F4 系列的 zip 包装上装完左侧版本号变绿才算成。现象编译报cannot open source input file bsp_can.h但文件明明在目录里。原因Keil 的头文件搜索路径没加Bsp目录或者加了但加的是相对路径、换了机器就失效。解决Options for Target - C/C - Include Paths里把Bsp加进去路径用相对工程文件的写法。另外.c文件要通过Add Existing Files to Group真正加进工程只放进文件夹不算数。5.3 运行类CAN 收不到、PID 不收敛、电机不使能现象程序在跑CAN_SendMotorCurrent也调了但motor_fb[0].speed一直是 0PID 输出顶到饱和电机猛转或者完全不转。原因接收中断没真正使能。可能是HAL_CAN_ActivateNotification没调可能是stm32f4xx_it.c里的CAN1_RX0_IRQHandler没调HAL_CAN_IRQHandler也可能是 CubeMX 里 CAN1 的 RX0 中断没在 NVIC 里勾上。解决三处挨个查最直接的办法是在回调函数第一行打个断点看进不进得来。现象反馈有数据但转速方向反了给正目标电机反转。原因电机三相线顺序接反了或者反馈解析时int16_t强转漏了。解决先换任意两根三相线试一下如果换线后正常说明是相序问题。如果换线没用回去查解析代码里的强制类型转换。现象一上电电机就自己转而且越转越快。原因PID 的积分项在目标为 0 时仍在累加或者输出限幅没生效。常见于max_integral给得过大、PID_Init没调用就直接PID_Calc。解决确认PID_Init在任务开头调过max_integral给个合理值另外在set_speed为 0 且实际转速小于阈值时把积分项清零。6. 二次开发把单电机 Demo 改成能撑起底盘的驱动层Demo 只驱动一个电机真做底盘至少要四个。改造的核心不是多写几个 PID而是把「一个电机的状态」打包成一个结构体让 PID、反馈、输出三者绑在一起避免满屏的全局变量。typedef struct { PID_t pid; int16_t target_speed; /* 目标转速由上层解算写入 */ int16_t out_current; /* PID 输出直接进 CAN 帧 */ MotorFB_t fb; /* 电调回传的反馈 */ } Motor_t; Motor_t chassis[4]; void Chassis_Task(void *arg) { for (;;) { for (int i 0; i 4; i) { chassis[i].pid.target chassis[i].target_speed; chassis[i].out_current (int16_t)PID_Calc(chassis[i].pid, chassis[i].fb.speed); } /* 四个电流值一帧发出去ID 顺序必须和电调编号对齐 */ CAN_SendMotorCurrent(chassis[0].out_current, chassis[1].out_current, chassis[2].out_current, chassis[3].out_current); osDelay(10); } }改造点和验证方式我整理成一张表照着走不容易漏改造点涉及文件验证方式多电机数组化pid.c/h、freertos.c单电机先跑通再逐个加编号目标速度由上层写入新增chassis.c串口打印目标值和实际值对比反馈帧 ID 对齐bsp_can.cCAN 分析仪抓 0x201~0x204 是否都有过温降额bsp_can.c手动加热或用吹风机模拟堵转保护freertos.c手按住电机看电流是否被限住做多电机的时候最容易出的问题是反馈帧和数组下标错位。0x201对应数组第 0 个0x202对应第 1 个这个映射一旦错一位PID 就会拿 A 电机的转速去控 B 电机的电流表现是底盘原地打转或者抖得厉害。我的习惯是上电先让四个轮子都不转用调试器看四个fb.speed是不是都接近 0然后手动拨某一个轮子看是不是只有对应的那个下标在变确认映射对了再给目标值。另一个值得做的是把 CAN 发送和 PID 计算拆成两个任务用队列传out_current。现在这样 10ms 一轮、算完就发单电机没问题四电机加上其它的控制逻辑之后一旦某一轮计算超时发送就被推迟电调那边会认为通信超时进保护。拆开之后发送任务可以跑更高的优先级稳定得多。从那以后我每次改这类电机驱动都会先把「手动拨轮子看反馈」这一步强制走一遍确认反馈链路通了再碰 PID 参数。这一步花两分钟能省掉后面半小时「到底是参数不对还是反馈不对」的自我怀疑。这套 Demo 的价值也在这里它把链路摊得足够开你照着拆一遍后面加多少电机都是同一套结构。希望帮到你。本文还有配套的精品资源点击获取
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →