# 模型评测（模型适配调试与连通性验证）
完成自定义机器人的URDF导入、任务场景搭建和轨迹生成后，需要验证自定义机器人与CloudRobo平台之间的数据流转换是否正常，以及模型推理能否正确驱动机器人执行动作。本节介绍如何在CloudRobo平台创建模型评测任务、在开发环境中调试r2c配置文件和transformer模块、在Metaengine仿真环境中测试模型连通性，以及保存调试完成的配置文件。
#### 前提条件
**自定义机器人评测阶段所需用户输入**
在进行模型评测之前，需完成以下文件的准备，并导入自定义机器人资产的custom-robot工程中：
```
robot_<机器人名称>    
|-- metadata.json  // 读取机器人名称信息，前面步骤已生成
|-- r2c_<机器人名称>.yaml  // 机器人和r2c之间的数据转换配置文件，需要用户自行准备
|-- <机器人名称>_transformer  // 转换机器人夹爪关节数据到模型输入之间的转换python模块，需要用户自行准备
      |-- my_parallel_gripper_transformers.py
      |-- pyproject.toml
```
配置文件r2c_{机器人}.yaml和transformer模块编写参考[仿真机器人（MetaEngine)](https://support.huaweicloud.com/sdkreference-cloudrobo/cloudrobo_03_0043.html)。此处的r2c_{机器人}.yaml要和前面部署的r2c.json对应。
**以** **UR5e** **平行夹爪为例，说明 my_parallel_gripper_transformers.py 模块中夹爪关节值和模型输入输出之间转换关系。**
- **MyGripperFingersToStrokeTransformer.transformer**：将夹爪左右关节值相加再乘以1250，转换为模型输入值。该计算逻辑需要与第三步数据处理保持完全一致。URDF中定义左右夹爪的各自移动范围是0\~0.04，左右夹爪相加表示它们之间的宽度，范围是0\~0.08，再乘以1250将其范围转换为0\~100进行模型推理。夹爪宽度范围的缩放基于用户训练模型的经验自主定义，只需与模型训练时的数据范围保持一致。
- **MyGripperStrokeToFingersTransformer.transformer**：将模型的输出值转换为夹爪左右关节值，数据处理是上述变换的逆操作：先除以1250，将模型的输出值范围从0\~100缩放到夹爪实际的宽度范围0\~0.08，再除以2转换为夹爪左右两个关节值范围0\~0.04。
```
"""Third-party value transformers for a parallel gripper (fingers <-> stroke).
This module demonstrates the ``r2c_sdk.transformers`` entry-point extension
mechanism.  It re-implements -- outside the SDK -- the parallel-gripper
finger/stroke conversions that the builtin ``parallel_gripper_fingers_to_stroke``
and ``parallel_gripper_stroke_to_fingers`` transformers provide, so a third
party can ship their own mapping rules without forking the SDK.
To make these classes discoverable:
1. Register them in ``pyproject.toml`` under ``[project.entry-points."r2c_sdk.transformers"]``
   (each name must point to an ``IValueTransformer`` **subclass**).
2. ``pip install -e .`` the package so the entry_point metadata is recorded.
3. Reference the registered name in a robot config YAML:
       mapping:
         # fingers -> stroke
         gripper_stroke:
           source: joint_states.position       # [left, right]
           transform: my_gripper_fingers_to_stroke
         # stroke -> fingers
         gripper_fingers:
           source: gripper_stroke
           transform: my_gripper_stroke_to_fingers
When a name collides with a builtin, the builtin always wins (see
``TransformerRegistry``), so the names below are prefixed to stay unique.
"""
from __future__ import annotations
from dataclasses import dataclass
from typing import Any, Mapping
from r2c_sdk.core.interfaces import IValueTransformer
@dataclass(frozen=True)
class MyGripperFingersToStrokeTransformer(IValueTransformer):
    """平行夹爪两手指位置 -> 行程转换器(第三方实现)。
    行程 = 左手指位置 + 右手指位置(单位: 米)。
    两手指读数不一致时同样直接相加(如 左=0.02, 右=0.04 -> 行程=0.06)。
    输入: ``[left, right]`` 两元素序列(单位: 米)
    输出: 行程 float(单位: 米)
    与内置 ``parallel_gripper_fingers_to_stroke`` 等价, 但作为独立的第三方
    模块演示通过 entry-point 机制扩展转换器。
    """
    def transform(self, value: Any, config: Any = None, context: Any = None) -> Any:
        if isinstance(value, (str, bytes, bytearray, Mapping)):
            raise TypeError(
                "my_gripper_fingers_to_stroke expects a 2-element "
                "sequence [left, right]"
            )
        try:
            left, right = value
        except (TypeError, ValueError) as exc:
            raise ValueError(
                "my_gripper_fingers_to_stroke expects a 2-element "
                "sequence [left, right]"
            ) from exc
        return (float(left) + float(right))*1250.
@dataclass(frozen=True)
class MyGripperStrokeToFingersTransformer(IValueTransformer):
    """平行夹爪行程 -> 两手指位置转换器(第三方实现, 逆变换)。
    行程 S -> ``[S/2, S/2]``(两手指对称运动, 各占行程一半)。
    输入: 行程 float(单位: 米)
    输出: ``[left, right]`` 两元素列表(单位: 米)
    与内置 ``parallel_gripper_stroke_to_fingers`` 等价, 这里同时提供反向变换
    使往返转换可逆, 便于在 device_to_r2c / r2c_to_device 两个方向成对使用。
    """
    def transform(self, value: Any, config: Any = None, context: Any = None) -> Any:
        if isinstance(value, (str, bytes, bytearray)):
            raise TypeError(
                "my_gripper_stroke_to_fingers expects a numeric "
                "stroke value in meters"
            )
        try:
            stroke = float(value)
        except (TypeError, ValueError) as exc:
            raise TypeError(
                "my_gripper_stroke_to_fingers expects a numeric "
                "stroke value in meters"
            ) from exc
        return [stroke / 2500., stroke / 2500.]
```
#### 一、在CloudRobo平台创建模型评测任务
1. 在左侧导航栏选择"模型开发 \> 模型评测"，进入"模型评测"页面。
2. 在页面右上角单击"创建任务"，进入"创建模型评测任务"页面，进行如下配置： 
   - "评测对象"："评测模型"选择 "空间资产-模型"中 8.8 模型部署章节部署的模型服务。
   
   - "评测配置"：选择"单任务评测"并选择包含自定义机器人的任务场景。
   
   - "资源配置"：选择"专属资源池"，勾选"开发环境"，并在"开发环境 OBS 挂载路径"选择框中单击![](https://support.huaweicloud.com/bestpractice-cloudrobo/figure/zh-cn_image_0000002761470687.png)，指定自定义机器人的配置文件所在OBS路径，如下图所示。
     图1指定自定义机器人的配置文件所在OBS路径   
     ![](https://support.huaweicloud.com/bestpractice-cloudrobo/figure/zh-cn_image_0000002758483517.png "点击放大") 
   
   - 其他输入参数可由用户自行设定。
   
   
   
   
 
#### 二、进入开发环境（r2c-client）
本步骤的目的是在开发环境中调试机器人侧的r2c配置文件和transformer模块，确保机器人侧、r2c和模型之间的数据流转换正常。
1. 在左侧导航栏选择"模型开发 \> 模型评测"，进入"模型评测"页面。
2. 在模型评测任务列表中，单击对应任务"操作"列的"进入开发环境"。 
   图2进入开发环境   
   ![](https://support.huaweicloud.com/bestpractice-cloudrobo/figure/zh-cn_image_0000002728805426.png "点击放大")
   ![](https://support.huaweicloud.com/bestpractice-cloudrobo/figure/zh-cn_image_0000002728804058.png "点击放大")
   主要目的：
   - 调试机器人侧r2c配置文件，r2c_\<机器人名称\>.yaml，确保机器人侧、r2c和模型之间的数据流转换正常。
   
   - 调试"\<机器人名称\>_transformer"python模块中机器人夹爪和模型输入、输出值之间的转换逻辑是否正常"\<机器人名称\>_transformer "。
   
   
   
   
3. 在开发环境的终端界面，r2c-client应用已自动启动。使用以下命令查看应用启动状态。 
   ```
   # 在命令行通过下面的命令查看应用启动状态
   cd /data/workspace/r2c_sdk_python 
   python3 ./examples/dev_services.py status 
   # 如果显示r2c-client正在运行, 说明配置文件r2c_<机器人名称>.yaml和<机器人名称>_transformer模块都可以正常工作，配置加载没问题
   # r2c-client                       RUNNING   pid 11184, uptime 0:46:17 
   # 同时可以查看r2c-client正常运行状态下的日志信息，代表程序正在等待订阅ROS2消息
   python3 ./examples/dev_services.py logs 
   # [r2c-client] 2026-09-02 11:58:30,206 - r2c_sdk.translators.device_translator - WARNING  - 50 - Observation incomplete - skipping tick. Not ready: streams.joint_states.position (streams.joint_states.position), streams.joint_states.position (streams.joint_states.position), streams.head (streams.head), streams.external (streams.external) 
   # 如果运行状态显示如下错误
   # r2c-client                       BACKOFF   Exited too quickly (process log may have details)
   # r2c-client                       FATAL     Exited too quickly (process log may have details) 
   # 则需查看错误信息，根据提示修正对应的输入：r2c_<机器人名称>.yaml 和 <机器人名称>_transformer python模块
   python3 ./examples/dev_services.py logs  # 只显示最新的50行日志， 默认日志LEVEL是INFO
   # 如果想查看更详细的DEBUG信息，可以重新设置日志LEVEL， 设置之后需要 python3 ./examples/dev_services.py restart 重启生效
   # 查看当前的日志LEVEL
   python3 ./examples/dev_services.py get_log_level
   # 设置DEBUG LEVEL示例, 也支持 INFO/WARNING/ERROR 设置
   python3 ./examples/dev_services.py set_log_level  DEBUG
   # 或者直接查看原始日志文件中
   vim /data/workspace/.dev_services/logs/r2c-client.log  
   # 根据错误提示修改完r2c_<机器人名称>.yaml和<机器人名称>_transformer模块之后（修改方式参考下文），需要重启r2c-client才能让改动生效python3 ./examples/dev_services.py restart  
   # 直到确保r2c-client服务处于正常运行状态
   ```
   **自定义机器人配置文件更新方式。**
   ```
   # 更新 r2c_<机器人名称>.yaml和<机器人名称>_transformer模块
   ## 方式1: 在用户本地修改之后，上传到评测任务创建时指定的自定义OBS路径中，dev开发环境的文件就自动更新了
   ## 方式2:  直接通过linux vim命令修改OBS路径中的文件 
   # 更新完之后需要把文件复制到程序指定的路径
   cp -r <用户自定义OBS路径> /data/workspace/eail/Assets/robots/robot_<机器人名称>
   ```
   
   
 
#### 三、进入ME仿真环境
r2c-client调试完成后，进入ME仿真环境测试模型连通性，验证整个数据流从仿真环境到r2c再到模型推理的链路是否畅通。
1. 在模型评测任务列表中，单击对应任务"操作"列的"进入仿真环境"。 
   图3进入仿真环境   
   ![](https://support.huaweicloud.com/bestpractice-cloudrobo/figure/zh-cn_image_0000002728965482.png "点击放大")
   
   
2. 打开"技能测评"对话框，进行如下设置。 
   1. 输入测试时长，范围0.01 \~ 300秒。
   
   2. 单击"测试模型连通性"，在弹框中单击"Yes", 开始调试。
   
   3. 在日志框中查看技能评测内部模型推理服务的反馈，主要包括下面几种情况：
      - \[时间戳\] 测试模型连通性命令已发送成功，将在xxx 秒后自动停止
      
      
      
      - \[时间戳\] 测试进行中...
      
      - \[时间戳\] 测试时长已到，正在自动停止模型测试 ...
      
      - \[时间戳\] 模型测试已自动停止
      
      - \[时间戳\] 轮询模型推理状态失败：推理错误--xxx （整个数据流有问题，需要按错误信息提示进行排查）
       
   
   
   图4技能测评   
   ![](https://support.huaweicloud.com/bestpractice-cloudrobo/figure/zh-cn_image_0000002758365323.png "点击放大")
   - 如果日志窗口无报错信息，且机器人正常执行动作，说明整个r2c适配流程没有问题，可以按模型评测流程进行模型效果评测。
   
   - 如果日志窗口无报错信息，但机器人未执行动作。
     1. 先确认开发环境界面中的r2c-client服务是否有报错。
     
     2. 如果有报错，修正错误后重启r2c-client服务，再重新测试模型连通性。
     
     3. 如果没有报错，说明r2c数据适配流程正常，需要排查以下内容：
        - \<机器人名称\>_transformer模块中夹爪关节值和模型输入输出之间的转换逻辑与模型训练中数据处理模块的逻辑是否相同。
        
        - 如果上述数据转换逻辑一致，则可能是模型性能问题，可以进入模型评测环节，考虑增加数据量提升模型性能。
         
      
   
   - 如果日志窗口有报错，根据错误提示排查，常见问题分为以下三类：
     1. **r2c配置文件中数据格式不匹配**
        r2c_\<机器人名称\>.yaml 中转换的r2c数据和模型部署时提供的r2c.json中定义的数据格式是否匹配。
        ```
        轮询模型推理状态失败：推理错误--Build model input from r2c failed. "Name 'wrist_1_joint1' not found in names: ['shoulder_pan_joint', 'shoulder_lift_joint', 'elbow_joint', 'wrist_1_joint', 'wrist_2_joint', 'wrist_3_joint', 'gripper']"
        ```
        
     
     2. **r2c.json中数据字段不支持模型推理解析**
        r2c.json中数据字段不支持模型推理进行解析。
        ```
        轮询模型推理状态失败：推理错误--Build model input from r2c failed. 'Missing "input_features" field in model_ext_meta****"****_mapping". Actual fields: [\'input_features1\', \'output_features\']'
        ```
        
     
     3. **r2c.json中模型输入输出数据格式不匹配**
        r2c.json中定义的模型输入和输出数据格式与模型内部使用的数据格式不匹配。
        ```
        轮询模型推理状态失败：推理错误--Get infer action failed, err: Error in inference server:[N]Traceback (most recent call last):[N]  File "/opt/cloud/lerobot_ascend/serving/websocket_policy_server.py", line 266, in _handler[N]    action_chunk = self._predict_action_chunk(obs)[N]  File "/opt/cloud/lerobot_ascend/serving/websocket_policy_server.py", line 200, in _predict_action_chunk[N]    action_tensor = self._get_action_chunk(observation)[N]  File "/opt/cloud/lerobot_ascend/serving/websocket_policy_server.py", line 153, in _get_action_chunk[N]    chunk = self._policy.predict_action_chunk(observation)[N]  File "/opt/miniconda3/envs/lerobot/lib/python3.10/site-packages/torch/utils/_contextlib.py", line 116, in decorate_context[N]    return func(*args, **kwargs)[N]  File "/opt/cloud/lerobot_ascend/lerobot/src/lerobot/policies/act/modeling_act.py", line 131, in predict_action_chunk[N]    batch[OBS_IMAGES] = [batch[key] for key in self.config.image_features][N]  File "/opt/cloud/lerobot_ascend/lerobot/src/lerobot/policies/act/modeling_act.py", line 131, in <listcomp>[N]    batch[OBS_IMAGES] = [batch[key] for key in self.config.image_features][N]KeyError: 'observation.images.head_camera'[N]
        ```
        
      
   
   
   
   
 
#### 四、保存相关文件
调试完成后，需要将调试好的机器人配置文件保存到空间资产中，使后续模型评测可以直接使用已调试好的配置。
1. 将开发环境调试完的所有文件保存到用户自己的OBS路径。
2. 整理保存的文件，按照要求的产物格式罗列。
3. 更新资产中心的资产（此处只能到OBS更新，资产中心不支持直接更新，但能查看）。
4. 更新机器人资产挂载的OBS路径下的机器人文件。后续搭建场景时使用已调试好的配置。
 
