Nie możesz wybrać więcej, niż 25 tematów Tematy muszą się zaczynać od litery lub cyfry, mogą zawierać myślniki ('-') i mogą mieć do 35 znaków.
 
 
 
 
 
 

26 KiB

AI 问询会话记录(2026-08-17 ~ 2026-08-23)


周一(2026-08-17):1 ms 轨迹规划审查

问:分析一下我的轨迹规划代码。
答:仓库里不是「一个规划器」,而是两套数学模型叠在一起,真正上硬件的只有 1 ms 时间域。运行路径是:段参数 → MotionBlockPlsrTrajectoryBeginPlsrPlannerConfigureFrameProfile → 1 ms 帧表(加速/匀速/减速)→ TIM6 每 1 ms 取下一帧写频率 → 硬件有限脉冲计数到 N 后停。PlsrPlannerBegin() 只做参数拷贝和合法性检查,不规划。执行器不管脉冲相位,只按 1 ms 取帧;硬件停靠的是 pulseCount,不是「帧全部播完」。也就是频率曲线按时间台阶走,停点按脉冲计数走。plsr_pulse_trajectory.c 更接近设计书里的脉冲域规划,但引擎里只拿它做 PlsrSystemMeasurePulseTrajectory() 计时,不参与输出。组块时 exitHz 只看加减时间,不看完成方式/后续方式,所以同向连续段也会先减到停止速度。

问:什么意思。
答:人话就是:规划器像按节拍一格一格调音量,执行器像按粒数停豆子。歌还没放到结尾,豆子可能已经数完;或者歌放完了,豆子还差几粒。长匀速、加减时间够时观感正常。短距离、刚起步、中途改速度时,尾速会偏高、加速段会缺一截、末尾会拖一小段慢爬。同向连续段也会先刹到停止速度再爬,因为组块没闻 sendMode

问:设计书写的和代码做的差在哪。
答:设计书 PLSR重构设计书_加减速与多段统一.md 要的是脉冲域 ∫f dt = kPlsrPlannerGenerate() 流式吐 STREAM_ITEM、量化误差按真实周期闭环、多段解析 NEXT_FRESH / NEXT_CARRY、STOP/EXT/改频统一从 appliedHzcommitted 重规划。现状是时间域 1 ms 台阶,没有 Generate 接口,只在 1 ms 帧上用 actualHz 求和,sendMode 写进段对象后基本不用,在线改频有了但 EXT 仍是 cut,tests/plsr_host 不测 planner/trajectory。PlsrPlannerBegin() 永远只回 OKINVALID,从不回 CLIPPED;clip 发生在 ConfigureFrameProfile() 之后,状态通道是死的。

问:1 ms 规划器结构上哪些值得留,真正的问题是什么。
答:值得留:规划/发布/执行分开(SELECT → PREPARE → PUBLISH → RUNNING),TIM6 只用段对象副本;轨迹是参数对象不是 N 个点的表;在线改频走 inactive bank;面积用量化后频率;短轮廓允许 clip。真正的问题:时间域曲线 vs 脉冲域停点(主矛盾);两套规划器数学不一致;S 曲线瞬时频率没有插值(64 级台阶);减速区第一帧被钉在峰值;候选搜索最后一帧估计不可靠;计算量配在 1 ms 任务里偏重;多段边界还没进规划;改频没扣 committed 预装载脉冲。

问:看一下这个时间域求根脚本(线性 / 分段二次 S / 正弦)。
答:脚本对每个脉冲 n 二分时间 (t_n),使 (N(t)=\int_0^{t} f(\tau),d\tau = n),再 (f[n]=1000/\Delta t_n)。这和设计书、脉冲域规划的定义一致,作为加速段连续金标这条是对的。时间折算也对:(T=|f_{end}-f_{start}|\cdot T_{conf}/f_{default})。最大问题是 S 曲线不是这份固件:脚本 (y(s)) 是分段二次 (2s^2) / (1-2(1-s)^2),plsr_pulse_trajectory.cplsr_planner.c 都是 smoothstep (3s^2-2s^3)。两端 (y(0)=0,y(1)=1,\int y=1/2) 都成立,平均脉冲数能对上,中间形状对不上。界面没有停止速度、总预算、三角/裁峰值、量化后 actualHz,不能当整段 MotionBlock 金标。对拍前必须先定:要对当前固件还是要对信捷/cla.py。


周二(2026-08-18):Codex 逆映射与段衔接

问:Codex 说轨迹对象多算、128 点少算,65 点逆相位锚点 + 一次 Newton。什么意思。
答:现在 S/正弦每个点在解逆问题:已知第 k 个脉冲,反求曲线相位,再求频率。正问题(相位→累计脉冲)容易,逆问题难,多半在 64 格积分表上二分/扫描,128 个点就炸。Codex 的招:建轨迹时先造两张 65 点锚点表(加速一张、减速一张),进度 0%、1/64、…、100% 对应曲线相位。以后每个点只做:算归一化进度 → 落在哪两个锚点 → 线性插值 → 最多 Newton 一次 → 相位→频率→BuildTimerSetting。从「每点大约 30 次二分」变成「查表+插值+最多一次校正」。不要直接存 128 个频率,因为 128 只是一批容量,下一批 firstPulse 不同、总脉冲数不同还要能算。RAM:65×4×2≈520 字节/对象。时间从实时 1 ms 挪到 OS 建段时算一次。

问:梯形、S、正弦的轨迹对象分开了吗。
答:算法分开了,对象没分开。当时是一个大结构体靠 curveMode 切换:0 线性用 (v^2) 递推,1/2 要逆相位,公式还不一样。但 accelerationNumerator 这种只有梯形才用的字段,S/正弦对象里也占着;128 点循环里还要反复 if (curveMode)。应先改成公共参数一份 + union{linear, smooth, sine}。好处:128 点入口按模式分派一次,循环里不再问 curveMode;以后只优化 S 不会把梯形搞坏。头文件后来已经拆过一刀,但 .c 里 128 点路径一度还是老写法。

问:这个方案的话,段与段之间衔接很慢了岂不是。
答:会,但只在一种接法下会。两件事别混:段里面每批 128 点会变快;段和段之间若下一段还没建好,电机会停。Codex 把几百微秒~1~2 ms 从出点挪到建对象。若仍在 PREPARE_SEGMENT 里做,S/正弦大约多等 1 拍。当前引擎本就是上一段完成后才 RESOLVE_NEXT → SELECT → PREPARE → PUBLISH,设计书 Bug 4 那个约 2.5 ms 低电平间隙,根子就是这个。梯形不用 65 点逆表,段间几乎不增。WAIT_TIME/换向方向延时、NEXT_FRESH 其实不在乎这 1 ms。真正在乎的是后续发送同向连续(NEXT_CARRY)。

问:怎么避免衔接变慢。
答:Codex 说「也可以只给当前段和预备段保存,压到约 1 KB」,意思就是双缓冲:当前段还在跑时,OS 空闲/提前几拍把下一段 Begin() + 65 点表算完;当前段最后几个脉冲时下一段对象已经热好,直接 publish / 切 cursor。交界只剩切对象、装第一拍。预计算放 Prepare、Prepare 放在段后 → 交界变厚;预计算放预备段、上一段还在跑时做完 → 交界可以更紧。你现在连 carry 都还没接上,每段还先减到停,那时候多 1 ms 建表不是主因。

问:为什么不要直接预存 128 个频率。
答:下一批 firstPulse 一变表就作废;总脉冲数一变(改频、EXT、剩余预算)又废。65 点逆表跟的是这条斜坡的形状(起速/峰值/终速/曲线),不是某一批 128 点。同向连续下一段只要参数定了,可以在上一段还没结束时就算好。这反而比「预存 128 频率」更适合衔接。


周三(2026-08-19):128 点耗时与正向表

问:组数据说明优化只完成一半:正弦对象 352 µs、128 点 2890 µs;S 型 319 / 2821。共同瓶颈是相位插值后 Newton + 64 位除法。想把进度→频率也前移。还能怎么优化。
答:诊断对。正弦和 S 已经打平,说明曲线公式不再是瓶颈。线性 128 点约 402 µs、每点约 3.1 µs 才是地板。热路径 CurveFastPointHz 每个点还在:逆表二分(512 格约 9 次比较)→ 插值出相位(一次 64 位除)→ Newton 最多 2 轮(速度、权重、有符号 64 位除)→ 两个 while 整数相位对齐 → 相位→频率 → BuildTimerSetting。1~4 是共同瓶颈。下一步「进度→频率」表方向对,但别用现在的每点算法去填 513 个频率锚点:22.6 µs×513≈11.6 ms,对象构建会从 0.3 ms 炸成十几毫秒。表必须正向填。头文件已经在往 entryFrequencyHz[513] 走,.c 里却还是 progressQ30 + 运行时 Newton。

问:OS 任务改计算哪些轨迹对象参数,TIM6 的 1 ms 里要怎么计算 128 个 setting。
答:分工一句话:OS 把轨迹做成查表就能用的对象;TIM6 每 1 ms 只抽出最多 128 个 setting,不准再解方程。OS 在 PREPARE_SEGMENT / 预备段 / 改频重规划时算一次:三种曲线都要的公共几何(start/peak/end、三段脉冲、时间、curveMode);梯形只填 (v^2) 分子,不建相位表;S/正弦正向铺进度均匀频率表(建议先 129 点)以及 entryStepQ30/exitStepQ30。TIM6 先处理 cut/完成/EXT,再读游标;入口按 curveMode 分派一次;S/正弦:p += stepindex = p >> shift,插值,同频则拷贝 setting。这一拍 n = min(128, 剩余脉冲, 队列空位)。表还没瘦到 400 µs 之前,128 点仍放 OS 预计算,TIM6 只消费现成数组。2.8 ms 那种东西不准进 1 ms 中断。

问:启动的时候 S 曲线和正弦的轨迹路径已经算好了吗。为什么算得这么慢。
答:不是。启动时算好的是「这条坡长什么样」(起/峰/终、三段脉冲数、大约一百来个频率锚点),不是「每一个脉冲已经排好队」。整段几千、几万个 PSC/ARR 不会在启动时算完:脉冲总数随段变,中途改频/EXT 一变后面全作废,RAM 也扛不住。慢的不是启动把整条路算完了,是每个点还在解很难的逆问题。对象 319~352 µs 可以忽略;128 点 2821~2890 µs,每点约 22 µs,比线性贵大约 7 倍。正弦已经和 S 一样慢,正好说明瓶颈不是 sin/cos。

问:所以这个方案是不是不好。
答:方向对,做到现在这一半不好。停在「相位均匀进度表 + 热路径 Newton」是累赘:结构更胖、速度几乎没变。只有热路径变成「进度累加 → 移位插值频率 → 同频拷贝 / BuildTimerSetting」,并且 128 点掉到大约 400~700 µs、对象仍 <1 ms,才配进 TIM6,才配预备段。撤当前热路径里的 Newton,留对象和分派;换成正向铺进度均匀频率表(先 129 点);timer 同频拷贝,否则永远打不穿线性那 402 µs 地板。

问:Begin 正向积分→P(x)→单调游标重采样→边界时间→直接频率表;128 点 cursor 递推→O(1) 索引→插值→timer。能用吗。你确定能满足时间要求吗。我改的代码你看一下。
答:可以用,而且比逆相位+Newton 更接近该做的那一刀。必须写对三件事:表的横轴是脉冲进度不是相位 x(按 x 均匀建表,按脉冲查又要二分);相邻 Δt 换频率要乘「这一格有多少脉冲」,写成 (f=1000/\Delta T) 会偏高大约 (N_{ramp}/M) 倍;正向游标是 O(表长) 不是 O(总脉冲)。时间上不能 100% 打包票:128 点 400~700 µs 高把握(对照线性 402 µs 还含 64 位 sqrt);Begin <1 ms 仅当闭式/现成正弦表、M=128/256、无 RK4/libm;TIM6 整拍硬吃 128 个不确定,先 OS 预热。你这版对象已是频率表、Newton 离开 128 点、同频拷贝 setting,但 BuildCurveMap 对 513 点各做 RK4(约八千次 64 位除),CurveTableHz 还要整数 sqrt。S 还改成了 cla.py 分段二次。N=1 用了 double。应扳回均匀 P(x)、先 129 点、禁止热路径 sqrt。


周四(2026-08-20):架构取舍与发波分支

问:我觉得我的架构有问题,你觉得呢。
答:有问题,而且不是 S 曲线慢那么点事,是坐标系和节拍没对齐。电机按脉冲活,控制按 1 ms 活。规划做了两份:plsr_trajectory(1 ms)在跑,plsr_pulse_trajectory 在 Measure。TIM6 现在是 1 ms 采样器,你要的 128 个 setting 是脉冲队列生产者,两种执行器塞进一个中断。段是「做完再备课」:RESOLVE → SELECT → PREPARE → PUBLISH 全在上一段停了之后。sendMode 进了段对象,组块仍永远 exitHz = stopSpeed。改频有 inactive bank,没扣 committed;EXT 是切断。优化发生在错误的所有权上:热路径该是执行器装下一项,却在给测量用的 BuildBatch 堆 RK4/sqrt。该留:规划对象/发布副本、MotionBlock 不读现场、CLIPPED 当短轮廓、线性 (v^2)、硬件预装载。应在 A(承认 1 ms 命令率)和 B(改成脉冲命令率)里选一个,不要混用。

问:现在切了一下分支,仔细分析这个分支里是怎么发波的。
答:当前分支 codex/plsr-2026-minimal 已经是脉冲域规划 + 软件队列 + 硬件按 run 发波,不是 TIM6 每 1 ms 改一次频。任务态把轨迹提前切成「同频重复 N 次」的 run,推进 1024 项环形队列(双 bank)。PWM 按当前 PSC/ARR 吐方波;TIM9/TIM12 数脉冲;当前 run 结束前一拍(CC1)预装下一 run,结束时(UIF)提交。IRQ 里不算规划。PlsrPulseTimerIrqSTREAM / STEP_TABLE / STOPPING 下直接 return。启动时 PrepareShortProfileTakeRun 第一 run → QueueBegin → Fill 到约 400 项 → StartCountedStreamPrepared。之后 1 ms 任务 Fill:水位低于 800 就补,每一拍最多 200 项。队列空且规划还没完成:借用最多 2 ms / 200 脉冲,按最后一个 setting 继续发,任务之后把同样数量的规划脉冲还债(推进 planner 但不入队)。

问:哪种方法好一点。没听懂。
答:这个分支更好。发波该按脉冲走,不该按 1 ms 走。上一套:规划按时间切台阶,硬件按脉冲个数停,短段/起步/改频一定会偏。现在这套:规划吐 {setting, N},换挡发生在完整脉冲边界,1 ms 任务只负责把未来几毫秒填进队列,IRQ 不准做规划数学。代价:冷启动要先填约 400 项才出第一波;斜坡 100 kHz 时 1 ms 要能产约 200 项;AB 还是旧逐脉冲 IRQ。人话:好的方法是先算每个脉冲多长,再按这个发;差的方法是每毫秒改一次速度,另外再数脉冲停。菜单是 {这一档周期, 连发 N 个};硬件发完这一页再翻下一页,翻页发生在一个完整波结束的时候。

问:现在的脉冲点对应的频率是怎么算的。我问你在哪算的。拆开揉碎和我讲讲。
答:每个脉冲频率 = 这个脉冲该占多长时间的倒数,不是「这一毫秒该跑多快」。第 k 个脉冲要满足:从开始到这个脉冲结束,速度曲线底下的面积刚好等于 k 个脉冲。(f[k]=1/(t_k-t_{k-1})),再收成 PSC/ARR。Begin 不算每个点,只切加速 N1 / 匀速 N2 / 减速 N3。斜坡上把总面积切成 N 等份;目标面积加一格;找时间边界 x(前两个脉冲二分,后面用斜率推);(\Delta x = x - )已经按真实频率走过的时间;(f_{req}=)总面积((N\cdot\Delta x));BuildTimerSetting;用 actualHz 把时间账往前推(闭环,量化误差不累计)。算的地方:plsr_planner.cPlsrPlannerTakeRampStep()(约 468 行)是斜坡频率核心;匀速在 TakeStep()(约 553 行)直接用 peakHzGenerate(约 701 行)是对外入口。plsr.cTakeRunLimited / Fill 只取出来入队。IRQ 不算。

问:流程和数据流。你觉得复杂吗。OS 是不是一直在算频率帧存到队列里。一拍算几个。计算速度怎么样。
答:总流程:解析本段 → PrepareShortProfile/PlannerBegin(还没有每个点的 Hz)→ TakeRun/Generate 算出下一项 → 灌队列 → 硬件开波 → 同时 1 ms 补队列、硬件按 run 翻页。数据:配置/现场合成 MotionBlock(不读 Modbus)→ Begin 得到三段脉冲数和 peak → Generate 吐 STREAM_ITEM → 入队成 PROFILE_ENTRY → CC1 预装、UIF 提交。三种 Hz 别混:配置目标、requested、actual。复杂在该复杂的地方(队列/双 bank/改频/handoff),公式本身就是面积÷时间。对,OS 一直在算 {周期, N} 存进队列,但不是 1 ms 时间帧。一拍最多 200 项(启动 400),堆到约 800 就停;斜坡一项常是 1 个脉冲,匀速一项 N 可以很大。速度:匀速一项约 1~3 µs;线性斜坡约 5~15 µs;S/正弦约 8~25 µs。一拍 200 项线性大约 1~3 ms,可能比 1 ms 墙钟还长,所以靠 8 ms 库存和 2 ms 借债。没有板上逐项 DWT,不能装测过。


周五(2026-08-21):队列节点与补货

问:现在是 OS 任务里算好(频率,几个点)放到 1024 的队列里,然后 UIF 不断去消费这个队列是吧。
答:骨架对,差两点。不是每个 UIF 都从队列拿一项:计数器盯当前这一 run,还差 1 个脉冲(CC1)才取下一项并预装 PSC/ARR;这一 run 的 N 个发完(UIF)才提交、换成下一项的 N。斜坡 N=1 时几乎每个脉冲翻一页;匀速 N=200 时发 200 个相同的波才吃队列一次。IRQ 里只拿现成项,不算频率。

问:队列的元素叫什么。算好 {这一档频率(PSC/ARR), 连续发几个脉冲} 怎么形容。脉冲 run 是怎么算的。
答:每一格叫 PLSR_PROFILE_ENTRY。整条队列叫 PLSR_PROFILE_QUEUE(1024 项,双 bank)。规划器吐出来还叫 PLSR_STREAM_ITEM,入队时加上 startsNextSegment。最短说法:一项 = 一档同频脉冲,「这一档的周期,连发 N 个」。不要说频率帧,要说脉冲 run / 同频项。run 不是单独一套公式:先算出下一个脉冲该用哪个周期,再决定这个周期连发几个。斜坡 repeatCount 通常=1;匀速先给剩下的全部,再被 PlsrProfileRunLimit 切成大约最多 2 ms;入队时量化后相同的相邻项还会再合并。

问:计算函数和塞队列函数在哪。每一个节点都放了什么。
答:算在 PLSR/Src/plsr_planner.cBegin(592)切三段,StartRamp(398)切面积,TakeRampStep(468)算斜坡 Hz,TakeStep(553)分派,Generate(701)对外入口,BuildStep(449)量化。塞在 PLSR/Src/plsr.cTakeRunLimited(1998)调 Generate 再按约 2 ms 切 N,Fill(3125)1 ms 循环算+写,AppendLocked(2624)真正写入、同档合并。IRQ 消费:PlsrExecTakeCountedRunIrq(4617)只读。节点只有四样:setting(PSC/ARR/actualHz)、requestedFrequencyHz(公式 Hz,发波不用)、repeatCountstartsNextSegment。没有脉冲序号、段号、面积。

问:计算脉冲 run 到塞进队列到消费的流程,函数调用链。
答:启动:BeginSegmentOutput(3680)→ PrepareShortProfileTakeRun 第一 run → QueueBeginFill(400)StageCountedHandoff → 再 Fill → StartCountedStreamPrepared。运行:PlsrPoll1ms(6028)→ 水位≤100 时预算改回 400 → Fill(800, 200或400)StageCountedHandoff → 再 Fill。Fill 每一圈:拷出 producerProfile → TakeRunLimited → 核对 generation/bank/epoch → 还债则推进 planner 不 Append,否则 AppendLocked。消费:TIM1_BRK_TIM9 / TIM8_BRK_TIM12HandleCounterIrq → CC1 PrepareNextStepIrqTakeCountedRunIrq 预写 PSC/ARR;UIF FiniteCounterIrq 提交换档。

问:解释 Poll1ms 里补队列那一段。后面就没有计算了是吧。
答:那段是 1 ms 任务后半截:先处理 START/STOP/CLEAR,再 ExecServiceCountedEvents(段边界必须在计数同步之前消费,否则会把下一段脉冲算进旧段),再位置掉电,再 ServiceCountedExecutor(这段波若已经发完就 HandleBoundary,本拍不再 Fill)。还在跑才看水位:≤100 则本拍预算从 200 加到 400。第一次 Fill 补当前段到 800(要 active 且 generatorComplete==0)。第二次 StageCountedHandoff 把下一段预热的第一/第二 run 接到队尾,再 Fill 一次。活队列「给当前段流式补 run」到两次 Fill 基本结束。后面还有:ServiceTimedStartPreparation(空闲 bank 预热下一段)、延时到 0 的 BeginSegmentOutput、动态改频 ReplanPulseDir。不是空白,是改干别的计算。


周六(2026-08-22):Fill、欠债、匀速切段

问:为什么会欠债。
答:欠债 = 队列空了,规划还没跟上,硬件先用上一档频率多打了几下。IRQ 要下一项时 read==write,但 generatorComplete 还是 0。常见原因:斜坡+高速(100 kHz 几乎一个脉冲一项,每毫秒吃 100 项,任务一拍最多算 200 项还可能超时);这一拍先忙了改频/handoff;Fill 在门外算的时候 IRQ 把剩下的格子吃光了。空了就停波会突然断一截。所以用 lastSetting 再连发最多约 2 ms(或 200 个脉冲),underrunDebtPulses += Ngeneration++。下一拍 Fill 发现有债:照样 Generate 把规划器往前拨 N 个,不 Append,减债。还清之前不能 generatorComplete=1。总脉冲数仍然要准:借的是「提前用末频打了规划里后面那 N 个」,不是额外赠送。波形上可能平一段末频(最多 2 ms)。

问:Fill 这段 while 在干什么。TakeRunLimited 在干什么。pendingRepeats==0 是什么情况。
答:Fill 每圈:关中断看还要不要算(不 active / 已 complete / 深度≥目标 / 预算用尽则成功返回)→ 先扣预算 → 记下 generation/epoch/债 → 拷出 profile → 开中断 Generate → 再关门核对 bank/世代/是否已满,对不上就 continue → 写回 profile → 有债减债不入队,没债 Append → 吐完且无债则 generatorComplete=1。TakeRunLimited:从规划器手里掰下下一项。pendingRepeats==0 才 Generate 拿新的一档(可能 N 很大);然后 N 取 min(本档剩余, 本段剩余, maximumRepeats, 约 2 ms 上限)。还债时 maximumRepeats=当前债。人话:规划器有时一次给整根同频香肠;pendingRepeats 是还没切出去的长度;等于 0 才再要一根。

问:假设加速 1000 个脉冲,匀速 5000 个,减速 1000,现象是什么样。
答:加速 1000:几乎一格一个波,频率一格一变,pendingRepeats 几乎每次都是 0,每圈都 Generate,队列大约 1000 格(量化相同才会合并)。匀速 5000:频率不变,但按约 2 ms 切开——1000 Hz 时一格约 2 个,约 2500 格;8000 Hz 时一格约 16 个。Generate 只发生一次,pendingRepeats 从 5000 往下减。示波器是很长一串等周期方波,看不出切了多少刀。减速 1000:再变回一格一波,频率下降。高速时盘子主要被斜坡撑满;匀速反而很省格子。

问:为什么匀速要切这么多次。是不是队列里放 8000,8000…8500 个,或 (8000,8500)、(7999,1)、(7998,1)。入队数量是动态的吗。
答:因为硬件一旦开始打这一项,就要把这一项的 N 个全部打完,中途改频也插不进这一项的肚子里。一次 8500、1000 Hz 就是 8.5 秒同一档,改频要等 8.5 秒。所以上限约 2 ms。多次的是 TakeRunLimited 进进出出,不是规划器算 4250 遍频率——频率只算 1 次。不是 (8000,8500) 一格吃完;不是频率 7999、7998 往下掉(那是案板上 pendingRepeats 在减,不进队列);也不是 8500 个光秃秃的 8000。实际是很多格、频率相同、每格 N 大约是 2 ms 能打多少个,例如 8000 Hz 时 (8000Hz, 16) 重复约 531 格加余数。入队多少、每一格里装几个都是当场算的:min(本档剩余, 本段剩余, 2 ms 能打几个, 还债则当前欠多少);这一拍塞几格也随水位和预算变。


周日(2026-08-23):Generate、规划上下文、会话记录

问:解释 PlsrPlannerGenerate。局部变量含义讲了吗。
答:这是规划器对外窗口:一次最多吐 capacity{setting, 请求频率, N},能合并的叠到上一项上。TakeRunLimited 每次 capacity=1,所以几乎总是吐 1 项就回去。开头指针空/active==0/capacity==0 则返回 0。循环条件:还能写,且 generatedPulses < pulseBudget。每圈 TakeStep 要下一步;失败则 active=0 并 break。若本次已有上一格且 setting 与 requestedHz 都相同且 N 不溢出,则只 repeatCount +=;否则新开一格、produced++。然后 generatedPulses += repeatCount(按脉冲不是按项数)。吐完预算则 active=0。返回 produced。局部变量:setting 是量化后 PSC/ARR;requestedFrequencyHz 是公式 Hz;repeatCount 是这一档连发几个;produced 是本次写了几项。capacity=1 时匀速 5000 会整项放在 output[0],切开 2 ms 是后面 TakeRunLimited 的事。

问:STREAM_ITEM 是什么。和队列节点差在哪。
答:PLSR_STREAM_ITEM 是规划器吐出来的一项,还没进 1024 队列:settingrequestedFrequencyHzrepeatCount。没有 startsNextSegmentpendingItem 就是一个 STREAM_ITEM。PROFILE_ENTRY 在这上面再加段边界标记,并可能把 N 切短。

问:TakeStep 是计算核心吗。
答:算「现在该出哪一档」的分派核心是它;算「这个斜坡脉冲多少 Hz」的核心不是它。它只看 generatedPulses 落在加速/匀速/减速哪一段,第一次进斜坡时调 StartRamp,加速/减速交给 TakeRampStep,匀速 repeatCount=剩下的匀速个数BuildStep(peakHz)。几何核心是 Begin;量化是 BuildStep

问:给 PLANNER_CONTEXT 带注释(含行尾 // 格式)。
答:按块输入、三段几何、已吐脉冲、当前斜坡账本排。block 是本块输入;phasePulses 是起点相位;startHz/peakHz/endHz 是裁剪后的实际端点;entry/steady/exitPulses 是三段脉冲数;generatedPulses 是已交给调用方的脉冲总数。ramp* 只在当前加速或减速内有效:相对序号、总脉冲、起终点、折算时间、上一档 actualHz、Q32 总面积/每格面积/目标面积/边界/已走时间/上一相位步长、第 1/2 个精确边界、余数及累加器。rampKind:0 无斜坡/匀速,1 加速,2 减速。clipped 表示预算不足已裁但仍可执行。active 表示是否还可 Generate。Q32 表示把斜坡归一化时间 0~1 放大到 (2^{32})。