news 2026/10/6 21:11:57

STM32定时器编码器模式:四倍频测速原理与CubeMX+HAL实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
STM32定时器编码器模式:四倍频测速原理与CubeMX+HAL实战

之前有个做运动控制项目的朋友跟我吐槽,他用外部中断读增量编码器,电机转速一上 3000 RPM 就开始丢步,方向判断偶尔还会抽风。我一看代码,两个通道的边沿中断全开了,ISR 里还要读另一个通道的电平去判方向,A/B 相靠得越近,中断嵌套越乱,CPU 忙得脚不沾地,还不讨好。

其实 STM32 早就把这活儿用硬件干了,就是定时器的编码器模式。尤其是 H743 这颗芯片,定时器资源又多又强,配合 CubeMX 配一个“四倍频测速”,十分钟就能跑起来,全程 CPU 零参与。这篇文章我就把这套方案从头到尾拆一遍:原理是什么、四倍频怎么来的、CubeMX 怎么点、HAL 库代码怎么写、实际跑起来会踩什么坑,一次性说透。适合正在做小车、机械臂、云台、传送带这类需要测转速或者数位置的场景的嵌入式开发者参考。

1. 先搞清原理:编码器模式为什么比外部中断香

1.1 外部中断测速的固有缺陷

用外部中断测增量编码器,思路很直观:A 相或 B 相来一个上升沿就进一次中断,在中断里计数。听起来简单,但工程上全是问题。

首先,中断频率和转速成正比。一个每转输出 1000 脉冲的编码器,在 3000 RPM 下,每秒要产生 50000 个上升沿,也就是 50 kHz 的中断频率。如果再用两相配合做四倍频,每秒中断直接到 200 kHz。每次中断少说几十个周期,光进出中断的开销就把 CPU 吃掉了大半。更致命的是,中断里如果还干了别的事,比如读引脚、判方向、累加计数,这个时间一抖动,边沿就漏了,测出来的转速忽高忽低。

其次,方向判断有天然竞态。外部中断方案里,通常是在 A 相中断里去读 B 相电平,电平高算正转,电平低算反转。问题是,如果 B 相边沿恰好和 A 相边沿挨得很近,你读到的那一瞬间可能 B 相已经跳变了,方向就判反了。这种错是随机性的,排查起来非常头疼。

第三,中断里做计数还涉及 volatile 变量共享问题。主循环读计数、中断改计数,如果不同步好,读到的可能是半新半旧的数据。加临界区保护吧,中断已经很高频了,再加保护等于雪上加霜。

1.2 定时器的编码器接口:硬件替你把脏活干了

STM32 定时器里的编码器模式,本质上是一个硬件正交解码器。A 相和 B 相直接接到定时器的两个输入通道上,硬件自己检测边沿、自己根据两相的电平关系判断方向、自己决定计数器加一还是减一。整个过程中 CPU 完全不需要参与,计数器自动反映编码器的位置和方向,你只需要隔一段时间去读一次 CNT 寄存器,算出差值,就能得到速度。

这种方式的好处太多了。一是零中断开销,转速再高也不会打断 CPU 的正常工作。二是方向判断是硬件在边沿发生的同一时刻完成的,不存在软件读电平的竞态问题,方向结果天然可靠。三是计数过程没有软件延迟,边沿来了就立即反应,测速精度远高于中断方案。

另外,硬件编码器接口还自带输入滤波,可以滤掉机械开关抖动或者线路噪声带来的毛刺。这一点在电机驱动这种强干扰环境里尤其重要,软件外部中断想要达到同样的抗干扰效果,得额外做好几层处理。

2. 四倍频是怎么来的:A/B 相正交信号与计数机制

2.1 增量式编码器的 A/B 相与 PPR

先明确一个基础概念。增量式编码器输出两路方波信号,一般叫 A 相和 B 相,两路信号相位相差 90°,所以也叫正交信号。电机转一圈,A 相输出的方波个数就是编码器的 PPR(Pulse Per Revolution),常见的有 11、13、100、360、1000、2048 这些值。PPR 越高,分辨率越高,但相应的信号频率也越高。

两路正交信号的价值在于:不仅可以通过脉冲数知道转了多少角度,还能通过两相的相位关系判断旋转方向。比如 A 相超前 B 相 90° 是正转,那 B 相超前 A 相就是反转。硬件解码器就是根据这个相位关系,决定计数器的增减方向。

2.2 模式3计数表:每个边沿都不白给

STM32 定时器的编码器模式有三种工作模式,分别对应 SMS 控制位的 001、010、011:

  • 模式1(SMS=001):只在 A 相边沿计数,B 相只看电平判方向。
  • 模式2(SMS=010):只在 B 相边沿计数,A 相只看电平判方向。
  • 模式3(SMS=011):A 相和 B 相的上升沿、下降沿全部参与计数,也就是每个完整正交周期数 4 下。

所谓“四倍频”,就是工作在模式3。因为编码器一个完整周期里,两相有 4 个边沿,每个边沿都让计数器加一或减一,所以硬件能把每个脉冲拆成 4 个计数步进。这就是四倍频测速的核心。

CubeMX 里没有直接标“模式3”这个说法,它把 Combined Channels 设置为 Encoder Mode 时,生成的就是TIM_ENCODERMODE_TI12,对应定时器的编码器模式3,也就是四倍频。这一点要记牢,别在代码里手滑改成TIM_ENCODERMODE_TI1或TIM_ENCODERMODE_TI2,不然你数出来的数值直接变成原来的四分之一或者二分之一。

2.3 从四倍频计数到实际转速的换算

假设编码器 PPR 为 N,工作在四倍频模式,那么电机转一圈,计数器变化的总量为:

counts_per_rev = N × 4

比如编码器是 11 PPR,四倍频后每转就是 44 个计数步。如果在采样时间 T(单位秒)内,计数器变化了 D 个步进,那么转速就是:

rev_per_sec = D / (N × 4) / T rpm = D × 60 / (N × 4 × T)

举个例子:PPR=11,T=0.1 秒,读到的 D=440,那么转速就是:

rpm = 440 × 60 / (44 × 0.1) = 6000 RPM

轮子线速度同理,把“每转的周长”代入就行:

speed_mps = D × 轮周长 / (N × 4 × T)

这里最需要注意的是单位的坑。很多人算出来数字不对,就是 PPR 混用了。有的编码器标的是“线数”,有的是“脉冲数”,还有的是“极对数”,一定要看数据手册确认 A 相一旋转圈数多少个完整方波。

3. CubeMX 保姆级配置

3.1 引脚规划与 GPIO 复用

先用 CubeMX 选好芯片型号,比如 STM32H743VIT6 或 ZIT6,然后打开 Pinout & Configuration 界面。

编码器 A、B 两相要接到同一个定时器的两个通道上。比如用 TIM3,那 A 相接 TIM3_CH1 对应的引脚,B 相接 TIM3_CH2 对应的引脚。H743 的引脚复用很灵活,TIM3_CH1 可能同时出现在多个引脚上,CH2 也是,但要注意选的两个引脚最好挨得近,方便布线。

在 CubeMX 里点一个引脚,把功能选为 TIM3_CH1,它会自动复用。需要注意,有些引脚默认接了 ADC、DAC 或者其他外设,如果被占用,引脚颜色会变红,这时候就要换引脚或者关闭冲突外设。我习惯的做法是先看数据手册的 AF 表格,确定一对不冲突的引脚,再回到 CubeMX 里配置。

3.2 TIM 编码器参数逐项讲解

选中 TIM3,在 Mode 面板里找到 Combined Channels,下拉选择 Encoder Mode。这一步是核心,选好之后,下方会出现一系列参数配置项。

各参数建议如下:

参数项推荐值说明
Prescaler0编码器模式下不要分频,否则丢计数
Counter ModeUp编码器方向由硬件自动决定,此项不影响
Counter Period65535设满量程,配合 uint16_t 读取最方便
Auto-reload preloadDisable可以禁用,溢出中断用不到预装载
Encoder ModeTI1 and TI2这就是四倍频模式
Input Filter IC1/IC20 或根据环境调见下方滤波说明
IC1/IC2 Prescaler1(不分频)输入信号分频,通常不用

Counter Period 的设置很关键。如果填 65535,计数器范围就是 0~65535,正好对应 uint16_t 类型,读取换算非常方便。有人喜欢按编码器每转计数设一个整数值,比如每转四倍频后 1000 就把 ARR 设 999,这样读数直接是位置,但溢出处理会比较绕,不太推荐新手这么做。

3.3 输入滤波:消除抖动和毛刺

CubeMX 里编码器模式的 Input Filter 对应定时器的 ICxF 数字滤波功能。它的原理是:输入信号必须在连续 N 个采样周期内保持稳定,才认为是一次有效电平跳变,否则视为抖动忽略掉。

在电机控制场景里,电机电刷火花、驱动板 PWM 串扰、接线太长引入的电磁干扰,都可能让编码器信号产生毛刺。毛刺如果被计数器当成有效边沿,测速就飘了。适当调大滤波值能明显改善。

但滤波不是越大越好。滤波本质上是延迟边沿检测,信号跳变后要等到采样满足条件才确认。如果编码器频率很高,两个有效边沿间隔很短,过大的滤波值可能把紧挨着的第二个边沿吞掉,导致计数值偏低。转速越高,能容忍的滤波值就越小。

我的经验值:普通直流电机霍尔编码器,信号线 20cm 以内,滤波值设 0 或者 1 就行;布线超过半米,或者电机启动瞬间干扰明显,从 6~8 开始试;工业长线编码器,建议买差分输出型再接硬件差分接收器,光靠滤波扛不住。

3.4 使能更新中断:做位置累计的关键

如果只是周期性读 CNT 算速度,更新中断不需要开。但如果你还要累计圈数、算绝对位置,计数器只有 16 位,一会儿就溢出回绕了,这时候就得开定时器更新中断。

在 CubeMX 的 NVIC Settings 里,勾上 TIM3 global interrupt。更新事件会在计数器从 65535 跳回 0(向上计数溢出)或者从 0 跳回 65535(向下计数下溢)时触发。中断服务函数里根据当前计数方向,把累计值加上或者减去一个满量程步长。

速度测量本身不用中断,但累计位置用中断,两个需求可以同时满足。

4. HAL 库代码实现

4.1 初始化与启动

CubeMX 生成工程后,定时器初始化函数已经生成好了。在MX_TIM3_Init里,编码器初始化的关键结构体长这样:

TIM_Encoder_InitTypeDef sEncoderConfig = {0}; htim3.Instance = TIM3; htim3.Init.Prescaler = 0; htim3.Init.CounterMode = TIM_COUNTERMODE_UP; htim3.Init.Period = 65535; htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; sEncoderConfig.EncoderMode = TIM_ENCODERMODE_TI12; sEncoderConfig.IC1Polarity = TIM_ICPOLARITY_RISING; sEncoderConfig.IC1Selection = TIM_ICSELECTION_DIRECTTI; sEncoderConfig.IC1Prescaler = TIM_ICPSC_DIV1; sEncoderConfig.IC1Filter = 0; sEncoderConfig.IC2Polarity = TIM_ICPOLARITY_RISING; sEncoderConfig.IC2Selection = TIM_ICSELECTION_DIRECTTI; sEncoderConfig.IC2Prescaler = TIM_ICPSC_DIV1; sEncoderConfig.IC2Filter = 0; HAL_TIM_Encoder_Init(&htim3, &sEncoderConfig);

在 main 函数里,初始化完成后启动编码器接口:

HAL_TIM_Encoder_Start(&htim3, TIM_CHANNEL_ALL);

如果是 H7 系列,注意一点:定时器时钟默认是开启的,不需要像某些外设一样单独__HAL_RCC_TIM3_CLK_ENABLE(),CubeMX 生成的代码里已经做了。

启动之后,TIM3->CNT会随着电机转动自动增减,你不用管它。

4.2 中断回调与溢出累计

如果需要开更新中断,还要在 NVIC 使能后实现回调函数。在stm32h7xx_it.c里已经有TIM3_IRQHandler调用了HAL_TIM_IRQHandler,你只需要在用户文件里重写弱函数:

volatile int64_t g_encoder_position = 0; void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if (htim->Instance == TIM3) { if (__HAL_TIM_IS_TIM_COUNTING_DOWN(&htim3)) { g_encoder_position -= (int64_t)(htim3.Instance->ARR + 1); } else { g_encoder_position += (int64_t)(htim3.Instance->ARR + 1); } } }

这里__HAL_TIM_IS_TIM_COUNTING_DOWN读取的是 CR1 里的 DIR 位,硬件在编码器模式下会根据 A、B 相相位自动维护这个位,所以在更新中断里判断方向是可靠的。如果用的是老版本 HAL 库没有这个宏,直接写(htim3.Instance->CR1 & TIM_CR1_DIR)就行。

有了这个累计值,再配合当前 CNT 就可以随时得到 64 位的绝对位置。这个方案的精度取决于 ARR 的设置,满量程 65536 步进,足够绝大多数应用用了。

4.3 周期采样测速代码

测速最实用的方法是定时采样,每 10ms 或者 50ms 去读一次 CNT,用先后两次的差值算速度。这段代码可以放在定时器更新的回调里做,也可以放在主循环配合HAL_GetTick()做。

以 10ms 采样周期为例:

#define ENCODER_PPR 11 #define SAMPLE_MS 10 #define COUNTS_PER_REV (ENCODER_PPR * 4) static uint16_t last_cnt = 0; static uint32_t last_tick = 0; void Encoder_Speed_Task(void) { if (HAL_GetTick() - last_tick >= SAMPLE_MS) { uint16_t now_cnt = __HAL_TIM_GET_COUNTER(&htim3); int16_t diff = (int16_t)(now_cnt - last_cnt); last_cnt = now_cnt; float rpm = (float)diff * 60.0f / (float)COUNTS_PER_REV / ((float)SAMPLE_MS / 1000.0f); printf("RPM: %.2f\r\n", rpm); last_tick = HAL_GetTick(); } }

这里最核心的一行是:

int16_t diff = (int16_t)(now_cnt - last_cnt);

这个写法不是随手写的,它自动处理了 16 位计数器的回绕问题。比如上一次读到的计数值是 65530,这次读到的值是 3,如果直接算3 - 65530是负数,但实际上是正转过了 9 个步进。把结果强转成int16_t后,(int16_t)(3 - 65530)就得到了 9,这就是有符号回绕运算的妙处。

代价是单次采样间隔内的计数值变化不能超过 32767,否则会溢出判错。按最大 4 倍频计数率保守估算,10ms 采样间隔下,只要每秒计数低于 327 万就不会出错,实际电机编码器很难做到这个量级,所以这个方案非常稳。

4.4 方向判断与顺滑滤波

方向不用额外读引脚,直接看 diff 的正负。diff 大于 0 是正转,小于 0 是反转。这点比外部中断方案清爽太多。

但是机械安装会导致 A、B 两相接反,这时候 diff 的符号正好反过来,转速数值是对的,方向反了。解决的办法有两个:一是交换 A、B 接线,二是把代码里now_cnt - last_cnt换成last_cnt - now_cnt,或者在初始化时把 IC1Polarity 和 IC2Polarity 改成下降沿,让硬件自动帮你反向。哪种都行,看布线方便。

如果算出来的转速跳变很大,可以加一个一阶低通滤波:

static float rpm_filtered = 0; const float alpha = 0.6f; rpm_filtered = rpm_filtered + alpha * (rpm - rpm_filtered);

alpha 越小越平滑,但延迟越大。实际调参时,先不滤波跑一下看原始数据波动幅度,再根据控制需求决定 alpha,不要上来就猛滤波,把真实转速信息也滤没了。

5. 实测调参与常见坑

5.1 测速结果不对的排查速查表

下面是我实际帮人调试时最常遇到的问题和排查思路,整理成表,建议收藏。

现象可能原因排查与解决
计数值完全不变化引脚复用不对、编码器模式没启动检查 A/B 相是否接对通道,检查HAL_TIM_Encoder_Start是否调用
数值倍频不对,变成四分之一编码器模式误用成单通道确认 EncoderMode 为TIM_ENCODERMODE_TI12
转速数值飘忽不定信号毛刺或采样时间过短调大 IC1Filter/IC2Filter,或增大采样周期
方向反了A/B 相接线反了交换 A/B 接线,或修改 IC 极性反向
高速时数值突然偏低滤波值太大吞边沿降低滤波值,或检查信号线质量
低速时读数不稳定机械抖动或编码器分辨率太低增加滤波和采样时间,换更高 PPR 编码器
位置累计方向错误溢出中断里方向判断逻辑反了调换__HAL_TIM_IS_TIM_COUNTING_DOWN分支的累加符号

排查顺序我建议是“信号 → 配置 → 代码”。先用示波器或者逻辑分析仪看 A/B 相波形是否存在、正交关系是否正常、是否有毛刺,再回头看 CubeMX 里的配置有没有选错模式,最后才怀疑代码逻辑。

5.2 我踩过的三个进坑实录

第一个坑是 PWM 输出和编码器输入共用了一个定时器。当时想省事,用一个定时器同时输出电机 PWM 和读编码器,结果计数器被两路信号反复拉扯,完全没法工作。定时器的编码器模式会占用整个定时器的通道,不能和 PWM 输出同时复用。解决方法是重新检查引脚分配,把编码器换到一个专门用于测速的定时器上。H743 定时器资源很丰富,没必要省。

第二个坑是 IC1Prescaler 或者 IC2Prescaler 被误设为 2 或者 4。刚接触时可能以为这是用来做四倍频的,实际上定时器编码器模式本身就是硬件四倍频,IC 分频反而会丢掉中间边沿,导致实际计数减半再减半。正常编码器模式这两个值都必须保持TIM_ICPSC_DIV1,不要动。

第三个坑是中断回调里用了阻塞操作。我在溢出中断里加过一个串口打印日志的调试语句,结果电机一转,打印还没发完,下一个更新中断就来了,程序直接卡死。更新中断回调里必须做极轻量级的操作,累加位置变量可以,打印、延时不不行。

5.3 关于采样周期和最大测速上限

很多人在意测速上限,本质上取决于采样周期和单次采样差值。用前面说的int16_t回绕判差方案,单次采样差值不能超过 32767,否则符号会判反。所以采样周期 T 和最大计数率 f_max 满足:

f_max × T < 32767

如果采样周期是 10ms,那最大计数率约 327 万次/秒,对应四倍频后每转 1000 计的编码器,转速上限接近 196000 RPM。这个余量对绝大多数应用完全够。如果你面对的真是超高转速场景,可以缩短采样周期,采样越频繁,单次差值越小,能覆盖的转速上限越高,代价是速度更新频率更快,数据噪声相对变大。

反过来,低速场景要注意采样周期不能太短。比如 PPR 是 11 的电机编码器,四倍频后一转才 44 个计数。如果采样周期只有 1ms,那么转速在 1363 RPM 以下时,很多采样周期里 diff 都是 0,算出来的速度跳变非常严重。解决办法是拉长采样周期到 50ms 或者 100ms,让每个周期内至少有几十个计数步进,速度曲线才平滑。

5.4 H743 特有的注意点

H743 这代芯片性能很强,但编码器模式下有些细节还是要留个心眼。

一是 D-Cache 问题。如果你用 DMA 去读取编码器计数或者做其他数据传输,开启了 D-Cache 之后会有缓存一致性问题,需要使用SCB_CleanDCache或SCB_InvalidateDCache等操作。但编码器测速直接读寄存器,完全不经过内存,所以不受 D-Cache 影响,这点不用慌。我见过不少人一听到 H743 就想着 DMA 读编码器,其实没必要,CNT 寄存器直接读,延迟极低。

二是引脚电压。H743 的大多数 GPIO 支持 3.3V 电平,如果你的编码器输出是 5V 电平(很多工业编码器模块都是 5V 供电),直接接上去可能损伤引脚。最好用电平转换芯片,或者选支持 5V 容忍的引脚,并且加上拉。H743 部分引脚的 FT(Five-volt tolerant)属性要看数据手册确认。

三是时钟树配置。H743 的定时器时钟源来自 APB1 和 APB2 定时器时钟,CubeMX 默认会根据你选的系统时钟自动计算,一般不用手动改。但如果你手工改过时钟树,对一下定时器时钟是不是你预期的频率。编码器模式不依赖定时器时钟计数,但数字滤波器的采样基准是定时器时钟,这个会影响滤波效果。

四是电源干扰。H743 开发板如果和电机驱动共用一个电源,电机启动瞬间电压跌落会导致芯片复位,编码器计数清零。这个和代码无关,但确实最容易让调试人员抓狂。建议电机电源和控制电源分开,或者至少用大电容稳定电压。

6. 扩展玩法:不在中断里也能知道自己转了多远

前面测速方案已经能解决大多数需求,但我还是想多提一句位置累计,因为做云台、机械臂、AGV 这类项目,速度和位置常常要同时用。

最省事的做法就是周期读取 CNT 后把 diff 累进一个int64_t变量:

static int64_t g_position = 0; void Encoder_Position_Task(void) { uint16_t now_cnt = __HAL_TIM_GET_COUNTER(&htim3); int16_t diff = (int16_t)(now_cnt - last_cnt); last_cnt = now_cnt; g_position += diff; }

这个方案不用开更新中断,靠周期性高频读取保证不丢计数。前提是读取间隔要足够密,不能超过 32767 个计数的时间。一般主循环跑得比这快,实际用起来也没什么问题。如果你需要绝对位置,并且不想依赖主循环的实时性,那就用前面讲到的更新中断累加法,两者可以并存。

还有一点,如果编码器线数不够,四倍频之后的分辨率还是不满意,可以考虑把减速比也利用起来。比如电机自带 30:1 的减速箱,在输出轴测位置,一圈的计数步进就是PPR × 4 × 30,分辨率立刻高了一个量级。算转速输出轴 RPM 时别忘了把减速比除回去。

这套编码器模式测速的方案,我前前后后在好几种芯片上改过,从 F103 到 H743,核心思路完全一致,区别只在时钟和引脚。H743 性能强,定时器多,跑起来可以很从容。你如果之前一直在用外部中断方案,我特别建议你花点时间把这套硬件解码思路吃透,测速逻辑会清爽很多,CPU 也能腾出来干真正需要计算的事。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/6 21:08:05

SOM v3.3.3实战指南:异常检测、特征压缩与聚类初始化

简介&#xff1a;本资源为SOM v3.3.3系统模块完整运行包&#xff0c;面向嵌入式开发、工业控制或Qt桌面应用调试人员&#xff0c;适用于需本地部署、二次开发或逆向分析该版本功能的中高级工程师。压缩包含275个文件&#xff0c;主体为196个Lua脚本&#xff08;承担核心逻辑与流…

作者头像 李华
网站建设 2026/10/6 21:06:27

Laya网络游戏开发:Socket通信机制与C#异步服务器实践

搞过Laya网络游戏的人都知道&#xff0c;客户端和服务器之间的通信&#xff0c;绕不开Socket。真正上手之后会发现&#xff0c;引擎自带的API只是冰山一角&#xff0c;从粘包拆包到C#回调处理&#xff0c;再到最头疼的端口被占用&#xff0c;每个环节都能磨掉你半天时间。这篇东…

作者头像 李华
网站建设 2026/10/6 21:03:12

Java分片加密上传与密钥管理实战:从AES-GCM到KeyStore

做安全级别要求比较高的数据管理平台时&#xff0c;大文件上传是个绕不开的坎。普通上传方案在数据机密性要求面前不够用&#xff1a;明文上传容易泄露&#xff0c;整体加密上传又扛不住网络中断&#xff0c;分片加密之后密钥怎么管又成了新问题。这篇文章我用Java示例完整讲一…

作者头像 李华
网站建设 2026/10/6 20:51:18

别再为护眼灯交智商税了!书客、明基、柏曼、霍尼韦尔等10款热门型号深度拆解:什么样的台灯最护眼?一篇讲透选购逻辑和隐藏坑

​最近后台问护眼灯的家长特别多&#xff0c;问题高度一致&#xff1a;想给孩子选一盏真正护眼的灯&#xff0c;怎么选才不踩坑&#xff1f;但现实很残酷。要么图便宜买低价网红款&#xff0c;孩子用不了多久就喊眼睛酸&#xff0c;回头一看参数全是虚标&#xff1b;要么冲着大…

作者头像 李华
网站建设 2026/10/6 20:48:59

华为OD机试真题 新系统 2026-09-16 JavaGoC【园区基站节能部署规划】

目录 题目 思路 Code 题目 题目内容: 一条直线型工业园区沿途分布着 n 个关键业务区域,其整数坐标由严格递增数组 positions 给出。 现有 m 个可用基站,每个启用基站使用相同的非负整数覆盖半径 k。每个关键区域都必须被至少一个基站覆盖。 为保证无缝漫游,相邻两个…

作者头像 李华
网站建设 2026/10/6 20:41:12

[LangGraph编译原理-02]面向通道定义Agent的状态

LangGraph编程基本围绕StateGraph进行&#xff0c;所以我们有必要对这个类型具有一个深刻的认识。这是一个泛型类型&#xff0c;四个泛型参数StateT、ContextT、InputT和OutputT分别表示状态、静态上下文、输入和输出类型&#xff0c;而且它们的类型都是一个StateLike类型。Sta…

作者头像 李华