0%

vscode devcontainer中codex插件代理问题记录

问题环境:

  • 系统:Windows + Docker Desktop + VS Code Dev Container
  • 容器:road_ortho
  • 项目路径:/code/road_ortho_code/road_lunwen_code/merge_tiffs_lunwen_code_newest
  • 代理端口:宿主机 7897
  • 容器访问宿主机代理地址:http://host.docker.internal:7897

问题现象:

在 VS Code 的 Codex 插件中反复出现网络请求失败,日志类似:

1
2
3
4
Error fetching error="TypeError: fetch failed" url=https://ab.chatgpt.com/v1/initialize
Error fetching error="TypeError: fetch failed" url=https://chatgpt.com/ces/v1/rgstr
Error fetching error="TypeError: fetch failed" url=/wham/accounts/check
Error fetching error="TypeError: fetch failed" url=/wham/tasks/list?limit=20&task_filter=current

同时还可能出现:

1
unsupported feature enablement `workspace_dependencies`

以及 Git 或 VS Code Remote 日志中出现:

1
2
Failed to connect to 127.0.0.1 port 7897
Failed to establish a socket connection to proxies: PROXY 127.0.0.1:7897

问题原因

核心原因:容器内的 127.0.0.1 不是 Windows 宿主机。

在 Windows 上代理软件监听:

1
127.0.0.1:7897

但是 Codex 插件运行在 Docker 容器中。容器内部访问 127.0.0.1:7897 时,访问的是容器自己,而不是 Windows 宿主机,因此会连接失败。

在 Docker Desktop 环境中,容器访问宿主机应使用:

1
host.docker.internal

因此容器内正确代理地址是:

1
http://host.docker.internal:7897

需要修改的位置

这个问题不是只改一个环境变量即可,因为 VS Code Codex 插件涉及多层进程:

  • VS Code Remote Server
  • VS Code extensionHost
  • OpenAI ChatGPT/Codex 插件
  • Codex app-server
  • Git

所以需要同时处理以下位置。

1. VS Code Remote Server 环境变量

文件:

1
/root/.vscode-server/server-env-setup

内容:

1
2
3
4
5
6
7
8
export HTTP_PROXY=http://host.docker.internal:7897
export HTTPS_PROXY=http://host.docker.internal:7897
export ALL_PROXY=http://host.docker.internal:7897
export http_proxy=http://host.docker.internal:7897
export https_proxy=http://host.docker.internal:7897
export all_proxy=http://host.docker.internal:7897
export NO_PROXY=localhost,127.0.0.1,::1
export no_proxy=localhost,127.0.0.1,::1

2. VS Code Remote settings

文件:

1
/root/.vscode-server/data/Machine/settings.json

需要包含:

1
2
3
4
5
{
"http.proxy": "http://host.docker.internal:7897",
"http.proxyStrictSSL": false,
"http.proxySupport": "off"
}

其中:

  • http.proxy:设置容器可访问的代理地址
  • http.proxySupport: off:避免 VS Code Remote 继续把宿主机的 127.0.0.1:7897 代理注入到容器内部

3. Git 全局代理

在容器中执行:

1
2
git config --global http.proxy http://host.docker.internal:7897
git config --global https.proxy http://host.docker.internal:7897

检查:

1
git config --global --get-regexp proxy

正确结果应类似:

1
2
http.proxy http://host.docker.internal:7897
https.proxy http://host.docker.internal:7897

4. Codex 插件启动脚本

Codex 插件自带的启动文件位置类似:

1
/root/.vscode-server/extensions/openai.chatgpt-版本号-linux-x64/bin/linux-x86_64/codex

需要将其替换为 wrapper,使 Codex app-server 启动时继承正确代理:

1
2
3
4
5
6
7
8
9
10
11
12
13
#!/bin/sh
export HTTP_PROXY=http://host.docker.internal:7897
export HTTPS_PROXY=http://host.docker.internal:7897
export ALL_PROXY=http://host.docker.internal:7897
export http_proxy=http://host.docker.internal:7897
export https_proxy=http://host.docker.internal:7897
export all_proxy=http://host.docker.internal:7897
export NO_PROXY=localhost,127.0.0.1,::1
export no_proxy=localhost,127.0.0.1,::1
if command -v /usr/local/bin/codex >/dev/null 2>&1; then
exec /usr/local/bin/codex "$@"
fi
exec "$(dirname "$0")/codex.real" "$@"

其中:

  • 原始插件二进制保存为:codex.real
  • 优先使用 npm 安装的稳定版:/usr/local/bin/codex
  • 如果没有 npm 版本,则回退到插件自带的 codex.real

一键修复脚本

已经在项目中保存脚本:

1
/mnt/f/code/road_ortho/road_ortho_code/road_lunwen_code/merge_tiffs_lunwen_code_newest/scripts/fix_codex_vscode_proxy.sh

以后再次出现类似问题,直接在项目根目录执行:

1
./scripts/fix_codex_vscode_proxy.sh

脚本默认参数:

1
2
3
CONTAINER_NAME=road_ortho
PROXY_URL=http://host.docker.internal:7897
RESTART_VSCODE=1

如果容器名或代理端口变化,可以这样运行:

1
CONTAINER_NAME=road_ortho PROXY_URL=http://host.docker.internal:7897 ./scripts/fix_codex_vscode_proxy.sh

如果只想写配置,不想立即停止 VS Code 相关进程:

1
RESTART_VSCODE=0 ./scripts/fix_codex_vscode_proxy.sh

完整脚本代码

文件名:

1
scripts/fix_codex_vscode_proxy.sh

完整内容如下:

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
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
#!/usr/bin/env bash
set -euo pipefail

CONTAINER_NAME="${CONTAINER_NAME:-road_ortho}"
PROXY_URL="${PROXY_URL:-http://host.docker.internal:7897}"
RESTART_VSCODE="${RESTART_VSCODE:-1}"

if ! docker inspect "$CONTAINER_NAME" >/dev/null 2>&1; then
echo "Container not found: $CONTAINER_NAME" >&2
echo "Set CONTAINER_NAME=... if your container name changed." >&2
exit 1
fi

echo "Fixing VS Code Codex proxy in container: $CONTAINER_NAME"
echo "Proxy: $PROXY_URL"

docker exec \
-e PROXY_URL="$PROXY_URL" \
-e RESTART_VSCODE="$RESTART_VSCODE" \
"$CONTAINER_NAME" \
sh -s <<'EOF'
set -eu

proxy="${PROXY_URL:-http://host.docker.internal:7897}"
restart="${RESTART_VSCODE:-1}"
vscode_root="/root/.vscode-server"

echo "Writing $vscode_root/server-env-setup"
mkdir -p "$vscode_root"
cat > "$vscode_root/server-env-setup" <<ENVEOF
export HTTP_PROXY=$proxy
export HTTPS_PROXY=$proxy
export ALL_PROXY=$proxy
export http_proxy=$proxy
export https_proxy=$proxy
export all_proxy=$proxy
export NO_PROXY=localhost,127.0.0.1,::1
export no_proxy=localhost,127.0.0.1,::1
ENVEOF
chmod 644 "$vscode_root/server-env-setup"

echo "Writing VS Code machine settings"
mkdir -p "$vscode_root/data/Machine"
python3 - <<PYEOF
import json
from pathlib import Path

p = Path("/root/.vscode-server/data/Machine/settings.json")
try:
data = json.loads(p.read_text())
except FileNotFoundError:
data = {}
except json.JSONDecodeError:
backup = p.with_suffix(p.suffix + ".bak_proxyfix")
backup.write_text(p.read_text())
data = {}

data["http.proxy"] = "$proxy"
data["http.proxyStrictSSL"] = False
data["http.proxySupport"] = "off"
p.write_text(json.dumps(data, indent="\t", ensure_ascii=False))
PYEOF

echo "Writing Git proxy"
git config --global http.proxy "$proxy"
git config --global https.proxy "$proxy"

echo "Patching OpenAI ChatGPT/Codex extension launchers"
for codex_bin in /root/.vscode-server/extensions/openai.chatgpt-*/bin/linux-x86_64/codex; do
[ -e "$codex_bin" ] || continue
codex_dir="$(dirname "$codex_bin")"

if [ ! -f "$codex_dir/codex.real" ]; then
cp "$codex_bin" "$codex_dir/codex.real"
fi

cat > "$codex_bin" <<SH_EOF
#!/bin/sh
export HTTP_PROXY=$proxy
export HTTPS_PROXY=$proxy
export ALL_PROXY=$proxy
export http_proxy=$proxy
export https_proxy=$proxy
export all_proxy=$proxy
export NO_PROXY=localhost,127.0.0.1,::1
export no_proxy=localhost,127.0.0.1,::1
if command -v /usr/local/bin/codex >/dev/null 2>&1; then
exec /usr/local/bin/codex "\$@"
fi
exec "\$(dirname "\$0")/codex.real" "\$@"
SH_EOF
chmod +x "$codex_bin"
echo "Patched: $codex_bin"
done

echo "Current proxy config:"
git config --global --get-regexp proxy || true
cat "$vscode_root/server-env-setup"

if [ "$restart" = "1" ]; then
echo "Stopping VS Code server/extensionHost/Codex app-server processes."
# VS Code will restart these when the window/dev-container reconnects.
pkill -f "openai.chatgpt.*codex" 2>/dev/null || true
pkill -f "codex app-server" 2>/dev/null || true
pkill -f "bootstrap-fork --type=extensionHost" 2>/dev/null || true
pkill -f "out/server-main.js" 2>/dev/null || true
fi
EOF

echo
echo "Done."
if [ "$RESTART_VSCODE" = "1" ]; then
echo "Now run 'Reload Window' in VS Code, or reconnect the Dev Container."
fi

修复后需要做的操作

执行脚本后,需要在 VS Code 中执行:

1
Reload Window

或者重新连接 Dev Container。

原因是:

1
/root/.vscode-server/server-env-setup

只会在新的 VS Code Remote Server / extensionHost 启动时读取。旧进程不会自动更新已经继承的环境变量。

验证方法

查看 Codex 进程

1
docker exec road_ortho sh -lc 'ps aux | grep -E "openai.chatgpt|codex|extensionHost" | grep -v grep'

查看 Codex app-server 环境变量

1
docker exec road_ortho sh -lc 'pid=$(pgrep -f "codex app-server" | head -n 1); tr "\0" "\n" < /proc/$pid/environ | grep -i proxy'

正确结果应包含:

1
2
3
4
HTTP_PROXY=http://host.docker.internal:7897
HTTPS_PROXY=http://host.docker.internal:7897
ALL_PROXY=http://host.docker.internal:7897
NO_PROXY=localhost,127.0.0.1,::1

查看 Git 代理

1
docker exec road_ortho git config --global --get-regexp proxy

正确结果:

1
2
http.proxy http://host.docker.internal:7897
https.proxy http://host.docker.internal:7897

测试容器访问代理

1
docker exec road_ortho curl -I --max-time 8 -x http://host.docker.internal:7897 https://chatgpt.com

如果能返回 HTTP 响应,说明容器可以通过宿主机代理访问外网。

注意事项

unsupported feature enablement workspace_dependencies 不一定是代理问题。

这个错误表示当前 Codex app-server 不支持 VS Code 插件尝试启用的某个 experimental feature。它可能和插件版本、Codex CLI 版本有关。

但是如果同时出现:

1
2
3
TypeError: fetch failed
Failed to connect to 127.0.0.1 port 7897
PROXY 127.0.0.1:7897

则优先处理代理问题。

总结

这个问题的关键不是代理端口本身,而是 Docker 容器中的地址语义:

1
127.0.0.1:7897

在容器中表示容器自己,不是 Windows 宿主机。

因此容器内所有 VS Code、Codex、Git 相关代理都应统一改为:

1
http://host.docker.internal:7897

并在修改后重启 VS Code Remote / Dev Container,使新环境变量生效。

PWA小游戏发布到GitHub Pages并在iPhone离线使用

这里记录一个完整流程:把一个普通的 HTML/CSS/JS 小功能改造成 PWA 离线应用,发布到 GitHub Pages,然后在 iPhone Safari 中“添加到主屏幕”。第一次联网打开后,后面断网也可以从主屏幕图标进入。

示例地址:

1
https://ouwenwu.github.io/minesweeper/

其中 minesweeper 是仓库名。以后换成自己的项目时,把它替换为自己的仓库名即可。

一、核心思路

PWA 不需要打包成 iOS 安装包,也不需要 Xcode。它的核心是:

  • 用 HTML/CSS/JS 写好网页功能
  • 加一个 manifest.webmanifest,让系统知道应用名称、图标、启动方式
  • 加一个 service-worker.js,缓存页面、JS、CSS、图标等资源
  • 第一次通过 HTTPS 访问 GitHub Pages
  • 在 iPhone Safari 中点击“分享” -> “添加到主屏幕”
  • 之后从主屏幕图标打开,已经缓存的资源可以离线使用

注意:第一次必须联网访问一次,离线能力是在第一次访问时缓存下来的。

二、项目结构

一个最小结构可以这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
my-game/
├── index.html
├── package.json
├── public/
│ ├── manifest.webmanifest
│ ├── service-worker.js
│ └── icons/
│ ├── icon-192.png
│ ├── icon-512.png
│ └── apple-touch-icon.png
├── src/
│ ├── main.js
│ └── styles.css
└── vite.config.js

如果不是 Vite 项目,也可以直接用静态 HTML,只要最后发布出去的是 index.htmlmanifest.webmanifestservice-worker.js 和相关资源即可。

三、添加 manifest.webmanifest

public/manifest.webmanifest 中写入:

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
{
"name": "扫雷",
"short_name": "扫雷",
"description": "可离线游玩的扫雷小游戏",
"start_url": "./",
"scope": "./",
"display": "standalone",
"background_color": "#c8d2e1",
"theme_color": "#f3f6fb",
"orientation": "portrait",
"icons": [
{
"src": "./icons/icon-192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "any maskable"
},
{
"src": "./icons/icon-512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "any maskable"
}
]
}

图标至少准备:

  • public/icons/icon-192.png
  • public/icons/icon-512.png
  • public/icons/apple-touch-icon.png

其中 apple-touch-icon.png 是 iPhone 添加到主屏幕后显示的图标,建议使用 180x180 或更高分辨率的 PNG。

四、在 index.html 中引入 PWA 配置

index.html<head> 中加入:

1
2
3
4
5
6
7
8
<meta name="theme-color" content="#f3f6fb" />
<meta name="description" content="可离线游玩的扫雷小游戏" />
<meta name="mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-title" content="扫雷" />
<meta name="apple-mobile-web-app-status-bar-style" content="default" />
<link rel="manifest" href="./manifest.webmanifest" />
<link rel="apple-touch-icon" href="./icons/apple-touch-icon.png" />

这里使用 ./manifest.webmanifest./icons/...,是为了让项目部署到 GitHub Pages 的子路径时也能正常加载。

五、添加 service-worker.js

public/service-worker.js 中写入:

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
61
62
63
64
65
66
67
68
69
const CACHE_NAME = 'minesweeper-pwa-v1';
const APP_SHELL = [
'./',
'./index.html',
'./manifest.webmanifest',
'./icons/icon-192.png',
'./icons/icon-512.png',
'./icons/apple-touch-icon.png',
];

self.addEventListener('install', (event) => {
event.waitUntil(
caches
.open(CACHE_NAME)
.then(async (cache) => {
const shellUrls = APP_SHELL.map((path) => new URL(path, self.registration.scope));
await cache.addAll(shellUrls);

const indexUrl = new URL('./index.html', self.registration.scope);
const indexResponse = await fetch(indexUrl);
const indexHtml = await indexResponse.clone().text();
await cache.put(indexUrl, indexResponse);

const assetUrls = [...indexHtml.matchAll(/(?:src|href)="([^"]+)"/g)]
.map((match) => new URL(match[1], indexUrl))
.filter((url) => url.origin === self.location.origin);

await cache.addAll(assetUrls);
})
.then(() => self.skipWaiting()),
);
});

self.addEventListener('activate', (event) => {
event.waitUntil(
caches
.keys()
.then((cacheNames) =>
Promise.all(cacheNames.filter((name) => name !== CACHE_NAME).map((name) => caches.delete(name))),
)
.then(() => self.clients.claim()),
);
});

self.addEventListener('fetch', (event) => {
if (event.request.method !== 'GET') return;

event.respondWith(
caches.match(event.request).then((cachedResponse) => {
if (cachedResponse) return cachedResponse;

return fetch(event.request)
.then((networkResponse) => {
const shouldCache =
networkResponse.ok &&
new URL(event.request.url).origin === self.location.origin &&
['document', 'script', 'style', 'image', 'manifest'].includes(event.request.destination);

if (shouldCache) {
const responseCopy = networkResponse.clone();
caches.open(CACHE_NAME).then((cache) => cache.put(event.request, responseCopy));
}

return networkResponse;
})
.catch(() => caches.match(new URL('./index.html', self.registration.scope)));
}),
);
});

CACHE_NAME 是缓存版本号。以后修改了 JS、CSS、图标或缓存策略,建议把它改成新的名字,例如:

1
const CACHE_NAME = 'minesweeper-pwa-v2';

这样 iPhone 下次联网打开时会更新缓存。

六、注册 service worker

在入口 JS 中加入:

1
2
3
4
5
6
7
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register(`${import.meta.env.BASE_URL}service-worker.js`).catch((error) => {
console.info('Service worker registration failed:', error);
});
});
}

如果不是 Vite 项目,可以写成:

1
2
3
4
5
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('./service-worker.js');
});
}

七、Vite 项目配置相对路径

GitHub Pages 的项目站点通常是子路径,例如:

1
https://ouwenwu.github.io/minesweeper/

为了避免资源路径从域名根目录开始找,Vite 项目建议加 vite.config.js

1
2
3
4
5
import { defineConfig } from 'vite';

export default defineConfig({
base: './',
});

这样构建后的 JS、CSS 会使用相对路径,部署到 /minesweeper/ 这种子路径也能正常访问。

八、本地构建验证

安装依赖:

1
npm install

构建:

1
npm run build

本地预览:

1
npm run preview

确认 dist/ 中有这些文件:

1
2
3
4
5
6
dist/index.html
dist/manifest.webmanifest
dist/service-worker.js
dist/icons/icon-192.png
dist/icons/icon-512.png
dist/icons/apple-touch-icon.png

九、创建 GitHub 仓库

新建一个独立仓库,例如:

1
minesweeper

不要直接放到 ouwenwu.github.io 这个 Hexo 根站仓库里,避免影响原来的博客。独立仓库发布后访问地址是:

1
https://ouwenwu.github.io/minesweeper/

十、提交源码到 main 分支

在项目目录中执行:

1
2
3
4
5
6
git init
git add .
git commit -m "Add minesweeper PWA"
git branch -M main
git remote add origin https://github.com/ouwenwu/minesweeper.git
git push -u origin main

如果你的仓库名不是 minesweeper,把命令里的仓库地址改成自己的:

1
git remote add origin https://github.com/你的用户名/你的仓库名.git

十一、把 dist 发布到 gh-pages 分支

先确保已经构建:

1
npm run build

然后把 dist/ 单独推送到 gh-pages 分支:

1
2
git subtree split --prefix dist -b gh-pages
git push -u origin gh-pages

如果之前已经有本地 gh-pages 分支,更新时可以先删除本地临时分支再重新生成:

1
2
3
git branch -D gh-pages
git subtree split --prefix dist -b gh-pages
git push origin gh-pages --force

这里的 gh-pages 分支只放构建后的静态文件,main 分支保留源码。

十二、配置 GitHub Pages

进入仓库:

1
https://github.com/ouwenwu/minesweeper

然后:

  1. 点击 Settings
  2. 左侧点击 Pages
  3. Source 选择 Deploy from a branch
  4. Branch 选择 gh-pages
  5. 文件夹选择 / (root)
  6. 点击 Save

等待一两分钟后访问:

1
https://ouwenwu.github.io/minesweeper/

如果能打开页面,说明 GitHub Pages 已经发布成功。

十三、iPhone 添加到主屏幕

在 iPhone 上操作:

  1. 用 Safari 打开 https://ouwenwu.github.io/minesweeper/
  2. 等页面完整加载
  3. 点击 Safari 底部的“分享”按钮
  4. 选择“添加到主屏幕”
  5. 名称可以保持“扫雷”
  6. 点击“添加”

之后桌面会出现一个类似 app 的图标。点击它会以独立窗口打开,不像普通网页那样显示 Safari 地址栏。

十四、离线测试

第一次添加后,建议联网状态下从主屏幕图标打开一次,停留几秒,让 service worker 完成安装和缓存。

然后测试:

  1. 关闭 Wi-Fi 和蜂窝网络
  2. 从 iPhone 主屏幕点击应用图标
  3. 如果页面正常打开,说明离线缓存成功

如果第一次离线打不开,通常是缓存还没安装完成。重新联网打开一次,停留几秒,再断网测试。

十五、更新版本

修改代码后,按这个流程更新:

1
2
3
4
5
6
7
npm run build
git add .
git commit -m "Update PWA"
git push origin main
git branch -D gh-pages
git subtree split --prefix dist -b gh-pages
git push origin gh-pages --force

如果改动涉及缓存资源,记得更新 public/service-worker.js 里的缓存名:

1
const CACHE_NAME = 'minesweeper-pwa-v2';

iPhone 下次联网打开后会拿到新缓存。如果仍然看到旧页面,可以删除主屏幕图标后重新添加,或者在 Safari 中清理网站数据后再访问。

十六、常见问题

1. 为什么不能直接把源码作为 Pages 发布?

Vite 项目的开发版 index.html 通常引用 /src/main.js,浏览器不能像 Vite dev server 那样直接处理模块和打包。因此应发布 npm run build 生成的 dist/

2. 为什么推荐独立仓库?

因为 ouwenwu.github.io 已经是 Hexo 博客根站。小游戏放到独立仓库后,会发布到:

1
https://ouwenwu.github.io/minesweeper/

这样不会影响博客首页:

1
https://ouwenwu.github.io/

3. iPhone 能不能完全不联网使用?

第一次必须联网打开一次,因为需要下载网页资源并安装 service worker。只要缓存成功,后面就可以离线打开已经缓存的应用。

4. 这是不是 iOS 原生 app?

不是。它是 PWA,本质上还是网页应用,但可以添加到主屏幕,并且支持离线缓存。优点是不需要 Xcode 和 Apple 开发者账号;缺点是不能使用所有原生 iOS 能力。

十七、最终效果

完成后,可以把这个地址分享给别人:

1
https://ouwenwu.github.io/minesweeper/

对方在 iPhone Safari 打开后,也可以通过“分享” -> “添加到主屏幕”安装成一个离线可用的小应用。

高斯泼溅形状

为什么是椭球

一维高斯分布函数

一维高斯分布(Gaussian Distribution),通常被称为正态分布(Normal Distribution),其概率密度函数(PDF)的数学方程如下:
$$
f(x) = \frac{1}{\sigma\sqrt{2\pi}} e^{-\frac{1}{2}\left(\frac{x-\mu}{\sigma}\right)^2}
$$
当概率取某个值时,x也对应的为一个常数值

二维高斯分布函数

二维高斯分布(Bivariate Gaussian Distribution 或 2D Normal Distribution)是一维高斯分布在二维平面上的推广。它的图像不再是一条平面曲线,而是一个三维空间中的“山包”或“钟形曲面”。

**矩阵形式:**若二维随机向量 $\mathbf{x} = [x, y]^T$ 服从均值向量为 $\mathbf{\mu}$,协方差矩阵为 $\mathbf{\Sigma}$ 的正态分布,其概率密度函数为:
$$
f(\mathbf{x}) = \frac{1}{2\pi \sqrt{|\mathbf{\Sigma}|}} \exp\left( -\frac{1}{2} (\mathbf{x} - \mathbf{\mu})^T \mathbf{\Sigma}^{-1} (\mathbf{x} - \mathbf{\mu}) \right)
$$
其中:

  • $$\mathbf{x} = \begin{bmatrix} x \ y \end{bmatrix}$$是二维变量
  • $$ y=\begin{bmatrix}u_x \u_y \end{bmatrix}$$是均值向量,决定了中心点的位置
  • $\mathbf{\Sigma} = \begin{bmatrix} \sigma_x^2 & \rho\sigma_x\sigma_y \ \rho\sigma_x\sigma_y & \sigma_y^2 \end{bmatrix}$是协方差矩阵,决定了分布的形状、方向和散度

当概率取某个常数值时:
$$
\left{\begin{array}{l}(\mathbf{x} - \mathbf{\mu})^T \mathbf{\Sigma}^{-1} (\mathbf{x} - \mathbf{\mu})=constant_1 \
\frac{(x-\mu_x)^2}{\sigma_x^2} - 2\rho\frac{(x-\mu_x)(y-\mu_y)}{\sigma_x\sigma_y} + \frac{(y-\mu_y)^2}{\sigma_y^2}=constant_2
\end{array}\right.
$$
此时xy的分布是一个椭圆,当概率值从[0,1]发生变化时,则x和y组成很多个椭圆,整体来说构成一个实心的椭圆面

三维高斯分布函数

在二维中,高斯分布是一个平面上的“山包”(等高线是椭圆)。 到了三维中,高斯分布描述的是三维空间中的一个**“云团”“雾状球体”。它的等值面(密度相同点的集合)是三维空间中的椭球体 (Ellipsoid)**

设三维随机向量 $\mathbf{x} = [x, y, z]^T$,其概率密度函数为:
$$
f(\mathbf{x}) = \frac{1}{(2\pi)^{3/2} |\mathbf{\Sigma}|^{1/2}} \exp\left( -\frac{1}{2} (\mathbf{x} - \mathbf{\mu})^T \mathbf{\Sigma}^{-1} (\mathbf{x} - \mathbf{\mu}) \right)
$$
其中:

  • $\mathbf{x}$ - 输入变量 (Input Vector): 三维空间中的一个点。 $\mathbf{x} = \begin{bmatrix} x \cr y \cr z \end{bmatrix}$

  • $\mathbf{\mu}$ - 均值向量 (Mean Vector): 这个三维“云团”中心点的位置坐标。 $\mathbf{\mu} = \begin{bmatrix} \mu_x \cr \mu_y \cr \mu_z \end{bmatrix}$

  • $\mathbf{\Sigma}$ - 3x3 协方差矩阵 (Covariance Matrix):

    这是最核心的部分,它定义了椭球体的形状、大小和方向。$$\mathbf{\Sigma} = \begin{bmatrix} \sigma_x^2 & \sigma_{xy} & \sigma_{xz} \cr \sigma_{yx} & \sigma_y^2 & \sigma_{yz} \cr \sigma_{zx} & \sigma_{zy} & \sigma_z^2 \end{bmatrix}$$

    • 对角线元素 ($\sigma_x^2, \sigma_y^2, \sigma_z^2$):也就是方差,决定了椭球体沿 XYZ 三个轴向的拉伸程度(“胖瘦”)。
    • 非对角线元素 ($$\sigma_{xy}, \sigma_{xz},…$$):也就是协方差,表示了分布的x轴、y轴、z轴之间的关系,决定了椭球体在空间中的旋转姿态。如果它们全为 0,说明分布的xyz是独立的,椭球体的轴就与坐标轴平行。

    同时可以用来表示分布的特性

    • 各向同性:在各个方向具有相同的扩散程度(梯度)$\mathbf{\Sigma} = \begin{bmatrix} \sigma^2 & 0 & 0 \cr 0 & \sigma^2 & 0 \cr 0 & 0 & \sigma^2 \end{bmatrix}$,三个方向的方差相同,互不相关,则表示为一个正球体
    • 各向异性:在各个方向的均值不同,且方向相关(进行了旋转),因此高斯椭球具有各向异性,可以表示空间中的各种分布

当概率值取某个常数时$(\mathbf{x} - \mathbf{\mu})^T \mathbf{\Sigma}^{-1} (\mathbf{x} - \mathbf{\mu})=constant$,x的分布为椭球体,则当概率从[0,1]发生变化时,组成很多个椭球体,则所有的分布构成一个实心的椭球,这个就是高斯椭球

椭球形状的控制

上面已经推断出协方差矩阵$$\mathbf{\Sigma} = \begin{bmatrix} \sigma_x^2 & \sigma_{xy} & \sigma_{xz} \cr \sigma_{yx} & \sigma_y^2 & \sigma_{yz} \cr \sigma_{zx} & \sigma_{zy} & \sigma_z^2 \end{bmatrix}$$控制了椭球的中心和各方向上的方向,根据椭球在空间上可以通过球进行仿射变换得到推出椭球的协方差矩阵可以通过球的协方差矩阵通过变换得到,即任意高斯可以看作标准高斯通过仿射变换得到。

对于高斯分布$$x \sim N\left( \mu ,I \right) $$,假设其每个x均经过$$w=Ax+b$$的仿射变换,那么其新的分布为$$x \sim N(A\mu+b, AI A^T)$$

仿射变换可以分解为旋转和缩放,即$A=RS$,$$\Sigma=AIA^T=RSIS^TR^T$$,反之,通过协方差矩阵之后可以通过特征值分求出R和S,即将高斯椭球的协方差矩阵用R、S进行表示,R作为旋转矩阵又可以通过四元数q进行表示,即最终的椭球,我们通过q、s进行表示

所以对于椭球的形状共有4元数+缩放3+中心3共10个参数

椭球的颜色

球谐函数

要理解球谐函数,你只需要理解一个概念:傅里叶变换(Fourier Transform)

  1. 一维的情况(傅里叶级数): 想象一段复杂的声波(音乐)。我们知道,这段复杂的波形其实是由无数个简单的**正弦波(Sine waves)**叠加而成的。
    • 低频正弦波决定了整体的大调。
    • 高频正弦波决定了细节和音色。
    • 结论: 任何一维周期函数,都可以分解为一组正弦波的加权和。
  2. 二维的情况(球谐函数): 现在,把目光从“一根线(声波)”移到“一个球(地球仪或灯光球)”上。 如果我想描述一个球体表面的颜色分布、凹凸起伏或者是围绕球体的光照强度,该怎么办?
    • 球谐函数就是定义在球表面上的“正弦波”
    • 结论: 任何定义在球表面上的函数(比如地球的地形、环境光照),都可以分解为一组球谐函数的加权和。

一句话总结定义: 球谐函数就是球面上的基函数(Basis Functions),就像积木一样,通过组合不同形状的积木(球谐函数),你可以拼出任何复杂的球面信号。

球谐函数公式

$$
\begin{gathered}
y_{l}^{m}\left( \theta ,\varphi \right) =\left{ \begin{array}{l}
\sqrt{2}K_{l}^{m}\cos \left( m\varphi \right) P_{l}^{m}\left( \cos \theta \right) \ \ \ m>0\
\sqrt{2}K_{l}^{m}\sin \left( -m\varphi \right) P_{l}^{-m}\left( \cos \theta \right) \ m<0\
K_{l}^{0}P_{l}^{0}\left( \cos \theta \right) \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ m=0\
\end{array} \right. \cr
P_n\left( x \right) =\frac{1}{2^n\cdot n!}\frac{d^n}{dx^n}\left[ \left( x^2-1 \right) ^n \right] \cr
P_{l}^{m}=\left( -1 \right) ^m\left( 1-x^2 \right) ^{\frac{m}{2}}\frac{d^m}{dx^m}\left( P_l\left( x \right) \right) \cr
K_{l}^{m}=\sqrt{\frac{\left( 2l+1 \right) \left( l-|m| \right) !}{4\pi \left( l+|m| \right) !}}
\end{gathered}
$$

对于一个球谐函数有4个输入分别代表

  • $$\theta、\varphi$$: 指定的角度,也就是一个球谐函数可以求出空间中任意角度的值y,y代表的是这个球谐函数在这个角度的l,即长度
  • l(Degree, 阶数): 代表频率或细节程度、l越大球表面的褶皱越多,能表现的细节越明显。l=0是光滑的球;
  • m(order,阶次): 代表方向和纹理分布,范围m为整数,且$$-l<=m<=l$$. 正负代表了是否旋转90度

部件的作用:

  • $$K_{l}^{m}$$:归一化常数,使得l和m不管怎么变,球谐函数在球面上的积分都等于1
  • $$P_{l}^m$$:伴随勒让德多项式,这是核心形状生成器,专门负责切分南北(纬度)方向的形状

下面是l取3时m的所有取值所得出的表面图像:

m_3

椭球的颜色

我们要让椭球的颜色真实,就不能让椭球的颜色在各个角度看都意义,因此引入球谐函数,因为球谐函数恰好定义了每个不同的角度的值,我们可以将这个值作为颜色,也就是上图中不同方向的值不一样,代表颜色的占比不必要。

但是不能用一个球谐函数去代表颜色,因为实际上各个方向的并不规则,与傅里叶变换一样,可以用多个正余弦函数去拟合场景中的任意波形,因此,可以考虑多个球谐函数的叠加,去拟合空间中的任意椭球,也就是颜色的公式如下:
$$
\begin{aligned}
f\left( t \right) &\approx \sum_l{\sum_{m=-l}^l{c_{l}^{m}y_{l}^{m}\left( \theta ,\varphi \right)}}\
&=c_{0}^{0}y_{0}^{0}+\
&\quad c_{1}^{-1}y_{1}^{-1}+c_{1}^{0}y_{1}^{0}+c_{1}^{1}y_{1}^{1}+\
&\quad c_{2}^{-2}y_{2}^{-2}+c_{2}^{-1}y_{2}^{-1}+c_{2}^{0}y_{2}^{0}+c_{2}^{1}y_{2}^{1}+c_{2}^{2}y_{2}^{2}+\
&\quad c_{3}^{-3}y_{3}^{-3}+…\
\end{aligned}
$$
在高斯椭球颜色的渲染中,选择3阶即l等于3,其中的每个y都可以通过公式计算出来,所有的c为参数。且可以得出对于l,他的参数为2*l+1当最高取3时,所有的参数量为$$1+3+5+7=16$$,同时,这仅能代表一个颜色值,对于真实的RGB,则需要$$16\cdot 3=48$$个参数

高斯椭球参数

  • 形状参数:4个四元数+3个缩放+3个中心位置参数 =10
  • 颜色参数:16·3=48
  • 不透明度:1个$\alpha$

一共10+48+1=59个参数

本篇介绍我的世界的各种特性

**铁傀儡刷新机制:**村民被吓+区域内5个以上村民,僵尸吓村民表现为晚上上床后立马起床

简易刷铁机的制作

原料:

任意方框: 泥土、玻璃、石头均可以

收集装置

首先搭建下面的装置,一格深的装置,两个小箱子组成的大箱子----箱子*2 方块*15

收集1

接下来放置9个漏斗,漏斗连接到箱子上,方法是shift+右键点击对应方块,即可将漏斗连接到方块上、将红色线圈内的漏斗连接到箱子,其余漏斗依次连接

检验是否安装成果: 向漏斗里面丢东西,最后可以在箱子里面找到

漏斗安装

铁傀儡处死装置

在收集装置基础上向上搭建20格,并形成通道,部分区域可简化,以节省方块,当然也可以想底下挖,将收集装置位于-20处以节省方块,需要方块16*20=320,告示牌*9

玻璃通道

在距收集装置2格处放置告示牌,悬浮告示牌放置需要将光标指向告示牌右侧 shift+右键单机(下面的截图均高了一格)

告示牌放置方法

告示牌安置结果

在告示牌安置上一个放置岩浆,上图红框范围

岩浆放置结果

铁傀儡生成

搭建铁傀儡生成后的移动平台,利用水流将铁傀儡推到处死装置里

搭建9*9的方格,并在方格外侧添加围栏,挡住水的流动

铁傀儡流动平台

先在中心添加栅栏门,挡住水流向下的移动,在四个角的方块上放置水源,将铁傀儡向中心推动

![水流装置](水流装置.p

僵尸放置轨道

在水流上方四格处,放置一个横着的9格,并搭建为如图所示,其中最上方为半砖

僵尸行动轨道

在僵尸过道两侧建立有3*3*2空间的空间的村民房

村民房

放入床并封顶,注意床要顶格放,否则僵尸吓不到村民,如果夜晚村民睡觉了,说明距离太远了,以及注意床的方向,床的方向不对,村民不会睡觉

村民床

在最底下可以放置灵魂篝火,以加快效率

灵魂篝火

注意:

铁傀儡生成规则

一、ContextCapture先降低图像分辨率做空三再恢复继续做

首先下采样图像分辨率,一般为30%

下降分辨率

提交空三

image-20250804100937058

二、已有图像初始位姿,计算准确位姿

针对于:手持激光扫描仪由于时间同步或标定或者其他问题导致的图像位姿不准,但已有较好的初值,需要计算更为准确的位姿

将初始位姿转为blockexchange 的xml格式

这个格式是COntextCapture、Photoscan等软件的通用格式

image-20250804142715704

contextCapture导入xml结果

在已有位姿上提交空三

contextCapture提交空三

最后导出block,在metashape中生成稀疏点云即可

指定ip走指定的网卡

本文实验适用于,vpn走wifi的网卡,其余走网线,这样vpn不会影响其余端口的实用

查询本地网卡与网关

1
route print

网卡与网关信息

其中:10.104.255.254是网线的网关、192.168.1.1是wifi的网关

添加条件:指定的ip走指定的网关

1
route add 219.139.228.228 mask 255.255.255.255 192.168.1.1 metric 10 if 6

其中219.139.228.228是指定的ip 192.168.1.1是这个ip走的网关,其中6是指wifi对于的接口

wifi接口信息

即可实现219.139.228.228走wifi,其余走网线

常见命令

拉取容器

1
sudo docker pull image_name

创建镜像

1
docker run -i -d --gpus all --shm-size=24g -v F:/ubuntu/docker_data/fast-livo2:/data --name fast-livo2 cuda_11_8
  • -d: 容器在后台运行,并返回容器的id
  • –gpus all: 该镜像可以访问到的gpu设置为全部
  • –shm-size=24g: 设置容器的共享内存,默认为64MB,对于深度学习等应用来说,不够
  • -v local_path:image_path : 将容器路径映射到本地路径,用于存放代码和数据等等
  • –name: 容器名称
  • cuda_11_8: 镜像名称

进入已有容器的命令行进行操作

1
2
docker ps -a
docker exec -it <容器id或名称> /bin/bash

删除容器和镜像

1

加载本地镜像

常见问题

vscode连接远程docker权限问题

Failed to connect. Is Docker runing?

1
sudo chmod 777 /var/run/docker.sock

vscode连接远程本地docker版本冲突

Failed to connect to the remote extension host server (Error: Connection error: Client refused: version mismatch)

1
2
pkill -f vscode-server || true
rm -rf ~/.vscode-server ~/.vscode-server-insiders

pyinstaller常用参数

  • -F: 打包为一整个exe

  • -D: 打包为一个文件夹

  • -w: 运行时不显示命令行窗口(仅对windows有效)

  • -i: 图标路径

基础命令