Activiti事件日志:从原理到实战的流程排错指南
1. 为什么要盯上Activiti的事件日志如果你搞过工作流引擎一定遇到过这种场景一个流程实例卡在某个节点任务列表里什么都有但流程就是不动或者某个监听器逻辑只在一部分实例上生效另一部分实例悄悄跳过再或者明明调用了completeTask但流程下一步的走向和预期完全不一样。这种时候翻数据库表、加断点、看日志通常能排查出大概方向但效率很低尤其是流程链路复杂、网关分支多的时候光靠打断点你可能得来回重启好几次应用才能定位到问题。我最早接触Activiti的时候也犯过这个毛病遇到问题先怀疑引擎本身怀疑是不是并发回写导致数据不一致怀疑是不是数据库锁表。后来真正把事件日志用起来才发现引擎其实把每一步运行时行为都暴露在事件流里了只是我们平时没去看。所谓事件日志简单说就是Activiti在运行过程中对外广播的各种事件记录——任务创建了、流程启动了、实例结束了、变量更新了、任务完成了这些都会触发引擎内部的事件机制。如果你开启了事件日志处理这些事件会经过内存缓存后统一写入引擎日志表留痕供后续分析。这篇文章不是官方文档的搬运也不是把某个配置项抄一遍而是从我实际用Activiti 5/6/7这几个版本折腾事件日志的经验出发讲清楚它到底解决什么问题、原理是什么、真实数据长什么样、以及在排错和二次开发里怎么用它。比较适合正在学工作流引擎、或者项目里已经用了Activiti但对内部运行机制还是黑盒的开发者。不管你是刚接触Activiti的初学者还是已经写过几个流程的熟练工这篇文章都值得读下去。2. 事件系统的基本盘引擎到底在广播什么2.1 事件类型不是只有创建完成这两类Activiti的事件机制不是单一通道而是一套围绕引擎生命周期的事件体系。理解它的切入点其实是ActivitiEventType这个枚举类里面定义了几十种事件类型。常见的像ENTITY_CREATED、ENTITY_UPDATED、ENTITY_DELETED这类实体级事件还有TASK_CREATED、TASK_ASSIGNED、TASK_COMPLETED这类任务级事件以及PROCESS_STARTED、PROCESS_COMPLETED这类流程级事件。很多初学者以为只要实现了ActivitiEventListener并在配置里注册这个监听器就能收到所有事件。实际并不是这样。监听器是否可以接收所有事件取决于你在注册时的isFailOnException和事件类型过滤条件。如果只给监听器指定了某个事件类型比如只监听TASK_CREATED那其他事件即使触发了这个监听器也收不到。这个设计本身是为了避免每个监听器都处理海量事件导致性能下降。事件日志则不同。开启后引擎会把所有事件都写入日志不管你有没有监听器。这个全量记录的特性恰好是排错时最需要的——因为你不知道该在哪一步出问题只有全量留痕才能事后回溯。2.2 事件分发链路上的关键角色从源码角度看事件的分发链路大致是这样的引擎内部有ProcessEngineConfigurationImpl这个核心配置类它持有一个事件分发器ActivitiEventDispatcher。当你构建流程引擎时可以通过setEventListeners注册自定义监听器也可以在流程定义XML的executionListener或taskListener里配置内联监听器。引擎运行时比如任务完成、流程跳转、变量更新都会调用事件分发器把对应的ActivitiEvent广播给所有匹配的监听器。事件日志就是在这一环接进来的。Activiti提供了一个专门的EventLogger类它本身就是一个ActivitiEventListener会在事件分发时被调用把事件翻译成EventLogEntry对象放到一个ThreadLocal缓存队列里最后通过EventFlusher批量写入数据库。这段链路看起来不复杂但理解它有一个实际好处你可以根据自己的需要在链路的不同位置插入自己的逻辑。比如在EventLogger这个监听器之外再套一个自定义监听器专门过滤出你关心的流程定义ID然后打点上报或者在EventFlusher里把日志写到专门的审计系统而不是引擎日志表。2.3 配置事件监听器的三种姿势先说结论再解释原理。配置事件监听器大概有这三种常见姿势第一种在processEngineConfiguration里配置自定义监听器ProcessEngineConfigurationImpl config new ProcessEngineConfigurationImpl(); ListActivitiEventListener listeners new ArrayList(); listeners.add(new MyEventListener()); config.setEventListeners(listeners);第二种用activiti.cfg.xml来配置适合Spring集成场景bean idprocessEngineConfiguration classorg.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration property nameeventListeners list bean classcom.example.MyEventListener / /list /property /bean第三种在流程定义BPMN文件里给具体环节装配监听器userTask idapprovalTask name审批 activiti:taskListenercom.example.ApprovalListener /这三种方式的使用场景不一样前两种是全局监听适合统一收集事件比如日志、监控第三种是局部监听适合在某个特定环节做额外处理。事件日志走的是前两种的路线它由引擎配置统一启用。2.4 监听器里的两个关键参数配置监听器的时候有两个参数要特别留意。第一个是isFailOnException。这个参数决定监听器执行抛异常时引擎是继续往下走还是直接抛错。默认是false即监听器出错不影响主流程。但如果你希望监听器异常能中断流程比如某个关键审计逻辑失败就不允许流程继续那就要设成true。我见过一个例子某团队自定义监听器里做了通知推送推送超时导致整个流程任务完成操作失败排查了半天才发现isFailOnException被设成了true。这类问题在事件日志里一般能直接看到异常栈但如果你没开事件日志可能要翻应用日志找半天。第二个是事件类型过滤也就是ActivitiEventListener里的onEvent方法引擎会传入具体的ActivitiEvent你需要自己判断事件类型或者在注册时用EventListenerSupport之类的封装来过滤。事件日志机制不依赖这个参数它会接收所有事件因为它要对全量事件留痕。3. 事件日志的启用与落库原理全量留痕是怎么做到的3.1 打开开关只需一个配置项Activiti的事件日志默认是关闭的。打开它的方式非常直接在ProcessEngineConfigurationImpl上设置enableEventLog为trueProcessEngineConfigurationImpl config new StandaloneProcessEngineConfigurationImpl(); config.setJdbcUrl(jdbc:h2:mem:activiti;DB_CLOSE_DELAY1000); config.setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE); config.setEnableEventLog(true); ProcessEngine processEngine config.buildProcessEngine();如果是Spring Boot项目可以在application.properties或application.yml里配置spring.activiti.enable-event-logtrue不过不同版本的Activiti对Spring Boot的配置前缀有差异5.x时代可能要自己装配ProcessEngineConfiguration7.x版本用spring.activiti这个前缀通常都对。需要注意的是开启事件日志会在每个流程实例的每个操作上产生额外开销所以生产环境要评估一下日志量不能无脑开。3.2 EventLogger和EventFlusher的分工事件日志的实现在org.activiti.engine.impl.event.logger包下。核心类就两个一个EventLogger一个EventFlusher。EventLogger负责翻译——把引擎分发出来的ActivitiEvent转换成EventLogEntry。这个转换过程不是简单地把事件对象丢到数据库而是把事件的类型、时间、流程实例ID、执行ID、流程定义ID、业务键等字段抽出来拼成一个可序列化的数据对象。EventFlusher负责刷盘——EventLogger收到事件后不直接写库而是先放进一个内存缓存等缓存积累到一定数量或者触发某个条件时再由EventFlusher批量写入数据库。为什么要搞缓存批量写直接来一次事件写一次库不行吗如果真这样做每个流程节点跳转都会产生数据库写操作频繁IO会拖慢整个引擎的事务响应时间。批量化之后写入次数大幅减少对主流程的影响也就小很多。但代价是日志写入存在延迟极端情况下应用宕机缓存里还没刷盘的事件会丢失。这个取舍在绝大多数场景下是值得的毕竟事件日志主要用于排错和分析不是强一致审计。3.3 日志表ACT_EVT_LOG的结构事件日志落库后对应的表是ACT_EVT_LOG。这张表的字段不多但每个字段都有明确的用途字段名说明LOG_NR_自增主键TYPE_事件类型比如ENTITY_CREATED、TASK_COMPLETED、PROCESS_STARTEDPROC_DEF_ID_流程定义IDPROC_INST_ID_流程实例IDEXECUTION_ID_执行实例IDTASK_ID_关联的任务ID比如任务事件里的任务标识TIME_STAMP_事件时间戳USER_ID_操作人ID比如完成任务时指定的操作人DATA_事件数据以JSON格式存储的扩展属性LOCK_OWNER_锁持有者通常和事务锁机制相关LOCK_TIME_锁时间IS_PROCESSED_是否已处理标记其中DATA_字段最值得关注它保存了事件发生时的上下文数据。比如TASK_COMPLETED事件DATA_里会包含任务的表单变量内容、任务定义Key、任务名称等ENTITY_UPDATED事件则包含更新前后实体类的关键字段变化。排查问题的时候80%的线索都藏在DATA_这段JSON里。3.4 一个容易被忽略的点事件日志≠历史数据刚开始用事件日志的人容易把它和Activiti的历史表混为一谈。其实两者定位完全不一样ACT_HI_*系列历史表记录的是流程实例、任务、活动实例的最终状态和关键明细是引擎内置的审计数据和流程定义、运行时状态强关联。ACT_EVT_LOG事件日志表记录的则是引擎运行过程中的事件轨迹包含的字段更偏事件流而不是实体状态快照。举个例子。一个流程实例发起后任务创建、任务完成、流程结束这三步在ACT_HI_TASKINST里只体现任务最终状态的变化但在ACT_EVT_LOG里则是好几条独立事件记录每条记录的时间、操作人、数据载荷都是独立的。所以你要分析某个时刻发生了哪些操作事件日志更合适要分析某个任务最终是谁处理的、耗时多久历史表更合适。4. 真实数据长什么样一个模拟流程的事件全记录4.1 搭建最小测试场景光看字段定义还是隔靴搔痒。我带你们看一组真实的事件日志数据这样才能直观知道这张表带给你的信息量有多大。假设我在模拟项目X里部署了一个最简单的请假审批流程流程节点是发起申请startEvent→ 部门审批userTask→ 人事审批userTask→ 结束endEvent。部署流程定义后启动一个流程实例然后连续完成两个审批任务最后流程结束。在这个过程里事件日志记录什么第一步启动流程实例。这会产生ENTITY_CREATED事件DATA_里通常会包含流程实例ID、流程定义ID、启动人信息。如果是通过runtimeService.startProcessInstanceByKey启动的启动人ID一般取自当前登录用户如果没设置就用null。第二步任务创建。部门审批任务创建时会触发TASK_CREATED事件。DATA_里会包含任务ID、任务定义Key、流程实例ID等字段。如果你是动态创建任务或者任务带有候选组这些信息也会体现在事件里。第三步完成任务。调用taskService.complete(taskId)后会触发TASK_COMPLETED事件同时因为任务完成可能导致流程继续向下跳转所以还会触发ACTIVITY_COMPLETED事件如果走到人事审批节点又会触发新的TASK_CREATED事件。这里要注意事件不是单条发生而是一串连锁事件排错时要注意看事件的时间序列。第四步流程实例结束。最后一个任务完成后流程走到结束节点触发PROCESS_COMPLETED事件。此时事件日志记录了整个流程生命周期的收尾。4.2 实际查询一下事件记录模拟环境跑完这些操作后我们可以用SQL直接查ACT_EVT_LOG表SELECT LOG_NR_, TYPE_, PROC_INST_ID_, TASK_ID_, TIME_STAMP_, USER_ID_ FROM ACT_EVT_LOG ORDER BY LOG_NR_ ASC;查出来的结果大概是这样LOG_NR_TYPE_PROC_INST_ID_TASK_ID_TIME_STAMP_USER_ID_1ENTITY_CREATED2501null2024-05-12 10:00:01zhangsan2TASK_CREATED250127052024-05-12 10:00:02zhangsan3TASK_COMPLETED250127052024-05-12 10:05:36lisi4ACTIVITY_COMPLETED2501null2024-05-12 10:05:36lisi5TASK_CREATED250128092024-05-12 10:05:36lisi6TASK_COMPLETED250128092024-05-12 10:12:02wangwu7PROCESS_COMPLETED2501null2024-05-12 10:12:02wangwu这组数据能说明很多问题。比如部门审批任务耗时是5分34秒从TASK_CREATED到TASK_COMPLETED的时间差人事审批任务耗时6分26秒。如果某个任务卡了三天没完成事件日志里就能看到这个任务只有TASK_CREATED记录没有TASK_COMPLETED记录那就说明它一直挂在待办状态。再比如如果第3条和第5条记录之间的时间戳有异常比如TASK_COMPLETED和下一个TASK_CREATED间隔很远那可能是流程图里在部门审批和人事审批之间有一个定时边界事件或者存在异步延续处理环节。在排错时这种时间线上的缝隙往往就是问题所在。4.3 DATA_字段里藏着的细节DATA_字段是JSON格式里面的信息更细。拿TASK_COMPLETED事件举例解析出来大概是这样{ class: org.activiti.engine.impl.persistence.entity.TaskEntity, id: 2705, name: 部门审批, taskDefinitionKey: deptApprove, assignee: lisi, processDefinitionId: leaveProcess:1:2500, processInstanceId: 2501, variables: { days: 2, reason: 家里有事 } }这里面最有用的是variables字段。你完成审批时传入的流程变量都会出现在这里。排查流程变量怎么没传过去这类问题时直接查这条事件记录就能确认变量到底是没写入还是在某个环节被覆盖了。另外entity类型字段还会记录实体的序列化信息。如果一个事件涉及实体更新比如任务被修改assigneeDATA_里会有更新前后的assignee变化。这意味着事件日志不仅能告诉你发生了修改还能直接告诉你从谁改成了谁。5. 事件日志的实战排错一次典型的流程不走排查链路5.1 问题现象与初步猜测这里分享一次我在模拟项目X里实际排过的错算是事件日志用得最值回票价的场景。当时有一个采购订单审批流程节点很多其中有一个总监审批节点后面跟了一个排他网关网关上配置了两个条件分支订单金额大于5000走财务复核否则直接走生成采购单。问题现象是部分订单流程走到总监审批完成后既没有创建财务复核任务也没有走生成采购单整个流程实例就这样悬着不出错也不前进。照理说排他网关必须满足至少一个条件才能流转如果两个条件都不满足引擎是会抛异常的。但这里没有异常又确实没有后续任务创建这就很诡异。一开始我怀疑是网关条件判断的数值问题可能流程变量没传对。于是先查了运行时任务表、历史变量表确认总监审批节点输入输出变量都正常订单金额确实是8000按条件应该走财务复核。那问题就出在网关判断之后到任务创建之间的某个环节。5.2 事件日志介入后的定位过程此时翻开ACT_EVT_LOG按流程实例ID筛选把时间线上的事件一条条拉出来看SELECT LOG_NR_, TYPE_, TIME_STAMP_, DATA_ FROM ACT_EVT_LOG WHERE PROC_INST_ID_ 5008 ORDER BY LOG_NR_ ASC;看到的事件序列是TASK_CREATED总监审批TASK_COMPLETED总监审批ACTIVITY_COMPLETED总监审批节点完成SEQUENCE_FLOW_TAKEN网关前的连线被走过ACTIVITY_COMPLETED网关已经执行完成然后就没有后续了。这说明网关确实被执行了但没有产生财务复核任务的TASK_CREATED事件。有意思的是正常情况下网关执行完要么走左分支要么走右分支总归会有SEQUENCE_FLOW_TAKEN事件。这里连SEQUENCE_FLOW_TAKEN都没有说明网关确实选择了某个分支但那个分支没继续走下去。我再去查那个分支目标节点财务复核的相关配置。看了BPMN文件才发现原来财务复核节点的activiti:asyncBeforetrue被误配成了activiti:asynctrue而那个节点前的连线又配了一个activiti:executionListener监听器里抛了个NullPointerException。因为事件分发器默认isFailOnException为false所以异常被吞掉了但异步任务也没生成成功流程就卡在那里。这里的关键是事件日志明确告诉我网关执行完了但没有后续连线动作缩小排查范围而正常跑日志应用控制台不一定能发现这个问题因为异常被吞了。实际触发这个问题的原因是我在模拟环境里给某个监听器传入了一个可选参数那个参数在部分流程实例里是null导致的空指针。5.3 修复后的事件日志验证定位到问题后修复监听器的空指针判断重新部署流程定义用同一个订单数据重新发起流程实例。这次翻开事件日志看到的事件序列变成TASK_CREATED总监审批TASK_COMPLETED总监审批ACTIVITY_COMPLETED总监审批SEQUENCE_FLOW_TAKEN网关前连线ACTIVITY_COMPLETED网关SEQUENCE_FLOW_TAKEN网关到财务复核的连线TASK_CREATED财务复核事件流的完整性和一致性立刻就对了。而且日志里还能看到ENTITY_UPDATED事件说明异步任务也正常生成了。整个排查过程如果没有ACT_EVT_LOG表我大概率要在代码里加一堆日志然后反复重启应用根本不可能在半小时内定位到问题。5.4 排错时的一条实用操作路线从这次排错我总结了一套用事件日志排查流程问题的操作路线后来基本成了固定套路第一步按流程实例ID从ACT_EVT_LOG查出全量事件序列按LOG_NR_排序。第二步对比事件序列与期望流程路径找出断点。重点看是否存在ACTIVITY_COMPLETED之后没有SEQUENCE_FLOW_TAKEN或者SEQUENCE_FLOW_TAKEN之后没有下一个TASK_CREATED的情况。第三步如果某个节点确实没创建任务查该节点的BPMN配置看async、监听器、边界事件等是否正常。第四步用DATA_字段确认当时的流程变量值和网关条件做对照。第五步定位到具体监听器或条件表达式的问题修复后重新跑通再用事件日志验证事件序列是否正确闭合。这条路线虽然不能解决所有问题但能解决80%以上的流程卡住任务未创建走了意外分支类问题。而且过程中不需要在代码里加任何临时日志也不会干扰线上运行。6. 事件日志的扩展玩法与几个隐藏的坑6.1 不只是排错从事件流里写自己的分析工具事件日志除了事后排查还能做不少其他事情。比如通过ACT_EVT_LOG里的TIME_STAMP_分析每个任务的耗时分布找出瓶颈节点或者统计每个流程定义的平均生命周期再或者分析某个用户在一天内完成了多少待办任务。这些跨事件的分析在Activiti自带的历史表里也能做但事件日志的优势在于它能还原操作时间线而历史表更偏向状态结果。比如分析用户提交任务后又撤回这种操作序列历史表里很难看出来事件日志却能通过TASK_COMPLETED和后续的TASK_CREATED事件组合判断出来。如果你想做得更专业可以直接在EventLogger链路上拦截事件把事件数据转发到外部监控平台。比如在EventLogger之外再加一个自定义监听器专门对PROCESS_COMPLETED事件做计时统计记录流程实例结束的耗时并推送指标。这个思路在二次开发里使用非常频繁。6.2 坑一事件日志不保证100%不丢前面提到过EventLogger先把事件放到内存缓存再由EventFlusher批量写入。如果你的应用在缓存未刷盘之前异常宕机这部分事件就会丢失。解决方式有两种一是缩短刷盘周期让事件更快写库代价是IO压力增大二是实现自己的EventFlusher在事件产生时同步写库但这样会拖慢主流程。我个人的建议是不要把事件日志当成可靠审计来用它更适合作为高性能的排错辅助。如果业务上有必须不丢的审计需求比如财务流程的合规审计应该在流程的各个关键节点自己用事务消息记录不能依赖事件日志。6.3 坑二DATA_字段里的JSON可能很大DATA_字段存储的JSON在某些场景下会膨胀得很厉害。比如任务完成了如果这个任务带有大量流程变量或者DATA_里包含了实体类的完整序列化内容一条记录可能就有几百KB甚至更大。生产环境里跑上几个月ACT_EVT_LOG表很容易膨胀到几GB。应对方案无非几种定期归档清理写一个定时任务把历史事件日志迁移到别的表或对象存储或者自定义EventLogger把不需要的DATA_内容剪裁掉。这个小改动看起来不起眼但能让ACT_EVT_LOG表在长时间运行后依然保持可查询性。6.4 坑三事件日志与异步任务日志别搞混Activiti里除了事件日志还有一套针对异步执行器AsyncExecutor的任务日志机制。异步任务的执行情况记录在ACT_RU_JOB和ACT_HI_JOB_LOG里。两套日志记录的是完全不同的东西但名字相近很容易搞混。如果流程卡住是因为异步任务没执行看ACT_HI_JOB_LOG才能看到具体的执行异常和重试次数如果流程卡住是因为事件流没推开那要看ACT_EVT_LOG。排查前先分清问题在哪一层可以节省不少时间。我见过一个同事排查了半天事件日志最后发现其实是一个异步任务的循环调用导致执行器线程阻塞和事件流完全没有关系。6.5 一个定制EventLogger的小例子如果你的团队想把事件日志改造成更适合自己业务的样子最简单的方式是继承EventLogger并覆盖默认的createEventLogEntry方法public class CustomEventLogger extends EventLogger { Override protected EventLogEntry createEventLogEntry(ActivitiEvent event) { EventLogEntry entry super.createEventLogEntry(event); // 这里可以根据event类型补充自定义数据 if (event.getType() ActivitiEventType.TASK_COMPLETED) { MapString, Object customData new HashMap(); customData.put(customFlag, something); entry.setData(customData); } return entry; } }然后在配置里替换默认的EventLoggerconfig.setEnableEventLog(true); config.setEventLogger(new CustomEventLogger());这样就可以在保持原有事件日志机制的同时嵌入自己的业务标记和扩展信息。不过要注意EventLogEntry的实现会直接影响DATA_字段的序列化方式改的时候最好先在模拟环境里验证数据格式是否符合预期免得影响下游分析脚本。7. 最后的实操体会写到这里前面把事件日志的机制、数据结构和排错实战都讲得差不多了。最后想再啰嗦两句我自己的体会。第一enableEventLog这个开关在开发环境尽量开着。开发调试的时候有事件日志和没有事件日志排查效率完全是两个量级。很多流程引擎的问题光看程序堆栈是看不出名堂的因为引擎已经把异常吞了但事件日志会留下痕迹。生产环境建议按业务量评估后再决定是否开启日志膨胀问题可以用定期清理任务兜底。第二如果你在流程引擎二开上走得比较深光会用ACT_EVT_LOG还不够更值得研究的是事件分发器那一层。遇到不满足于默认行为的场景可以自己往事件链路上加处理器而不是把业务逻辑堆在BPMN文件里的监听器上。第三和所有日志系统一样事件日志的数据一定要有保留策略。线上的ACT_EVT_LOG表如果只进不出再扎实的排错手段也会被磁盘空间和查询性能拖垮。我目前习惯的做法是给事件日志表加一个按时间分区每季度做一次归档把超过半年的记录导到脱机存储。这样既能保留长期数据用于趋势分析又不影响线上交互查询。希望这篇经验分享能帮你在Activiti的路上少踩几个坑。如果你也在用事件日志做定制欢迎结合自己的实际场景去改EventLogger这套机制的扩展空间比看起来要大得多。

相关新闻

SpringBoot+Vue在线考试系统毕业设计:从数据库到部署全流程实战

SpringBoot+Vue在线考试系统毕业设计:从数据库到部署全流程实战

1. 为什么在线考试系统是毕设“性价比之王”:需求背景与选题思路每年毕设季,总有一批人会卡在选题上。要么太简单被导师一眼否掉,要么太复杂做到一半开始后悔。如果你想要一个业务场景清晰、技术栈主流、可演示性强、踩坑可控的题目&#xff…

2026/10/15 12:11:44 阅读更多 →
实习报告别写成流水账,科迅捷AI帮你把实践写成能力证明

实习报告别写成流水账,科迅捷AI帮你把实践写成能力证明

https://www.kexunjie.com/ 上的科迅捷AI实践报告功能,帮我改掉了一个坏习惯——把实习报告写成流水账。很多专科生写实习报告,就是第一天做了什么、第二天做了什么按时间排下来,导师看完只觉得你去了,不觉得你会了。 实践报告和…

2026/10/15 12:11:44 阅读更多 →
Python爬虫实战:BeautifulSoup清洗网页并生成Word文档

Python爬虫实战:BeautifulSoup清洗网页并生成Word文档

简介:这是一份面向Python初学者的爬虫与数据存储实战教程,围绕网页抓取、MongoDB文档数据库应用及Word文档导出展开,适合需要快速掌握requests、BeautifulSoup、pymongo等库联动使用的学习者。资源以单个docx文档形式提供,全文约6…

2026/10/15 12:10:44 阅读更多 →

最新新闻

Hyper-Frames:无标签视频+里程计自监督预训练,实现低成本运动预测

Hyper-Frames:无标签视频+里程计自监督预训练,实现低成本运动预测

做自动驾驶、移动机器人、无人机导航的同行,应该都有一段被数据集标注反复折磨的经历。最近 Waymo Research 放出的 Hyper-Frames 框架,让我终于看到一条不太一样的路:不用一开始就堆人天去标框、标轨迹、标地图,用无标签视频加里…

2026/10/15 12:59:25 阅读更多 →
Wand图像批处理实战:ImageMagick的Python绑定与PIL替代方案

Wand图像批处理实战:ImageMagick的Python绑定与PIL替代方案

1. 为什么我要认真聊聊Wand这个模块 第一次接触Wand是在处理一批商品图的时候。运营那边丢过来三百多张图片,要求统一裁成正方形、加白色边框、压缩到指定体积以下,还要把每张图的平均色提取出来做配色统计。我当时第一反应是用PIL,但写着写着…

2026/10/15 12:59:25 阅读更多 →
轴承行业竞价内卷加剧,杭州巨宇科技GEO优化让品牌被AI看见

轴承行业竞价内卷加剧,杭州巨宇科技GEO优化让品牌被AI看见

当空压机设备厂商向豆包提问"空压机轴承生产厂家哪家靠谱",向DeepSeek询问"精密轴承供应商推荐、高速轴承哪个品牌好",向Kimi搜索"轴承型号怎么对应、P4级精度是什么水平"时,AI给出的专业回答里,你…

2026/10/15 12:59:25 阅读更多 →
CHAN、Taylor、AOA定位算法实战:从公式到融合避坑指南

CHAN、Taylor、AOA定位算法实战:从公式到融合避坑指南

简介:这份资源围绕无线通信与导航系统中的经典定位算法展开,面向从事物联网、雷达、卫星导航及无线网络定位开发的学习者与研究人员,重点讲解CHAN、Taylor与AOA三种基本方法的原理与实现思路。压缩包共37个文件,以m脚本与mat文件为…

2026/10/15 12:59:25 阅读更多 →
Logstash数据入库MySQL/PostgreSQL:logstash-output-jdbc在线与离线安装全攻略

Logstash数据入库MySQL/PostgreSQL:logstash-output-jdbc在线与离线安装全攻略

Logstash的数据要写进MySQL或者PostgreSQL,很多人在第一步就卡住了:官方自带的输出插件只有elasticsearch、file、stdout这些,偏偏没有关系型数据库。社区里用得最多的补位方案就是logstash-output-jdbc。这个插件本身不难装,但“…

2026/10/15 12:59:25 阅读更多 →
C++结构体详解:定义、使用与最佳实践

C++结构体详解:定义、使用与最佳实践

1. 结构体基础概念解析在C编程中,结构体(struct)是一种将不同类型的数据组合成一个整体的复合数据类型。它允许我们将多个相关的变量打包成一个逻辑单元,这在处理复杂数据时特别有用。比如要记录一个班级的学生信息,每个学生有学号、姓名、成…

2026/10/15 12:58:24 阅读更多 →

日新闻

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

1. 为什么选择容器化方式部署 LoRa 网络服务LoRa 物联网项目落地时,网络服务器是整个链路的核心枢纽。它负责接收网关上报的数据包、完成去重与解密、再把有效载荷分发给上层应用。早期大家习惯把网络服务器直接装在物理机上,依赖一堆系统库和特定版本的…

2026/10/15 0:00:46 阅读更多 →
共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

1. 共享推理服务里,扩缩容到底难在哪做过大模型推理服务部署的人,大概都经历过这样的场景:一个团队共用一套推理集群,A 业务跑的是 7B 的小模型,B 业务跑的是 72B 的大模型,C 业务白天流量高、晚上几乎归零…

2026/10/15 0:00:46 阅读更多 →
Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

1. 引子:一次内核恐慌引发的追查先从一个真实场景说起。某次我给一台老旧的嵌入式设备折腾系统裁减,为了把启动时间压进两秒以内,我手贱地把一串自认为“没用”的 systemd 服务给 disable 了,其中就包括systemd-remount-fs.servic…

2026/10/15 0:00:46 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/14 3:30:28 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/14 16:30:29 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/14 3:29:58 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 0:14:36 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 22:45:47 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 9:49:14 阅读更多 →