VersaAssistant 调试助手#

物理接口#

  • 串口通信(支持传统串口和USB虚拟串口)

  • 网络调试
    • Tcp Server(支持连接多个客户端)

    • Tcp Client

    • Udp Socket

  • USB调试(USB-HID)

小技巧

注意

  • 部分USB虚拟串口的物理设备可能没有较好的处理数据发送和接收包长,在单次发送大于64、256等字节数据时,可能存在数据丢失问题,这个属于设备端问题,请优先排查设备端。

  • 使用USB-HID设备时,需要考虑数据包长为64、256,来对协议数据的数据长度进行限制。

编码格式#

理论上支持以下编码格式(部分):UTF-8、ISO-8859-1、latin1、CP819、Big5、GBK、GB2312、EUC-KR、ISO-2022-JP、windows-1252、CP1252、KOI8-R 等,完整列表请参考运行环境所使用编码库文档。

通信协议#

  • 仅支持本软件通信协议,不支持 printf 等低效方式。

  • 协议显示窗口,可进行简单数据处理,将数据转换到一定范围,进行数据波动观察

波形显示#

  • 支持基于通信协议的波形显示功能,最大支持28条波形显示。

  • 波形绘制横坐标单位为秒。

危险

波形刷新时请勿进行波形数据导入/导出;操作前请先停止刷新(关闭通信或协议)。导入前请先清空波形。

模型加载#

  • 支持加载自定义3D模型。

  • 支持模型的六轴姿态显示。

  • 支持机械臂的姿态解算,进行机械臂3D姿态显示。

  • 设置模型后需要重启操作。

{
  "ModelPath": [
    "E:/projects/VersaAssistant/VersaAssistant/models/DJIMatrice30T.obj",
    "E:/projects/VersaAssistant/VersaAssistant/models/dummy.obj"
  ],
  "VersaRobot": {
    "DJIMatrice30T.obj": {
      "Matrice30T": {
        "parent": "DJIMatrice30T.obj",
        "action": "transform"
      }
    },
    "dummy.obj": {
      "robot-node1": {
        "parent": "dummy.obj",
        "action": "transform"
      },
      "robot-node2": {
        "parent": "robot-node1",
        "action": "rotation",
        "rotation_coordinates": [
          0.0,
          0.0835,
          0.0
        ],
        "rotation_axis_enable": [
          0.0,
          1.0,
          0.0
        ],
        "rotation_limit_angle": [
          -180.0,
          180.0
        ]
      },
      "robot-node3": {
        "parent": "robot-node2",
        "action": "rotation",
        "rotation_coordinates": [
          0.035,
          0.109,
          0.0
        ],
        "rotation_axis_enable": [
          0.0,
          0.0,
          1.0
        ],
        "rotation_limit_angle": [
          -180.0,
          180.0
        ]
      },
      "robot-node4": {
        "parent": "robot-node3",
        "action": "rotation",
        "rotation_coordinates": [
          0.035,
          0.255,
          0.0
        ],
        "rotation_axis_enable": [
          0.0,
          0.0,
          1.0
        ],
        "rotation_limit_angle": [
          -180.0,
          180.0
        ]
      },
      "robot-node5": {
        "parent": "robot-node4",
        "action": "rotation",
        "rotation_coordinates": [
          0.023,
          0.307,
          0.0
        ],
        "rotation_axis_enable": [
          1.0,
          0.0,
          0.0
        ],
        "rotation_limit_angle": [
          -180.0,
          180.0
        ]
      },
      "robot-node6": {
        "parent": "robot-node5",
        "action": "rotation",
        "rotation_coordinates": [
          0.150,
          0.307,
          0.0
        ],
        "rotation_axis_enable": [
          0.0,
          0.0,
          1.0
        ],
        "rotation_limit_angle": [
          -180.0,
          180.0
        ]
      }
    }
  }
}

参数说明#

模型加载配置#

参数

用途

ModelPath

模型路径列表

VersaRobot

机器人/模型节点配置

动作配置#

VersaRobot 下列举各模型文件名(如 DJIMatrice30T.objdummy.obj)。

  • DJIMatrice30T.obj:单节点 Matrice30T,配置其父节点;actiontransform 表示六自由度(旋转+平移)。

  • dummy.obj:六节点,每节点相对前一节点变换,节点一相对原点,用 transform;其余可用 rotation

  • actionrotation 表示旋转,需设置 rotation_coordinates``(相对原点位置 [x,y,z])和 ``rotation_limit_angle [angle1, angle2]。

  • 绕某轴旋转时,rotation_coordinates 中该轴以外分量须准确(姿态求解为先平移回原点再旋转再平移回)。

  • rotation_limit_angle 为保留项,软件未用 JSON 中的角度限制,实际固定为 [-180, 180]。

小技巧

调试机械臂时 rotation_coordinates 的正确设置很重要,可参考稚辉君的 dummy 模型。

模型为 Blender 导出的 .obj,其他平台未测试;不兼容时可经 Blender 重新导出。建议勿用层级过复杂的模型,可在 Blender 中合并节点,仅保留需控制的整体(如 dummy.obj),在 Blender 中查看节点结构。

Snipaste_2023-10-31_13-26-34.png Snipaste_2023-10-31_13-27-58.png

导出#

Snipaste_2023-10-31_13-31-40.png

变换过程

坐标变换的过程

/*!
 * 姿态求解,更新为四元数
 * @param pNode
 */
void VersaOpenGLWidget::updataTranslateMatrix(VersaNode *pNode)
{
     // 平移矩阵
     pNode->mTranslateMatrix.setToIdentity();
     pNode->mTranslateMatrix.translate(pNode->mTranslate3D);

     QQuaternion pitch, yaw, roll;

     // 绕轴旋转矩阵
     if (pNode->mRotation3DAxisEnable.x() != 0)
     {
             pitch = QQuaternion::fromAxisAndAngle(QVector3D(1, 0, 0),
                                                   pNode->mRotation3DAngle.x());
     }
     if (pNode->mRotation3DAxisEnable.y() != 0)
     {
             yaw = QQuaternion::fromAxisAndAngle(QVector3D(0, 1, 0),
                                                 pNode->mRotation3DAngle.y());
     }
     if (pNode->mRotation3DAxisEnable.z() != 0)
     {
             roll = QQuaternion::fromAxisAndAngle(QVector3D(0, 0, 1),
                                                  pNode->mRotation3DAngle.z());
     }

     QQuaternion rotation = yaw * pitch * roll;

     // 旋转矩阵
     pNode->mRotationMatrix.setToIdentity();
     pNode->mRotationMatrix.translate(pNode->mRotation3DCoordinates.x(),
                                      pNode->mRotation3DCoordinates.y(),
                                      pNode->mRotation3DCoordinates.z());
     pNode->mRotationMatrix.rotate(rotation);
     pNode->mRotationMatrix.translate(-pNode->mRotation3DCoordinates.x(),
                                      -pNode->mRotation3DCoordinates.y(),
                                      -pNode->mRotation3DCoordinates.z());

     // 缩放矩阵
     pNode->mScaleMatrix.setToIdentity();
     pNode->mScaleMatrix.scale(pNode->mScale);

     // 相对位置矩阵,控制中不操作,为模型加载时的相对位置
     // mRelativeMatrix

     // 最终的变换矩阵
     pNode->mTransformMatrix =
                     pNode->mTranslateMatrix *
                     pNode->mRotationMatrix *
                     pNode->mScaleMatrix *
                     pNode->mRelativeMatrix;
}

模型绘制过程#

void VersaOpenGLWidget::drawNode(const VersaNode *pNode, QMatrix4x4 drawScaleMatrix)
{
    // drawScaleMatrix可以理解为父节点变换矩阵

     drawScaleMatrix *= pNode->mTransformMatrix;
     QMatrix4x4 modelViewMatrix;
     QMatrix3x3 normalMatrix;
     QMatrix4x4 mvp;

     // 先缩放,再旋转,再平移 。
     modelViewMatrix = mCameraView * drawScaleMatrix;
     normalMatrix = modelViewMatrix.normalMatrix();
     mvp = mProjection * modelViewMatrix;
     mShaderProgram.setUniformValue("MV", modelViewMatrix);
     mShaderProgram.setUniformValue("N", normalMatrix);
     mShaderProgram.setUniformValue("MVP", mvp);

     for (int imeshes = 0; imeshes < pNode->mMeshes.size(); ++imeshes)
     {
             if (pNode->mMeshes[imeshes]->material->mName == QString("DefaultMaterial"))
             {
                     setMaterialUniforms(&mMaterialInfo);
             }
             else
             {
                     setMaterialUniforms(pNode->mMeshes[imeshes]->material.data());
             }
             {
                     mVao.bind();
                     glDrawElements(GL_TRIANGLES,
                                    pNode->mMeshes[imeshes]->mIndexCount,
                                    GL_UNSIGNED_INT,
                                    (const void *) (pNode->mMeshes[imeshes]->mIndexOffset * sizeof(unsigned int)));
                     mVao.release();
             }
     }
     // 递归绘制该节点的子节点
     for (int inn = 0; inn < pNode->mNodes.size(); ++inn)
     {
             drawNode(&pNode->mNodes[inn], drawScaleMatrix);
     }
}

主题颜色#

{
  "theme-color": [
    "#FFFFFF",
    "#0066FF",
    "#29A9FF",
    "#91C6FF",
    "#118DF0",
    "#C70039",
    "#FFC773"
  ]
}

依次为背景色、标题栏颜色、侧边栏颜色、滑块颜色、按钮悬停颜色、按钮按下颜色、菜单选项选中颜色。

支持保存工作区布局#

  • 可以将工作区布局保存为单个文件,同时可以将部分数据和工作区一起保存。

  • 可以选择配置文件以恢复工作区。

性能#

  • 常规调试界面大量数据不卡顿。

  • 实测 2M 波特率串口、每 ms 发送 28 个 float(约 256 字节)可长时间稳定。

  • 大量波形绘制依赖设备性能,低配可能卡顿,可减小数据量。