2025年9月

参考链接

https://docs.sillytavern.app/installation/docker/


为酒馆创建目录,例如~/sillyTavern,然后创建以下目录

mkdir config data plugins extensions

创建脚本文件start.sh,写入下面的内容并保存,执行脚本(需要魔法),它便开始自动拉取最新的酒馆镜像,映射端口(默认为8000,可自行修改),映射目录,在后台启动容器。

#!/bin/bash
SILLYTAVERN_VERSION="latest"
PUBLIC_PORT="8000"
CONFIG_PATH="./config"
DATA_PATH="./data"
PLUGINS_PATH="./plugins"
EXTENSIONS_PATH="./extensions"

docker run -d \
  --name="sillytavern" \
  -p "$PUBLIC_PORT:8000/tcp" \
  -v "$CONFIG_PATH:/home/node/app/config:rw" \
  -v "$DATA_PATH:/home/node/app/data:rw" \
  -v "$EXTENSIONS_PATH:/home/node/app/public/scripts/extensions/third-party:rw" \
  -v "$PLUGINS_PATH:/home/node/app/plugins:rw" \
  ghcr.io/sillytavern/sillytavern:"$SILLYTAVERN_VERSION"

接下来就可以通过http:127.0.0.1:8000来访问酒馆了。

image.png

被白名单机制屏蔽了,下面显示的被屏蔽的IP是宿主机(也就是网关)在docker默认网络中的IP,我们把这个IP加入到./config/config.yaml中去。

# 找到以下内容,将宿主机IP追加到后面
whitelist:
  - ::1
  - 127.0.0.1

重启容器。

docker container restart sillytavern

image.png

成功。

1. 高斯分布

假定信号传播过程中的噪声,在实部和虚部都是高斯分布的:均值为0,方差为$\sigma^2$,

$$ noise = a + bj \quad, \quad a \sim N(0, \sigma^2), \quad b \sim N(0, \sigma^2) $$

高斯分布的噪声,它的平均功率被称为AWGN(Average White Gaussian Noise),

$$ P_n = 2\sigma^2 $$

对噪声时域序列做傅立叶变换,那么频谱中每一个频率点的噪声复数,同样是高斯分布的,

$$ F(k) = A + Bj \quad, \quad A \sim N(0, \sigma^2), \quad B \sim N(0, \sigma^2) $$


2. 卡方分布

如果我们对噪声傅立叶变换结果(我们只关注其中一个频率点,后文省略符号$k$)取能量(模方),得到能量频谱$C$,

$$ C = A^2 + B^2 $$

令$X = A/\sigma$,$Y = B/\sigma$,则$X$和$Y$就是标准正态分布的。令$Z = X^2 + Y^2$,则$Z$是自由度为2的中心卡方分布,$Z \sim \chi_2^2$。根据自由度为$k$的卡方分布的概率密度$f(x, k)$

$$ f(x, k) = \frac 1{2^{\frac k2}\Gamma(\frac k2)} x^{\frac k2 - 1} e^{-\frac x2} $$

我们可以写出$Z$的概率密度$f_Z(z)$为

$$ f_Z(z) = \frac 12 e^{- \frac z2} $$

由于$C = A^2 + B^2 = \sigma^2 (X^2 + Y^2) = \sigma^2 Z$,所以$C$也服从自由度为2的卡方分布,$C \sim \chi_2^2$,下面推导$C$的概率密度

$$ \begin{align} \int_{\infty} f_C(c) dc &= \int_{\infty} f_Z(z) dz \\ \int_{\infty} f_C(\sigma^2 z) \sigma^2 dz &= \int_{\infty} f_Z(z) dz \\ f_C(\sigma^2 z) \sigma^2 &= \frac 12 e^{- \frac z2} \\ f_C(c) &= \frac 1{2 \sigma^2} e^{- \frac c{2 \sigma^2}} \end{align} $$

这个形式同时也是$\lambda = \frac 1{2 \sigma^2}$的指数分布。


3. Gamma分布

$shape = \alpha$,$velocity = \beta$的$Gamma(\alpha, \beta)$分布的概率密度为

$$ f(x, \alpha, \beta) = \frac {\beta^{\alpha}}{\Gamma(\alpha)} x^{\alpha - 1} e^{-\beta x} \quad,(x \ge 0) $$

参数为$\lambda$的指数分布是Gamma分布的特殊形式,它的$\alpha = 1$,$\beta = \lambda$,那么能量频谱$C$实际上也服从$Gamma(1, \frac 1{2 \sigma^2})$分布。

如果我们对能量频谱进行积分(在离散的场景下对应为求和),定义积分次数为$N$的能量频谱积分$X$为

$$ X = \Sigma_{i = 0}^N C_i $$

由于$C$是$Gamma$分布的,并且$Gamma$分布具有可加性($N$个$Gamma(\alpha, \beta)$分布的随机变量之和服从$Gamma(N\alpha, \beta)$分布),所以能量频谱积分$X$服从$Gamma(N, \frac 1{2 \sigma^2})$分布,我们写出$X$的概率密度,

$$ f(x, N, \frac 1{2 \sigma^2}) = \frac 1{(N - 1)! 2 (\sigma^2)^N} x^{N - 1} e^{-\frac x{2 \sigma^2}} \quad,(x \ge 0) $$

3.1 累积分布函数

累积分布函数CDF(Cumulative Distribution Function)是概率密度从$-\infty$到$x_0$的积分,对于服从$Gamma(N, \frac 1{2 \sigma^2})$分布的$X$来说,CDF为

$$ CDF(x_0, N, \frac 1{2 \sigma^2}) = \int_0^{x_0} f(x, N, \frac 1{2 \sigma^2}) dx $$

积分结果无法用初等函数表示,如果给定概率$P(0 \le x \le x_0)$,我们就暂且把对应的阈值$x_0$使用CDF的反函数表示出来(具体值可以通过数值计算求解)

$$ x_0 = CDF^{-1}(P(0 \le x \le x_0), N, \frac 1{2 \sigma^2}) $$

3.2 尺度变换

$Gamma$分布的尺度因子$\theta$和速率因子$\beta$互为倒数,即$\beta \theta = 1$,对速率因子进行$n$倍缩放,等价于对尺度因子进行$\frac 1n$倍缩放。设$y_1 \sim Gamma(\alpha, \beta)$,$y_2 \sim Gamma(\alpha, n\beta)$,两者的概率密度分别为

$$ \begin{align} f(y_1) &= \frac {\beta^{\alpha}}{\Gamma(\alpha)} y_1^{\alpha - 1} e^{-\beta y_1} \quad,(y_1 \ge 0) \\ g(y_2) &= \frac {(n \beta)^{\alpha}}{\Gamma(\alpha)} y_2^{\alpha - 1} e^{-n\beta y_2} \quad,(y_2 \ge 0) \\ &= n \cdot \frac {\beta^{\alpha}}{\Gamma(\alpha)} (ny_2)^{\alpha - 1} e^{-\beta (n y_2)} \\ &= n \cdot f(ny_2) \end{align} $$

令$\alpha = N$,$\beta = 1$,$n = \frac 1{2 \sigma^2}$,则能量频谱积分$X$的CDF为

$$ \begin{align} CDF(x_0, N, \frac 1{2 \sigma^2}) &= \int_0^{x_0} f(x, N, \frac 1{2 \sigma^2}) dx \\ &= \int_0^{x_0} nf(nx, N, 1) dx \\ &= \int_0^{x_0} f(nx, N, 1) d(nx) \\ &= \int_0^{n x_0} f(y, N, 1) dy \\ &= \int_0^{\frac {x_0}{2 \sigma^2}} f(y, N, 1) dy \\ &= CDF(\frac {x_0}{2 \sigma^2}, N, 1) \\ \end{align} $$

$$ \begin{align} &\therefore \frac {x_0}{2 \sigma^2} = CDF^{-1} (P(0 \le y \le \frac {x_0}{2 \sigma^2}), N, 1) \\ &\because P(0 \le x \le x_0) = P(0 \le y \le \frac {x_0}{2 \sigma^2}) \\ &\therefore x_0 = 2 \sigma^2 \cdot CDF^{-1}(P(0 \le x \le x_0), N, 1) \end{align} $$


4. 虚警率及其门限

定义虚警率$P_{fa}$为:噪声的能量频谱积分$X(k)$在$M$个频率点中的至少一个频率点处的能量积分超过门限值$x_0$的概率。

$$ P_{fa} = 1 - (P(0 \le x \le x_0))^M $$

其中$P(0 \le x \le x_0)$是能量频谱积分在任一频率点处,能量积分的值都不超过$x_0$的概率。若虚警率是给定的常数,则

$$ P(0 \le x \le x_0) = 1 - P_{fa}^{\frac 1M} $$

将这个表达式代入到尺度变换后的能量积分频谱的CDF阈值中,可得阈值$x_0$为

$$ x_0 = 2 \sigma^2 \cdot CDF^{-1}(1 - P_{fa}^{\frac 1M}, N, 1) $$

将噪声的平均功率$P_n$代入上式,

$$ x_0 = P_n \cdot CDF^{-1}(1 - P_{fa}^{\frac 1M}, N, 1) $$

这就是虚警率为$P_{fa}$对应的捕获门限值。其中的CDF反函数值,在虚警率、积分次数和频谱宽度给定的情况下,是与噪声平均功率无关的常数K,我们可以提前用数值求解方法计算出来。

$$ \begin{align} K &= CDF^{-1}(1 - P_{fa}^{\frac 1M}, N, 1) \\ x_0 &= P_n \cdot K \end{align} $$

0. 本文参考链接

https://www.bilibili.com/opus/750451200508297219

https://www.bilibili.com/video/BV1h84y1o7Zp/?spm_id_from=333.1007.top_right_bar_window_custom_collection.content.click

https://docs.pikvm.org/wol/

https://docs.pikvm.org/faq/

https://github.com/pikvm/pikvm/blob/master/docs/wol.md

https://toolshu.com/docker-mirror


1. 制作PiKVM

1.1 物料和硬件

物品名称作用
orangePi-zeroPiKVM服务器本体。
Micro SD卡作为PiKVM服务器的硬盘。(>=8G)
采集卡被控机的HDMI输出作为输入数据(HDMI输入),同时转发给显示器(HDMI输出)和PiKVM服务器(Micro-USB输出)。
网线将PiKVM服务器接入网络。
USB-typeA to Micro-USB数据线PiKVM模拟键鼠信号,通过USB发送给被控机。
  • orangePi-zero只有一个USB-typeA和一个Micro-USB接口(它同时也是电源输入接口)。
  • 采集卡的Micro-USB连接至orangePi-zero的USB接口传输数据,同时它也给采集卡供电。
  • orangePi-zero需要使用一个USB接口连接至被控机,发送模拟键鼠信号。

考虑到接口数量限制,系统的供电方案分为以下三种:

被控机是否支持关机USB供电有无USB-HUB供电方案
-采集卡的Micro-USB连接至orangePi-zero的USB-typeA,orangePi-zero的Micro-USB连接至被控机的USB-typeA
×orangePi-zero的Micro-USB连接至5V电源,orangePi-zero的USB-typeA连接至USB-HUB,USB-HUB连接至采集卡的Micro-USB和被控机的USB-typeA
××采集卡的Micro-USB连接至orangePi-zero的USB-typeA,使用特制的线缆将orangePi-zero的Micro-USB连接至被控机的USB-typeA,同时连接至5V电源

方案三和它使用的特质线缆制作方法参考这篇B站文章

1.2 下载和烧录镜像

这里下载名称为"v2-hdmiusb-generic-arm-orangepi-zero.img.bz2"的镜像文件,将镜像文件烧录到Micro SD卡中,然后插入到orangePi-zero中,上电开机,等几秒钟后主板上的绿灯亮起,就可以了。


2. 配置用户

在路由器后台找到orangePi-zero的IP地址,直接在浏览器中访问,使用默认用户名admin和默认密码admin登录。

PiKVM_web.png

登陆后,显示了这个页面,选择中间的"Terminal"进入终端。

         _____ _        _  ____      ____  __
        |  __ (_)      | |/ /\ \    / /  \/  |
        | |__) |   __  | ' /  \ \  / /| \  / |
        |  ___/ | (__) |  <    \ \/ / | |\/| |
        | |   | |      | . \    \  /  | |  | |
        |_|   |_|      |_|\_\    \/   |_|  |_|

    Welcome to Pi-KVM - Open Source IP-KVM based on Raspberry Pi
    ____________________________________________________________________________

    The root filesystem of Pi-KVM is mounted in read-only mode by default.
    Use command "rw" to remount it in the RW-mode and "ro" to switch it back.

    To prevent kernel messages from printing to the terminal use "dmesg -n 1".

    To change KVM password use command "kvmd-htpasswd set admin".

    Useful links:
      * https://pikvm.org
      * https://wiki.archlinux.org/index.php/Network_configuration

首先修改root用户密码,默认密码是root。执行下面的命令。

# login root
su
# enter read-write mode
rw
# edit root password
passwd

然后删除掉web管理界面的默认用户admin,创建你自己的用户,并设置密码。

# delete user "admin"
kvmd-htpasswd del admin
# create your user for web-control
kvmd-htpasswd set YourUserName
# restart service
systemctl restart kvmd kvmd-nginx

3. 配置WOL (Wake On Line)

3.1 被控机

以Windows被控机为例,在设备管理器中找到网络适配器,找到网卡设备,右键选择属性。

设备管理器.png

在“高级”选项中,将“关机 网络唤醒”和“魔术封包唤醒”的值修改为“开启”。

高级.png

在“电源管理”选项中,勾选“允许此设备唤醒计算机”。

电源管理.png

3.2 PiKVM

打开文件/etc/kvmd/override.yaml,使用以下内容覆盖该文件。

kvmd:
    wol:
        mac: 38:60:77:cc:ed:b7

其中mac填写你的被控机对应网卡的MAC地址,注意,这里使用的是官方文档给出的单被控机用法,只支持一台被控机WOL(由于系统镜像的原因,ugpio库缺少wol插件,所以目前无法通过官方给出的gpio的方式WOL多台被控机,或许可以使用exec脚本而非gpio的方式实现,有兴趣可以自行研究)。

修改完成后,重启kvmdkvmd-nginx服务。

systemctl restart kvmd kvmd-nginx

在浏览器输入IP重新进入PiKVM控制页面,选择KVM,把右上角的“System”标签展开后可以看到新增了一项“Wake on Line Server”。

WOL.png

点击后,PiKVM就会广播发送魔术包,默认端口号是9,但只有MAC地址与配置文件中一致的设备(被控机)才会执行WOL,其他设备会把包丢弃。

WOL2.png

现在可以看到被控机完成了WOL,并且视频输出正常。

开机.png

开机2.png


4. 在docker部署zerotier(可选)

4.1 安装docker

直接使用可执行文件二进制包,在docker官网找到armhf版本(我们使用的PiKVM镜像OS运行在armv7l架构,32位arm),下载下来。将解压后的可执行文件复制到/usr/local/bin目录中即可使用。

4.2 更换国内可访问的docker镜像源

在配置文件/etc/docker/daemon.json写入以下内容

{
        "registry-mirrors": [
                "https://docker.1ms.run",
                "https://docker.m.ixdev.cn",
                "http://docker.m.daocloud.io",
                "https://dockerproxy.net",
                "https://xdark.top"
        ]
}

重启docker,因为我们是直接使用docker二进制文件,所以只需要将守护进程dockerd杀死,重新执行。

# kill
ps | grep dockerd | awk '{print $1;}' | xargs kill -9
# restart
dockerd &

4.3 部署

为了方便维护,建议在宿主机创建一个配置文件目录(例如/path/to/config),映射到容器中。执行下面的命令创建并运行zerotier容器。

docker run -d \
  --name 容器名称 \
  --restart=always \
  --device=/dev/net/tun \
  --cap-add=NET_ADMIN \
  --network=host \
  -v /path/to/config:/var/lib/zerotier-one \
  zerotier/zerotier:latest \
  你的网络hash

其中,--cap-add=NET_ADMIN给容器控制网络的权限,--network=host让容器使用宿主机的网络命名空间,最后将容器的CMD设置为你的网络hash,这样容器一启动就自动加入网络。

创建好容器后,进入容器的bash,使用下面的命令追踪你自己的私有moon节点。

# enter container
docker exec -it 容器名称或hash bash
# orbit
zerotier-cli orbit 你的moon节点hash 你的moon节点hash