宇树科技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
- 如果需要控制,接受这些日志或使用过滤机制

 

posted @ 2025-12-11 16:09  aiplus  阅读(18)  评论(0)    收藏  举报
悬浮按钮示例