宇树科技go2机器狗官网sdk问题。
import time
import sys
from unitree_sdk2py.core.channel import ChannelSubscriber, ChannelFactoryInitialize
from unitree_sdk2py.idl.default import unitree_go_msg_dds__SportModeState_
from unitree_sdk2py.idl.unitree_go.msg.dds_ import SportModeState_
from unitree_sdk2py.go2.sport.sport_client import (
SportClient,
PathPoint,
SPORT_PATH_POINT_SIZE,
)
import math
from dataclasses import dataclass
@dataclass
class TestOption:
name: str
id: int
option_list = [
TestOption(name="damp", id=0),
TestOption(name="stand_up", id=1),
TestOption(name="stand_down", id=2),
TestOption(name="move forward", id=3),
TestOption(name="move lateral", id=4),
TestOption(name="move rotate", id=5),
TestOption(name="stop_move", id=6),
TestOption(name="hand stand", id=7),
TestOption(name="balanced stand", id=9),
TestOption(name="recovery", id=10),
TestOption(name="left flip", id=11),
TestOption(name="back flip", id=12),
TestOption(name="free walk", id=13),
TestOption(name="free bound", id=14),
TestOption(name="free avoid", id=15),
TestOption(name="walk upright", id=17),
TestOption(name="cross step", id=18),
TestOption(name="free jump", id=19)
]
class UserInterface:
def __init__(self):
self.test_option_ = None
def convert_to_int(self, input_str):
try:
return int(input_str)
except ValueError:
return None
def terminal_handle(self):
input_str = input("Enter id or name: \n")
if input_str == "list":
self.test_option_.name = None
self.test_option_.id = None
for option in option_list:
print(f"{option.name}, id: {option.id}")
return
for option in option_list:
if input_str == option.name or self.convert_to_int(input_str) == option.id:
self.test_option_.name = option.name
self.test_option_.id = option.id
print(f"Test: {self.test_option_.name}, test_id: {self.test_option_.id}")
return
print("No matching test option found.")
if __name__ == "__main__":
print("WARNING: Please ensure there are no obstacles around the robot while running this example.")
input("Press Enter to continue...")
ChannelFactoryInitialize(0, 'enx9c4782c27346')
test_option = TestOption(name=None, id=None)
user_interface = UserInterface()
user_interface.test_option_ = test_option
sport_client = SportClient()
sport_client.SetTimeout(10.0)
sport_client.Init()
while True:
user_interface.terminal_handle()
print(f"Updated Test Option: Name = {test_option.name}, ID = {test_option.id}\n")
if test_option.id == 0:
sport_client.Damp()
elif test_option.id == 1:
sport_client.StandUp()
elif test_option.id == 2:
sport_client.StandDown()
elif test_option.id == 3:
ret = sport_client.Move(0.3,0,0)
print("ret: ",ret)
elif test_option.id == 4:
sport_client.Move(0,0.3,0)
elif test_option.id == 5:
sport_client.Move(0,0,0.5)
elif test_option.id == 6:
sport_client.StopMove()
elif test_option.id == 7:
sport_client.HandStand(True)
time.sleep(4)
sport_client.HandStand(False)
elif test_option.id == 9:
sport_client.BalanceStand()
elif test_option.id == 10:
sport_client.RecoveryStand()
elif test_option.id == 11:
ret = sport_client.LeftFlip()
print("ret: ",ret)
elif test_option.id == 12:
ret = sport_client.BackFlip()
print("ret: ",ret)
elif test_option.id == 13:
ret = sport_client.FreeWalk()
print("ret: ",ret)
elif test_option.id == 14:
ret = sport_client.FreeBound(True)
print("ret: ",ret)
time.sleep(2)
ret = sport_client.FreeBound(False)
print("ret: ",ret)
elif test_option.id == 15:
ret = sport_client.FreeAvoid(True)
print("ret: ",ret)
time.sleep(2)
ret = sport_client.FreeAvoid(False)
print("ret: ",ret)
elif test_option.id == 17:
ret = sport_client.WalkUpright(True)
print("ret: ",ret)
time.sleep(4)
ret = sport_client.WalkUpright(False)
print("ret: ",ret)
elif test_option.id == 18:
ret = sport_client.CrossStep(True)
print("ret: ",ret)
time.sleep(4)
ret = sport_client.CrossStep(False)
print("ret: ",ret)
elif test_option.id == 19:
ret = sport_client.FreeJump(True)
print("ret: ",ret)
time.sleep(4)
ret = sport_client.FreeJump(False)
print("ret: ",ret)
time.sleep(1)
是的,你观察得很准确!让我解释一下这个现象:
## DDS在Unitree SDK中的作用和必要性
### 1. **为什么需要DDS?**
DDS(数据分发服务)在Unitree SDK中扮演着**关键角色**:
- **通信框架**:DDS是机器狗内部各个组件(运动控制、传感器、视频流等)之间通信的中间件
- **实时性**:DDS提供了实时、可靠的数据传输,适合机器人控制这种对延迟敏感的应用
- **发布-订阅模型**:不同的模块(如运动控制、状态反馈)通过DDS进行数据交换
### 2. **为什么会频繁显示连接失败?**
这些`ddsi_udp_conn_write failed with retcode -1`日志显示DDS在尝试:
1. **多播发现**:尝试连接`239.255.0.1:7400`,这是DDS的标准多播地址,用于节点发现
2. **点对点通信**:尝试连接`192.168.123.161:XXXXX`,这是与机器狗本身的通信
**失败的原因可能包括**:
- 某些DDS端口在机器狗上没有对应的服务在运行
- 网络配置或防火墙阻止了某些端口的连接
- 这是DDS的正常发现机制,它会不断尝试连接可能的节点
### 3. **为什么控制需要DDS,而纯视频流不需要?**
**纯视频流**:
- 使用的是GStreamer管道,直接通过UDP多播接收视频数据
- 不需要与机器狗的控制系统交互
- 不需要订阅机器狗状态或发送控制指令
**机器狗控制**:
- 需要实时发送控制指令(如`StandUp()`、`Move()`)
- 需要接收机器狗的状态反馈(如`error_code`)
- 需要通过DDS进行双向、可靠的数据交换
### 4. **这些失败是否影响功能?**
从你的测试来看:
```python
zmrobo@SmartPi:~/Project$ python gocon.py
Go2 SDK初始化成功
当前状态码: 1001
站立
StandUp 执行结果: 0
```
**控制功能正常工作**!虽然DDS显示连接失败,但关键的控制指令(`StandUp`)成功执行了。
## DDS的工作逻辑解释
DDS的工作流程类似于这样:
```
你的程序 (DDS节点)
├── 尝试发现其他DDS节点 (多播: 239.255.0.1:7400)
├── 尝试连接机器狗上的各种服务 (单播: 192.168.123.161:各种端口)
├── 建立必要的连接进行控制指令传输
└── 保持心跳和维护连接
```
**为什么频繁显示失败?**
- DDS会定期尝试连接所有可能的端点
- 机器狗可能只开放了部分必要的端口
- 某些端口可能只在特定状态下才可用
- 这是一种"尽力而为"的连接策略
## 解决方案建议
### 1. **完全接受并忽略DDS日志**
既然控制功能正常工作,可以接受这些日志作为SDK的正常行为。使用我之前提供的日志过滤方法来减少控制台输出:
```python
import os
import sys
class DDSLogFilter:
def __init__(self, original_stderr):
self.original_stderr = original_stderr
def write(self, message):
# 过滤掉DDS相关的UDP错误日志
if "ddsi_udp_conn_write" in message and "failed with retcode -1" in message:
return # 丢弃这条日志
self.original_stderr.write(message)
def flush(self):
self.original_stderr.flush()
# 应用过滤器
sys.stderr = DDSLogFilter(sys.stderr)
```
### 2. **调整DDS日志级别(如果SDK支持)**
有些DDS实现允许通过环境变量调整日志级别:
```python
# 在程序开始时设置
os.environ['NDDS_CONFIG_LOG_VERBOSITY'] = 'WARNING' # 或 'ERROR', 'SEVERE'
os.environ['NDDS_QOS_PROFILES'] = ''
```
### 3. **创建专门的视频流类(推荐)**
如果你只需要视频流,创建一个不初始化DDS的专门类:
```python
class Go2VideoOnly:
"""仅用于视频流,不初始化DDS"""
def __init__(self, interface=None):
self.interface = interface
self.cap = None
def start_video_stream(self, width=480, height=320):
"""使用GStreamer直接获取视频流"""
gstreamer_str = (
f"udpsrc address=230.1.1.1 port=1720 multicast-iface={self.interface} "
"! application/x-rtp, media=video, encoding-name=H264 "
"! rtph264depay ! h264parse ! avdec_h264 "
f"! videoscale ! video/x-raw,width={width},height={height} "
"! videoconvert ! video/x-raw,format=BGR ! appsink drop=1"
)
self.cap = cv2.VideoCapture(gstreamer_str, cv2.CAP_GSTREAMER)
return self.cap.isOpened()
def get_frame(self):
ret, frame = self.cap.read()
return frame if ret else None
def cleanup(self):
if self.cap:
self.cap.release()
```
### 4. **结论**
**是的,DDS日志是创建机器狗控制对象导致的**,但:
- DDS是Unitree SDK控制机器狗的**必需组件**
- 这些"失败"日志通常是**正常的发现机制**,不是真正的错误
- 核心控制功能不受影响
- 如果你不需要控制,使用纯视频流方案避免DDS
- 如果需要控制,接受这些日志或使用过滤机制
*

浙公网安备 33010602011771号