Pular para o conteúdo principal

Configuração do PC2 (Unitree)

Para este tutorial é recomendado o uso desta ferramenta.

Copiar certificados​

Ainda no HOST, use SSH para criar o diretório de configuração no PC2 e copie os arquivos:

ssh unitree@192.168.123.164 'mkdir -p ~/.config/xr_teleoperate'
scp ~/Desktop/Teleoperacao/xr_teleoperate/teleop/televuer/cert.pem \
~/Desktop/Teleoperacao/xr_teleoperate/teleop/televuer/key.pem \
unitree@192.168.123.164:~/.config/xr_teleoperate/

Verifique se os certificados foram copiados:

ssh unitree@192.168.123.164 'ls -l ~/.config/xr_teleoperate/'

Saída esperada:

cert.pem
key.pem

Instalar pacotes no PC2​

Estas notas resumem a instalação realizada nesta máquina para que possa ser reproduzida em outro sistema Ubuntu estilo Unitree/Jetson.

1. Instalar Miniconda​

wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-aarch64.sh -O /tmp/miniconda.sh
bash /tmp/miniconda.sh -b -u -p /home/unitree/miniconda3
rm /tmp/miniconda.sh

Se o Conda exigir aceitação dos Termos de Serviço do canal:

/home/unitree/miniconda3/bin/conda tos accept --override-channels --channel https://repo.anaconda.com/pkgs/main
/home/unitree/miniconda3/bin/conda tos accept --override-channels --channel https://repo.anaconda.com/pkgs/r

Inicialize o Conda para bash:

/home/unitree/miniconda3/bin/conda init bash
source ~/.bashrc

2. Criar ambiente teleimager​

/home/unitree/miniconda3/bin/conda create -n teleimager python=3.10 -y
conda activate teleimager

3. Instalar pacotes do sistema​

sudo apt update
sudo apt install -y libusb-1.0-0-dev libturbojpeg-dev

4. Clonar e instalar Tele Imager​

cd unitree
git clone https://github.com/silencht/teleimager
cd teleimager
pip install -e ".[server]"
4a. Certificado para o Tele Imager​

Pro precaução, abra outro terminal no HOST e copie os certificados para o recém clonado repositório do Tele Imager

scp ~/Desktop/Developer/unitree-g1/packages/xr_teleoperate/teleop/televuer/cert.pem \
~/Desktop/Developer/unitree-g1/packages/xr_teleoperate/teleop/televuer/key.pem \
unitree@192.168.123.164:~/unitree/teleimager/

5. Correção de dependência logging_mp​

A versão atual do pacote logging_mp expõe getLogger, mas o Tele Imager 1.5.0 chama get_logger. Isso fazia tanto o teleimager-server quanto o teleimager-client falharem na inicialização.

Edite o pyproject.toml local, alterando:

"logging_mp",

para:

"logging_mp==0.1.6",

Em seguida, reinstale:

cd /home/unitree/teleimager
pip install -e ".[server]"

E foi verificado, que para solucionar 100% dos erros é necessário trocar as seguintes linhas dos seguintes arquivos:

src/teleimager/image_server.py

  • linha 15: logging_mp.basicConfig → logging_mp.basic_config
  • linha 16: logging_mp.getLogger → logging_mp.get_logger

src/teleimager/image_client.py

  • linha 33: logging_mp.getLogger → logging_mp.get_logger

6. Modificações no código TeleImager​

As seguintes alterações foram feitas localmente no teleimager para suportar a câmera RealSense D435i em modo OpenCV.

6a. setup_uvc.sh — Tolerante a driver UVC ocupado​

O script original falhava quando o driver uvcvideo estava em uso. Agora ele avisa e continua, apague o step 4 e cooloque esse:

if sudo $MODPROBE_PATH -r uvcvideo; then
sudo $MODPROBE_PATH uvcvideo debug=0
else
echo "Warning: uvcvideo is currently in use; skipping driver reload."
echo "Stop camera/video services or reboot if you need to reload it."
fi
echo "UVC setup completed successfully."

Execute:

cd /home/unitree/teleimager
bash setup_uvc.sh
6b. src/teleimager/image_server.py — Múltiplas correções, troque os código atuais pelo os que serão passados:​

UVC reload warning:

def reload_uvc_driver():
try:
subprocess.run("sudo modprobe -r uvcvideo", shell=True, check=True)
time.sleep(1)
subprocess.run("sudo modprobe uvcvideo debug=0", shell=True, check=True)
time.sleep(1)
logger_mp.info("UVC driver reloaded successfully.")
except subprocess.CalledProcessError as e:
logger_mp.warning(f"UVC driver is currently in use; skipping reload: {e}")

RealSense import error inclui a exceção real:

def get_realsense_module(self) -> object:
try:
import pyrealsense2 as rs
return rs
except (ImportError, OSError) as e:
msg = f"{msg}\nOriginal import error: {e}"
raise RuntimeError(msg)

_is_like_rgb — usa índice numérico V4L2 com até 3 tentativas:

def _is_like_rgb(self, video_path):
source = int(video_path.replace("/dev/video", "")) if video_path.startswith("/dev/video") else video_path
for _ in range(3):
cap = cv2.VideoCapture(source, cv2.CAP_V4L2)
if cap.isOpened():
ret, frame = cap.read()
cap.release()
if ret and frame is not None and frame.ndim == 3 and frame.shape[2] == 3:
return True
else:
cap.release()
time.sleep(0.5)
return False

OpenCVCamera.__init__ — usa índice numérico V4L2:

def __init__(self, cam_topic, video_path, img_shape, fps, ...):
super().__init__(...)
self._video_path = video_path
source = int(self._video_path.replace("/dev/video", "")) if self._video_path.startswith("/dev/video") else self._video_path
self.cap = cv2.VideoCapture(source, cv2.CAP_V4L2)

CameraFinder.__init__ — parâmetro probe_rgb:

def __init__(self, realsense_enable=False, verbose=False, probe_rgb=True):
# ...
self.uvc_rgb_video_paths = self._list_uvc_rgb_video_paths() if probe_rgb else []

ImageServer.__init__ — só faz probe RGB quando necessário:

probe_rgb = any(
(cam_cfg.get("enable_zmq", False) or cam_cfg.get("enable_webrtc", False))
and cam_cfg.get("type", "uvc").lower() in ("opencv", "uvc")
and not cam_cfg.get("physical_path")
for cam_cfg in cam_config.values()
)
self._cam_finder = CameraFinder(realsense_enable, camera_finder_verbose, probe_rgb=probe_rgb)

get_vpath_by_ppath — resolve pelo menor índice da interface, sem probe OpenCV:

def get_vpath_by_ppath(self, physical_path):
base = "/sys/class/video4linux/"
matches = []
for v in os.listdir(base):
sys_path = os.path.realpath(os.path.join(base, v, "device"))
if sys_path == physical_path:
index_path = os.path.join(base, v, "index")
try:
index = int(open(index_path).read().strip())
except Exception:
index = 999
matches.append((index, f"/dev/{v}"))
if not matches:
return None
matches.sort()
return matches[0][1]
6c. src/teleimager/image_client.py — Suporte headless​
parser.add_argument('--no-display', action='store_true',
help='Receive frames without opening OpenCV display windows')
args = parser.parse_args()
display_enabled = not args.no_display and bool(os.environ.get("DISPLAY"))
if not display_enabled:
logger_mp.info("Display disabled; receiving frames without OpenCV windows.")

E cada cv2.imshow(...) envolvido em if display_enabled:.

7. Configuração da câmera (cam_config_server.yaml)​

head_camera:
enable_zmq: true
zmq_port: 55555
enable_webrtc: true
webrtc_port: 60001
webrtc_codec: h264
type: opencv
image_shape: [480, 640]
binocular: false
fps: 30
video_id: null
serial_number: null
physical_path: /sys/devices/platform/3610000.xhci/usb2/2-3/2-3:1.3

left_wrist_camera:
enable_zmq: false
enable_webrtc: false

right_wrist_camera:
enable_zmq: false
enable_webrtc: false

O physical_path usado é o da interface RGB da RealSense (sub-interface 1.3, V4L2 index 0). Isso evita a ambiguidade de serial number (onde /dev/video2 e /dev/video4 compartilham o mesmo serial 252443060502).

8. Script de inicialização (run_realsense_server.sh)​

Crie o arquivo /home/unitree/teleimager/run_realsense_server.sh com o seguinte conteúdo:

#!/bin/bash
set -euo pipefail

SERVER_BIN="${TELEIMAGER_SERVER:-/home/unitree/miniconda3/envs/teleimager/bin/teleimager-server}"
VIDEO_HUB="/unitree/module/video_hub_pc4/videohub_pc4"
STOPPED_MASTER=0

cleanup() {
if [ "$STOPPED_MASTER" -eq 1 ]; then
sudo systemctl start master_service || true
fi
}

existing_server_pids=$(pgrep -f "teleimager-server" || true)
if [ -n "$existing_server_pids" ]; then
echo "TeleImager server is already running: $existing_server_pids"
echo "Stop it first with Ctrl+C in its terminal, or run: pkill -TERM -f teleimager-server"
exit 1
fi

trap cleanup EXIT
sudo systemctl stop master_service || true
STOPPED_MASTER=1
sudo pkill -TERM -f "$VIDEO_HUB" || true
for _ in 1 2 3 4 5; do
if ! pgrep -f "$VIDEO_HUB" > /dev/null; then
break
fi
sleep 1
done

if pgrep -f "$VIDEO_HUB" > /dev/null; then
sudo pkill -KILL -f "$VIDEO_HUB" || true
fi

"$SERVER_BIN"

Torne executável:

chmod +x /home/unitree/teleimager/run_realsense_server.sh

9. Verificação​

Verifique se os comandos estão disponíveis:

teleimager-server --help
teleimager-client --help

Execute a descoberta de câmera:

cd /home/unitree/teleimager
python -m teleimager.image_server --cf

10. Uso típico no PC2​

Inicie o servidor:

cd /home/unitree/teleimager
./run_realsense_server.sh

Para testar o stream localmente (PC2 com display):

teleimager-client --host 127.0.0.1

Ou em modo headless (PC2 sem display):

teleimager-client --host 127.0.0.1 --no-display

Portas usadas:

PortaFunção
60000Config responder
55555ZMQ video stream
60001WebRTC

Câmera RealSense D435i​

A câmera utilizada é uma Intel RealSense D435i conectada na porta USB 9 do G1.

Porta USB do Unitree G1 para câmera

Por que OpenCV em vez do SDK RealSense?​

O modo nativo RealSense (pyrealsense2) não é compatível com o GLIBC do Ubuntu do G1 (aarch64). As wheels disponíveis para pyrealsense2 exigem GLIBC_2.32, mas o sistema possui uma versão mais antiga. Por isso, a câmera é configurada em modo OpenCV, tratando a RealSense como uma câmera UVC padrão.

Identificar o physical path​

Conecte a câmera na porta USB 9 e execute:

rs-enumerate-devices

Saída esperada:

Device info:
Name : Intel RealSense D435I
Serial Number : 406122071162
Firmware Version : 05.15.01.55
Physical Port : /sys/devices/platform/3610000.xhci/usb2/2-3/2-3:1.0/video4linux/video0

O Physical Port completo aponta para a interface 1.0 (meta-dados). A interface RGB que queremos é a 1.3:

/sys/devices/platform/3610000.xhci/usb2/2-3/2-3:1.3

Para confirmar os dispositivos V4L2 associados:

ls -l /sys/class/video4linux/

E verificar qual índice V4L2 cada interface expõe:

for v in /sys/class/video4linux/video*; do
echo "$(basename $v): interface=$(basename $(readlink $v/device)), index=$(cat $v/index 2>/dev/null), name=$(cat $v/name 2>/dev/null)"
done

Configurar cam_config_server.yaml​

No PC2, edite /home/unitree/teleimager/cam_config_server.yaml:

head_camera:
enable_zmq: true
zmq_port: 55555
enable_webrtc: true
webrtc_port: 60001
webrtc_codec: h264
type: opencv
image_shape: [480, 640]
binocular: false
fps: 30
video_id: null
serial_number: null
physical_path: /sys/devices/platform/3610000.xhci/usb2/2-3/2-3:1.3

O campo physical_path é a chave para o funcionamento correto. O teleimager modificado (veja seção 6b) usa get_vpath_by_ppath() para resolver o physical_path ao menor índice V4L2 disponível sob aquela interface USB, sem precisar fazer probe OpenCV.

Iniciar o servidor​

cd /home/unitree/teleimager
./run_realsense_server.sh

Testar o stream​

Do host:

conda activate tv
cd ~/unitreeG1/packages/xr_teleoperate/teleop
python -m teleimager.image_client --host 192.168.123.164 --no-display

Ou do próprio PC2 (com display):

teleimager-client --host 127.0.0.1

Portas​

PortaFunção
60000Config responder
55555ZMQ video stream
60001WebRTC

Referências​