第 6 章 网络与时间同步¶
机器人上位机的网络层同时服务三件事:上层的 ROS 2 DDS 多机通信(组播、单播、跨网段),下层的现场总线网关(SocketCAN 走 slcand、Modbus TCP 走 502),旁路的观测与运维(SSH、抓包、日志回传)。时间同步是自主导航与多传感器融合的地基——use_sim_time 只在仿真里有意义,真机上跨机时戳对齐靠的是 chrony / PTP。
本章覆盖配置栈(netplan 与 NetworkManager)、地址与桥接、观测命令组(ip / ss / tcpdump)、防火墙(ufw / nftables)、服务发现(Avahi / mDNS、dnsmasq)与应用侧常驻服务(Mosquitto MQTT broker)、时间同步(timedatectl / chrony / PTP);抓包的深度与 CAN 侧的排查在第 9 章与 fieldbus-note 第 9 章承接。
1. netplan 与 NetworkManager¶
Ubuntu 上有两套并存的配置栈:
- netplan:YAML 描述期望配置,底层可以是
systemd-networkd(服务器 / cloud image 默认)或NetworkManager(桌面默认);配置文件在/etc/netplan/*.yaml;netplan apply生效。 - NetworkManager:面向桌面的守护进程,带
nmcli/nmtui/ GUI 前端;管理连接、DHCP、Wi-Fi、VPN。
选择规则:
| 场景 | 默认栈 | 说明 |
|---|---|---|
| Ubuntu Desktop | NetworkManager(通过 netplan 描述) | 桌面右上网络菜单走 nm |
| Ubuntu Server | systemd-networkd(通过 netplan 描述) | 无 GUI、脚本化部署友好 |
| Cloud image(云镜像) | systemd-networkd + cloud-init | 首启动由 cloud-init 写 /etc/netplan/50-cloud-init.yaml |
| 板端(树莓派 Ubuntu / RasPi OS 新版) | NetworkManager(Bookworm 起) | 老版本用 dhcpcd,别混淆 |
/etc/netplan/ 是声明源、不是生效结果:改完必须 sudo netplan apply(或 try 带回滚);NetworkManager 走 nmcli connection reload。双基线默认栈对照:22.04 Desktop 默认 NetworkManager(GNOME 会话)、Server 与 cloud image 默认 systemd-networkd;24.04 保持同一划分但 netplan 与 NM 版本上台阶(netplan 0.106 → 0.107、NM 1.44 → 1.46),字段语义以 man netplan 与所用渲染器为准。
判据:一台机器上 netplan 与 nm 谁管哪些连接要看 /etc/NetworkManager/conf.d/* 里的 unmanaged-devices 名单。混配最容易引发"我明明改了 netplan 但 IP 不变"——因为该接口被 NM 接管了。
两套配置栈与底层后端的接法:
flowchart LR
yaml["/etc/netplan/*.yaml"] --> apply["netplan apply"]
apply --> nd["systemd-networkd<br/>服务器镜像默认"]
apply --> nm["NetworkManager<br/>桌面默认"]
nd --> ifc["网卡 / 地址 / 路由 / 桥接"]
nm --> ifc
ifc --> up["DDS 多机 · Modbus TCP · SSH"]
2. 地址、路由、桥接与 VLAN¶
2.1 netplan 静态 IP 与网关¶
完整 netplan 配置(点击展开)
## /etc/netplan/01-static.yaml
network:
version: 2
renderer: networkd
ethernets:
enp0s31f6:
dhcp4: false
addresses:
- 192.168.10.20/24
routes:
- to: default
via: 192.168.10.1
nameservers:
addresses: [192.168.10.1, 8.8.8.8]
sudo chmod 600 /etc/netplan/01-static.yaml # netplan 权限过开会 warning
sudo netplan try # 试生效,120 秒无回车自动回滚
sudo netplan apply
netplan try 是避免远程改把自己踢下线的关键动作——回滚窗口内 SSH 若失联,配置自动还原。
2.2 nmcli 视角¶
桌面或混合环境下用 nmcli:
nmcli device status # 各接口的 managed / unmanaged
nmcli connection show
nmcli connection add type ethernet con-name lab ifname enp0s31f6 \
ipv4.method manual ipv4.addresses 192.168.10.20/24 ipv4.gateway 192.168.10.1
nmcli connection up lab
2.3 桥接、VLAN 与机器人多网口¶
机器人上位机常有两个网口:一个连车间/校园、一个连板端内网。桥接(bridge)把多个物理口拼成一个逻辑口给容器或 VM 用:
bridges:
br0:
interfaces: [enp0s31f6, enp0s31f7]
addresses: [192.168.10.20/24]
dhcp4: no
VLAN 走 802.1Q:
vlans:
vlan10:
id: 10
link: enp0s31f6
addresses: [10.10.0.20/24]
判据:给容器 / KVM / ROS 2 仿真造网络时优先桥接、生产网段之间要隔离时优先 VLAN。具体部署形态属容器/虚拟化主题,本板块止于 netplan 语法层。
3. 观测与防火墙¶
3.1 ip / ss / ip route¶
ifconfig / netstat / route 是老 net-tools 家族,Ubuntu 服务器默认不装;一律改走 iproute2:
ip -br addr # 简洁列接口与地址(br = brief)
ip addr show dev eno1 # 详细,含 MAC / MTU / state
ip route show # 路由表;`ip r get 8.8.8.8` 看会走哪条路
ip -s link # 累计计数器(rx/tx errors、drops)
ss -tulpn # 监听端口与所属进程(t=tcp u=udp l=listen p=pid n=numeric)
ss -ti # 每 TCP 连接的 RTT / retrans / cwnd
判据:ss -tulpn | grep :<port> 找端口占用;ip route get <ip> 判断"发往这个 IP 走哪张网卡、用哪个源地址"——DDS 多机不通的第一问就是这里。
3.2 tcpdump 与 Wireshark¶
sudo tcpdump -i any -nn -s0 -w /tmp/now.pcap 'udp port 7400 or tcp port 502'
## 抓 DDS 组播(7400 RTPS 发现端口)与 Modbus TCP(502),落 pcap
-i any 全接口、-nn 不解析协议名与端口名、-s0 全 payload、-w 落文件。回来在本地 Wireshark 打开,或 tshark -r /tmp/now.pcap -Y 'btsdp' 命令行看。Modbus TCP 与 CAN 帧的解码在 fieldbus-note 第 9 章,本章只交代工具语法。
3.3 防火墙三层¶
- iptables:老接口,用户态命令;实际后端在 nft 内核。
- nftables(
nft):内核新框架;iptables-nft/ip6tables-nft只是兼容壳。 - ufw:面向人的前端,写
/etc/ufw/*.rules,最终落到 iptables 或 nft。
Ubuntu Desktop 默认 ufw 关闭,Server 默认也关。开:
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 22/tcp comment 'SSH'
sudo ufw allow from 192.168.10.0/24 to any port 11311:11400 proto udp comment 'DDS'
sudo ufw enable
sudo ufw status verbose
判据:DDS 组播被过滤是"多机不通"的头号成因。DDS 用 7400 + 域号偏移(默认 domain 0 用 7400、domain 42 用 7400+42×250+…);生产 ROS 2 环境应显式列 UDP 端口段放行,或干脆把控制机置于同一二层内网不做包过滤。
4. 服务发现与局域网配套¶
4.1 Avahi 与 mDNS¶
机器人上位机之间用 robot-arm.local 这类主机名互相找到,靠的是 Avahi(Linux 侧 mDNS / DNS-SD 实现)。Ubuntu 22.04 桌面版装完即带 avahi-daemon 且开机自启(systemctl is-active avahi-daemon 返回 active),自身主机名可由 getent hosts <主机名>.local 解析出来:
avahi-resolve -n robot-arm.local # 名字 → IP
avahi-browse -art # 列出所有广播的 DNS-SD 服务
sudo systemctl status avahi-daemon
/etc/avahi/avahi-daemon.conf 关键项:host-name= 定主机名、allow-interfaces= 白名单网卡(多网口机器避免在车间网上广播)。
判据:.local 解析不通先查 avahi-daemon 是否跑、再看防火墙放 5353/udp;跨路由器的 mDNS 需要 avahi-reflector 或 mDNS gateway,不是默认能力。ROS 2 多机通信不依赖 mDNS(DDS 用 IP 组播),但 ros2 launch 里的脚本与 robot_state_publisher 之类常用主机名,mDNS 挂了会连带不通。
4.2 dnsmasq 供机器人局域网¶
无路由器的现场(比如调试台把机器人 PC 直接连到一块),让上位机自己发 IP:
## /etc/dnsmasq.d/robot-lan.conf
interface=enp0s31f7
bind-interfaces
dhcp-range=192.168.50.50,192.168.50.150,255.255.255.0,12h
dhcp-option=3,192.168.50.1 # 网关
dhcp-option=6,192.168.50.1 # DNS
sudo systemctl restart dnsmasq;上位机自己在 enp0s31f7 上静态 192.168.50.1。判据:dnsmasq 与 NetworkManager 内置 DHCP 会抢 67/udp,服务器与板端二选一。
4.3 Mosquitto 部署¶
机器人项目里常见的 IoT 消息总线。装法:
sudo apt install mosquitto mosquitto-clients
sudo systemctl enable --now mosquitto
mosquitto_sub -h localhost -t 'robot/status' -v # 订阅
mosquitto_pub -h localhost -t 'robot/status' -m 'idle'
配置在 /etc/mosquitto/mosquitto.conf;allow_anonymous false + password_file 是最小生产加固。协议与应用侧集成(消息格式、topic 规划)归 ROS2-note,本板块只负责"作为上位机常驻服务的部署形态"——systemctl 视角在第 4 章,EnvironmentFile= 与单元依赖在第 10 章现场部署。
5. 时间同步¶
时间同步有三个层次,越往下越贵、越精确:
5.1 timedatectl 与 NTP¶
timedatectl # 综合:本地时间、RTC、时区、NTP 状态
timedatectl status
timedatectl set-timezone Asia/Shanghai
timedatectl set-ntp true # 打开 systemd-timesyncd(默认装)
Ubuntu Server 桌面与服务器默认使用 systemd-timesyncd(一个最小 SNTP 客户端),精度大致毫秒级、有阶跃调整(step)。判据:timesyncd 只适合"知道今天是几号"的场景;控制回路与传感器对齐不够。
timedatectl show 的几个关键标志位:NTP 表示 NTP 服务是否激活、NTPSynchronized 表示时钟是否已同步到源(脚本里判断"时间是否可信"就用这个布尔量)、CanNTP 表示是否允许开 NTP。常见的是 NTP=yes 但 NTPSynchronized=no——服务在跑、却没同步到任何源(无可达 NTP 源时的常态),此时日历时间靠 RTC 兜底。拿不到同步时,先查源可达与防火墙 UDP/123,而不是直接信系统时钟。
"没同步"与"被关了"不同源:若 systemd-timesyncd 被 masked,timedatectl show 会返 NTP=no,systemctl enable 报 is masked, ignoring;区分方法是 systemctl is-enabled systemd-timesyncd。
精度与调整方式:timesyncd 只到毫秒且会阶跃,要做上位机基准供时就得换 chrony(先装包并 disable timesyncd,二者互斥);装不上时 chronyc tracking 报 506 Cannot talk to daemon(chronyd 未运行),也就给不出与源的偏差。
三个层次的精度与调整方式不同,越往下越贵:
flowchart TD
src["时间源:internet NTP / GPS / 上位机"] --> syncd["systemd-timesyncd<br/>毫秒级、会阶跃"]
src --> chrony["chrony<br/>可预测偏差、makestep 可控阶跃"]
chrony --> lan["上位机作基准<br/>向下游提供 NTP"]
lan --> sub["相机 / 激光雷达 / 板端对齐"]
ptp["PTP + 网卡硬件时间戳"] --> sub2["多传感器亚微秒对齐"]
坑:NTP=yes 不等于已同步
NTPSynchronized=no 时服务在跑却没有可信时间(源不可达的常态);脚本里判断“时间是否可信”只能看这个布尔量,不能看 NTP。
5.2 chrony:可预测偏差、可平滑¶
sudo apt install chrony
sudo systemctl disable --now systemd-timesyncd # 二者互斥
sudo systemctl enable --now chronyd
chronyc tracking # 与源的偏差、频率漂移
chronyc sources -v # NTP 源池
/etc/chrony/chrony.conf 关键项:
server ntp.example.com iburst:源,iburst加快首同步。makestep 1.0 3:偏差 > 1 秒且步数 ≤ 3 时允许阶跃。rtcsync:把内核时间写回 RTC(否则 NTP 校完系统时钟、RTC 依旧老)。allow 192.168.10.0/24:向上位机局域网内提供 NTP,给板端对齐。
判据:上位机做基准、下游设备(相机、激光雷达、板端)以这台上位机为 NTP 源,比每台各自 internet 同步更稳。跨传感器时间戳核对给一次最小算式:两台同时 date +%s.%N 与 chronyc sourcestats,差值应在亚毫秒。
5.3 PTP:硬件时间戳与 IEEE 1588¶
工业级传感器对齐(多相机触发、LiDAR + IMU 融合)走 PTP(Precision Time Protocol);配合网卡的硬件时间戳才能到亚微秒:
sudo apt install linuxptp
sudo ptp4l -i eno1 -m -H # -H 用硬件戳
phc2sys -s eno1 -c CLOCK_REALTIME -m # 把 PHC 同步到系统时钟
sudo ethtool -T eno1 # 查网卡是否支持 hw-transmit/hw-receive
判据:ethtool -T 输出里没有 hardware-transmit / hardware-receive 的网卡,跑 PTP 精度只与 NTP 同数量级。常见形态:Intel I210 系列带 PTP 硬件时钟,I225/I226 视具体 SKU,Realtek RTL8111/8125 多数不带硬件戳;板端与 USB 网卡尤其要看 ethtool -T 的实际输出,不以型号名推断。
判据:上位机做基准、下游跟着它同步
让相机、激光雷达与板端以上位机为 NTP 源,比各自去同步 internet 更稳;跨传感器时间戳核对时,两台的 chronyc sourcestats 偏差应在亚毫秒。
5.4 RTC 与时区¶
硬件时钟(RTC)走 UTC 还是 local time 是跨系统踩点。Linux 默认用 UTC:
sudo hwclock -r # 读 RTC
sudo hwclock --systohc # 系统 → RTC
cat /etc/adjtime # 记录 UTC / LOCAL 与漂移
双基线差异:与 Windows 双启动时 Windows 默认 LOCAL,两边会互相把时间改错;生产机器人上位机不装 Windows,此坑少见但值得记一笔。
这一口径在真实机器上很容易碰到:timedatectl show 里 RTCTimeUSec 若直接带本地时区后缀(如 Thu 2026-11-05 16:26:56 CST),说明 RTC 被设成了 local time 而非 Linux 默认的 UTC——口径由 /etc/adjtime 末行记录决定(装机选择或与 Windows 共存都会改写它)。核对以 timedatectl show 的 RTCTimeUSec 后缀为准;要回归 UTC 用 sudo timedatectl set-local-rtc 0,否则 hwclock 与内核会在 UTC/LOCAL 间反复把时间改偏。
6. 要点¶
- 配置栈按产品形态分家:Desktop = NetworkManager(netplan 描述)、Server = systemd-networkd(netplan 描述)、cloud image = cloud-init 首启动写 netplan;改配置前先看当前接口归谁管。
iproute2家族统一替代 net-tools:ip -br addr、ip route get、ss -tulpn是日常三件套;ifconfig/netstat服务器不装属正常。- 防火墙默认关、放行按端口段:DDS 组播与单播 UDP 段先允许,Modbus 502 / SSH 22 / MQTT 1883 各自开;ufw 上层写、nftables 底层。
- mDNS / Avahi 不等于 DDS 组播:
.local主机名靠 avahi-daemon + 5353/udp;ROS 2 多机靠 DDS 组播 + 7400+,两套并存但独立排障。 - 时间同步三档:timesyncd 只是够日历、chrony 做上位机基准 + 对局域网供时、PTP 需网卡硬件时间戳;跨机时间戳核对给算式与判据。
- 应用服务只负责"系统侧部署形态":dnsmasq / Mosquitto / chrony 都是 systemd 单元,依赖与自动启动写在第 4 章、第 10 章;协议内容与 ROS 侧集成归各自板块。