资讯动态

用Python绘制鞋垫压力分布:从散点插值到异形热力图全解析

发布时间:2026/9/20 12:48:40 来源:尧图企业网站定制
简介鞋垫式柔性压力传感器阵列采集的离散压力点非常适合详细演示异形热力图的绘制方法资源代码基于Python编程语言完整完成数据筛选、插值、归一化以及传感器不规则布局到二维坐标的映射最终输出足部压力分布色图可用于生物力学分析、运动医学和穿戴设备开发等场景。压缩包仅68KB共3个文件其中Python脚本为源码负责数据处理与可视化测试图和输出图分别用于验证坐标映射和查看最终热力效果方便学习者对照实践。目前已有229人学习浏览这份轻量级示例结构清晰、依赖少既能帮助初学者理解从离散点到连续热力分布的计算思路也可替换传感器坐标后迁移至其他柔性接触压力分布场景是相关研究与产品开发中值得参考的代码模板。1. 为什么普通热力图搞不定“异形”需求1.1 这个需求到底难在哪我跟你讲第一次接到这个需求的时候我也有点低估了。表面上看不就是画个热力图吗matplotlib里一个imshow就能搞定。但实际情况完全不是这么回事。鞋垫上的柔性压力传感器阵列它不是一个规规矩矩的矩形网格而是按照脚掌的实际形状来排布的。脚趾区域、前掌区域、足弓区域、后跟区域传感器密度不一样甚至有些地方是空缺的。你从采集端拿到的数据本质上是几十个乃至上百个离散点每个点包含三个信息物理坐标 (x, y) 和压力值。普通热力图工具要求你喂进去的是一张完整的二维矩阵比如 32x32 的像素阵列。可传感器数据哪里是矩阵它就是不规则的“点云”。你要是硬把点云掰成矩阵要么空缺位置填零导致热力图出现大片假性的“零压区”要么就得做插值。更麻烦的是鞋垫本身是异形的你要是直接画一张矩形热力图边缘会延伸到鞋垫外面去看起来特别业余也没法跟实物对应起来。所以这个项目的本质其实是两个技术问题的叠加第一把稀疏离散点插值成连续的压力分布场第二用鞋垫的真实轮廓把这张连续场裁剪成异形区域。1.2 整体方案选型与思路当时我手头的环境是 Python 3.9数据处理用 NumPy SciPy可视化成图用 Matplotlib。工具的选型思路很简单SciPy 的griddata是目前做散点插值最成熟的方案支持最近邻、线性、三次立方等多种插值方式Matplotlib 的Path类可以直接做多边形裁剪。这两个库配合起来完全不需要额外引入像 OpenCV 或者 Plotly 这种偏重量级的依赖轻巧、可控、出了问题也好排查。完整的处理链路是这样的先做数据清洗和坐标归一化再通过griddata把离散点插值到一块规则网格上紧接着用鞋垫轮廓多边形生成掩膜把它跟插值后的网格做一次蒙版运算最后用contourf或imshow配合自定义色带渲染出异形热力图。这一套流程拆开看每一步都不难难就难在中间几个“坑位”你事先不知道等到画出来的图歪七扭八才开始排查那效率就太低了。下面我把每个环节需要关注的细节逐一展开。2. 数据准备从离散点到网格的第一步2.1 传感器布局与坐标映射在做任何插值之前先得搞清楚你的传感器到底长在哪儿。我这边用的鞋垫是 8x12 的阵列一共 96 个采样点传感器间距大约 1 厘米左右脚掌边缘区域的点会刻意往内侧收一收避免传感器悬空在鞋垫边缘。这时候如果你只看数据不看实物很容易把坐标方向搞反。我建议你这样做拿到鞋垫之后先画一张坐标图把传感器的物理位置一个一个标出来。坐标原点我习惯放在后跟中心点x 轴指向脚掌左侧y 轴指向前脚掌方向单位用厘米。这一步很土但特别管用。因为后面的插值网格、轮廓多边形、甚至实拍图的叠加全都依赖这个坐标基准。映射的时候有个小细节传感器的行列号跟实际物理坐标往往不是线性对应的尤其是靠近脚趾的弧形区域第 1 行传感器可能并不是严格等间距排列。所以千万不要用“我觉得它大概在这个位置”来填坐标一定要用手册里的结构图或者用游标卡尺实际量把每个点的坐标落到表格里。坐标错 2 毫米插值出来的云图在边界上就会出现肉眼可见的扭曲和凹陷。import numpy as np # 传感器物理坐标单位cm以鞋垫后跟中心为原点 # 这里只展示前几个点实际应是完整的 96 点坐标表 sensor_coords np.array([ [3.0, 20.5], [5.2, 20.7], [7.4, 20.6], [9.6, 20.4], # 脚趾区 [2.8, 18.9], [5.1, 19.1], [7.6, 19.0], [9.8, 18.7], # ... 其他行数据 ]) # 同帧的压力数据单位可先不做换算用原始 ADC 值也行 pressure_vals np.array([128, 256, 512, 341, 156, 234, 689, 879, ...])2.2 压力数据的采集与预处理坐标搞对了接下来是对压力值本身做预处理。柔性压阻传感器的原始数据其实挺脏的特点有两个一是零漂就是传感器在不受力的时候输出不一定是 0可能有个几十的基线二是高频抖动特别是人在行走落地的那一瞬间ADC 采样值上下跳得厉害。我处理的方式是这样静态标定阶段采集 5 秒空载数据对每个通道取平均值作为该通道的基线值之后所有原始值先减掉这个基线。然后再上滑窗均值滤波窗口宽度取 5 个采样点。别小看这两步减基线能让你热力图的冷区颜色真正反映出“无压力”而不是“压力 20”滤波能防止热力图出现那种一帧一个样的雪花噪点。另外做科研或产品演示的时候压力单位最好统一。柔性压阻传感器出来的原始值是 ADC 码值可以用砝码压出几个已知力值做一次线性回归把码值换算成 kPa。这一步可以事后做但在项目一开始就要把接口留好不然热力图画完才发现要换单位又得重新跑一遍全流程。def preprocess(raw_data, baseline, window5): # raw_data shape: (n_frames, n_channels) data raw_data - baseline kernel np.ones(window) / window filtered np.apply_along_axis( lambda m: np.convolve(m, kernel, modesame), axis0, arrdata ) return np.clip(filtered, 0, None)3. 插值算法与网格化把稀疏点变成连续面3.1 为什么必须插值以及有哪些选择离散点是没法直接画成云图的你总不能让热力图上一格一格的色块悬在半空。所以必须插值。插值说白了就是根据已知点的压力值推测未知位置的压力值。这个“推测”的逻辑不同出来的效果差异会非常大。在 SciPy 的griddata里常见三种方法。最近邻nearest是把离目标网格最近的那个传感器的值直接赋过去结果像马赛克几乎不可用。线性插值linear是用周围三个点拟合成一个平面计算快、稳定性好但出来的图有棱有角视觉上不够顺滑。三次立方插值cubic是用周围更多点去拟合一个光滑曲面视觉效果最好但有副作用它可能算出负数或者超出传感器最大量程的值这在物理上是不成立的。我的建议是做预览和调试用线性插值速度飞快做最终成果图和数据报告用 cubic然后对结果做个截断处理把负值归零超过上限的压回上限。如果你拿到一款传感器数据的空间分布比较均匀比如点间距大致一致cubic 基本不会出幺蛾子但要是某个区域传感器特别稀疏比如足弓区域可能只有一两个点cubic 就容易在空洞区域出现明显的“过冲”看起来像有个压力尖峰遇到这种情况就得考虑把整个区域的数据单独处理或者降低插值阶数。插值方法优点缺点适用场景nearest计算最快严格保留原始值块状感强不连续快速预览、点间距极大linear速度快数值稳定视觉有棱角不够平滑调试阶段、数据稀疏cubic光滑连续视觉效果好可能出现物理不可能值最终出图、演示汇报3.2 基于 SciPy 的插值实现插值的实现本身不复杂核心就几行代码。先把目标网格建出来在鞋垫所在的空间范围内生成一个足够密集的规则网格。网格密度要怎么选假如鞋垫长 25 厘米宽 10 厘米传感器间距是 1 厘米那么网格间距取 0.05 厘米就已经非常精细再密就是纯粹增加计算负担。按这个密度一个网格就是 500x200总计十万个点griddata跑 cubic 也只需要一两秒。这里要提醒一个容易翻车的细节griddata要求输入的网格点是“坐标对”的形式也就是你要用np.meshgrid生成 X 方向和 Y 方向的坐标矩阵然后ravel()拉平。标准的输入是这样points是传感器坐标数组shape 为 (N, 2)values是压力值数组shape 为 (N,)xi是你要插值的目标坐标数组shape 为 (M, 2)。from scipy.interpolate import griddata # 定义网格范围比传感器最外侧坐标稍微外扩一点 grid_x, grid_y np.mgrid[ x_min:x_max:500j, y_min:y_max:200j ] # 插值返回的 grid_z 就是二维矩阵 grid_z griddata( sensor_coords, pressure_vals, (grid_x, grid_y), methodcubic ) # 修正 cubic 插值可能出现的物理不可能值 grid_z np.clip(grid_z, 0, np.max(pressure_vals))插值完成后你手上就有一块规则的二维矩阵grid_z它覆盖了整个鞋垫外接矩形范围。这一步做完大多数教程就停在这里了直接拿imshow把矩阵画出来。但画出来的图必然是矩形的边缘会渗出鞋垫范围这也是“异形热力图”和“矩形热力图”拉开差距的关键节点。4. 异形边界裁剪与最终可视化4.1 用多边形掩膜实现鞋垫轮廓要裁剪出鞋垫轮廓就得先有鞋垫轮廓。获取轮廓的方式不外乎两种如果鞋垫是你们自己设计的结构图纸里可以直接导出轮廓点如果是买来的现成鞋垫最简单的方式是把它平放在扫描仪或者坐标纸上手动描点采样轮廓每隔一厘米取一个点就行精度完全足够。拿着轮廓点集用matplotlib.path.Path生成一个闭合多边形。这个路径对象可以做两件事第一判断某个坐标点是否在多边形内部第二批量判断一组点。后面这件事才是我们真正需要的。实现思路是这样的把插值网格里的每一个坐标点取出来逐一判断它在不在鞋垫轮廓内部。如果在内部保留如果在外部标记为无效。这里我强烈建议用Path.contains_points方法它支持一次性传入整个坐标数组返回一个同样形状的布尔数组效率比逐点循环高得多。然后把插值结果grid_z转成一个 masked array外部区域的数值设为掩膜视觉上就会变成透明或者背景色。from matplotlib.path import Path import numpy.ma as ma # outline_points: 鞋垫轮廓坐标首尾相连的闭合多边形 outline Path(outline_points) # 把网格坐标拉平后做包含判断 points_flat np.column_stack([grid_x.ravel(), grid_y.ravel()]) mask outline.contains_points(points_flat) mask mask.reshape(grid_x.shape) # 外部区域掩膜 grid_masked ma.masked_where(~mask, grid_z)这一步做完你就已经拥有一个“鞋垫形状”的二维压力场了。凡是鞋垫外面的网格点都是掩膜状态画图的时候不会显示。4.2 完整的绘制代码与效果调优接下来是最后一步把掩膜后的二维矩阵渲染出来。这里有两种常用渲染方式一种是用imshow另一种是contourf。imshow适合展示像素化的连续压力分布色彩过渡细腻contourf适合展示等压线层次带一圈一圈的云图效果科研论文里比较多见。我个人更推荐先用imshow把连续场画出来在上面叠加一层半透明的contour线既有视觉细腻度又能看出压强梯度方向。色带的选择上jet这种彩虹色带虽然经典但容易误导人红绿色盲用户看了会非常痛苦。我用的是coolwarm蓝色表低压、红色表高压中间色过渡自然也符合人对“冷低压、热高压”的直觉。import matplotlib.pyplot as plt from matplotlib.colors import Normalize plt.figure(figsize(6, 10)) norm Normalize(vmin0, vmax800) # 按实际数据范围调整 # 绘制插值热力图注意要关闭插值平滑保留真实分辨率 img plt.imshow( grid_masked, extent[x_min, x_max, y_min, y_max], originlower, cmapcoolwarm, normnorm, aspectequal, interpolationbilinear ) # 叠加轮廓线 plt.contour(grid_x, grid_y, grid_masked, levels8, colorsgray, linewidths0.5, alpha0.5) # 把传感器位置标出来方便排查坐标映射错误 plt.scatter(sensor_coords[:, 0], sensor_coords[:, 1], ck, s8) plt.colorbar(img, labelPressure (kPa)) plt.xlabel(x (cm)) plt.ylabel(y (cm)) plt.title(Foot Pressure Distribution) plt.tight_layout() plt.show()绘制过程中有三个小细节值得注意。坐标轴向问题。imshow默认的原点在左上角图像会上下颠倒。如果你拿到的传感器坐标是后跟在下、脚趾在上那就必须设置originlower让图像和物理位置一致。否则画出来的热力图跟穿着鞋垫时的方向正好相反踩后跟的时候热力图是在“脚尖”亮排查半小时才发现是这一步的问题。aspectequal一定要加。不加的话Matplotlib 会自动拉伸纵横比脚掌的宽度会被拉长整个图看上去像被挤过比例完全失真。设成equal之后一厘米就是一厘米跟真实鞋垫比例严格一致。还有掩膜区域的边界锯齿问题。因为鞋垫轮廓是用的折线段多边形网格分辨率不够高时边界看起来会有锯齿。要解决这个问题可以在生成鞋子轮廓时多采点样轮廓点越多掩膜边界越顺滑或者把网格密度调高比如从 500x200 提到 1000x400锯齿肉眼就基本看不出来了。4.3 左右脚镜像与多帧动画说完单帧热力图还有一个实际项目中绕不开的场景你有左右两只鞋垫或者你要连续处理几十帧数据来展示一次完整的行走过程。先说说左右脚。左脚和右脚的传感器布局是镜像关系如果代码里坐标表的构建方式是一样的比如都是从“后跟中心为原点、x 轴指向脚外侧”这种方式推导的那么左脚画出来的热力图跟右脚画出来的在视觉上就是互为镜像。这个在单张图上问题不大但要并排对比左右脚受力时建议都统一转换成“从脚背视角看”的坐标或者明确标注“足迹方向”。多帧数据做动画就更有意思了。把每一帧的压力值都过一遍同样的插值和掩膜流程然后生成图片列表用matplotlib.animation.FuncAnimation串起来。这里要给个性能提示96 个点插值到 500x200 的网格单帧耗时大约 100~300 毫秒30 帧的动画就是大概 3~9 秒。如果非常卡先把网格密度降到 300x120或者改用线性插值动画跑顺了再回到高精度出静态图。5. 我在实际项目中踩过的坑5.1 坐标错位导致的热力图漂移第一个大坑是坐标方向搞反。我最初做第一版的时候x 轴和 y 轴写反了热力图整体转了 90 度怎么调都觉得不对最后把传感器点一个一个画出来比对才发现问题。建议你在写代码第二步也就是映射坐标完成之后就先画一张散点图把传感器点位置跟鞋垫轮廓叠在一起看看。这一步只花一分钟能省掉后面一整天的排查时间。5.2 cubic 插值产生的“鬼影”高压区第二个问题是情绪化比较强的cubic 插值会在数据稀疏区域产生一些本来不存在的尖峰。我遇到过一次足弓部位只有一个传感器点周围全是边界cubic 插值在这个区域跑出一个数值比实际踩着后跟还高的“假高压区”。排查了半天最后把数据单独筛选出来看才发现是插值算法在“硬凹造型”。遇到稀疏区域要么直接用线性插值要么就把这个区域在网格中标记为无效别让算法自作主张。5.3 数据同步和数据缺失第三个很容易忽略的问题是数据同步和缺失值。有些柔性传感器阵列在采集时个别通道可能因为接触不良静置状态下是一条直线压力变化时完全没有响应。这类通道如果直接参与插值会在它的坐标点周围画出一块恒定低压区形成“死区假象”。对策是逐通道检查数据的标准差如果某个通道在压力变化期间的标准差几乎是 0基本可以断定是坏道果断剔除或者置为 NaN不要让坏数据污染整张热力图。5.4 关于色彩映射的一个忠告最后说一句色彩的事。热力图这东西颜色就是信息。很多人喜欢用彩虹色jet觉得鲜艳专业但你知道吗jet在低压区域是深蓝色、高压区域是红色中间会经过青、绿、黄这些都是视觉上容易被误读成“等距多级分类”的颜色本质上它的亮度不是单调的。我用coolwarm或者viridis之后很多合作方的反馈是“看着舒服多了也更可信了”。如果你们公司有自己的品牌色完全可以用LinearSegmentedColormap自定义色带把品牌色融入热力图展示效果会更有辨识度。回过头来看异形热力图这个需求看起来只是“画个图”真正做起来其实是一套完整的数据处理流程从物理坐标到逻辑坐标从离散采样到连续场重建再到几何裁剪和视觉呈现。每个环节都有独立的技术细节都不算难但环环相扣任何一环掉链子都会让成品图“一眼假”。希望这篇实操记录能帮你绕开我当年踩过的那些坑。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价