第 9 节

其他操作

作者
董佳辉的头像董佳辉
发布于 2024/03/30更新于 2026/10/06

工控机如何连wifi

当我们工控机从一个环境移动到另一个环境,且新的环境的wifi我们没有连接过,我们没有屏幕如何让工控机连上网呢?

进入工控机的硬盘目录,进入/etc/netplan/,底下有很多yaml,找到一个最符合下方格式的yaml:

network: 
  version: 2 
  wifis: 
    NM-6f414fe0-2658-48ff-89ee-c7981b87bc96: 
      renderer: NetworkManager 
      match: 
        name: "wlan0" 
      dhcp4: true 
      dhcp6: true 
      access-points: 
        "EMIS_Vinci_Robocon_5G": 
          auth: 
            key-management: "psk" 
            password: "vinci666" 
          networkmanager: 
            uuid: "6f414fe0-2658-48ff-89ee-c7981b87bc96" 
            name: "EMIS_Vinci_Robocon_5G" 
            passthrough: 
              wifi-security.auth-alg: "open" 
              ipv6.addr-gen-mode: "default" 
              ipv6.ip6-privacy: "-1" 
              proxy._: "" 
      networkmanager: 
        uuid: "6f414fe0-2658-48ff-89ee-c7981b87bc96" 
        name: "EMIS_Vinci_Robocon_5G"

将其改为新wifi的名和密码:

network:
  version: 2
  wifis:
    wlan0:
      dhcp4: true
      dhcp6: true
      access-points:
        "EMIS_Vinci_Robocon_5G":
          password: "vinci666"

工控机开机即可连上网。

SSH远程开发

环境准备

1.硬件准备

首先要有一台工控机(X86小型电脑,专业工控机,树莓派等等)。

控制系统的硬件载体是具有多样性的,常用的多是基于ARM、x86等架构的处理器,比如:PC、工控机、树莓派、NVIDIA Jetson...。不同的处理器都存在一定的优缺点,PC和工控机,处理器性能强大,但是功耗高、体积大、灵活性差,嵌入式系统则反之。对于我们而言,可以根据机器人平台的电气、载重、空间以及用途等诸多要素自主选择合适的控制系统。

无论选用何种处理器,只要是要进行机器人的开发,安装了ROS或者ROS2,那么对于开发人员而言,在使用上,没有任何本质的区别,或者换言之,作为软件工程师,部分场景下无需关注于硬件的选型。

2.设置固定IP

远程连接时,不管使用何种工具,需要根据IP地址定位到被连接的主机,再通过账号和密码登录该主机,因此,我们需要先获取该IP地址。并且每次连接时,都需要使用到IP,为了保证连接的便利性和稳定性,最好将被连接主机的IP地址设置为固定IP,具体操作如下。

1.进入设置界面

启动被连接的主机(启动时需要连接显示器或使用HDMI采集卡,并且配置完SSH远程访问之后,可以不再使用显示器或HDMI采集卡),并进入设置界面。

2.配置所连接的网络

3.设置固定IP

查看当前IP地址。

设置固定IP。

至此,IP配置完毕。

环境搭建

概念

SSH(Secure Shell)是一种通用的、功能强大的、基于软件的网络安全解决方案。计算机每次向网络发送数据时,SSH都会自动对其进行加密。数据到达目的地时,SSH自动对加密数据进行解密。整个过程都是透明的,使用OpenSSH工具将会增进你的系统安全性。SSH安装容易、使用简单。

实现

SSH实现架构上分为客户端和服务器端两大部分,客户端是数据的发送方,服务端是数据的接收方,当前场景下,我们需要从本地主机发送数据到远程主机,那么本地主机需要安装并启动SSH客户端,而远程主机则需要安装并启动SSH服务端,整个实现具体流程如下:

  1. 本地主机安装SSH客户端,远程主机安装SSH服务端;

  2. 远程主机启动SSH服务;

  3. 本地主机登陆远程主机;

  4. 实现数据传输。

1.安装SSH客户端与服务端

默认情况下,Ubuntu系统已经安装了SSH客户端,因此只需要在远程主机安装SSH服务端即可,安装命令如下:

 sudo apt install openssh-server

如果SSH客户端需要自行安装,那么调用如下命令:

sudo apt install openssh-client

2.远程主机启动SSH服务

远程主机启动 ssh 服务命令如下:

sudo /etc/init.d/ssh start

启动后可使用如下命令查看服务是否正常运行:

ps -e | grep ssh

如果启动成功,会包含 sshd 程序。

以后需要频繁的使用ssh远程登录,为了简化实现,可以将远程主机的ssh服务设置为开机自启动,命令如下:

sudo systemctl enable ssh

3.本地远程登录

登录远程主机可以调用如下命令:

ssh -X 用户名@ip地址

然后根据提示,录入登陆密码,即可成功登录。

如果退出,可以调用exit命令:

exit

4.实现数据传输

通过SSH在本地主机只需调用相关指令,便可方便的实现与远程主机的数据上传或下载,指令格式如下所示:

上传文件指令格式如下:

scp 本地文件路径 账号@ip:路径

上传文件夹指令格式如下:

scp -r 本地文件夹路径 账号@ip:路径

下载文件指令格式如下:

scp 账号@ip:路径 本地文件夹路径

下载文件夹指令格式如下:

scp -r 账号@ip:路径 本地文件夹路径

优化

每次远程登录时,都需要输入密码,使用不方便,可以借助密钥简化登录过程,实现免密登录,提高操作效率。实现思想是:生成一对公钥私钥,私钥存储在本地,公钥上传至服务器,每次登录时,本地直接上传私钥到服务器,服务器有匹配的公钥就认为是合法用户,直接创建SSH连接即可。具体实现步骤只有两步:

  1. 本地生成密钥对;

  2. 将公钥上传至远程主机。

1.生成密钥对

本地客户端生成公私钥:(一路回车默认即可)

ssh-keygen

上面这个命令会在用户目录.ssh文件夹下创建公私钥:

  1. id_rsa (私钥);

  2. id_rsa.pub (公钥)。

2.公钥上传

上传指令如下:

ssh-copy-id -i ~/.ssh/id_rsa.pub 账号@ip

上面这条指令是将本地公钥上传到远程主机的ssh目录下,该目录下有文件authorized_keys保存了公钥内容。

以后再远程登录就无需录入密码了。

使用VScode优化

上一节,我们介绍了ssh远程连接的使用,但是如果只是纯粹使用ssh也存在一些不足,比如:编辑文件内容时,需要使用vi编辑器,且在一个终端内,无法同时编辑多个文件。本节将介绍一更为实用的功能——VSCode结合插件实现远程开发,这使我们可以以图形化的方式实现远程开发,比ssh的使用更方便快捷,可以大大的提高程序开发效率。

1.准备工作

VScode远程开发依赖于ssh,请首先按照上一节内容配置ssh远程连接。

2.为VScode安装远程开发插件

启动VScode,首先点击侧边栏的扩展按钮,然后在扩展:商店的搜索栏输入Remote Development并点击同名插件,最后在右侧显示区中点击安装。

3.配置远程连接

步骤1:使用快捷键ctrl + shift + p打开命令输入窗口,并输入Remote-SSH:Connect to Host...,弹出列表中选择与之同名的选项。

步骤2:步骤1完成将弹出一个新的命令窗口如下,选择下拉列表中的 Add New SSH Host。

步骤3:步骤2完成又将弹出一个新的命令窗口,在其中输入:ssh -X ubuntu@192.168.43.164,其中,ubuntu需要替换为你的登录用户名,192.18.43.164 则替换为被远程连接主机的ip地址。

步骤4:选择步骤3完成后的弹窗列表中的第一个选项(或直接回车),即可完成配置,配置成功后会有提示信息。

4.使用

步骤1:继续使用快捷键ctrl + shift + p打开命令输入窗口,并输入Remote-SSH:Connect to Host...,此时列表中将显示步骤3中配置的ip地址,直接选择,选择后,VScode将打开一个新的窗口。

或者,也可以点击侧边栏的远程资源管理器,在弹出的服务器列表中选择要连接的服务器,并右击,选择在本窗口或新窗口中实现远程连接。

步骤2:选择菜单栏的文件下的打开文件夹,在弹窗列表中选择需要打开的文件夹并点击确定即可。

最终,我们就可以像操作本地文件一样实现远程开发了。

进阶

使用hostname进行ssh

当一个设备他的IP一直变化,可以用hostname来进行连接。

可以先看你要远程开发的设备的hostname是什么?

hostname

比如输出的hostname是Dell-G15-5511

那么在这个设备上装上avahi-daemon:

sudo apt install avahi-daemon
sudo systemctl enable --now avahi-daemon

然后你就可以在同局域网的电脑上尝试:

ping Dell-G15-5511.local

如果能够ping通就可以了。

那么以后ssh就可以

ssh 用户名@Dell-G15-5511.local

远程桌面

因为用X11转发效率太低太低,所以还是要选用远程访问桌面的形式来看Rviz2和Gazebo等等(如果有需求的话)

我们选择使用VNC来看。

  1. 安装VNC服务器:

这里以TigerVNC为例进行安装:

sudo apt install tigervnc-standalone-server tigervnc-common
  1. 设置VNC密码:

为VNC用户设置密码,运行以下命令:

vncpasswd
  1. 配置VNC启动脚本:

创建VNC配置文件,在用户家目录内的.vnc文件夹中创建启动文件:

mkdir -p ~/.vnc
nano ~/.vnc/xstartup

在打开的编辑器里面输入以下内容(以GNOME为例):

#!/bin/sh
unset SESSION_MANAGER
unset DBUS_SESSION_BUS_ADDRESS
gnome-session

保存并退出(在nano中按Ctrl + O保存,然后按Ctrl + X退出)。

  1. 给予执行权限:

为xstartup文件设置执行权限:

chmod +x ~/.vnc/xstartup
  1. 启动VNC服务器:

vncserver -geometry 1920x1080 -localhost=0

geometry 选项指定窗口大小,localhost 选项设为 0 以开放连接。(设为1是只允许本地连接)

你将看到类似于:1的输出,这表示VNC会话的显示编号。例如,如果输出为:1,则VNC监听的端口为5901(5900 + 显示编号)。

  1. 查看已开启的VNC服务器:

vncserver -list

  1. 连接到VNC服务器:

使用VNC客户端(如vncviewer)连接到VNC服务器,输入你的服务器IP和端口。例如,如果服务器IP为192.168.31.10,且显示编号为1,你应该连接到192.168.31.10:1,或者直接输入192.168.31.10:5901。

  1. 停止VNC服务器:

如果需要停止VNC服务器,可以使用以下命令:

vncserver -kill :1

将:1替换为你实际使用的显示编号。

开关图形界面

可以先看当前默认模式:

systemctl get-default

如果是graphical.target就是有图形模式,如果是multi-user.target就是命令行模式。

关掉图形界面(设置为命令行模式):

sudo systemctl isolate multi-user.target

默认关闭图形界面(默认进入命令行模式):

sudo systemctl set-default multi-user.target

重启

sudo reboot

USB设备名持久化

USB端口设置

在 Linux 中,USB 串口设备插拔之后,系统分配的设备节点可能会发生变化,例如:

/dev/ttyUSB0
/dev/ttyUSB1
/dev/ttyACM0
/dev/ttyACM1

如果程序、ROS 节点或者启动脚本直接写死 /dev/ttyUSB0,当设备重新插拔、增加新的 USB 设备或者系统启动顺序发生变化时,就可能出现端口号改变的问题。

因此,在机器人、无人车、嵌入式设备等场景中,一般需要给 USB 设备建立一个稳定的设备路径或固定别名。

常见方案主要有:

  1. 使用 Linux 自动生成的 /dev/serial/by-id/;

  2. 使用 Linux 自动生成的 /dev/serial/by-path/;

  3. 根据设备的 idVendor、idProduct、serial 等属性编写 udev 规则;

  4. 根据主机 USB 物理接口的位置编写 udev 规则;

  5. 对摄像头等设备使用 /dev/v4l/by-id/ 或 /dev/v4l/by-path/。


首先配置串口权限

Linux 下普通用户默认可能没有串口设备的访问权限,因此首先将当前用户加入 dialout 用户组。

#### 将当前用户加入 dialout 用户组
sudo usermod -aG dialout $USER

#### 临时在当前终端加载新的用户组
newgrp dialout

#### 查看当前用户组中是否存在 dialout
groups

如果输出中包含:

dialout

说明当前终端已经具有串口访问权限。

也可以注销当前桌面会话后重新登录,使用户组权限在整个用户会话中生效。


优先使用 Linux 自带的固定设备路径

在自己编写 udev 规则之前,建议首先查看系统是否已经为设备生成了稳定的符号链接。

对于 USB 串口设备:

ls -l /dev/serial/by-id/
ls -l /dev/serial/by-path/

例如:

usb-1a86_USB_Serial-if00-port0 -> ../../ttyUSB0
usb-STMicroelectronics_STM32_Virtual_ComPort_123456-if00 -> ../../ttyACM0

其中:

  • /dev/serial/by-id/:主要根据设备本身的身份信息生成;

  • /dev/serial/by-path/:主要根据设备连接到主机的 USB 物理路径生成。


使用 /dev/serial/by-id/

如果设备具有稳定且唯一的序列号,通常优先使用:

/dev/serial/by-id/xxx

例如:

/dev/serial/by-id/usb-STMicroelectronics_STM32_Virtual_ComPort_123456-if00

即使对应的设备节点从:

/dev/ttyACM0

变成:

/dev/ttyACM2

by-id 路径通常仍然保持不变。

因此,ROS 2 参数中可以直接写:

port: /dev/serial/by-id/usb-STMicroelectronics_STM32_Virtual_ComPort_123456-if00

这种方案最大的优点是:

  • 不依赖 ttyUSBn 或 ttyACMn 的编号;

  • USB 设备换到其他接口后,一般仍然能够识别;

  • 对于有唯一序列号的设备非常稳定;

  • 不需要自己编写 udev rules。


使用 /dev/serial/by-path/

如果设备没有唯一序列号,或者系统无法通过设备本身区分多台完全相同的设备,可以使用:

/dev/serial/by-path/

查看:

ls -l /dev/serial/by-path/

例如:

pci-0000:00:14.0-usb-0:3:1.0-port0 -> ../../ttyUSB0
pci-0000:00:14.0-usb-0:4:1.0-port0 -> ../../ttyUSB1

这种方式实际上是根据:

设备插在主机的哪个 USB 接口

来区分设备。

例如无人车有两个完全相同的雷达:

前雷达 -> 固定插在 USB 口 A
后雷达 -> 固定插在 USB 口 B

此时可以直接在程序中使用两个不同的 by-path 路径。

缺点是:

如果交换 USB 接口,设备对应的逻辑身份也会发生变化。


查看 USB 设备信息

使用 lsusb

接入 USB 设备之后,可以首先执行:

lsusb

例如:

Bus 001 Device 007: ID 1a86:7523 QinHeng Electronics CH340 serial converter

其中:

1a86:7523

分别对应:

idVendor  = 1a86
idProduct = 7523

需要注意:

idVendor + idProduct 只能表示某一类 USB 产品,通常不能唯一表示某一个具体设备。

例如两块完全相同的 CH340:

CH340 A -> 1a86:7523
CH340 B -> 1a86:7523

它们的 idVendor 和 idProduct 是完全相同的。

因此:

  • 不同型号设备可以通过 idVendor + idProduct 区分;

  • 多个相同型号设备不能只依赖 idVendor + idProduct 区分。


查看设备完整属性

对于串口设备,可以使用:

udevadm info --query=property --name=/dev/ttyACM0

或者:

udevadm info --query=property --name=/dev/ttyUSB0

只查看常用信息:

udevadm info --query=property --name=/dev/ttyACM0 \
    | grep -E 'ID_(VENDOR_ID|MODEL_ID|SERIAL|SERIAL_SHORT|PATH|USB_INTERFACE_NUM)'

常见输出:

ID_VENDOR_ID=0483
ID_MODEL_ID=5740
ID_SERIAL=STMicroelectronics_STM32_Virtual_ComPort_123456
ID_SERIAL_SHORT=123456
ID_PATH=pci-0000:00:14.0-usb-0:3:1.0
ID_USB_INTERFACE_NUM=00

常用属性说明:

属性含义
ID_VENDOR_IDUSB 厂商 ID
ID_MODEL_IDUSB 产品 ID
ID_SERIAL完整设备序列信息
ID_SERIAL_SHORTUSB 设备序列号
ID_PATH设备连接到主机的物理路径
ID_USB_INTERFACE_NUMUSB Interface 编号

查看 udev 可匹配属性

如果需要自己编写 udev 规则,可以使用:

udevadm info -a -p $(udevadm info -q path -n /dev/ttyACM0)

或者:

udevadm info -a -p $(udevadm info -q path -n /dev/ttyUSB0)

也可以使用:

udevadm info --attribute-walk --name=/dev/ttyUSB0

例如只查看 KERNELS:

udevadm info --attribute-walk --name=/dev/ttyUSB0 | grep KERNELS

这些信息可以用于编写自定义 udev rules。


根据 USB 设备型号绑定端口

需求

Ubuntu 系统中连接一台雷达和一台智能小车,两台设备型号不同,希望给它们建立固定别名。

例如:

雷达 -> /dev/mylidar
小车 -> /dev/mycar

查找 idVendor 和 idProduct

调用:

lsusb

假设得到:

雷达:10c4:ea60
小车:1a86:7523

其中:

10c4 -> idVendor
ea60 -> idProduct

1a86 -> idVendor
7523 -> idProduct

编写 udev 规则

在:

/etc/udev/rules.d/

目录下创建规则文件,例如:

sudo vim /etc/udev/rules.d/99-usb-serial.rules

输入:

KERNEL=="ttyUSB*", ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="ea60", MODE:="0777", SYMLINK+="mylidar"

KERNEL=="ttyUSB*", ATTRS{idVendor}=="1a86", ATTRS{idProduct}=="7523", MODE:="0777", SYMLINK+="mycar"

其中:

  • KERNEL:匹配 Linux 内核分配的设备名称;

  • ATTRS{idVendor}:USB 厂商 ID;

  • ATTRS{idProduct}:USB 产品 ID;

  • MODE:="0777":设置设备节点权限;

  • SYMLINK+="xxx":为设备建立固定符号链接。

例如:

/dev/mylidar -> /dev/ttyUSB0
/dev/mycar   -> /dev/ttyUSB1

使规则生效

Ubuntu 和 Fedora 都可以统一使用:

sudo udevadm control --reload-rules
sudo udevadm trigger

然后重新插拔 USB 设备。

也可以只触发 tty 子系统:

sudo udevadm control --reload-rules
sudo udevadm trigger --subsystem-match=tty

测试
ls -l /dev/mylidar
ls -l /dev/mycar

或者:

readlink -f /dev/mylidar
readlink -f /dev/mycar

可能得到:

/dev/ttyUSB0
/dev/ttyUSB1

缺点

这种方式只适合:

不同 USB 型号的设备。

如果接入两台完全相同型号的设备:

CH340 A -> 1a86:7523
CH340 B -> 1a86:7523

那么两个设备都会匹配同一条 udev rule。

因此:

KERNEL=="ttyUSB*", ATTRS{idVendor}=="1a86", ATTRS{idProduct}=="7523", SYMLINK+="mycar"

不能可靠地区分两个完全相同的 USB 设备。

此时需要继续使用:

  • USB Serial Number;

  • /dev/serial/by-id/;

  • /dev/serial/by-path/;

  • 主机 USB 物理接口。


根据设备 Serial Number 绑定端口

如果多台设备型号相同,但是每台设备具有不同的 USB 序列号,那么这是最推荐的自定义 udev 绑定方式。

例如:

设备 A:
idVendor  = 1a86
idProduct = 55d4
serial    = 0001

设备 B:
idVendor  = 1a86
idProduct = 55d4
serial    = 0002

可以编写:

KERNEL=="ttyACM*", ATTRS{idVendor}=="1a86", ATTRS{idProduct}=="55d4", ATTRS{serial}=="0001", MODE:="0777", SYMLINK+="ttyACM_Lidar"

KERNEL=="ttyACM*", ATTRS{idVendor}=="1a86", ATTRS{idProduct}=="55d4", ATTRS{serial}=="0002", MODE:="0777", SYMLINK+="ttyACM_MCU"

这样:

/dev/ttyACM_Lidar
/dev/ttyACM_MCU

就可以稳定对应两台设备。

即使:

/dev/ttyACM0
/dev/ttyACM1

的编号发生交换,两个固定别名依然可以指向正确设备。


推荐检查方法
udevadm info --query=property --name=/dev/ttyACM0 | grep SERIAL

例如:

ID_SERIAL=STMicroelectronics_STM32_Virtual_ComPort_123456
ID_SERIAL_SHORT=123456

如果 ID_SERIAL_SHORT 是唯一的,就非常适合作为设备标识。


根据主机 USB 物理接口绑定端口

需求

无人车上安装前后两台完全相同型号的雷达。

两台雷达:

  • idVendor 相同;

  • idProduct 相同;

  • 没有唯一 Serial Number。

此时可以根据主机 USB 接口的位置区分。


方法一:直接使用 /dev/serial/by-path/

首先执行:

ls -l /dev/serial/by-path/

例如:

pci-0000:00:14.0-usb-0:3:1.0-port0 -> ../../ttyUSB0
pci-0000:00:14.0-usb-0:4:1.0-port0 -> ../../ttyUSB1

可以规定:

USB 口 0:3 -> 前雷达
USB 口 0:4 -> 后雷达

程序直接使用:

/dev/serial/by-path/pci-0000:00:14.0-usb-0:3:1.0-port0
/dev/serial/by-path/pci-0000:00:14.0-usb-0:4:1.0-port0

即可。


方法二:根据 KERNELS 创建自定义别名

查看第一台设备:

udevadm info --attribute-walk --name=/dev/ttyUSB0 | grep KERNELS

查看第二台设备:

udevadm info --attribute-walk --name=/dev/ttyUSB1 | grep KERNELS

假设对应:

第一台:
KERNELS=="1-1.3:1.0"

第二台:
KERNELS=="1-1.4:1.0"

则可以创建:

sudo vim /etc/udev/rules.d/99-lidar.rules

写入:

KERNEL=="ttyUSB*", KERNELS=="1-1.3:1.0", MODE:="0777", SYMLINK+="rplidar_front"

KERNEL=="ttyUSB*", KERNELS=="1-1.4:1.0", MODE:="0777", SYMLINK+="rplidar_back"

重新加载:

sudo udevadm control --reload-rules
sudo udevadm trigger

重新插拔之后:

ls -l /dev/rplidar_front
ls -l /dev/rplidar_back

缺点

这种方式绑定的是:

主机 USB 接口

而不是:

USB 设备本身

因此设备必须插在固定 USB 接口。

如果前后两台雷达交换接口:

前雷达插到后雷达 USB 口

那么系统仍然会将该接口识别为:

/dev/rplidar_back

根据 ID_PATH 绑定端口

与使用 KERNELS 类似,也可以根据 ID_PATH 建立规则。

查看:

udevadm info --query=property --name=/dev/ttyUSB0 | grep ID_PATH

例如:

ID_PATH=pci-0000:00:14.0-usb-0:3:1.0

可以写:

SUBSYSTEM=="tty", ENV{ID_PATH}=="pci-0000:00:14.0-usb-0:3:1.0", MODE:="0777", SYMLINK+="rplidar_front"

另一个接口:

SUBSYSTEM=="tty", ENV{ID_PATH}=="pci-0000:00:14.0-usb-0:4:1.0", MODE:="0777", SYMLINK+="rplidar_back"

相比直接写 KERNELS,这种写法通常更加直观。


综合多个属性绑定端口

实际工程中,可以同时匹配多个属性。

例如:

KERNEL=="ttyACM*", ATTRS{idVendor}=="1a86", ATTRS{idProduct}=="55d4", ATTRS{serial}=="0001", MODE:="0777", SYMLINK+="ttyACM_Lidar"

KERNEL=="ttyACM*", ATTRS{idVendor}=="1a86", ATTRS{idProduct}=="55d4", ATTRS{serial}=="0002", MODE:="0777", SYMLINK+="ttyACM_MCU"

KERNEL=="ttyUSB*", ATTRS{idVendor}=="1a86", ATTRS{idProduct}=="7523", MODE:="0777", SYMLINK+="ttyUSB_IMU"

也可以使用:

SUBSYSTEM=="tty", ENV{ID_VENDOR_ID}=="0483", ENV{ID_MODEL_ID}=="5740", ENV{ID_SERIAL_SHORT}=="12345678", MODE:="0777", SYMLINK+="ysu_mcu"

最终得到:

/dev/ysu_mcu

这种方式非常适合机器人项目。


推荐使用统一业务设备名称

对于机器人项目,不建议 ROS 2 节点直接使用:

/dev/ttyUSB0
/dev/ttyACM0

可以统一建立业务名称,例如:

/dev/ysu_mcu
/dev/ysu_gimbal
/dev/ysu_imu
/dev/ysu_lidar_front
/dev/ysu_lidar_back

例如:

SUBSYSTEM=="tty", ENV{ID_VENDOR_ID}=="0483", ENV{ID_MODEL_ID}=="5740", ENV{ID_SERIAL_SHORT}=="12345678", MODE:="0777", SYMLINK+="ysu_mcu"

SUBSYSTEM=="tty", ENV{ID_VENDOR_ID}=="10c4", ENV{ID_MODEL_ID}=="ea60", ENV{ID_SERIAL_SHORT}=="ABCDEF", MODE:="0777", SYMLINK+="ysu_imu"

SUBSYSTEM=="tty", ENV{ID_PATH}=="pci-0000:00:14.0-usb-0:3:1.0", MODE:="0777", SYMLINK+="ysu_lidar_front"

然后 ROS 2 参数写:

serial_port: /dev/ysu_mcu
imu_port: /dev/ysu_imu
lidar_port: /dev/ysu_lidar_front

这样后续更换设备节点编号时,ROS 2 配置文件完全不需要修改。


ttyUSB 与 ttyACM 的区别

并非所有 USB 串口设备都使用:

/dev/ttyUSBn

常见类型如下:

设备类型内核名称示例说明
USB 转串口芯片ttyUSB*CH340、CP210x、FTDI 等
USB CDC ACMttyACM*STM32 CDC、Arduino 等
主机物理串口ttyS*PC 板载 UART
USB 摄像头video*V4L2 摄像头
磁盘sd*USB 硬盘、U 盘
输入设备event*键盘、鼠标、手柄
蓝牙串口rfcomm*Bluetooth RFCOMM

所以 udev 规则中的:

KERNEL=="ttyUSB*"

并不是固定写法。

如果 STM32 CDC 对应:

/dev/ttyACM0

则应该匹配:

KERNEL=="ttyACM*"

也可以统一使用:

SUBSYSTEM=="tty"

再配合其他属性进行筛选。


USB 摄像头固定设备路径

USB 摄像头通常对应:

/dev/video0
/dev/video1
/dev/video2
...

而且一台摄像头有时会生成多个 /dev/videoN 节点。

因此不建议简单假设:

video0、video2、video4 一定是摄像头采集节点

因为设备节点编号与接口顺序可能发生变化。


查看系统生成的摄像头固定路径

执行:

ls -l /dev/v4l/by-id/
ls -l /dev/v4l/by-path/

例如:

/dev/v4l/by-id/usb-USB_Camera-video-index0
/dev/v4l/by-id/usb-USB_Camera-video-index1

如果摄像头具有唯一身份信息,推荐使用:

/dev/v4l/by-id/

如果需要按照固定 USB 接口区分:

/dev/v4l/by-path/

查看摄像头节点

可以安装:

sudo apt install v4l-utils

然后:

v4l2-ctl --list-devices

例如:

USB Camera:
    /dev/video0
    /dev/video1

也可以进一步查看某个节点支持的格式:

v4l2-ctl -d /dev/video0 --all

磁盘设备的固定路径

对于 USB 硬盘、U 盘、SSD 等,不建议直接依赖:

/dev/sda
/dev/sdb

Linux 同样提供:

/dev/disk/by-id/
/dev/disk/by-path/
/dev/disk/by-uuid/
/dev/disk/by-label/

例如:

ls -l /dev/disk/by-id/

因此,不同类型设备都遵循类似原则:

动态设备节点
        ↓
系统生成固定路径
        ↓
必要时自己编写 udev rule

使 udev 规则生效

修改:

/etc/udev/rules.d/*.rules

之后,Ubuntu 和 Fedora 均可以使用:

sudo udevadm control --reload-rules
sudo udevadm trigger

然后重新插拔设备。

对于串口设备,也可以:

sudo udevadm trigger --subsystem-match=tty

如果规则仍然没有生效,可以直接重启系统。


测试固定别名

查看符号链接:

ls -l /dev/ysu_mcu

查看最终实际设备:

readlink -f /dev/ysu_mcu

例如:

/dev/ttyACM1

即使之后重新插拔设备变成:

/dev/ttyACM3

如果 udev 规则匹配正确:

/dev/ysu_mcu

仍然会指向正确设备。


排查 udev 规则

如果规则没有生效,可以按照下面的方法检查。


检查规则语法
sudo udevadm verify /etc/udev/rules.d/99-usb-serial.rules

如果当前系统版本支持该命令,可以直接检查规则文件。


查看设备属性
udevadm info --query=property --name=/dev/ttyUSB0

或者:

udevadm info --attribute-walk --name=/dev/ttyUSB0

重点检查:

idVendor
idProduct
serial
ID_SERIAL
ID_SERIAL_SHORT
ID_PATH
KERNELS
SUBSYSTEM

监控设备插拔事件

执行:

udevadm monitor --environment --udev

然后重新插拔 USB 设备。

可以观察系统识别设备时生成的 udev 属性。


查看设备实际权限
ls -l /dev/ttyUSB0

例如:

crwxrwxrwx 1 root dialout ...

如果规则中使用:

MODE:="0777"

设备重新生成后会按照规则设置权限。


推荐选择顺序

USB 设备固定端口时,推荐按照下面的顺序选择。

                USB设备
                   │
                   ▼
       是否已有系统固定路径?
         /dev/serial/by-id
                   │
             ┌─────┴─────┐
             │           │
            有           无
             │           │
             ▼           ▼
          直接使用     是否存在唯一Serial?
                         │
                   ┌─────┴─────┐
                   │           │
                  有           无
                   │           │
                   ▼           ▼
             按Serial绑定   是否固定USB接口?
                               │
                         ┌─────┴─────┐
                         │           │
                        是           否
                         │           │
                         ▼           ▼
                    by-path/      尽量从设备固件
                    ID_PATH       层增加唯一Serial

简单来说:

能够识别设备本身,就按设备本身绑定;无法识别设备本身,就按设备连接的 USB 接口绑定。


最终推荐方案

情况一:单个设备,具有唯一 Serial

首选:

/dev/serial/by-id/

或者自己建立:

/dev/ysu_mcu

情况二:多个不同型号设备

可以使用:

idVendor + idProduct

建立自定义 udev rule。


情况三:多个相同设备,但 Serial 不同

使用:

idVendor + idProduct + serial

这是非常稳定的方案。


情况四:多个完全相同设备,没有唯一 Serial

使用:

/dev/serial/by-path/

或者:

KERNELS
ID_PATH

根据主机 USB 物理接口区分。


情况五:STM32 自定义 USB CDC

如果设备固件由自己控制,推荐在 USB Descriptor 中配置唯一 Serial Number。

这样 Linux 可以根据设备自身身份建立稳定路径,例如:

/dev/serial/by-id/usb-STMicroelectronics_STM32_Virtual_ComPort_xxxxx-if00

之后无论插在哪一个 USB 接口,都可以识别同一块控制板。


总结

Linux USB 设备的固定命名,本质上可以分为两类:

根据设备是谁
    ↓
by-id
Serial Number
idVendor + idProduct + serial


根据设备插在哪里
    ↓
by-path
ID_PATH
KERNELS

工程中建议优先级:

/dev/serial/by-id/
        ↓
唯一 Serial Number
        ↓
/dev/serial/by-path/
        ↓
自定义 udev rules

而在机器人项目中,最终可以统一建立:

/dev/ysu_mcu
/dev/ysu_gimbal
/dev/ysu_imu
/dev/ysu_lidar_front
/dev/ysu_lidar_back

之后 ROS 2、C++ 程序和启动文件全部使用这些固定名称,而不再依赖:

/dev/ttyUSB0
/dev/ttyUSB1
/dev/ttyACM0
/dev/ttyACM1

这样即使 USB 设备重新插拔、启动顺序发生变化或者系统重新分配端口编号,也不会影响上层程序。

Linux分区Gui工具

sudo apt install gparted
sudo dnf install gparted

DDNS

挂载内网网页

可以把本地的网页挂载到路由器端,或者同内网段其他设备上,甚至可以挂载在本地。

下面以安卓设备为例子,由于安卓设备暂时不能直接打开html,所以我们可以把html挂载到本地服务器,再通过浏览器查看。

由于安卓系统基于Linux内核,所以我们可以下载一个叫termux的app来敲一些Linux命令:

https://github.com/termux/termux-app

首先打开app后,先更新软件缓存:

pkg update

然后换源

nano $PREFIX/etc/apt/sources.list

将该文件内容替换为下面:

deb https://mirrors.bfsu.edu.cn/termux/termux-packages-24 stable main
  • 如果你使用的是nano编辑器:

    • 按 Ctrl+O 保存文件。

    • 按 Enter 确认文件名。

    • 按 Ctrl+X 退出编辑器。

  • 如果你使用的是vim编辑器:

    • 按 Esc 退出编辑模式。

    • 输入 :wq 保存并退出。

接下来重新更新软件源:

pkg update && pkg upgrade

安装python:

pkg install python3

通过Termux访问设备存储 :

如果文件在安卓设备上,可以使用Termux的文件管理器访问设备的存储:

termux-setup-storage
  1. 进入HTML文件所在的目录 :

    1. 使用cd命令进入HTML文件所在的目录:

cd ~/your_html_folder
  1. 启动Python HTTP服务器 :

    1. 运行以下命令启动一个简单的HTTP服务器:

    python3 -m http.server 8000
    1. 这会在端口8000上启动一个Web服务器。

  • 启动服务器后,Termux会显示日志信息,例如:

Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
  • 这表示服务器已成功启动。

在浏览器中访问HTML文件

  1. 在同一设备上访问 :

    1. 打开安卓设备上的浏览器(如Chrome),输入以下地址:

    http://localhost:8000
    1. 如果HTML文件中有index.html,它会自动加载;否则,你需要手动点击文件链接。

RustDesk

RustDesk无人值守远程控制

目前rustdesk还没办法进行无人值守在wayland下,所以我们需要把登陆密码,锁屏和休眠待机全部关掉.

设置 KDE6 自动登录
  1. 可以先查自己的sddm类型

ls /usr/share/xsessions/
ls /usr/share/wayland-sessions/

看哪个会输出东西,输出东西的文件夹就是你拥有的sddm,比如xsessions输出内容的话,那就是x11的sddm,如果wayland-sessions输出内容的话你就是wayland的sddm.

  1. 编辑 SDDM 配置:

sudo nano /etc/sddm.conf

在文件最上面有个Autologin,在这个底下加入下面的内容:

[Autologin]
User=你的用户名
Session=plasma.desktop   # 或 plasma.desktop(X11),方案一可用 Wayland
  1. 保存退出后重启 SDDM:

sudo systemctl restart sddm

✅ 开机后自动进入桌面,无需输入密码

关闭锁屏在 KDE6 系统设置里:

打开 系统设置 → 屏幕锁定(Screen Locking) 关闭自动锁定 关闭屏幕关闭 / 屏幕保护锁定 这样 RustDesk 可以开机后直接控制桌面,不会被锁屏阻挡

关闭休眠系统设置 → 电源管理(Power Managerment)

禁用“休眠”或“自动挂起” 保证电脑开机一直在线,RustDesk 可随时远控 如果必须休眠,可在 BIOS 设置里禁用休眠,保证开机即联网

RustDesk自建服务器

在服务器端安装
命令行方式
  1. 打开官网:https://rustdesk.com/docs/zh-cn/self-host/

alt text

  1. 用ssh访问你需要安装RustDesk的服务器

ssh tungchiahui@10.0.0.4

alt text

  1. 使用方法二的命令

alt text

wget https://raw.githubusercontent.com/techahold/rustdeskinstall/master/install.sh
chmod +x install.sh
./install.sh

./install.sh的设置看下一节

  1. 配置与查看KEY

这里如果你是想直接用公网ip则选择1)IP,如果你是DDNS等用域名绑定了IP地址,则选择2).

alt text

这个选2)No

alt text

这个是KEY,要复制下来,等后面软件配置的时候要用.

alt text

  1. 配置防火墙

    1. 如果你是Debian系 安装防火墙

    sudo apt install ufw

    配置防火墙

    sudo ufw allow 22
    
    ufw allow 21114:21119/tcp
    ufw allow 21116/udp
    sudo ufw enable
    1. 如果你是RHEL系 安装防火墙

    # 如果你是RHEL系
    sudo dnf install firewalld
    sudo systemctl enable --now firewalld

    配置防火墙

    sudo firewall-cmd --add-port=22/tcp --permanent
    sudo firewall-cmd --reload
    
    sudo firewall-cmd --add-port=21114-21119/tcp --permanent
    sudo firewall-cmd --reload
    
    sudo firewall-cmd --add-port=21116/udp --permanent
    sudo firewall-cmd --reload
    
    sudo systemctl enable --now firewalld
1Panel方式
  1. 在面板里搜RustDesk并安装

alt text

  1. 配置防火墙

alt text

添加几个端口 alt text alt text alt text

  1. 查看KEY alt text

复制KEY

alt text

自建服务器后配置RustDesk软件
  1. PC端

alt text

alt text

出现就绪就是配置成功了 alt text

  1. 移动端

alt text

alt text

注意的问题

如果你是家庭的内网服务器,则需要让路由器也放行对应端口

找到openwrt防火墙设置,点击添加 alt text

alt text

alt text

然后还有ipv6也要进行放行,那个debian一般默认ipv6不设防,但是会被上游openwrt防住,所以,要在openwrt里开启对应规则。

去防火墙 - 通信规则里添加下面俩配置。

Allow-RustDesk-IPv6-TCP
WAN → LAN
目标:你服务器的ipv6公网
TCP 21115-21119
动作:接受转发

Allow-RustDesk-IPv6-UDP
WAN → LAN
目标:你服务器的ipv6公网
UDP 21116
动作:接受转发

这样才可以让你的服务器端口被真正成功放行.

WireGuard

简介

WireGuard是用于在室外通过VPN来访问内网设备的一种工具.

这里我的教程将会教你如何在室外访问你家里的内网设备.

我的环境

内网

家庭内网网段:10.0.0.0/24

alt text

外部设备

如果你是在办公室等地方链接办公室的wifi,那么你需要知道室外内网网段,如果你直接用手机数据流量的话,那么什么都不需要知道.

室外内网网段:192.168.31.0/24 室外数据流量运营商:中国联通

安装

服务端-软路由端

SSH登录OpenWrt,或者用LuCI界面:

opkg update
opkg install wireguard-tools luci-proto-wireguard kmod-wireguard qrencode

装完刷新一下浏览器LuCI页面.

客户端-手机,电脑端

https://www.wireguard.com/install/

  1. Android

https://play.google.com/store/apps/details?id=com.wireguard.android&hl=zh&pli=1

  1. iOS

美区:https://apps.apple.com/us/app/wireguard/id1441195209 日区:https://apps.apple.com/jp/app/wireguard/id1441195209 台区:https://apps.apple.com/tw/app/wireguard/id1441195209

  1. Linux

    1. Fedora

     sudo dnf install wireguard-tools
    
    1. Ubuntu/Debian

sudo apt install wireguard

配置

服务端-初始化配置

在luci界面中,点击网络 → 接口 → 添加新接口

  1. 协议选 WireGuard VPN

  2. 名称填 wg0

  3. 点下一步,进到详情页

alt text

按照上图的步骤,依次配置. 这里解释一下. 监听端口:51820(可自定义,使用一个未被占用的端口即可,记住这个端口,不懂的话就照我的设置) IP地址:10.10.10.1/24(VPN内网网段,跟你软路由的局域网段不能冲突,不懂的话就照我的设置)

你必须确保你的家庭内网网段和你的室外内网网段以及刚才填的那个IP地址不能属于同一网段.

比如我的家庭内网网段是10.0.0.0/24,而室外内网网段是192.168.31.0/24,VPN的网段10.10.10.0/24.

然后点击防火墙设置,选择WireGuard.

alt text

服务端-对端配置

这里配置的就是你的手机/笔记本作为客户端连进来的信息:

这个界面很方便,LuCI可以直接帮你生成客户端的密钥对并直接导出配置文件/二维码,不需要你手动在手机上先生成公钥再倒腾过来,效率高很多。按下面顺序填:

  1. 描述 phone (或者你自己好记的名字,比如"iPhone17")

  2. 公钥 + 私钥 —— 直接点"生成新的密钥对" 点这个按钮,系统会自动帮你同时生成一对新的公钥+私钥,并自动填到"公钥"和"私钥"两个框里。 这个做法比你自己在手机App里生成再抄公钥过来省事得多,因为最后可以直接导出完整配置文件甚至二维码给手机扫码导入,私钥也是配置好的,不用手动输入。

  3. 预共享密钥(PSK) —— 建议点"生成预共享密钥" 这是可选的额外加密层,加上更安全(抗未来量子计算破解),点一下"生成预共享密钥"按钮自动填好即可,不需要手动操作。

  4. 允许的IP 点绿色+号,填: 10.10.10.2/32 这是分配给这台手机的隧道内网IP(如果以后要加第二台设备,比如笔记本,就用10.10.10.3/32,以此类推)。

  5. 为允许的IP创建路由 —— 勾选 这个决定了服务端要不要给这个IP自动建路由,勾上,否则服务端不知道怎么把去往10.10.10.2的包转发给这个peer。

  6. 对端地址 (Image 2) 这个字段填的是客户端的地址,但因为你的手机是移动设备、IP会变,服务端这边留空即可,不用填。(这个字段一般是给"两个固定IP的服务器互联"场景用的,你这种手机/笔记本客户端场景不需要)

  7. 对端端口 同理,留空或者保持默认51820占位符不用管,服务端这边这个字段对客户端场景没意义,跳过。

  8. 持续保持连接(Persistent Keepalive) 填: 25 因为手机大概率处在NAT环境下(无论是WiFi还是4G/5G),这个值能防止连接因为长时间无流量被NAT设备判定断开。

  9. 保存后 —— 点"生成配置..." 这一步是重点:全部填完先点右下角"保存",保存完之后再进来点 配置导出 那个"生成配置..."按钮,系统会:

自动生成一份完整的客户端配置文件(包含私钥、服务端公钥、Endpoint地址等全部信息) 通常还会显示一个二维码

手机WireGuard App直接扫这个二维码就能一键导入配置,完全不用你手动输入任何字段,最省事。

  1. 填完这些保存之后,点"生成配置"

    alt text

    1. 连接端点 —— 改成DDNS域名 没有ddns域名的请看ddns教程. 现在显示的是 112.233.39.5 这个具体的公网IPv4,这是系统自动探测到的当前公网IP,但建议改成:ddns.xxxxxx.cn 原因很简单:家庭宽带的公网IP大概率是动态的(如果你是中国移动,你完全不需要担心这件事情,你没有公网ipv4,你只有公网ipv6),过一阵子运营商重新分配IP后,这个写死的IP就失效了,配置文件也就废了。用DDNS域名的话,只要你路由器上的DDNS客户端正常更新,域名自动跟着IP变化解析,配置文件永久有效,不用来回改。 直接把这个下拉框里的值改成 ddns.tungchiahui.cn 即可,如果下拉框里没有这个选项,看能不能手动输入替换。

    2. 允许的IP —— 这个要看你的使用需求,建议改 现在是 0.0.0.0/0 + ::/0,意思是全局代理模式:连上VPN后,手机/电脑的所有上网流量(刷网页、看视频、聊天全部)都会先绕道回你家里,再从家里的网络出去上网。 这跟最初的需求("在外面访问家里NAS")不太一致,全局模式的影响:

    ✅ 优点:所有流量都通过加密隧道,公共WiFi环境下更安全 ❌ 缺点:手机所有网络流量的速度都取决于你家宽带的上传带宽,如果你家是常见的百兆宽带(上传可能才几十M甚至更低),刷视频、下载会明显变慢;而且家里NAT要承担所有流量的转发压力

    如果你只是想访问家里内网的NAS/PVE/Debian,建议改成分离隧道模式,把这两行改成: 10.10.10.0/24 10.0.0.0/24 删掉 0.0.0.0/0 和 ::/0 这两行(点它们右边的红色×号),只保留上面这两个网段。这样只有"访问家里内网"这一类流量才走VPN隧道,其他正常上网流量不受影响,速度不受影响。 如果你确实希望VPN也当"全局代理"用(比如出门用境外的WiFi想用家里的网络环境访问某些内容),那就保留 0.0.0.0/0 不用改。

    1. DNS服务器:10.0.0.1 —— 这是你家庭内网的软路由的LAN地址,连VPN后用它解析域名没问题,能顺带解析你家内网可能配置的一些本地域名

    2. 地址:10.10.10.2/32 —— 这个就是你想给你的这个手机分配一个VPN网段

alt text

然后掏出手机的客户端,使用QRcode导入配置即可.

alt text

服务端-防火墙设置

需要加的规则 去 网络 → 防火墙 → 通信规则(Traffic Rules),点"添加":

alt text

名称:WireGuard(随便起) 协议:UDP 或者 TCP/UDP 源区域:wan 目标区域:设备(输入) —— 因为WireGuard监听的是软路由本机端口,不是转发给内网其他设备 目标端口:51820(跟你wg0接口"监听端口"那里填的一致) 操作:接受(Accept)

alt text

保存,应用。

客户端

拿iOS举例子

我这里使用数据流量来测试,使用的是中国联通4G.

像使用普通的VPN一样打开你导入的这个配置即可.

alt text

测试

然后可以尝试访问你的内网设备, 比如我的 openwrt是10.0.0.1, pve是10.0.0.2, nas是10.0.0.5都可以被正常访问了.

alt text

alt text

alt text

将WireGuard交由Clash Meta管理(可选)

将纯WireGuard配置放入Clash Meta

如果手机或电脑上已经长期运行支持Mihomo / Clash Meta内核的客户端,再单独启动WireGuard客户端会比较麻烦,而且同一台移动设备通常不能同时启用两个VPN隧道。

因此,可以将WireGuard节点直接写入Clash Meta配置,让Clash Meta统一负责:

  1. 普通互联网流量直连;

  2. 家庭内网流量通过WireGuard访问;

  3. 根据规则自动判断流量应该走哪个出口。

本节只介绍不包含机场或其他代理节点的纯WireGuard配置。

配置完成后,不再需要打开独立的WireGuard客户端。使用该配置时,应关闭WireGuard App中的隧道,只打开Clash Meta。

查看WireGuard客户端参数

先打开已经能够正常使用的WireGuard客户端配置,需要记录以下信息:

Clash Meta字段WireGuard客户端中的对应字段
serverPeer中的Endpoint域名或公网IP
portPeer中的Endpoint端口
ipInterface中的Addresses,只填写IPv4地址,不写/32
private-keyInterface中的Private key
public-keyPeer中的Public key,也就是WireGuard服务器公钥
pre-shared-keyPeer中的Preshared key
persistent-keepalivePeer中的Persistent keepalive

这里最容易弄错的是两个公钥:

  • Interface中的Public key是当前客户端的公钥,不能填入Clash Meta的public-key;

  • Peer中的Public key才是WireGuard服务器公钥,需要填入Clash Meta的public-key。

WireGuard客户端参数位置

这一张图同时包含Interface和Peer两部分,已经足够说明各个字段应该从哪里获取,不需要再增加其他截图。

创建YAML配置

新建一个扩展名为.yaml的文件,例如:

home-wireguard.yaml

写入下面的模板:

# 仅让家庭内网流量通过WireGuard
# 普通互联网流量保持直连
# 启用本配置时,不要同时开启独立的WireGuard客户端

mixed-port: 7890
allow-lan: false
mode: rule
log-level: info
ipv6: true

tun:
  enable: true
  stack: mixed
  auto-route: true
  auto-detect-interface: true

proxies:
  - name: 家庭-WG
    type: wireguard

    # WireGuard客户端Peer中的Endpoint
    server: ddns.example.com
    port: 51820

    # WireGuard客户端Interface中的Addresses
    # 例如原配置为10.10.10.2/32,这里只填写10.10.10.2
    ip: 10.10.10.2

    # WireGuard客户端Interface中的Private key
    private-key: "gBzeTbZH..."

    # WireGuard客户端Peer中的Public key,即服务器公钥
    # 不要填写Interface中的客户端公钥
    public-key: "UUS0nrKE..."

    # WireGuard客户端Peer中的Preshared key
    pre-shared-key: "NGYvFsCle..."

    allowed-ips:
      - 0.0.0.0/0

    udp: true
    persistent-keepalive: 25
    mtu: 1380

rules:
  # 家庭局域网网段
  - IP-CIDR,10.0.0.0/24,家庭-WG,no-resolve

  # WireGuard隧道网段
  - IP-CIDR,10.10.10.0/24,家庭-WG,no-resolve

  # 其他网络流量保持直连
  - MATCH,DIRECT
修改模板中的参数

需要按照自己的WireGuard配置修改以下内容。

1. server

将:

server: ddns.example.com

改成WireGuard客户端Peer中Endpoint冒号前面的域名或公网IP。

例如Endpoint为:

ddns.example.com:51820

那么填写:

server: ddns.example.com

2. port

填写Endpoint冒号后面的端口,例如:

port: 51820

3. ip

填写WireGuard客户端Interface中的Addresses,但不要填写CIDR后缀。

例如原配置是:

10.10.10.2/32

Clash Meta中填写:

ip: 10.10.10.2

4. private-key

填写WireGuard客户端Interface中的Private key:

private-key: "客户端私钥"

5. public-key

填写WireGuard客户端Peer中的Public key,也就是服务器公钥:

public-key: "服务器公钥"

不要误填Interface中的Public key。Interface中的Public key属于当前客户端,Peer中的Public key才属于服务器。

6. pre-shared-key

填写WireGuard客户端Peer中的Preshared key:

pre-shared-key: "预共享密钥"

WireGuard密钥是Base64字符串,复制时不要漏掉末尾可能存在的=。

7. rules

将规则中的家庭局域网网段和WireGuard隧道网段改成自己的实际网段。

例如:

rules:
  - IP-CIDR,10.0.0.0/24,家庭-WG,no-resolve
  - IP-CIDR,10.10.10.0/24,家庭-WG,no-resolve
  - MATCH,DIRECT

表示:

  • 访问10.0.0.0/24家庭局域网时走WireGuard;

  • 访问10.10.10.0/24WireGuard隧道网段时走WireGuard;

  • 其他流量全部直连,不经过家庭宽带。

关于allowed-ips

模板中的:

allowed-ips:
  - 0.0.0.0/0

可以保持不变。

它表示这个WireGuard代理节点能够承载全部IPv4流量,但实际哪些流量交给WireGuard,仍然由最下面的rules决定。

例如:

rules:
  - IP-CIDR,10.0.0.0/24,家庭-WG,no-resolve
  - IP-CIDR,10.10.10.0/24,家庭-WG,no-resolve
  - MATCH,DIRECT

在这套规则下,只有家庭内网和WireGuard隧道网段走WireGuard,其他互联网流量继续直连。

需要注意,WireGuard服务端Peer中的Allowed IPs仍然应该是分配给该客户端的地址,例如:

10.10.10.2/32

不要把WireGuard服务端Peer中的Allowed IPs改成0.0.0.0/0。

导入并测试

将YAML文件导入支持Mihomo / Clash Meta内核的客户端,然后:

  1. 关闭独立的WireGuard客户端隧道;

  2. 在Clash Meta中选择刚才导入的配置;

  3. 使用“规则”模式;

  4. 打开Clash Meta连接开关;

  5. 访问家庭内网设备进行测试。

例如访问软路由:

http://10.0.0.1

或者在iOS文件App中连接NAS:

smb://10.0.0.5

同时可以进入WireGuard服务端,查看对应Peer的Latest Handshake。

如果开启Clash Meta后握手时间刷新,并且能够访问家庭内网设备,就说明配置成功。

常见问题

如果YAML能够启动,但无法产生WireGuard握手,优先检查:

  1. public-key是否误填成客户端公钥;

  2. private-key是否为当前客户端私钥;

  3. pre-shared-key是否完整,末尾的=是否遗漏;

  4. ip是否与服务端分配给该Peer的地址一致;

  5. Endpoint域名和端口是否正确;

  6. 软路由防火墙是否已经放行WireGuard端口;

  7. 独立WireGuard客户端是否仍然处于开启状态。

如果报错类似:

decode pre shared key: illegal base64 data

通常说明pre-shared-key复制不完整,常见原因是末尾的=被遗漏。

将WireGuard加入包含代理节点的YAML

Alist

简介

安装

WebUI

S3协议

开启S3

先进行使能S3协议。

去/opt/1panel/apps/alist/alist/data/data目录修改掉如下图的设置从false改成true。

"s3": {
  "enable": true,
  "port": 5246,
  "ssl": false
}

最后点击重启。

测试S3链接

在部署AList服务器本身敲

curl -i http://127.0.0.1:5246/

如果显示HTTP/1.1 200 OK,那证明是OK的。

后续我也把他反向代理成了一个域名s3.tungchiahui.cn,经测试,也没毛病:

配置S3

在AList的管理界面找到设置里的S3,然后点底下的Add来添加哪个路径为桶:

  • Name就是桶的名字

  • Path是你想让AList的哪个路径作为桶子的根目录

  • S3 access key id 是S3授权ID

  • S3 secret access key是S3授权密钥

测试S3

先准备好你的配置

Endpoint:
http://127.0.0.1:5246
或者
https://s3.tungchiahui.cn

Bucket:
ObjectStorage

Access Key:
AList 后台生成的 S3 Access Key ID

Secret Key:
AList 后台生成的 S3 Secret Access Key

Region:
us-east-1

使用一个可以使用S3的软件,比如我使用我做的VScode插件来进行测试。

插件链接如下: https://marketplace.visualstudio.com/items?itemName=tungchiahui.md-image-uploader

我把配置填到了VScode中的settings.json使用我的插件进行了测试:

{
  "mdImageUploader.s3.accessKeyId": "2Vxxxxxxxxxxxxxxxxxxxxxx",
  "mdImageUploader.s3.secretAccessKey": "FWx6xxxxxxxxxxxxxxxxxx",
  "mdImageUploader.s3.endpoint": "https://s3.tungchiahui.cn",
  "mdImageUploader.s3.region": "us-east-1",
  "mdImageUploader.s3.bucket": "ObjectStorage",
  "mdImageUploader.cdnUrl": "https://cdn.tungchiahui.cn",
  "mdImageUploader.datedUploadPath": "/tungwebsite/assets/images",
  "mdImageUploader.undatedUploadPath": "/tungwebsite/assets/images/undate"
}

经过测试,文件已经成功被保存到了桶子里。

创建文件直链

去AList后台

管理
→ 设置
→ 全局
→ 关闭Sign All

如果开着 Sign All,关掉。因为只要全局 Sign All 开着,下面单独存储关签名也没用。AList 官方明确说 Sign All 会给所有文件直链添加签名。

然后找到你的存放AList的桶子的存储地点进行编辑。

关闭启用签名 / Enable signing。

然后保存。

然后你可以通过![](https://alist.tungchiahui.cn/d/AList文件的路径/文件名.webp)来访问文件了。

高阶操作(可选):

把cdn.tungchiahui.cn也和alist.tungchiahui.cn同源,然后在腾讯云edgeone里加速,在规则引擎里新加一个cdn路径前缀增加的规则。

  • HOST选择cdn.tungchiahui.cn

  • 操作选回源URL重写

  • 类型选增加路径前缀

  • 增加路径前缀选/d/AList文件的多余路径/

比如增加路径前缀我填的/d/NAS/ObjectStorage, 那么我用https://cdn.tungchiahui.cn/tungwebsite/assets/images/2024/03/30/1787159371048-e2e3749f.webp 将会同等于https://alist.tungchiahui.cn/d/NAS/ObjectStorage/tungwebsite/assets/images/2024/03/30/1787159371048-e2e3749f.webp, 都会回源到我的https://服务器ip地址/d/NAS/ObjectStorage/tungwebsite/assets/images/2024/03/30/1787159371048-e2e3749f.webp。

再设置一个缓存规则:

缓存规则可以这样理解:

  • 节点缓存 TTL:30 天
    EdgeOne 会把图片缓存到边缘节点 30 天。后续访问优先从 CDN 节点返回,可以明显减少 AList、服务器 和 服务器宽带 的压力。

  • 强制缓存:关闭
    保持关闭更稳妥。如果源站明确要求某些资源不要缓存,EdgeOne 会尊重源站设置,避免把特殊资源错误缓存。

  • Cache Key:查询字符串全部忽略
    例如:

    xxx.webp
    xxx.webp?a=1
    xxx.webp?a=2
    xxx.webp?a=3

    都会被认为是同一个缓存资源。这样可以防止别人不断修改 URL 参数绕过缓存,反复回源打你的 AList 和 服务器,而我这里文件还存储在NAS上,也会进一步防止他反复打我的NAS。

  • 忽略大小写:关闭
    abc.webp 和 ABC.webp 会被当成两个不同文件,避免不同文件被错误合并到同一个缓存。

  • PHP / JSP / ASP / ASPX:不缓存
    这些通常属于动态页面,单独设为不缓存可以防止动态内容被 CDN 长时间缓存。对于纯图片 CDN 来说基本属于防呆设置。

  • 图片浏览器缓存 TTL:1 天
    jpg、png、gif、bmp、svg、webp 等图片会在用户浏览器中缓存 1 天。用户短时间内重复访问时,浏览器可以直接读取本地缓存,连 EdgeOne 都不用重新请求。

最终缓存链路大致如下:

浏览器
  ↓ 本地缓存 1 天
EdgeOne
  ↓ 节点缓存 30 天
AList
  ↓
NAS

绝大多数重复访问会停在浏览器或 EdgeOne,不会真正回源到家里的 服务器 和 NAS。

MinIO 自建对象存储

简介

项目腾讯 COS自建 MinIO
对象存储✅✅
S3 API✅✅
图片/附件✅✅
Bucket✅✅
SDK 上传✅✅
私有对象/签名 URL✅✅
Versioning✅✅
存储空间腾讯提供吃你服务器硬盘
带宽腾讯提供吃学校服务器出口带宽
高可用腾讯负责你负责
数据冗余腾讯负责你负责
硬盘损坏云厂商处理可能直接丢数据
运维几乎没有Docker、磁盘、备份、升级、安全都自己搞
费用存储+流量软件可以自建,但硬盘/服务器/带宽自己承担
CDN很容易接腾讯 CDN/EOMinIO 本身不是 CDN
可控性一般非常高

安装

首先你需要服务器上安装一个1panel.

进入商店,搜索minio.

点击安装.

大多数设置都默认即可,但账号密码要自己填写,这个端口外部访问这里,如果你的服务器在内网,可以选择打开,如果你的服务器的IP是一个公网IP,那么不建议你打开.

配置项建议
名称minio
版本选 1Panel 当前提供的稳定版本
用户自己设一个,不要用太简单的
密码自己设一个密码复杂点的
WebUI 端口9001
S3 API 端口9000
会话持续时间默认 12h 即可
启用 WebUI开
WebUI 动画随意
端口外部访问建议不开公网暴露,但如果测试的话,先开公网暴露也可以.

最后点击安装.

配置MinIO

登陆MinIO

在浏览器输入服务器ip:9001:

比如我这个内网10.0.0.4:9001

然后登陆账号密码.

测试桶

创建一个桶,比如我这里叫test

再随便上传一张图片试试.

测试S3协议

S3协议的网址应该是服务器ip:9000:

比如我这个内网10.0.0.4:9000

在你电脑上测试 S3 API 健康状态:

curl -i http://10.0.0.4:9000/minio/health/live

如果显示HTTP/1.1 200 OK,那证明是OK的。

同样,测试下面这个是否S3就绪:

curl -i http://10.0.0.4:9000/minio/health/ready

如果显示HTTP/1.1 200 OK,那证明是OK的。

接入AList测试

这个一定要开着。

配置完后,可以返回文件列表,发现刚才的文件可以被正常查看了。