0%

推开那扇木门,我瞬间懂了什么叫”合法的恋物癖”。这里不像商店,倒像电影里那种高智商连环杀手的收藏室——没有血腥,只有令人窒息的秩序感,和对死亡与静止之美的病态迷恋。

玻璃罩里,巨大的海胆骨骼白得像骷髅,巨齿蛉透明的翅膀上脉络清晰,仿佛下一秒就要冲出来咬断你的喉咙。架子上,甲虫标本整齐排列,黑色甲壳闪着冷光,像一支沉默的军队。松果、种子荚、矿物原石,被像珠宝一样陈列着,透着一种诡异的、偏执的美学张力。

在这个信息爆炸、注意力只有15秒的时代,我们习惯了被碎片信息狂轰滥炸,什么都知道一点,却对什么都不甚了解。而在这里,时间仿佛凝固了。

如果有时有闲,能花几个小时去研究一颗矿石的结晶,或者辨认一只蝴蝶翅膀的鳞片,那该是怎样一种奢侈的快乐?它不是多巴胺的瞬间爆发,而是内啡肽的持久回甘。

走出店门,手里多了一只蓝色闪蝶。看着它静止的翅膀,我突然觉得,在这个疯狂加速的世界里,做一个迷恋”死物”的怪人,或许才是找回人生乐趣的捷径。毕竟,只有当你真正凝视一只甲虫的时候,你才是活着的。

连阴了几日,周末还飘了雨,网上刷到北京金顶妙峰山的路,山桃花铺满一路,美得让人想立刻开车去。真去了,天却阴沉沉的,心里又装着些杂事,总觉得不够清爽。

今天终于放晴,一出门就撞进蓝蓝的天里,那股再走一趟的念头又冒出来,偏被工作绊住脚。突然就怕了 “下次” 这个词 —— 年龄长了,才懂 “下次” 多虚浮。看桃花这事,过了这一周,今年就再没机会,得等明年。可明年的周末会不会晴?会不会加班、出差?谁都说不准。

花有重开日,人无再少年。这般错过,想想就唏嘘。有些事当下没做,下次未必真能成,就像这花期,就像这年岁,过去了,就真的过去了。

花有重开日

花有重开日

花有重开日

花有重开日

花有重开日

花有重开日

花有重开日

花有重开日

晴天了

下午出来洗车时,我坐在车里,水枪的力道撞在车玻璃上,溅起的水花先是星星点点,慢慢连成一片雾。水声隔着车厢漫过来,像被棉花滤过似的,蒙着层不真切的软。看水雾一点点爬满窗,把外面的树影、车声都晕成模糊的色块,世界忽然就静了——不是没声音的那种静,是心里的躁被这层水膜轻轻罩住的静,想就这么多待一会儿,什么都不用急。

正发着呆,微信读书里的声音刚好飘过来:“一生一世!” 是《霍乱时期的爱情》最后那句。明明是不相干的瞬间,偏就这么撞上了。泡沫还在车窗上慢慢淌,外面的光透过水层变得温温柔柔,心里的澎湃却像被什么东西托着,稳稳的,又满当当的。

忽然就懂了什么叫心流——不是刻意去追的状态,是这一刻的静、这句刚好的话、这满窗的泡沫,凑在一起,把人轻轻裹住了。时间好像慢了半拍,又好像刚刚好,连呼吸都跟着轻了。

其实现在的AI真的很强,随便选择一家的产品,都能得到想要的结果。从这一方面来看,用博客来记录,或多或少都没有什么太大必要。因此在这篇里,主要记录个思路,细节上的调整都可以交给大模型来处理。

我的初衷是来自于写了一个自动发布博客的脚本,每天定时用hexo发布github pages。但由于中国大陆访问github总是不太稳定,脚本经常失败。想找一个既能保证推送稳定,又能使用github pages的方法。这才想到是不是可以将gitee.io作为源头,每次用hexo发布到gitee.io。将github.io作为目的地,同步gitee.io的内容,发布github pages博客。

主要的步骤其实很简单:

  1. 在gitee建立一个名称为 XXXXX.gitee.io 的仓库

  2. 在github建立一个名称为 XXXXX.github.io 的仓库

  3. 在本地生成SSH密钥

    1
    ssh-keygen -t ed25519 -C "your_email@example.com"

    按提示操作

    1
    2
    3
    4
    5
    6
    7
    8
    # 第1次提示:选择保存位置(直接回车使用默认位置)
    > Enter file in which to save the key (/Users/yourname/.ssh/id_ed25519): [回车]

    # 第2次提示:设置 passphrase(直接回车不设密码)
    > Enter passphrase (empty for no passphrase): [回车]

    # 第3次提示:确认 passphrase(直接回车)
    > Enter same passphrase again: [回车]

    会生成id_ed25519id_ed25519.pub

  4. 配置Gitee SSH公钥

    • 登录Gitee访问https://gitee.com/profile/sshkeys

    • 点击 「添加公钥」按钮

    • 填写信息。标题 GitHub Actions Sync (可自定义);公钥内容黏贴id_ed25519.pub 的完整内容

    • 点击 「确定」

  5. 配置Github Secrets

    • 在Github仓库XXXXX.github.io 点击 「Settings」 标签

    • 左侧菜单找到 「Secrets and variables」 → 「Actions」

    • 添加2个Secrets

      Secret 名称 说明
      GITEE_SSH_PRIVATE_KEY id_ed25519 私钥完整内容 用于 Gitee SSH 认证
      GITEE_PAGES_REPO_URL git@gitee.com:用户名/仓库名.git Gitee 仓库 SSH 地址
  6. 创建GitHub Actions工作流

    • 在Github仓库XXXXX.github.io 创建 .github/workflows/sync-from-gitee.yml 文件

    • 粘贴以下配置到文件中

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      name: Sync from Gitee

      on:
      schedule:
      - cron: '*/10 * * * *'
      workflow_dispatch:

      jobs:
      sync:
      runs-on: ubuntu-latest

      steps:
      # 1. 检出 GitHub 仓库(启用凭证持久化)
      - name: Checkout GitHub Repo
      uses: actions/checkout@v4
      with:
      fetch-depth: 0
      persist-credentials: true # ✅ 关键:启用凭证持久化

      # 2. 配置 SSH 密钥(仅用于 Gitee)
      - name: Setup SSH Key for Gitee
      run: |
      mkdir -p ~/.ssh
      echo "${{ secrets.GITEE_SSH_PRIVATE_KEY }}" > ~/.ssh/id_ed25519
      chmod 600 ~/.ssh/id_ed25519
      ssh-keyscan -H gitee.com >> ~/.ssh/known_hosts
      git config --global user.name "GitHub Actions"
      git config --global user.email "actions@github.com"

      # 3. 添加 Gitee 远程仓库
      - name: Add Gitee Remote
      run: |
      git remote add gitee ${{ secrets.GITEE_PAGES_REPO_URL }}

      # 4. 从 Gitee 拉取最新代码
      - name: Fetch from Gitee
      run: |
      git fetch gitee
      # 自动检测 Gitee 的主分支名称
      GITEE_BRANCH=$(git remote show gitee | grep 'HEAD branch' | awk '{print $NF}')
      git checkout -b gitee-temp gitee/$GITEE_BRANCH 2>/dev/null || \
      git checkout -b gitee-temp gitee/main 2>/dev/null || \
      git checkout -b gitee-temp gitee/master

      # 5. 合并代码到主分支
      - name: Merge Gitee to GitHub
      run: |
      # 自动检测本地主分支名称
      LOCAL_BRANCH=$(git remote show origin | grep 'HEAD branch' | awk '{print $NF}')
      git checkout $LOCAL_BRANCH
      git merge gitee-temp --no-edit --allow-unrelated-histories
      git branch -D gitee-temp

      # 6. 推送到 GitHub(使用 GITHUB_TOKEN)
      - name: Push to GitHub
      run: |
      LOCAL_BRANCH=$(git remote show origin | grep 'HEAD branch' | awk '{print $NF}')
      git push origin $LOCAL_BRANCH
      env:
      GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # ✅ 关键:使用 GitHub Token
  7. 开启读写权限

    • 在仓库 Settings 找到 Code and automation

    • 点击 Actions → General

    • 将 Workflow permissions 改为 Read and write permissions

2025年在网上跟着老师画了不少水彩画,控水不容易,尤其对于云朵把握的还不好。不过更重要的是自己要开心,不要有太多的功利心,画得好看更开心一些,主要是要让自己能够换换脑筋,让紧张的神经放松下来。

2025-01-30

2025-02-02

2025-02-04

2025-02-16

2025-03-01

2025-04-12

2025-04-13

2025-04-20

2025-05-04

2025-05-05

2025-05-31

2025-06-01

2025-06-02

2025-06-07

2025-09-21

2025-11-16

2025-12-07

2025-12-28

目前还没有完全确认使用的正确性,使用OpenCL的推理速度远慢于CPU的推理速度

D4: RK3399上MNN的OpenCL推理

1. MNN的OpenCL支持

可以在CMakeLists.txt里直接修改,打开OpenCL支持

1
2
3
4
5
6
7
8
9
10
11
# backend options
option(MNN_METAL "Enable Metal" OFF)
option(MNN_OPENCL "Enable OpenCL" ON)
option(MNN_OPENGL "Enable OpenGL" OFF)
option(MNN_VULKAN "Enable Vulkan" OFF)
option(MNN_ARM82 "Enable ARM82" OFF)
option(MNN_ONEDNN "Enable oneDNN" OFF)
option(MNN_AVX512 "Enable AVX512" OFF)
option(MNN_CUDA "Enable CUDA" OFF)
option(MNN_TENSORRT "Enable TensorRT" OFF)
option(MNN_COREML "Enable CoreML" OFF)

如果是交叉编译,为了方便起见,关闭MNN_USE_SYSTEM_LIB不使用系统中的依赖库

1
2
# build options
option(MNN_USE_SYSTEM_LIB "For opencl and vulkan, use system lib or use dlopen" OFF)

编译后会生成libMNN_CL.so库。

2. MNN推理配置

在创建会话的时候,通过修改配置,让MNN使用OpenCL进行推理。

1
2
3
4
std::shared_ptr<MNN::Interpreter> net(MNN::Interpreter::createFromFile(_param_path.c_str()));
MNN::ScheduleConfig config;
config.type = MNN_FORWARD_OPENCL;
session = net->createSession(config);

3. 主函数修改

为了能够正确调用OpenCL,需要在main函数中手动加载libMNN_CL.so库。否则MNN会报错找不到OpenCL backend。

1
2
3
4
5
6
#include <dlfcn.h>
int main(int argc, char *argv[])
{
dlopen("libMNN_CL.so", RTLD_NOW);
...
}

踩了两个坑,一个是MNN的交叉编译,一个是OpenCV的编译。编译方法都很直接,主要记录一下坑吧。

D3: MNN和OpenCV编译

1. MNN的交叉编译

其实直接参照MNN文档的示例,就可以编译完成。

1
2
3
4
5
6
7
8
9
export cross_compile_toolchain=linaro/aarch64
mkdir build && cd build
cmake .. \
-DCMAKE_SYSTEM_NAME=Linux \
-DCMAKE_SYSTEM_VERSION=1 \
-DCMAKE_SYSTEM_PROCESSOR=aarch64 \
-DCMAKE_C_COMPILER=$cross_compile_toolchain/bin/aarch64-linux-gnu-gcc \
-DCMAKE_CXX_COMPILER=$cross_compile_toolchain/bin/aarch64-linux-gnu-g++
make -j4

最开始编译的时候遇到了一个类型转换的问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
[ 36%] Built target MNNTransform
[ 36%] Built target MNNUtils
[ 36%] Building CXX object CMakeFiles/MNNCPU.dir/source/backend/cpu/compute/WinogradInt8Helper.cpp.o
/data/mnn/source/backend/cpu/compute/WinogradInt8Helper.cpp: In function 'void MNN::TRANS_4x4(MNN::VecType&, MNN::VecType&, MNN::VecType&, MNN::VecType&)':
/data/mnn/source/backend/cpu/compute/WinogradInt8Helper.cpp:39:48: note: use -flax-vector-conversions to permit conversions between vectors with differing element types or numbers of subparts
auto m0 = vtrn1q_s32(vec0.value, vec1.value), m1 = vtrn2q_s32(vec0.value, vec1.value);
^
/data/mnn/source/backend/cpu/compute/WinogradInt8Helper.cpp:39:48: error: cannot convert 'int8x16_t {aka __vector(16) signed char}' to 'int32x4_t {aka __vector(4) int}' for argument '1' to 'int32x4_t vtrn1q_s32(int32x4_t, int32x4_t)'
/data/mnn/source/backend/cpu/compute/WinogradInt8Helper.cpp:40:48: error: cannot convert 'int8x16_t {aka __vector(16) signed char}' to 'int32x4_t {aka __vector(4) int}' for argument '1' to 'int32x4_t vtrn1q_s32(int32x4_t, int32x4_t)'
auto m2 = vtrn1q_s32(vec2.value, vec3.value), m3 = vtrn2q_s32(vec2.value, vec3.value);
^
/data/mnn/source/backend/cpu/compute/WinogradInt8Helper.cpp:42:29: error: 'm1' was not declared in this scope
vec1.value = vtrn1q_s64(m1, m3);
^~
/data/mnn/source/backend/cpu/compute/WinogradInt8Helper.cpp:42:33: error: 'm3' was not declared in this scope
vec1.value = vtrn1q_s64(m1, m3);
^~
CMakeFiles/MNNCPU.dir/build.make:2054: recipe for target 'CMakeFiles/MNNCPU.dir/source/backend/cpu/compute/WinogradInt8Helper.cpp.o' failed
make[2]: *** [CMakeFiles/MNNCPU.dir/source/backend/cpu/compute/WinogradInt8Helper.cpp.o] Error 1
CMakeFiles/Makefile2:141: recipe for target 'CMakeFiles/MNNCPU.dir/all' failed
make[1]: *** [CMakeFiles/MNNCPU.dir/all] Error 2
Makefile:129: recipe for target 'all' failed
make: *** [all] Error 2

参考官方github上的#1560即可解决。

2. OpenCV编译

其实没啥好说的,主要是有些组件需要在线拉取,国内这个网络环境实在是令人无语,花了几个美元租了国外的云服务,直接编译就可以。编译方法参考官方教程即可。

本来以为可以很顺利地开始玩耍了,结果只把开发环境搭建成功了,交叉编译MNN失败:(

D2: 开发环境搭建

因为是为了兴趣而做的小开发,平时又太忙,基本都是零碎时间来敲代码,所以目标是在平时办公的装Windows10的笔记本上搭建开发环境,如果是Linux应该会方便很多。

1. 启动WSL2

开启虚拟机平台

用管理员权限打开PowerShell,运行以下命令。或者新建WSL2.bat脚本,以管理员权限运行。

1
dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart

安装WSL2内核

下载WSL2内核更新包,相关说明可以查阅微软的文档

重启系统后,用管理员权限打开PowerShell,运行以下命令,将WSL2设置为默认版本

1
wsl --set-default-version 2

2. 安装DOCKER

安装

我安装的是Docker Desktop 3.5.1,直接下载安装即可,注意勾选“Use WSL 2 based engine”选项。我的系统是家庭版,默认勾选此选项。

Docker Desktop

修改镜像存储位置(可选操作)

由于系统盘空间有限,将Docker的默认根目录调整到数据盘。

  1. 退出Docker Desktop

  2. 确认所有WSL应用已退出,所有应用都应该是stop状态

    1
    wsl --list -v
  3. 迁移docker-desktop

    1
    2
    3
    wsl --export docker-desktop "D:\DOCKER-ENGINE\docker-desktop.tar"
    wsl --unregister docker-desktop
    wsl --import docker-desktop D:\DOCKER-ENGINE\run "D:\DOCKER-ENGINE\docker-desktop.tar" --version 2
  4. 迁移docker-desktop-data

    1
    2
    3
    wsl --export docker-desktop-data "D:\DOCKER-ENGINE\docker-desktop-data.tar"
    wsl --unregister docker-desktop-data
    wsl --import docker-desktop-data D:\DOCKER-ENGINE\data "D:\DOCKER-ENGINE\docker-desktop-data.tar" --version 2

3. 使用Ubuntu 18.04镜像建立开发容器

建立基础容器

  1. Docker Hub上直接拉取镜像

    1
    docker pull ubuntu:18.04
  2. 启动容器

    1
    docker run -it -v D:\DOCKER-SHARE:\data --name toy-project ubuntu:18.04 bash

基础环境安装

  1. 更新源

    1
    apt-install update
  2. 安装开发基础库

    1
    apt-get install repo git-core gitk git-gui gcc-arm-linux-gnueabihf u-boot-tools device-tree-compiler gcc-aarch64-linux-gnu mtools parted libudev-dev libusb-1.0-0-dev python-linaro-image-tools linaro-image-tools gcc-arm-linux-gnueabihf libssl-dev liblz4-tool genext2fs lib32stdc++6 gcc-aarch64-linux-gnu g+conf autotools-dev libsigsegv2 m4 intltool libdrm-dev curl sed make binutils build-essential gcc g++ bash patch gzip bzip2 perl tar cpio python unzip rsync file bc wget libncurses5 libqt4-dev libglib2.0-dev libgtk2.0-dev libglade2-dev cvs git mercurial rsync openssh-client subversion asciidoc w3m dblatex graphviz python-matplotlib libssl-dev texinfo fakeroot libparse-yapp-perl default-jre patchutils lib32gcc-7-dev g++-7 libstdc++-7-dev
  3. 使用交叉编译工具链

    下载工具链,我用的是Linaro工具链,按常规方式编译程序,例如MNN的示例是

    1
    2
    3
    4
    5
    6
    7
    8
    9
    export cross_compile_toolchain=linaro/aarch64
    mkdir build && cd build
    cmake .. \
    -DCMAKE_SYSTEM_NAME=Linux \
    -DCMAKE_SYSTEM_VERSION=1 \
    -DCMAKE_SYSTEM_PROCESSOR=aarch64 \
    -DCMAKE_C_COMPILER=$cross_compile_toolchain/bin/aarch64-linux-gnu-gcc \
    -DCMAKE_CXX_COMPILER=$cross_compile_toolchain/bin/aarch64-linux-gnu-g++
    make -j4

离开一线技术研发有点久了,最近一直在做偏市场和研发管理的角色。总觉得应该稍微调整一下,听从一个前辈的建议:每天都做一点自己感兴趣的小项目,让自己对技术保持嗅觉。因此,打算鼓捣鼓捣点RK3399Pro上的小东西。

D1: RK3399Pro操作系统Ubuntu安装

我购买的是Firefly RK3399Pro JD4开发板,配有核心板和底板。商家还贴心的给了一个U盘,内有各种工具和文档。因为我自己不会安装开发,第一件事就是把板子的系统重新烧成Ubuntu18.04。

0. 连接开发板

开发板上电,通过USB口连接PC主机。

1. 安装USB驱动助手

在PC主机上直接打开“DriverInstall.exe”,安装USB驱动

安装驱动

2. 烧录系统

首先,准备好固件镜像。例如我选用的是“AIO-RK3399PRO-JD4-Ubuntu_18.04.5_LTS_DESKTOP_PYTHON3.5-RKNN-OPENCV-20210413-1746.img”,基本的环境都已经安装后,后续使用比较方便。如果需要做极致优化,再考虑手动编译内核和库的安装。

其次,烧系统。使用“AndroidTool.exe”进行固件烧录,虽然叫AndroidTool,但是也可以烧录Ubuntu系统。在“Update Firmware”标签页中,点击“Firmware”选择上述固件路径。

最后,注意连接开发板。按住开发板上的Recovery键,轻按Reset键,直到烧录工具显示“Found One LOADER Device”,松开按键。点击Upgrade开始烧录。烧录进度完成后,重启连接屏幕,即可进入Ubuntu桌面系统。

烧录系统