Wiki
開發問題與排查
本文由簡體中文內容確定性轉換,並受版本化術語表保護。
1.開發問題與排查
1.1.SSH 連接 ysu.local 時解析到 MID360 網口地址
1.1.1.原因
現象是:電腦與工控機連接同一個 Wi-Fi,直接 ping 工控機的 Wi-Fi 地址 192.168.2.183 正常,也能通過這個地址 SSH 登錄;但使用 ssh ysu@ysu.local 時,主機名卻解析到 192.168.1.50,提示 Connection closed by 192.168.1.50 port 22。
此前拔掉 MID360 網線並重啓工控機後,主機名又能解析到 Wi-Fi 地址,但保留兩張網卡時問題再次出現。
工控機同時使用 Wi-Fi 和有線網口,兩張網卡的用途不同:
| 接口或設備 | 本次地址 | 用途 |
|---|---|---|
工控機 Wi-Fi wlp4s0 |
192.168.2.183/24 |
SSH、局域網通信和聯網,地址由 DHCP 分配 |
工控機雷達有線口 enp3s0 |
192.168.1.50/24 |
接收 MID360 點雲和 IMU 數據 |
| MID360 | 192.168.1.107 |
向配置的上位機地址發送數據 |
**192.168.1.50 是雷達數據的接收端地址,不是 MID360 的默認網關。
**該地址填寫在驅動配置的 host_net_info 中,雷達自身地址填寫在 lidar_configs 中。
Wi-Fi 和雷達使用不同的 /24 網段,本次故障不是這兩個網段重疊造成的。
2026-10-19 保留兩張網卡復現後,抓包和進程跟蹤確認:工控機上的 NoMachine 9.7.3 在 Wi-Fi 上錯誤發佈了 ysu.local → 192.168.1.50,與 Avahi 發佈的正確地址衝突。
| 發佈進程 | 在 Wi-Fi 上返回的 A 記錄 | 記錄有效期 TTL |
|---|---|---|
avahi-daemon,PID 735 |
ysu.local → 192.168.2.183 |
120 秒 |
nxserver.bin,PID 3955,發送線程 4400 |
ysu.local → 192.168.1.50 |
600 秒 |
在工控機 wlp4s0 上抓到的響應均來自 192.168.2.183:5353,但記錄內容分別為 .183 和 .50。進一步跟蹤系統調用,確認 NoMachine 的發送線程將包含 .50 的記錄從 Wi-Fi socket 發出,而 Avahi 發出的是 .183。因此不是單憑“哪個進程監聽了 5353 端口”推測原因,已經定位到錯誤記錄的實際發送進程。
兩個服務使用同一個主機名給出不同的地址,電腦收到並緩存錯誤記錄後,就會把 SSH 發往雷達網段。
600 秒錶示記錄有效期,並不是地址優先級;實際解析結果還受響應先後和緩存更新影響,所以會出現有時正常、有時指向 .50 的現象。
本次電腦去往 .50 的實際路由還進入了 FlClash TUN,去往 .183 則直接經過 Wi-Fi。
錯誤地址來自工控機 NoMachine 的 mDNS 發佈;FlClash 接管去往錯誤地址的連接,是後續路徑問題。
不能僅憑 Connection closed 判斷工控機的 SSH 服務故障。
Avahi 當時已正確回答 Wi-Fi 地址,systemd-resolved 的 mDNS 未啓用,兩端檢查的 hosts 文件中也沒有靜態配置 ysu.local → .50。先關閉 NoMachine 自帶的局域網發現後,主機名立即恢復正常,驗證了故障來源;單獨重啓 Avahi 或限制其接口,無法阻止 NoMachine 繼續發送錯誤記錄。隨後進一步通過 NoMachine 自身的 NXDListenAddress 配置綁定 Wi-Fi 地址,恢復原生自動發現,並補上 DHCP 地址變化時的同步。下面先記錄臨時方案,再説明本機最終採用的完整修復。
1.1.2.臨時方案:關閉 NoMachine 自動發現
1. 臨時連接:使用工控機當前的 Wi-Fi 地址。(前提是你得知道它的 IP 地址)
以下命令在電腦端執行,本次地址為:
ssh ysu@192.168.2.183
Wi-Fi 地址可能隨 DHCP 租約變化,可以在工控機本地或路由器客户端列表中查詢。工控機上也可以執行:
nmcli -f DEVICE,TYPE,STATE,CONNECTION device status
ip -br -4 addr
本次工控機 Wi-Fi 接口為 wlp4s0,雷達有線接口為 enp3s0。排查其他機器時,以實際輸出為準。

2. 工控機:確認 NoMachine 也在使用 mDNS。
以下操作在工控機上執行,不是在用於遠程連接的電腦上執行:
sudo ss -ulpn 'sport = :5353'
sudo /etc/NX/nxserver --version
修復前,5353 UDP 端口同時由 avahi-daemon 和 nxserver.bin 使用。本次 NoMachine 版本為 9.7.3。多個進程監聽同一端口本身不代表一定故障,應結合實際回答的地址判斷;本次已通過抓包和進程跟蹤確認 NoMachine 發佈錯誤記錄。
3. 工控機:關閉 NoMachine 的局域網自動發現。
先備份配置。下面的備份文件名用於本次排查;再次操作時換一個文件名,保留已有備份:
sudo cp -a /usr/NX/etc/server.cfg /usr/NX/etc/server.cfg.before-mdns-fix-20261019
sudoedit /usr/NX/etc/server.cfg
找到本次安裝版本中的配置項:
#EnableLocalNetworkBroadcast 1
去掉開頭的 # 並改為:
EnableLocalNetworkBroadcast 0
這會關閉 NoMachine 的局域網自動發現,其他電腦不再通過它的廣播自動發現該工控機;仍可通過 IP 或能夠正常解析的主機名手動連接 NoMachine。 無需卸載 NoMachine,也無需停掉 Avahi。NoMachine 官方端口與配置説明
不同版本的配置項可能不同,舊資料中還會出現 EnableNetworkBroadcast。應以安裝版本的 server.cfg 註釋和對應文檔為準,不能把舊名稱直接添加到本次 9.7.3 的配置中。
4. 工控機:重啓 NoMachine,使配置生效。
先結束正在使用的 NoMachine 遠程桌面會話,再執行;重啓會暫時中斷該服務:
sudo /etc/NX/nxserver --restart
sudo /etc/NX/nxserver --status
隨後檢查:
sudo ss -ulpn 'sport = :5353'
sudo ss -ltnp 'sport = :4000'
臨時方案生效後,5353 端口只剩 Avahi,NoMachine 的 nxserver、nxnode 和 nxd 服務恢復運行,4000 TCP 端口繼續監聽。配置保存在文件中,系統重啓後仍會讀取;本次沒有修改 Avahi 配置,也沒有修改 Wi-Fi、雷達 IP 或路由。
5. 電腦端:驗證主機名和 SSH。
getent ahostsv4 ysu.local
avahi-resolve-host-name -4 ysu.local
ssh ysu@ysu.local
預期解析到工控機當前的 Wi-Fi 地址,本次為 192.168.2.183,並且可以正常登錄。已經緩存的錯誤記錄可能需要等待過期或被正確記錄更新;若仍返回 .50,應繼續抓包確認是否還有錯誤發佈源,不能僅憑服務狀態認定修復完成。
如需抓包,在工控機執行以下命令,將接口名換成實際 Wi-Fi 接口:
sudo tcpdump -ni wlp4s0 -vv 'udp port 5353'
觀察 ysu.local 的 A 記錄內容,而不只是數據包的源 IP。本次錯誤響應的數據包源 IP 也是 .183,真正錯誤的是其中的 A 記錄指向了 .50。
1.1.3.臨時方案驗證結果
2026-10-19 完成上述修改並重啓 NoMachine 後:
| 檢查項目 | 結果 |
|---|---|
| 電腦端三次名稱查詢 | 均得到 192.168.2.183 |
| 工控機 Wi-Fi 上的三次 mDNS 查詢響應 | 均為 Avahi 返回的 .183,該輪抓包未再出現 .50 |
ssh ysu@ysu.local |
密碼登錄成功,遠端 hostname 返回 ysu |
| NoMachine 狀態 | 服務正常,4000 TCP 端口繼續監聽 |
| 電腦斷開並重新連接原 Wi-Fi 後 | 主機名仍解析到 .183 |
臨時方案只將 /usr/NX/etc/server.cfg 中的 EnableLocalNetworkBroadcast 設為 0,原配置備份在同目錄的 server.cfg.before-mdns-fix-20261019。後續已恢復自動發現,最終狀態以下面的完整修復為準。
1.1.4.完整修復:讓 NoMachine 自動跟隨 Wi-Fi 當前 IP
這裏固定的是用於遠程連接的 Wi-Fi 接口,不是某一個 IP。NXDListenAddress 本身需要填寫具體地址,不能靠寫一行配置就自動追蹤 DHCP。動態效果來自下面的同步程序:每次讀取 Wi-Fi 當前地址,再把它寫入 NoMachine 配置。
例如當前地址為 .183,程序就寫入 .183;以後 DHCP 分配了其他地址,程序會用新地址替換它。不要手動把 NXDListenAddress 永久寫成 192.168.2.183,也不要把 ysu.local 填進這個字段來解析自己;電腦端連接時才使用 ysu.local。NoMachine 監聽和廣播地址配置説明
本機已完成下面的部署,現有工控機無需重複安裝。下面給出完整步驟,方便在其他機器複用;首次部署會重啓 NoMachine,應先結束正在使用的遠程桌面會話。
1. 工控機:確認需要跟隨的 Wi-Fi 接口。
nmcli -f DEVICE,TYPE,STATE,CONNECTION device status
ip -br -4 addr
本機 Wi-Fi 為 wlp4s0,雷達網口為 enp3s0。後面的 Python 腳本和 NetworkManager 事件腳本中各有一處接口設置,其他機器應把兩處 wlp4s0 都換成實際 Wi-Fi 接口名。Wi-Fi 繼續使用 DHCP,無需固定 IP,也無需修改雷達網口。
2. 工控機:安裝讀取當前 IP 的同步程序。
創建文件:
sudoedit /usr/local/sbin/nomachine-wifi-sync
填入下面的內容。腳本中只指定接口名 INTERFACE,沒有寫死 Wi-Fi IP;PORT 對應本機 NoMachine 默認的 4000 端口。
#!/usr/bin/python3
"""Keep NoMachine bound to the robot's management Wi-Fi IPv4 address."""
import fcntl
import json
import os
from pathlib import Path
import re
import stat
import subprocess
import tempfile
INTERFACE = "wlp4s0"
CONFIG = Path("/usr/NX/etc/server.cfg")
PORT = 4000
def run(*args):
return subprocess.check_output(args, text=True, timeout=10)
def replace_config(content):
metadata = CONFIG.stat()
temporary = None
try:
with tempfile.NamedTemporaryFile(mode="w", dir=CONFIG.parent, delete=False) as output:
temporary = Path(output.name)
output.write(content)
output.flush()
os.fchown(output.fileno(), metadata.st_uid, metadata.st_gid)
os.fchmod(output.fileno(), stat.S_IMODE(metadata.st_mode))
os.fsync(output.fileno())
os.replace(temporary, CONFIG)
finally:
if temporary is not None and temporary.exists():
temporary.unlink()
def main():
with open("/run/nomachine-wifi-sync.lock", "w") as lock:
fcntl.flock(lock, fcntl.LOCK_EX)
interfaces = json.loads(run("/usr/sbin/ip", "-j", "-4", "address", "show", "dev", INTERFACE))
addresses = [
address["local"]
for interface in interfaces
for address in interface.get("addr_info", [])
if address.get("scope") == "global" and address.get("valid_life_time") != 0
]
if not addresses:
print(f"{INTERFACE} has no IPv4 address; leaving configuration unchanged.", flush=True)
return
if len(addresses) != 1:
raise RuntimeError(f"Expected one Wi-Fi IPv4 address; got {addresses!r}")
address = addresses[0]
old = CONFIG.read_text()
updated, count = re.subn(
r"(?m)^[ \t]*#?NXDListenAddress[^\S\n]+[^\n]*$",
f'NXDListenAddress "{address}"',
old,
)
if count != 1:
raise RuntimeError(f"Expected one NXDListenAddress entry; got {count}")
listeners = run("/usr/bin/ss", "-H", "-ltn", f"sport = :{PORT}")
bound = any(line.split()[3] == f"{address}:{PORT}" for line in listeners.splitlines())
if updated == old and bound:
print(f"NoMachine already bound to {INTERFACE} {address}:{PORT}; no restart.", flush=True)
return
if updated != old:
replace_config(updated)
print(f"Synchronizing NoMachine to {INTERFACE} {address}:{PORT}.", flush=True)
subprocess.run(["/etc/NX/nxserver", "--restart"], check=True, timeout=75)
listeners = run("/usr/bin/ss", "-H", "-ltn", f"sport = :{PORT}")
if not any(line.split()[3] == f"{address}:{PORT}" for line in listeners.splitlines()):
raise RuntimeError(f"NoMachine is not listening on {address}:{PORT} after restart")
print(f"Verified NoMachine listener {address}:{PORT}.", flush=True)
if __name__ == "__main__":
main()
這個程序讀取接口當前的唯一全局 IPv4,更新現有的 NXDListenAddress 配置項。當前地址和監聽都正確時直接退出;地址改變或監聽未恢復時,才重啓 NoMachine 並檢查結果。接口暫時沒有 IPv4 時不修改配置,存在多個 IPv4 時明確報錯,避免隨意選擇地址。
3. 工控機:設置開機同步和網絡變化觸發。
創建一次性同步服務:
sudoedit /etc/systemd/system/nomachine-wifi-sync.service
填入:
[Unit]
Description=Bind NoMachine to the current management Wi-Fi IPv4 address
Wants=network-online.target
After=network-online.target nxserver.service
[Service]
Type=oneshot
ExecStart=/usr/local/sbin/nomachine-wifi-sync
TimeoutStartSec=100
[Install]
WantedBy=multi-user.target
再創建 NetworkManager 事件腳本:
sudoedit /etc/NetworkManager/dispatcher.d/90-nomachine-wifi
填入:
#!/bin/sh
# NetworkManager runs this as root after connection/address changes.
case "${1:-}:${2:-}" in
wlp4s0:up|wlp4s0:dhcp4-change|wlp4s0:reapply)
/usr/bin/systemctl --no-block start nomachine-wifi-sync.service
;;
esac
設置文件所有者和執行權限:
sudo chown root:root /usr/local/sbin/nomachine-wifi-sync /etc/systemd/system/nomachine-wifi-sync.service /etc/NetworkManager/dispatcher.d/90-nomachine-wifi
sudo chmod 755 /usr/local/sbin/nomachine-wifi-sync /etc/NetworkManager/dispatcher.d/90-nomachine-wifi
sudo chmod 644 /etc/systemd/system/nomachine-wifi-sync.service
這樣,Wi-Fi 連接、DHCPv4 租約變化或重新應用連接配置時,系統都會觸發同步。NetworkManager 官方事件説明
上述三個文件是本次部署的輔助配置,不是 NoMachine 自帶功能;只修改 server.cfg、沒有安裝事件腳本,地址就不會自動跟隨 DHCP。
4. 工控機:啓用同步,再恢復 NoMachine 自動發現。
先加載並啓用同步服務,讓程序根據當前 Wi-Fi 地址生成 NXDListenAddress:
sudo systemctl daemon-reload
sudo systemctl enable --now nomachine-wifi-sync.service
然後編輯 NoMachine 配置:
sudoedit /usr/NX/etc/server.cfg
將局域網自動發現配置項改為:
EnableLocalNetworkBroadcast 1
**NXDListenAddress 這一行交給同步程序維護,不手動填寫 .183。**首次打開自動發現後,重啓 NoMachine,使該開關生效:
sudo /etc/NX/nxserver --restart
後續 IP 變化由同步服務自動處理,無需再手動修改配置或重啓;地址發生變化時的服務重啓會中斷原有 NoMachine 會話。
可以檢查程序生成的地址和日誌:
sudo grep '^NXDListenAddress' /usr/NX/etc/server.cfg
systemctl is-enabled nomachine-wifi-sync.service
sudo journalctl -u nomachine-wifi-sync.service -n 30 --no-pager
這裏看到的是程序最後一次同步的具體地址,應與 wlp4s0 當前 IPv4 一致,不代表給網卡設置了靜態 IP。服務是一次性任務,執行成功後顯示 inactive 屬於正常情況,應看日誌和 Result,不要求它一直處於 active。
需要手動重新同步時執行:
sudo systemctl start nomachine-wifi-sync.service
NoMachine 恢復自動發現後仍會使用 5353 UDP 端口。此時不能把“看到 nxserver.bin 監聽 5353”直接當作故障,應檢查它發佈的地址是否與當前 Wi-Fi IP 一致。
5. 電腦端:始終使用主機名連接。
avahi-browse -rt _nomachine._tcp
getent ahostsv4 ysu.local
ssh ysu@ysu.local
自動發現的 A 記錄和 TXT 中的 ip= 應等於工控機此時的 Wi-Fi IPv4,而不是永遠等於本次排查時的 .183。本次 NoMachine 的 SRV 記錄端口為 0,實際 NX 連接端口由 TXT 中的 service=nx:4000 給出,不能據此誤判服務沒有監聽。
手動創建 NoMachine 連接時使用主機名:
协议:NX
主机:ysu.local
端口:4000
用户名:ysu
電腦仍需與工控機處於能夠進行局域網發現和互訪的網絡中。IP 變化時,由 Avahi 更新主機名記錄、同步程序更新 NoMachine 綁定地址,電腦端繼續使用 ysu.local。
1.1.5.完整修複驗證結果
| 檢查項目 | 結果 |
|---|---|
| 原生 NoMachine 自動發現 | 已恢復,能發現 ysu,A 記錄和 TXT 的 IP 均為 .183 |
| Wi-Fi 抓包 | Avahi 和 NoMachine 都回答 .183,該輪未再出現 .50 |
| 4000 TCP 端口 | 綁定 192.168.2.183:4000,電腦端實際 TCP 連接成功 |
| SSH 主機名連接 | ssh ysu@ysu.local hostname 密碼登錄成功,返回 ysu |
| 配置殘留舊 IP 的模擬 | 將保存地址臨時改成 .254 後觸發 DHCP 變化處理,同步任務自動恢復 .183、重啓服務並驗證監聽成功 |
| 地址未變化的事件 | 同步任務直接退出,不重啓 NoMachine |
| 開機同步 | 服務已啓用 |
完整修復前的配置備份為 /usr/NX/etc/server.cfg.before-wifi-binding-20261019。本次沒有改變工控機實際 Wi-Fi IP,也沒有做整機重啓或圖形桌面登錄測試;模擬舊配置驗證了同步處理流程,真實換 IP 和完整圖形會話仍應在實際使用時確認。
1.2.ROS 2 遠端查看 MID360 點雲導致降頻、延遲和 RViz 不顯示
1.2.1.原因
現象是:電腦通過 Wi-Fi 打開 RViz,或者僅運行遠端 ros2 topic hz,工控機上的 MID360 點雲和 IMU 就明顯降頻,連工控機本地測頻也受到影響;關閉遠端訂閲後恢復。切換手機熱點和其他 Wi-Fi 都未直接解決問題。
本次排查發現了兩個獨立問題:
-
電腦端 DDS 公佈了不適合本次跨機通信的地址。
電腦端 DDS 同時公佈了 Wi-Fi 地址、Docker 網橋地址
172.17.0.1和 Clash TUN 虛擬網卡地址198.20.0.1。工控機根據這些地址發送數據,其中發往 Clash TUN 地址的數據被路由到雷達網口的網關192.168.1.1,鄰居解析未完成。實測部分 UDP 發送調用阻塞約 250~330 毫秒,發送阻塞拖慢發佈,點雲和 IMU 出現積壓。本次定位到的明顯阻塞與 Clash TUN 地址有關,沒有單獨驗證 Docker 地址是否造成同樣阻塞。不過,電腦 Docker 網橋地址也不適合作為本次跨機接收地址,應一併排除。選擇接口的原則是:只使用 ROS 對端實際可達、並且本次通信確實需要的接口。
-
電腦端 UDP 接收緩衝不足以承受本次點雲突發流量。
修正接口並重建電腦端 daemon 後,工控機已恢復點雲約 20 Hz、IMU 約 200 Hz,但電腦端點雲仍只有約 12.8 Hz,接收 socket 仍有緩衝丟包。此時默認接收緩衝約為 208 KiB;將請求值增大到 4 MiB 後,電腦端點雲恢復約 20 Hz,測試期間該 socket 的緩衝丟包計數為零。
因此,緩衝丟包不能全部歸因於選錯網卡。大點雲的分片集中到達、接收線程短暫來不及處理時,正確接口也可能出現緩衝溢出。增大接收緩衝是按數據量選擇的優化;本次 MID360 接收環境有實測依據,建議保留 4 MiB。Fast DDS 大數據傳輸説明
ros2 topic hz 會訂閲完整消息來統計頻率,同樣需要接收整幀點雲,因此僅開啓遠端測頻也可能觸發上述問題。ROS 2 Humble 測頻實現
點雲延遲到十幾秒甚至幾十秒後,點雲時間戳可能早於 RViz 保存的 TF 歷史。即使 TF 樹連通、最新變換正常,RViz 仍無法完成點雲時間戳對應的座標變換,表現為不顯示點雲。本次將 Fixed Frame 臨時改為點雲自身的 front_mid360 後出現點雲,驗證了這個方向;解決傳輸積壓後,就可以使用正常的顯示座標系。
1.2.2.解決方法
1. 工控機:建議保留異步發佈。
在實際運行 ROS 的用户的 ~/.bashrc 中加入。
export RMW_FASTRTPS_PUBLICATION_MODE=ASYNCHRONOUS
修改後執行:
source ~/.bashrc
然後在加載了該環境的終端中重新啓動 MID360 驅動,使新進程繼承設置。
異步發佈將實際發送交給後台線程,可以減輕網絡發送對驅動發佈線程的直接影響;單獨設置它未能解決本次問題,仍需配合下面的電腦端配置。Fast DDS 在 ROS 2 Humble 中的發佈模式説明
2. 電腦端:通過 source 加載工程內的 dds_env.bash。
先查詢自己網絡網卡:
ip -br -4 addr
看ip就能看出來,只有10.xx.xx.xx和192.168.xx.xx這種才是NAT4下發的ip。
所以這個wlp0s20f3才是你和ROS用於局域網通信的網卡。

也可以點擊WIFI按鈕確認一下ip,和上方説的一模一樣:

當前腳本位於工程根目錄:
/ysu2026_rm_sentry_nav/dds_env.bash
現在初始化當前終端後即可正常使用 rviz2 和 ros2 命令。
這份配置應首先加載到電腦端,因為接收地址由電腦 DDS 公佈,接收緩衝也屬於電腦上的接收進程。工控機使用電腦公佈的地址發送點雲;增大工控機的接收緩衝不能替代電腦端設置。
腳本讀取所選接口當前的 IPv4,生成 XML,並導出 FASTRTPS_DEFAULT_PROFILES_FILE。UDP 使用所選地址和本機迴環地址 127.0.0.1,同時保留本機 SHM 通信。XML 保存在工程根目錄,文件名為 .dds-env-<用户ID>-<配置摘要>.xml,已加入 Git 忽略規則。Fast DDS 接口白名單
以下命令在電腦的 ROS 終端執行。兩端 ROS_DOMAIN_ID 必須一致,本次為 6;網卡名 wlp0s20f3 應按實際接口修改:
source /opt/ros/humble/setup.bash
export ROS_DOMAIN_ID=6
export DDS_INTERFACE=wlp0s20f3
export DDS_RECV_BUFFER=4MiB
source /你的存放工程的路径/ysu2026_rm_sentry_nav/dds_env.bash
比如:source /home/tungchiahui/UserFolder/MySource/ysu2026_rm_sentry/ysu2026_rm_sentry_nav/dds_env.bash
兩個參數可以分別配置:
| 參數 | 用法 |
|---|---|
DDS_INTERFACE |
指定網卡名稱,支持逗號分隔的多個接口;未設置時自動選擇唯一處於 UP 狀態且有 IPv4 的物理網卡,多個候選時返回錯誤,要求明確選擇 |
DDS_RECV_BUFFER |
不設置或 0 表示系統默認;純整數表示字節;支持 B、KB、MB、GB、KiB、MiB、GiB 單位,數值須為非負整數 |
4MiB 等於 4194304 字節,4MB 等於 4000000 字節。緩衝參數是腳本定義的選項,最終會轉換為字節寫入 Fast DDS XML。本次接收 MID360 點雲建議使用已驗證有效的 4MiB。請求值還受系統 net.core.rmem_max 上限約束,超出上限時腳本會提示,但不會自動修改系統參數。
腳本成功加載時不打印,出錯或系統上限不足時給出提示。它僅配置 Fast DDS,不改變 ROS_DOMAIN_ID、消息 QoS 或當前 shell 的選項。
我電腦容器使用 host 網絡,可以訪問宿主 Wi-Fi 接口。其他 Docker 網絡模式應選擇容器實際可見且對端可達的接口;
接口白名單不能替代 bridge 網絡所需的 NAT 或發現配置。跨容器使用 SHM 還取決於 IPC 配置。(一勞永逸,直接host即可)
3. 首次啓用或切換網絡後,重建電腦端 daemon。
先關閉舊的 RViz 和遠端測頻進程,在已加載上述 DDS 配置的終端執行:
ros2 daemon stop
ros2 daemon start
daemon 本身也會創建 DDS Participant,應繼承相同的接口配置,避免保留原先的虛擬網卡地址。本次排查中,重建 daemon 是恢復正常通信的一步;不必每次測頻都重啓它。
4. 直接打開 RViz 和測頻。
在已加載配置的電腦終端中打開 RViz:
rviz2
另一個終端也先加載第 2 步相同的 ROS 和 DDS 環境,再運行:
ros2 topic hz /livox/lidar/pointcloud --window 40
--window 40 只是縮短統計窗口,方便觀察近期變化;真正起作用的是網卡白名單和接收緩衝設置。
5. 日常使用:可加入電腦 ROS 用户的 .bashrc。
將以下三行放在已有 ROS 環境加載語句之後。當前電腦容器使用 root,對應 /root/.bashrc:
export DDS_INTERFACE=wlp0s20f3
export DDS_RECV_BUFFER=4MiB
source /你的存放工程的路径/ysu2026_rm_sentry_nav/dds_env.bash
比如:source /home/tungchiahui/UserFolder/MySource/ysu2026_rm_sentry/ysu2026_rm_sentry_nav/dds_env.bash
這樣,新打開並讀取 .bashrc 的終端會自動初始化 DDS。通過服務或非交互方式啓動的 ROS 程序,需要在其啓動環境中主動加載配置。腳本位於個人工程中,加載現有 /opt/ros/humble/setup.bash 不會修改 ROS 安裝文件。
切換 Wi-Fi、改變所選接口或接收緩衝後,應重新 source 腳本,重建 daemon,並重新啓動相關 ROS 程序;已經運行的進程不會自動讀取新的環境變量或 XML。腳本本身不會重啓這些進程。
1.2.3.本次驗證結果
完成接口限制、daemon 重建和 4 MiB 接收緩衝配置後的現場遠端實測結果如下:
| 測量位置 | 點雲頻率 | IMU 頻率 |
|---|---|---|
| 工控機本地 | 約 20 Hz | 約 200 Hz |
| 電腦端 | 約 20 Hz | 約 197~199 Hz |
放大緩衝後的測試節點接收緩衝丟包計數為零。打開 RViz 後,工控機仍保持正常頻率,點雲持續顯示,延遲不再增長到幾十秒。這是本次測試期間的結果。
上表來自整理新腳本之前的現場遠端測試。後續新增 dds_env.bash,在 ysu_ros_humble 中驗證了 XML 加載、接收緩衝生效、本機約 262 KB 點雲接收 20/20 幀,以及單位轉換、靜默加載和參數錯誤時保留原環境。新版腳本的驗證範圍包含這些項目;上述遠端頻率仍引用此前現場實測。
配置整理在工程中新增了 DDS 環境腳本和生成 XML 的 Git 忽略規則;本次排查未修改 MID360 驅動、SLAM 或控制節點的業務代碼。
1.3.點雲和 TF 正常但二維地圖不更新
1.3.1.現象與排查入口
2026-10-20,工控機 ysu.local 在容器 ysu_rm_ros_humble 中進行實車建圖,啓動參數包含 slam:=True use_sim_time:=False。現場表現為點雲持續刷新,odom 到底盤的 TF 和位姿輸出正常,但二維地圖沒有繼續生成。
當前二維建圖鏈路為:
Point-LIO → cloud_registered
→ loam_interface → registered_scan(odom系点云)
→ 地形分析 → terrain_map_ext
→ pointcloud_to_laserscan → obstacle_scan
→ slam_toolbox → /map
先檢查點雲有沒有變成二維掃描,再檢查掃描有沒有進入地圖。 點雲刷新只説明三維處理鏈有輸出;ros2 node list 能看到轉換節點、ros2 topic list 能看到話題,也不代表該節點正在發佈有效數據。
以下檢查在工控機實際運行建圖的 ROS 容器內執行,先加載對應環境。工程路徑和 ROS_DOMAIN_ID 按現場修改,本次為:
source /opt/ros/humble/setup.bash
source /home/ysu/robomaster/ysu2026_rm_sentry/install/setup.bash
export ROS_DOMAIN_ID=6
# 分别观察二维转换的直接输入和输出,每项最多运行10秒。
timeout 10s ros2 topic hz /terrain_map_ext --window 40
timeout 10s ros2 topic hz /obstacle_scan --window 40
# 查看是否有发布端和订阅端;有连接仍需结合实际收帧判断。
ros2 topic info /obstacle_scan --verbose
上面的 timeout 到時結束屬於預期行為;在沒有消息時,測頻命令可能一直沒有頻率輸出。本次已存在 slam_toolbox 訂閲,轉換節點仍然沒有輸出,不能解釋為“沒人訂閲,所以節點沒有接收點雲”。
1.3.2.現場證據
本次在工控機本地連續訂閲採樣 10 秒,結果如下:
| 話題 | 接收數量 | 説明 |
|---|---|---|
/cloud_registered |
199 | Point-LIO 點雲持續更新 |
/registered_scan |
197 | 轉到 odom 系的點雲持續更新 |
/terrain_map |
199 | 近場地形持續更新 |
/terrain_map_ext |
195 | 二維轉換的直接輸入持續更新,約20 Hz |
/odometry |
196 | 位姿輸出持續更新 |
/obstacle_scan |
0 | 二維轉換的輸出斷流 |
/map |
20 | 仍在發佈舊地圖,消息時間約落後405秒 |
/terrain_map_ext 最後一幀有 2700 個點,其中 1204 個點滿足配置的 intensity=[0.1,2.0] 門檻。這裏的 intensity 是地形節點計算的相對地面高差,單位為 m。該結果説明輸入並非全部被這項門檻篩掉,但其他幾何條件仍需單獨檢查;本次確定死鎖依靠下面的線程和鎖證據。
slam_toolbox 的參數服務正常返回,use_sim_time=false,scan_topic=obstacle_scan;轉換節點的參數請求在採樣期間沒有返回。這與轉換節點的主執行線程被阻塞相符。
單獨看到 /map 的時間戳較舊,不能直接斷言死鎖:機械人靜止或新掃描未達到入圖門檻時,地圖內容也可能不變。本次同時確認了輸入持續更新、輸出完全斷流、參數請求不返回,以及兩個線程互相等待鎖。
1.3.3.原因:轉換節點內部的 TF 等待死鎖
現場安裝版本為:
ros-humble-tf2 0.25.23-1jammy.20260907.213559
ros-humble-tf2-ros 0.25.23-1jammy.20260907.224752
ros-humble-tf2-sensor-msgs 0.25.23-1jammy.20260907.225802
轉換節點使用 tf2_ros::MessageFilter,等待把 odom 系地形點雲轉換到 base_footprint 所需的 TF。這個版本的異步等待路徑存在鎖順序反轉:點雲處理線程先持有一把鎖、再等另一把鎖,TF 接收線程按相反順序持鎖,可能形成永久的相互等待。Humble 上游修復説明
本次轉換節點容器 PID 為 2902,抓取線程堆棧後立即脱離。兩條關鍵調用鏈為:
主线程2902:
pthread_mutex_lock(0x5dad8dbf3840)
→ tf2::BufferCore::addTransformableRequest
→ tf2_ros::Buffer::waitForTransform
→ tf2_ros::MessageFilter<PointCloud2>::add
TF接收线程3006:
pthread_mutex_lock(0x5dad8dbf38f0)
→ tf2_ros::Buffer的transform-ready回调
→ tf2::BufferCore::testTransformableRequests
→ tf2::BufferCore::setTransformImpl
→ tf2_ros::TransformListener::subscription_callback
讀取兩把鎖的所有者,得到:
| 鎖地址 | 持有該鎖的線程 | 等待該鎖的線程 |
|---|---|---|
0x5dad8dbf3840 |
3006 | 2902 |
0x5dad8dbf38f0 |
2902 | 3006 |
線程2902和3006各持有對方需要的鎖,已經形成死鎖。這裏的線程編號和地址只描述本次現場,重新啓動後會變化。
外部 TF 正常與這個死鎖可以同時存在。 每個轉換進程有自己的 TF Buffer 和處理線程;本次卡住的是二維轉換進程內部的等待路徑,其他節點仍能接收 TF、處理點雲和輸出位姿。轉換節點進程保持存在,卻停止處理新點雲,slam_toolbox 收不到新的 obstacle_scan,於是只能繼續發佈舊地圖。相同版本與調用鏈的上游報告 #992
1.3.4.修復版本與時間
Humble 已有對應修復:
| 日期 | 上游進展 |
|---|---|
| 2026-09-10 | 修復鎖順序反轉的補丁合入 Humble 分支,見PR #990 |
| 2026-09-15 | tf2/tf2_ros 0.25.24 的版本記錄列出這項死鎖修復,見tf2版本記錄、tf2_ros版本記錄 |
2026-10-20 直接檢查工控機所配置的清華 ROS 軟件源在線索引,確認已有以下 amd64 包:
ros-humble-tf2 0.25.24-1jammy.20260915.203746
ros-humble-tf2-ros 0.25.24-1jammy.20260915.204410
但容器當時的 apt-cache policy 仍把 0.25.23 列為候選,本地 ROS 軟件源索引停在2026-10-02。因此需要先刷新索引,再檢查候選版本;“上游有修復”“本地安裝了修復版”“運行中的進程已經加載修復版”是三個不同狀態。
1.3.5.處理方法
以下為修復操作步驟,本次診斷時尚未執行。
1. 在運行建圖的 ROS 容器內刷新索引,核對候選版本。
以下以容器中的 root 用户為例;普通用户執行軟件包管理命令時需使用 sudo:
apt update
apt-cache policy ros-humble-tf2 ros-humble-tf2-ros
確認兩個包的 Candidate 均為 0.25.24 或包含該修復的後續 Humble 版本。如果仍為 0.25.23,先檢查索引刷新是否成功及鏡像是否同步,再進行升級。
2. 結束本輪建圖,升級相關包並核對安裝結果。
apt install --only-upgrade ros-humble-tf2 ros-humble-tf2-ros
dpkg-query -W -f='${Package} ${Version}\n' ros-humble-tf2 ros-humble-tf2-ros
由包管理器處理依賴,檢查命令是否成功以及實際安裝版本。單獨重啓轉換節點有機會暫時恢復,但繼續使用 0.25.23 仍可能再次觸發死鎖。
3. 重新啓動建圖,檢查掃描與地圖恢復。
使用原來的實車建圖啓動方式重新運行,隨後檢查:
/terrain_map_ext持續輸入,/obstacle_scan恢復連續輸出,且掃描中存在有效距離值。- 轉換節點的參數請求能夠正常返回。
- 在滿足掃描入圖條件的實際移動後,
/map內容繼續更新。不要只憑靜止時地圖不變判斷修復失敗。
升級磁盤上的庫不會解除舊進程已經形成的死鎖,必須重啓使用舊庫的建圖進程。修改高度、強度、TF容差或RViz顯示參數,也無法解除這次已經形成的鎖等待環。
1.3.6.本次驗證狀態
已完成: 現場話題採樣、參數服務檢查、線程堆棧和鎖所有者核對;確認二維轉換節點死鎖;核實上游修復版本及當前配置鏡像中修復包可用。
待完成: 容器軟件包升級、建圖重啓,以及升級後二維掃描與地圖恢復的實車驗證。截至2026-10-20本次記錄,尚未執行這些修復操作,不能將上游修復結論寫成已經完成本機恢復驗證。