MP-DQN无人机避障与目标追踪:深度强化学习复现与调参实践
简介面向具备Python与机器学习基础的研究人员和从业者这份资料以TensorFlow 2.2复现论文中的MP-DQN算法完整演示无人机在栅格环境下的自主避障与目标追踪流程。内容围绕环境参数设定、两层全连接Q网络与双模态网络结构、成功经验池/失败经验池/临时经验池的差异化回放机制、距离与方向奖励函数设计以及ε-greedy策略细化展开训练部分采用批处理梯度下降同步更新评估网络与目标网络并给出独立测试环节的平均追捕成功率。资源包为1个docx文档共28KB文档内包含可运行的Python代码与逐段中文解释约18页覆盖从环境搭建、模型构建、奖励计算到训练调优的完整实现思路。目前已有382人学习下载适合需要快速上手DQN变体、开展强化学习导航项目或复现相关论文的开发者。1. 为什么避障与目标追踪要放进同一个决策MP-DQN 解决的核心问题无人机在空中执行追踪任务时最反直觉的一点是避障不能等发现障碍物再反应也不能把“先飞到目标点、再避开沿途障碍”拆成两个独立阶段。真实场景里目标在移动障碍在侧面或背后出现无人机必须在“朝目标靠近”和“远离眼前障碍”之间做同一个决策。把这两个目标写成两个独立逻辑无人机就会在走廊尽头或门框边来回拉扯表现就是训练曲线一直震荡、永远收敛不了。MP-DQN 做的事情是把避障与追踪放进同一个网络、同一份奖励、同一个动作空间里学。它属于深度强化学习算法里 DQN 的变体在普通 DQN 的基础上通过多帧状态输入和经验回放加权让无人机既能看到“障碍在逼近”的短时趋势也能记住“目标在哪个方向”的长期信息。这篇复现笔记面向的是想跑通论文代码、或者想把 MP-DQN 移植到自己仿真环境里的开发者目标是从问题建模开始一直讲到训练参数、翻车现场和验证方法。2. 任务拆解把“避障追踪”写成可训练的 MDP强化学习的第一步不是写网络而是把任务写成一个 MDP马尔可夫决策过程。无人机避障与追踪这个题目MDP 里最容易出问题的是状态空间和奖励函数。这两个东西没定义好后面换什么网络都救不回来。2.1 状态空间与动作空间连续坐标怎么映射到离散动作无人机的状态在物理世界是连续的位置 (x, y, z)、速度 (vx, vy, vz)、姿态角、距离目标的相对坐标、周围障碍物的距离。DQN 是离散动作算法所以“动作”需要离散化。常见做法是把飞行控制拆成 7 个动作前进、后退、左移、右移、上升、下降、悬停。每个动作带一个固定速度比如 0.5 m/s模拟一个简化的速度闭环。我一般会把状态分成传感器向量和任务向量两部分来组织自身状态当前三维位置或相对起始点的偏移速度状态三轴线速度用于判断运动趋势目标状态目标相对无人机的方位角和距离避障状态8 个方向上的激光测距值前、后、左、右、左上、右上、左下、右下历史状态最近 4 帧的上述状态拼接这是后续 MP-DQN 的“多帧输入”基础矩阵形式大致是state [x, y, z, vx, vy, vz, target_dist, target_angle, laser_0 ... laser_7]单帧约 17 维4 帧拼接后约 68 维。这里有个容易翻车的细节目标距离和激光距离的量纲一样但数值范围可能差 10 倍以上。直接拼在一起网络会把注意力全放在数值大的分量上。所以所有状态量都要归一化到 [-1, 1] 之间这是深度强化学习算法里价值网络能稳定收敛的前提。2.2 奖励函数避障优先还是追踪优先奖励函数是无人机强化学习里最大的玄学。不少人一上来就写“靠近目标 1撞到障碍 -10”结果训练时无人机学到的策略是原地起飞到最高处——因为最高处既碰不到障碍又能看到目标但永远不靠近每步拿生存奖励。这是我见过的最典型的翻车现场。我的做法是把奖励拆成四个部分每部分带权重r w₁·r_survive w₂·r_approach w₃·r_collision w₄·r_arriver_survive每步存活给 0.1鼓励无人机持续飞行而不是停在原地r_approach当前帧与上一帧相比无人机与目标距离的减少量 × 0.5这是一个密集奖励让网络随时知道“刚才那个动作对不对”r_collision碰撞或进入安全距离-10并终止本回合r_arrive到达目标点半径 1 米范围内20终止本回合这里关键的参数是 r_approach。如果只用“距离越近奖励越大”这种稀疏设计无人机在仿真环境里需要几百万步才能偶然碰到一次目标点训练效率低到没法看。用距离差分的密集奖励网络能得到稳定的梯度信号。避障部分不单独给正向奖励因为避障已经体现在“碰撞终止”这条路径上了无人机学会避障的本质是学会“不触发终止条件”。2.3 最小可跑环境PyBullet 搭一个无人机训练场我建议直接用 PyBullet 作为训练环境它比 Gazebo 轻量Python 接口也干净适合强化学习训练时频繁 reset 的需求。至于要不要用完整的无人机动力学模型比如 rotors_simulator我的结论是复现 MP-DQN 算法阶段不需要。用一个带简单动力学、能响应速度指令的刚体模型就够重点在验证算法本身而不是十几页论文里的电机转速公式。import pybullet as p import pybullet_data import numpy as np import gymnasium as gym from gymnasium import spaces class UAVEnv(gym.Env): def __init__(self, max_steps200): super().__init__() # 动作前进后退、左右平移、上下、悬停 self.action_space spaces.Discrete(7) # 状态维度自身坐标3 速度3 目标距离1 目标方位1 8向激光 4帧冗余 self.observation_space spaces.Box(low-np.inf, highnp.inf, shape(17,), dtypenp.float32) self.max_steps max_steps self.step_count 0 self.p p self._connect_sim() def _connect_sim(self): self.client p.connect(p.DIRECT) # headless 模式训练时不显示界面 p.setAdditionalSearchPath(pybullet_data.getDataPath()) p.setGravity(0, 0, -9.8) p.loadURDF(plane.urdf) # 这里传入你自己准备的无人机 URDF 文件路径 self.drone p.loadURDF(quadrotor.urdf, [0, 0, 1.0]) def reset(self, seedNone, optionsNone): self.step_count 0 p.resetBasePositionAndOrientation(self.drone, [0, 0, 1.0], [0, 0, 0, 1]) self.target np.array([np.random.uniform(5, 10), np.random.uniform(-5, 5), 2.0]) self.drone_pos np.array([0.0, 0.0, 1.0]) self.last_dist np.linalg.norm(self.drone_pos - self.target) return self._get_obs(), {} def step(self, action): self.step_count 1 # 动作映射0前 1后 2左 3右 4上 5下 6悬停 direction { 0: [1, 0, 0], 1: [-1, 0, 0], 2: [0, 1, 0], 3: [0, -1, 0], 4: [0, 0, 1], 5: [0, 0, -1], 6: [0, 0, 0] } vel 0.5 * np.array(direction[action]) # 简化的速度指令直接设置刚体速度绕过了电机动力学 p.resetBaseVelocity(self.drone, velocityvel.tolist()) p.stepSimulation() self.drone_pos np.array(p.getBasePositionAndOrientation(self.drone)[0]) dist np.linalg.norm(self.drone_pos - self.target) reward 0.1 0.5 * (self.last_dist - dist) terminated False if dist 1.0: # 到达目标 reward 20.0 terminated True if self.drone_pos[2] 0.2: # 碰撞地面 reward - 10.0 terminated True self.last_dist dist truncated self.step_count self.max_steps return self._get_obs(), reward, terminated, truncated, {} def _get_obs(self): # 这里省略激光雷达的具体实现用真实距离代替 laser np.zeros(8) obs np.concatenate([ self.drone_pos, [0.0, 0.0, 0.0], # 速度简化为 0 [np.linalg.norm(self.drone_pos - self.target)], [np.arctan2(self.target[1] - self.drone_pos[1], self.target[0] - self.drone_pos[0])], laser ]).astype(np.float32) return obs这段代码的核心逻辑是reset 时随机化目标位置避免每次都从同一起点飞向同一个目标——不随机化网络很快就会过拟合到固定路线。step 里奖励拆成存活奖励和距离差分奖励碰撞和到达用终止条件截断。代码里省略了真正激光雷达的数据获取只留了 8 维占位实际复现时可以把 8 条射线打到障碍物上测最近距离。整个环境类兼容 gymnasium 接口reset 返回(obs, info)二元组这是新版 gymnasium 的规范旧版 gym 里env.reset()只返回单个 obs混合使用会报too many values to unpack之类的坑。3. MP-DQN 的算法骨架比普通 DQN 多了什么MP-DQN 里的“MP”在不同论文里解释不完全一致复现前先看你手头源论文的定义。我做的版本是按 Multi-Pass多通道状态输入来理解的不再往网络里塞单帧状态而是把连续 4 帧状态作为一条“时间通道”输入让 Q 网络看到短时运动趋势。这和普通 DQN 的“只基于当前观测决定动作”有本质区别也是这个算法在无人机避障场景里更稳的核心原因。3.1 网络结构多帧输入下的双 Q 与目标网络网络输入形状是(batch, 4, 17)4 是帧数17 是单帧状态维度。第一层用 1D 卷积沿时间维做特征提取然后展平进全连接层。这样设计的好处是网络天然具备“看到障碍在靠近”的时间感知而不是只判断“此刻距离有多近”。import torch import torch.nn as nn import torch.nn.functional as F class MP_DQN(nn.Module): def __init__(self, frame_len4, state_dim17, action_dim7): super().__init__() # 多帧输入的时序卷积层 self.conv1 nn.Conv1d(in_channelsstate_dim, out_channels32, kernel_size3, padding1) self.conv2 nn.Conv1d(in_channels32, out_channels64, kernel_size3, padding1) self.flatten nn.Flatten() # 展平后维度64 * frame_len self.fc1 nn.Linear(64 * frame_len, 256) self.fc2 nn.Linear(256, 128) self.q_out nn.Linear(128, action_dim) # 输出每个动作的 Q 值 def forward(self, state_seq): # state_seq: (batch, frame_len, state_dim) x state_seq.permute(0, 2, 1) # (batch, state_dim, frame_len) x F.relu(self.conv1(x)) x F.relu(self.conv2(x)) x self.flatten(x) x F.relu(self.fc1(x)) x F.relu(self.fc2(x)) return self.q_out(x)这里需要注意的是permute那一步。PyTorch 的 Conv1d 期望输入是(batch, channels, length)而环境给到的是(batch, length, channels)这种时序数据排列不做维度交换卷积层直接报格式错误。两个网络实例在线网络和目标网络用同一套类创建目标网络不参与梯度计算每 N 步把在线网络的参数拷贝过去。这种 Double DQN 的做法能抑制 Q 值过估计——普通 DQN 会倾向于高估所有动作的 Q 值无人机在狭小空间里容易做出过于激进的飞行决策。3.2 经验回放为什么碰撞样本需要加权无人机训练过程中经验池里绝大多数样本是“正常巡航”样本碰撞样本占比往往不到 5%。普通 DQN 随机从经验池采样时网络几乎学不到碰撞后的负反馈。MP-DQN 的经验回放会做两件事一是保存多帧轨迹而不是单帧状态二是按优先级采样本。我在复现时没有写完整的 SumTree 结构而是用一个可复现性更高、代码量更小的方式给碰撞样本设定高采样权重普通样本低权重用np.random.choice按权重采样。class PriorityBuffer: def __init__(self, capacity100000, alpha0.6): self.capacity capacity self.alpha alpha self.buffer [] self.pos 0 def push(self, state_seq, action, reward, next_state_seq, done): # 碰撞样本获得更高优先级权重 priority 3.0 if reward -5 else 1.0 if len(self.buffer) self.capacity: self.buffer.append((state_seq, action, reward, next_state_seq, done, priority)) else: self.buffer[self.pos] (state_seq, action, reward, next_state_seq, done, priority) self.pos (self.pos 1) % self.capacity def sample(self, batch_size): weights np.array([item[5] for item in self.buffer]) ** self.alpha probs weights / weights.sum() indices np.random.choice(len(self.buffer), batch_size, pprobs) batch [self.buffer[i] for i in indices] return batch, indices def __len__(self): return len(self.buffer)alpha0.6是我调过几轮之后比较稳的值。alpha 太接近 1碰撞样本反复被抽中网络开始震荡alpha 太接近 0优先级机制形同虚设又回到均匀采样。碰撞判定在 reward 上做标记只是一个极简方案如果要把这个 buffer 用到正式训练里可以改成按 TD-error 动态更新优先级——每轮采样后记录该样本 TD-error作为下一轮采样权重。我不建议一上来就上 TD-error 版本先把固定优先级版本跑稳定再去加动态优先级层级递进排查问题更容易定位。3.3 训练循环目标网络同步与探索率退火def train_step(batch, online_net, target_net, optimizer, gamma0.99): state_seq, action, reward, next_state_seq, done zip(*batch) state_seq torch.FloatTensor(np.array(state_seq)) action torch.LongTensor(np.array(action)).unsqueeze(1) reward torch.FloatTensor(np.array(reward)) next_state_seq torch.FloatTensor(np.array(next_state_seq)) done torch.FloatTensor(np.array(done)) q_values online_net(state_seq).gather(1, action).squeeze(1) with torch.no_grad(): next_q target_net(next_state_seq).max(1)[0] target_q reward gamma * next_q * (1 - done) loss F.mse_loss(q_values, target_q) optimizer.zero_grad() loss.backward() optimizer.step() return loss.item()这段代码里next_q的计算用的是 max 而不是 online 网络的 argmax这在 Double DQN 里算是一个简化处理。目标网络的参数不是每步都更新而是每 1000 步从在线网络同步一次。同步频率太频繁目标网络会和在线网络一起震荡失去“稳定目标”的意义太久不同步目标网络又跟不上最新的策略变化。1000 步是我在无人机环境里试出来的平衡点。训练时每轮 episode 结束后探索率按epsilon max(epsilon_min, epsilon * decay)退火从 1.0 降到 0.05前期多探索后期收敛到利用已学策略。4. 训练参数与曲线一组能先跑通的配置和调参思路4.1 从零跑起的基础参数表下面这组参数是我在 PyBullet 环境里能稳定跑出收敛效果的起点。把它当作一个可修改的模板而不是“最优解”。参数取值说明learning_rate1e-4过大会导致训练后期 Q 值振荡gamma0.99追踪任务里未来奖励占主导batch_size64配合 GPU 显存和训练速度的平衡点replay_capacity100000无人机飞行一步产生一个样本1 万步很快就填满target_update1000每 1000 步同步一次目标网络epsilon_start1.0初始探索率前几百回合基本随机飞epsilon_min0.05最终探索率保证环境变化时还有应变能力epsilon_decay0.995每回合结束后乘一次max_steps200单回合最长步数超过则截断action_vel0.5 m/s单步移动速度过高会导致动作空间太暴力Python 运行环境建议用 Python 3.9 或 3.10配合 gymnasium 和 PyBullet。安装依赖时用虚拟环境避免和系统其他项目互相污染python -m venv venv source venv/bin/activate # Windows 下是 venv\Scripts\activate pip install numpy torch pybullet gymnasium matplotlib如果你在安装时遇到pybullet报编译错误先检查一下 python 环境变量配置尤其是 Windows 下多版本 Python 共存时pip 和 python 是否指向同一个解释器。用python -m pip install而不是裸pip install可以避开这种版本错位的问题。在 VS Code 里调试时确认右下角选择的解释器和终端里激活的是同一个虚拟环境否则跑起来会发现 import 的 module 来源不一致。4.2 学习率、探索率与环境随机化怎么配学习率在这个任务里有一个很直观的判断标准如果训练前 200 回合的 loss 从几万一路掉到几千但 reward 没明显上升说明网络在“学习但没有学到好策略”常见原因是奖励函数设计有问题如果 loss 一直平稳但 reward 也不动那更可能是网络容量不够或者探索率太低导致前期探索不足。我倾向于先把学习率固定在 1e-4优先调奖励权重。学习率低于 1e-5 时训练速度慢到无法判断算法是否正确高于 1e-3 时 Q 值波动剧烈训练曲线像心电图。探索率退火的衰减系数值得多花时间调。epsilon_decay 0.995 意味着第 100 回合时探索率降到约 0.60第 200 回合降到 0.36收敛偏快。如果你的环境里目标位置随机范围很大探索率降太快会导致无人机永远找不到目标点。可以把 decay 改成 0.998让探索期更久。反过来如果你加了大量的环境随机化障碍位置每次重置都变探索率不能降得太低否则环境一变化策略就失效。环境随机化是我强烈建议加的训练项。每次 episode 重置时把目标位置、无人机起始位置、障碍物摆放都随机化。道理很简单固定场景下 DQN 很容易记住“朝某个方向飞就能得分”而不是学会“朝目标方位飞”一旦换场景就完全失灵这属于过拟合的一种特殊形态。4.3 训练曲线怎么读该看的不只是 rewardimport matplotlib.pyplot as plt def plot_metrics(episode_rewards, collision_rates, arrive_rates): fig, ax plt.subplots(3, 1, figsize(10, 12)) ax[0].plot(episode_rewards) ax[0].set_title(Episode Reward) ax[1].plot(collision_rates) ax[1].set_title(Collision Rate per Episode) ax[2].plot(arrive_rates) ax[2].set_title(Arrive Rate per Episode) plt.tight_layout() # 图像默认不显示就直接关掉了这里显式保存到本地文件 plt.savefig(training_curve.png, dpi150) plt.close()这是我的一个血泪教训训练跑了好几个小时plot 窗口一闪而过什么都没留下。养成跑完自动保存一张曲线的习惯比实时盯屏可靠得多。读曲线时注意episode_reward因为存活奖励的存在会一直正向增长需要同时看碰撞率曲线。碰撞率不为零很正常只要在下降就行。如果奖励在涨但碰撞率也在涨说明无人机找到了“贴着障碍飞”的捷径——因为它既能拿存活奖励又能拿接近奖励还没碰到碰撞终止阈值。这说明碰撞惩罚权重太低需要增大r_collision的绝对值或者缩小安全距离判定半径。5. 复现避坑MP-DQN 训练中翻车最多的 5 个问题5.1 训练前中期无人机一直绕圈现象是训练曲线里的 reward 稳定上升但可视化看无人机永远在目标点周围画圈从不进入到达半径内。原因是“靠近奖励”只奖励距离减少但当无人机在同一距离上环绕时距离差分接近 0而存活奖励每步都在给所以绕圈成了一个局部最优解。解决思路是给“到达”一个更大的奖励信号 在奖励里加一项距离惩罚项不是差分而是绝对距离的负向积分。我改成每步reward - 0.01 * current_dist之后绕圈问题明显缓解因为远处悬停也会持续扣分无人机被迫往目标方向走。5.2 碰撞检测触发了但避障策略没学到现象是训练曲线里终止事件频繁但无人机依然直直撞墙。原因出在碰撞负反馈的传播路径太长——碰撞只发生在回合终止那一步前面几十步的状态都没有直接负奖励网络很难把“远处那面墙”和“最终碰撞”关联起来。解决方式是缩小碰撞判定的安全距离让无人机在离障碍物还有一段距离时就接收到负信号而不是等真撞上。在奖励函数里可以加一个近障碍惩罚当任一激光测距小于 1.0 米时每步给一个额外的负奖励。这样避障信号从稀疏变成了密集学起来会快很多。这个经验来自我自己第一次复现时碰撞率卡在 40% 下不去的教训。5.3 多帧输入维度写错导致训练直接报错现象是网络 forward 时报Expected 3D input, got 2D。原因是在构建经验回放时把状态存成了(batch, 68)而不是(batch, 4, 17)时序维和特征维合并了。解决方式是在经验池 push 前就统一状态格式不要在训练循环里反复 reshape。我在代码里加了断言def push(self, state_seq, action, reward, next_state_seq, done): assert state_seq.shape (4, 17), fstate shape mismatch: {state_seq.shape}训练循环里做数据校验比 CRT 调半天维度靠谱得多。5.4 gym 老代码与 gymnasium 接口不兼容现象是老复现代码在env.reset()时抛TypeError: too many values to unpack。原因是旧 gym 的 reset 返回单个 obsgymnasium 的 reset 返回(obs, info)元组。解决方式是统一走 gymnasium 接口所有环境的 reset 都写成obs, _ env.reset()。同时检查 step 返回值的数量最新规范是 5 个返回obs, reward, terminated, truncated, info旧代码里只有 4 个甚至 3 个。强行兼容两套接口是给自己埋坑建议直接把环境层统一成 gymnasium 标准。5.5 训练后期过拟合换目标点就迷失现象是训练 1000 回合后训练场景里成功率很高但把目标点换到新位置就直接失败。原因是无人机记住了场景的“布局”而不是“目标相对方位”。前面第 2 章提到过环境随机化这里要再次强调——阶段性地把障碍物位置和数量全部重新生成。如果环境本身不允许随机化比如固定的室内平面图那就在训练时人为增加多种起始位置和目标位置的排列组合让网络必须学到“朝目标相对方向飞”而不是“朝某个绝对坐标飞”。6. 验证与进阶从复现到跑通自己的实验训练曲线只是过程指标复现一个论文算法要敢下结论得把验证指标定义清楚。我一般会统计三个核心指标成功率到达目标点次数 / 总回合数、碰撞率碰撞回合 / 总回合数、平均追踪距离所有已到达回合中无人机与目标的平均距离。这三个指标统一写进评估函数每训练 100 回合跑一次评估评估时关闭探索epsilon 置为 0测试 50 个随机场景取平均。进阶方向按需求可选如果动作空间要连续控制可以在 MP-DQN 基础上换成 DDPG 或 SAC但要把状态输入的多帧结构保留如果环境要加入动态障碍物注意 DQN 的帧输入结构天然适合捕捉运动趋势反而是奖励函数要重新设计——动态障碍物场景下近障碍惩罚的权重需要调高否则无人机会在移动障碍靠近时来不及避让。如果手头有真实无人机的 PX4 飞控可以把 PyBullet 的输出动作映射成 MAVLink 速度指令但这个链路需要先在仿真里跑几十个小时确保策略对噪声有容忍度不然真机测试大概率会“炸鸡”——代价太高。最后说一个我自己的习惯作为收尾每次改完奖励函数我会先在同一个随机种子上跑 100 回合对比曲线而不是直接开长训。种子固定的好处是排除了运气成分让你能看清楚改动带来的真实影响。我吃过一次大亏调了一版新奖励直接开长训跑了 6 小时结果发现环境随机化把起始位置换了个方向训练效果变好根本是偶然。固定种子、短批次、快速试错这套流程在深度强化学习算法复现里救了我很多次希望帮到你。本文还有配套的精品资源点击获取

相关新闻

STM32驱动DS1302实时时钟:从时序原理到代码调试全解析

STM32驱动DS1302实时时钟:从时序原理到代码调试全解析

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

2026/10/12 1:10:38 阅读更多 →
数据库原理A卷拆解:关系代数、范式与事务恢复的复习要点

数据库原理A卷拆解:关系代数、范式与事务恢复的复习要点

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

2026/10/12 1:10:38 阅读更多 →
AI协同开发STM32:嵌入式软件人机分工新范式

AI协同开发STM32:嵌入式软件人机分工新范式

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

2026/10/12 1:09:37 阅读更多 →

最新新闻

季节尺度M-K突变检测的Python实现:原理、代码与实用避坑指南

季节尺度M-K突变检测的Python实现:原理、代码与实用避坑指南

简介:基于Python的季节尺度M-K突变检测脚本,面向气候、水文、环境等领域的科研人员与有一定编程基础的学生,用于从SPEI等季节性时间序列数据中识别趋势突变点。脚本以SPEI3.xlsx为示例数据,完整演示了数据读取、缺失值检查、季节性…

2026/10/12 4:23:37 阅读更多 →
用md2wechat-skill将Markdown转换为公众号排版:本地转换工具实战指南

用md2wechat-skill将Markdown转换为公众号排版:本地转换工具实战指南

做技术公众号的人大概都有一份隐蔽的困扰:内容管理用Markdown,发布却要面对微信编辑器那一套网页排版。写的时候行云流水,粘贴进后台就原形毕露——代码块塌掉、表格错位、图片裂开。前前后后我折腾过好几套转换方案,目前用得最顺…

2026/10/12 4:23:37 阅读更多 →
OBCA题库拆解OceanBase硬知识:Paxos、Zone与运维实战

OBCA题库拆解OceanBase硬知识:Paxos、Zone与运维实战

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

2026/10/12 4:23:36 阅读更多 →
专业数据库数据共享策略:从数据孤岛到可控流通的落地拆解

专业数据库数据共享策略:从数据孤岛到可控流通的落地拆解

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

2026/10/12 4:23:36 阅读更多 →
lakeFS Java SDK 中的 GarbageCollectionRules:垃圾回收保留策略的数据模型与配置实战

lakeFS Java SDK 中的 GarbageCollectionRules:垃圾回收保留策略的数据模型与配置实战

数据工程数据湖大数据对象存储后端 【免费下载链接】lakeFS lakeFS - Data version control for your data lake | Git for data 项目地址: https://gitcode.com/gh_mirrors/la/lakeFS 点击查看 免费下载 本文以 GarbageCollectionRules.md 为骨架,系统…

2026/10/12 4:23:36 阅读更多 →
PS5游戏信息聚合工具实战:Python爬虫、数据清洗与全文搜索构建记录

PS5游戏信息聚合工具实战:Python爬虫、数据清洗与全文搜索构建记录

先交代一下背景。我是个游戏库存控,平时最大的爱好就是逛各个商店页面和评分站,看看最近有什么值得入手的PS5游戏。可时间一长,我发现自己每天至少要在五六个不同站点之间来回切换——想确认口碑得去媒体评分站,想比价格得看商店页…

2026/10/12 4:22:36 阅读更多 →

日新闻

复古胶片颗粒感噪点合成器:Canvas ImageData 像素高斯杂色注入算法

复古胶片颗粒感噪点合成器:Canvas ImageData 像素高斯杂色注入算法

在数码相机、高清显示屏与现代矢量图形技术高度发达的今天,画面可以做到绝对的锐利、平滑与无瑕。然而,当一张秋日手账插画或拍立得照片过于“平整无瑕”时,往往会散发出一种冰冷生硬的“数码塑料感(Digital Plasticity&#xff0…

2026/10/12 0:00:59 阅读更多 →
活字印刷古籍线装排版:Canvas 竖排文字与栏线自适应算法

活字印刷古籍线装排版:Canvas 竖排文字与栏线自适应算法

在现代网页与移动端设计中,横排(Horizontal Layout)早已经成为了绝对的主流。然而,当我们翻开泛黄的线装古籍、宋版木刻诗集,或是欣赏一张茶道雅集的手写便签时,那种**自上而下纵向书写、自右向左逐列铺展&…

2026/10/12 0:00:59 阅读更多 →
周日晚间的“精神松绑减震器”:无压力情绪倾倒箱与温和轻声陪伴

周日晚间的“精神松绑减震器”:无压力情绪倾倒箱与温和轻声陪伴

每到周日的晚上八点到十点,很多人心里都会悄悄亮起一盏警示灯。 在心理学上,这种现象有一个专门的称谓——“周日夜晚焦虑症(Sunday Scaries)”。明天又是周一,闹钟又要重新在七点响彻卧房;脑海里仿佛有一个…

2026/10/12 0:00:59 阅读更多 →

周新闻

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

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

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

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

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

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

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

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

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

2026/10/12 0:16:43 阅读更多 →

月新闻

我发现了一个新思路:用 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/11 10:45:37 阅读更多 →
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/11 14:36:53 阅读更多 →
黑夜航拍船只数据集训练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/11 14:36:54 阅读更多 →