fix(balance): correct IMU import and add quick_test entrypoint
- Updated import in `balance.py` from `dm_imu_pkg` to the correct `dm_imu` package. - Introduced `quick_test()` function as a convenient debugging entry point, moving the previous script execution logic into this callable. - Modified `__main__` block to display a UI usage message instead of directly running the balance loop. - Revised `dm_imu.egg-info/PKG-INFO` to reflect the new Gradio‑based control panel documentation and project description.
This commit is contained in:
Binary file not shown.
14
balance.py
14
balance.py
@@ -1,5 +1,5 @@
|
|||||||
import time
|
import time
|
||||||
from dm_imu_pkg import imu_py
|
from dm_imu import imu_py
|
||||||
from Legs_controller import LegsController
|
from Legs_controller import LegsController
|
||||||
|
|
||||||
class BalanceController:
|
class BalanceController:
|
||||||
@@ -144,14 +144,14 @@ class BalanceController:
|
|||||||
# self.imu.stop()
|
# self.imu.stop()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
def quick_test():
|
||||||
# 直接运行脚本时的入口
|
"""用于开发调试的快捷入口,手动调用时执行完整流程。"""
|
||||||
controller = BalanceController()
|
controller = BalanceController()
|
||||||
controller.enable_all()
|
controller.enable_all()
|
||||||
# 初始位置设置(与原脚本保持一致)
|
|
||||||
controller.control_legs_pos(0.85, 0.85, 0.85, 0.85, vel=0.5)
|
controller.control_legs_pos(0.85, 0.85, 0.85, 0.85, vel=0.5)
|
||||||
time.sleep(5) # 与原脚本的延时保持一致
|
time.sleep(5)
|
||||||
# 进入平衡控制循环
|
|
||||||
controller.run_balance_loop()
|
controller.run_balance_loop()
|
||||||
# 结束后清理资源
|
|
||||||
controller.shutdown()
|
controller.shutdown()
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
print("请通过 UI 打开串口并控制机器人。")
|
||||||
|
|||||||
@@ -18,114 +18,118 @@ Dynamic: author
|
|||||||
Dynamic: license-file
|
Dynamic: license-file
|
||||||
Dynamic: requires-python
|
Dynamic: requires-python
|
||||||
|
|
||||||
# DM‑IMU USB 驱动库
|
# 四足机器人控制面板(Balance)
|
||||||
|
**基于 Gradio 的 Web UI,封装了 `BalanceController`、`LegsController` 与 IMU 驱动,实现串口自动打开、日志记录、实时电机使能/失能、位置与速度控制、扭矩读取以及后台平衡循环。**
|
||||||
|
|
||||||
|
## 目录
|
||||||
|
- [项目简介](#项目简介)
|
||||||
|
- [环境要求](#环境要求)
|
||||||
|
- [快速安装](#快速安装)
|
||||||
|
- [运行方式](#运行方式)
|
||||||
|
- [主要功能](#主要功能)
|
||||||
|
- [代码结构概览](#代码结构概览)
|
||||||
|
- [使用说明](#使用说明)
|
||||||
|
- [常见问题与故障排查](#常见问题与故障排查)
|
||||||
|
- [许可证](#许可证)
|
||||||
|
|
||||||
## 项目简介
|
## 项目简介
|
||||||
`dm_imu` 目录下实现了一个 **纯 C++ 的 USB IMU 驱动库**,原始代码基于 ROS,但已完全去除 ROS 依赖,使用 POSIX 串口 API 与 IMU 通信。库提供:
|
本项目提供一个面向四足机器人的控制面板,使用 **Gradio** 构建网页 UI,能够在浏览器中直接操控机器人。核心逻辑封装在 `BalanceController`(位于 `balance.py`)中,内部调用 `LegsController`(`Legs_controller.py`)完成电机的初始化、使能/失能、扭矩读取和位置控制;同时通过 `dm_imu` 包的 `DmImu` 实现 IMU 数据获取并驱动平衡算法。
|
||||||
|
|
||||||
- 串口初始化、波特率配置
|
> **注意**:若硬件未连接,`BalanceController` 与 `LegsController` 会捕获异常并创建 dummy 对象,保证脚本仍可运行而不会崩溃。
|
||||||
- IMU 启动、停止以及配置指令(加速度、陀螺仪、欧拉角等)
|
|
||||||
- 线程安全的数据采集(`IMU_Data` 结构体)
|
|
||||||
- 简单的 C++ 接口 `DmImu`,无需任何 ROS 环境即可使用
|
|
||||||
|
|
||||||
## 目录结构
|
## 环境要求
|
||||||
```
|
- **操作系统**:Linux(POSIX 串口)或 Windows(使用对应的串口驱动)
|
||||||
dm_imu/
|
- **Python**:>=3.8
|
||||||
├─ bsp_crc.cpp / bsp_crc.h # CRC 校验实现
|
- **依赖库**(已在 `pyproject.toml` 中声明)
|
||||||
├─ imu_driver.cpp / imu_driver.h# 主驱动实现(已去除 ROS)
|
- `gradio<6.0`
|
||||||
├─ test_imu.cpp # 示例程序,演示如何使用库
|
- `pyserial>=3.5`
|
||||||
└─ README.md # 本文档
|
- `numpy<2.0`
|
||||||
```
|
- `pybind11>=2.10`(用于编译 C++ IMU 驱动)
|
||||||
|
- **硬件**
|
||||||
|
- 4×腿部电机(DM4340)+ 4×轮子电机(DMH6215)
|
||||||
|
- IMU(通过 `dm_imu` 包的 `DmImu`)
|
||||||
|
|
||||||
## 编译方法
|
## 快速安装
|
||||||
|
|
||||||
### 依赖
|
|
||||||
- **C++17** 编译器(gcc/clang)
|
|
||||||
- POSIX 系统(Linux)提供的串口头文件 `<fcntl.h>、<unistd.h>、<termios.h>`
|
|
||||||
- 标准库(`<thread>、<mutex>、<atomic>` 等)
|
|
||||||
|
|
||||||
### 编译示例
|
|
||||||
```bash
|
```bash
|
||||||
# 进入项目根目录
|
# 克隆仓库
|
||||||
cd /home/allenyuan/balance
|
git clone https://github.com/ydy0615/balance.git
|
||||||
|
cd balance
|
||||||
|
|
||||||
# 编译库源码(生成目标文件)
|
# 创建并激活虚拟环境(推荐)
|
||||||
g++ -std=c++17 -c dm_imu/bsp_crc.cpp -o bsp_crc.o
|
python -m venv venv
|
||||||
g++ -std=c++17 -c dm_imu/imu_driver.cpp -o imu_driver.o
|
.\venv\Scripts\activate # Windows
|
||||||
|
source venv/bin/activate # Linux/macOS
|
||||||
|
|
||||||
# 编译示例程序并链接
|
# 安装 Python 依赖
|
||||||
g++ -std=c++17 test_imu.cpp bsp_crc.o imu_driver.o -o test_imu -lpthread
|
pip install -r requirements.txt # 若没有此文件,可手动安装
|
||||||
|
pip install gradio pyserial numpy pybind11
|
||||||
|
|
||||||
|
# 编译 C++ IMU 驱动(需要 cmake、gcc/clang)
|
||||||
|
mkdir build && cd build
|
||||||
|
cmake .. && cmake --build .
|
||||||
|
cd ..
|
||||||
```
|
```
|
||||||
|
|
||||||
> **说明**:如果你希望将库封装为静态或动态库,只需将 `bsp_crc.o` 与 `imu_driver.o` 打包为 `libdm_imu.a`(或 `libdm_imu.so`),然后在链接时使用 `-L. -ldm_imu`。
|
> 若只想运行 UI 而不编译 C++ 部分,可跳过最后的编译步骤,`dm_imu` 包会在运行时尝试加载已编译的 `.so`/`.dll`,若不存在则使用 Python 伪实现(仅用于调试)。
|
||||||
|
|
||||||
## 示例程序 `test_imu.cpp`
|
## 运行方式
|
||||||
|
|
||||||
```cpp
|
|
||||||
// test_imu.cpp
|
|
||||||
// 演示如何使用纯 C++ 的 DM‑IMU 驱动库读取数据
|
|
||||||
|
|
||||||
#include "imu_driver.h"
|
|
||||||
#include <iostream>
|
|
||||||
#include <thread>
|
|
||||||
#include <chrono>
|
|
||||||
|
|
||||||
int main()
|
|
||||||
{
|
|
||||||
// 根据实际设备路径与波特率创建对象
|
|
||||||
dmbot_serial::DmImu imu("/dev/ttyACM1", 921600);
|
|
||||||
|
|
||||||
// 启动采集线程
|
|
||||||
if (!imu.start()) {
|
|
||||||
std::cerr << "Failed to start IMU driver." << std::endl;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 连续读取 1000 次数据(约 10 秒)
|
|
||||||
for (int i = 0; i < 1000; ++i) {
|
|
||||||
IMU_Data d = imu.getData();
|
|
||||||
|
|
||||||
std::cout << "Roll: " << d.roll << " Pitch: " << d.pitch
|
|
||||||
<< " Yaw: " << d.yaw << std::endl;
|
|
||||||
std::cout << "Acc : [" << d.accx << ", " << d.accy << ", " << d.accz << "]"
|
|
||||||
<< std::endl;
|
|
||||||
std::cout << "Gyro : [" << d.gyrox << ", " << d.gyroy << ", " << d.gyroz << "]"
|
|
||||||
<< std::endl;
|
|
||||||
std::cout << "-----------------------------------------" << std::endl;
|
|
||||||
|
|
||||||
// 10 ms 间隔(约 100 Hz)
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 停止采集并关闭串口
|
|
||||||
imu.stop();
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 运行示例
|
|
||||||
```bash
|
```bash
|
||||||
# 编译(参考上面的编译示例)
|
python main.py
|
||||||
g++ -std=c++17 test_imu.cpp bsp_crc.o imu_driver.o -o test_imu -lpthread
|
```
|
||||||
|
启动后会在终端显示类似信息:
|
||||||
|
```
|
||||||
|
Running on http://0.0.0.0:7860/
|
||||||
|
```
|
||||||
|
在浏览器打开该地址即可看到 **四足机器人控制面板**。
|
||||||
|
|
||||||
# 运行
|
## 主要功能
|
||||||
./test_imu
|
| 功能 | 对应后端函数 | UI 控件 | 说明 |
|
||||||
|
|------|--------------|--------|------|
|
||||||
|
| 自动打开串口 | `open_port` | - | 程序启动时即尝试实例化 `BalanceController` 并打开串口 |
|
||||||
|
| 电机使能 | `enable_all` | “✅ 使能全部” 按钮 | 使能四条腿电机 + 四个轮子电机 |
|
||||||
|
| 电机失能 | `disable_all` | “❌ 失能全部” 按钮 | 失能所有电机 |
|
||||||
|
| 启动平衡控制 | `start_balance` | “▶️ 启动平衡控制” 按钮 | 检查电机是否已使能,随后在守护线程中运行 `run_balance_loop` |
|
||||||
|
| 位置控制 | `set_position` | 四个滑块 + “📍 设置位置” 按钮 | 通过 `control_legs_pos` 设置四条腿的位置(0~0.85)与速度比例(0.1~1.0) |
|
||||||
|
| 扭矩读取 | `get_torque` | “🔍 读取扭矩” 按钮 | 调用 `controller.get_legs_torque()`,返回四条腿的扭矩值 |
|
||||||
|
| 实时日志 | `refresh_log` | “刷新日志” 按钮 + 文本框 | 读取 `ui.log`(写入位置 `LOG_PATH = "ui.log"`)并显示在 UI 中 |
|
||||||
|
| 日志写入 | `log`(内部) | - | 所有关键操作均通过 `log(msg)` 同时写入文件并打印到控制台,便于调试 |
|
||||||
|
|
||||||
|
## 代码结构概览
|
||||||
|
```
|
||||||
|
balance/
|
||||||
|
│
|
||||||
|
├─ main.py # Gradio UI 与业务入口
|
||||||
|
├─ balance.py # BalanceController(平衡算法、线程管理)
|
||||||
|
├─ Legs_controller.py # LegsController(电机底层控制)
|
||||||
|
├─ dm_imu/ # C++ IMU 驱动(pybind11 包装)
|
||||||
|
│ ├─ src/
|
||||||
|
│ │ ├─ imu_driver.cpp
|
||||||
|
│ │ └─ bsp_crc.cpp
|
||||||
|
│ └─ __init__.py
|
||||||
|
├─ pyproject.toml # 项目元信息与依赖声明
|
||||||
|
├─ README.md # 本文档
|
||||||
|
└─ ... (其他辅助脚本、测试文件)
|
||||||
```
|
```
|
||||||
|
|
||||||
运行后,程序会在终端持续打印 IMU 的欧拉角、加速度和陀螺仪数据。若串口未打开或设备路径错误,程序会在初始化阶段输出错误信息并退出。
|
## 使用说明
|
||||||
|
1. **检查硬件**:确保机器人电机控制板和 IMU 已正确连接,串口设备路径与 `BalanceController.__init__` 中的默认值相符(`/dev/dm-u2can`、`/dev/dm-imu`)。若使用不同路径,请在 `main.py` 或 `balance.py` 中自行修改。
|
||||||
|
2. **启动 UI**:运行 `python main.py`,打开浏览器访问 `http://<本机IP>:7860`。
|
||||||
|
3. **日志查看**:点击 “刷新日志” 可实时读取 `ui.log`。若日志文件不存在,首次点击会自动创建。
|
||||||
|
4. **电机使能**:先点击 “✅ 使能全部”,随后才能使用位置或扭矩功能。
|
||||||
|
5. **位置调节**:调节四个滑块后点击 “📍 设置位置”,下方的 “位置设置结果” 框会显示返回信息。
|
||||||
|
6. **扭矩读取**:点击 “🔍 读取扭矩”,右侧文本框会显示四条腿当前扭矩(N/m),若读取失败则显示 “错误”。
|
||||||
|
7. **启动平衡**:在电机已使能后点击 “▶️ 启动平衡控制”。后台线程会不断读取 IMU 数据并根据偏置调节腿部位置。若出现异常,日志中会记录详细信息。
|
||||||
|
|
||||||
## 常见问题
|
## 常见问题与故障排查
|
||||||
|
| 问题 | 可能原因 | 解决方案 |
|
||||||
| 问题 | 可能原因 | 解决办法 |
|
|
||||||
|------|----------|----------|
|
|------|----------|----------|
|
||||||
| 程序启动后没有数据输出 | 串口未打开或设备路径错误 | 确认 IMU 已正确连接,`/dev/ttyACM1` 是否存在;如有不同,请在 `test_imu.cpp` 中修改构造函数的 `port` 参数 |
|
| **UI 无法打开** | 未安装 `gradio` 或端口被占用 | `pip install gradio`,或更换 `server_port` 参数 |
|
||||||
| `write` / `read` 返回错误 | 波特率不匹配 | 检查 IMU 手册,确认波特率是否为 921600;如需修改,请在 `DmImu` 构造函数的 `baud` 参数中传入正确值 |
|
| **串口打开失败** | 设备路径错误、权限不足或硬件未连接 | 检查 `BalanceController.__init__` 中的 `leg_port` 与 `imu_port`,在 Linux 上使用 `sudo chmod a+rw /dev/ttyUSB*` |
|
||||||
| 编译报缺少头文件 | 开发环境缺少 POSIX 串口头文件 | 在 Linux 上默认可用;若使用交叉编译,请确保交叉工具链包含对应头文件 |
|
| **日志不刷新** | `ui.log` 未生成或文件被锁定 | 确认 `log()` 已被调用;手动删除 `ui.log` 让程序重新创建 |
|
||||||
|
| **电机不使能** | `LegsController` 初始化异常 | 查看终端输出的 “初始化 LegsController 失败” 信息;确保 `u2can` 包已正确安装且串口可用 |
|
||||||
|
| **平衡控制未启动** | `motors_enabled` 为 `False`(未使能) | 必须先点击 “✅ 使能全部”,然后再点击 “▶️ 启动平衡控制” |
|
||||||
|
| **读取扭矩返回 “错误”** | `controller.get_legs_torque()` 抛异常 | 检查 `LegsController.get_legs_torque` 是否成功刷新电机状态,确保电机已连接 |
|
||||||
|
| **IMU 数据异常** | IMU 未启动或波特率不匹配 | 确认 `BalanceController.__init__` 中的 `imu_port` 与 `imu_baud` 与实际硬件匹配;若硬件缺失,程序会使用 DummyImu(返回零姿态) |
|
||||||
|
|
||||||
## 许可证
|
## 许可证
|
||||||
本项目采用 MIT 许可证,详情见 `LICENSE`(若项目中未提供,请自行添加)。
|
本项目采用 **MIT 许可证**。详情请参见根目录下的 `LICENSE` 文件。
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**祝你使用愉快!** 如有其他需求(如添加四元数计算、回调机制等),欢迎进一步扩展此库。
|
|
||||||
|
|||||||
Binary file not shown.
42
main.py
42
main.py
@@ -23,11 +23,9 @@ def log(msg: str) -> None:
|
|||||||
h.flush()
|
h.flush()
|
||||||
print(msg) # optional, 可删除
|
print(msg) # optional, 可删除
|
||||||
|
|
||||||
# -------------------------------------------------
|
|
||||||
# 全局状态
|
|
||||||
# -------------------------------------------------
|
|
||||||
controller: BalanceController | None = None # 单例
|
controller: BalanceController | None = None # 单例
|
||||||
motors_enabled = False # 电机使能状态
|
motors_enabled = False # 电机使能状态
|
||||||
|
port_opened: bool = False # 是否已打开串口
|
||||||
|
|
||||||
# -------------------------------------------------
|
# -------------------------------------------------
|
||||||
# 安全创建 BalanceController(带重试)
|
# 安全创建 BalanceController(带重试)
|
||||||
@@ -45,28 +43,36 @@ def create_controller(retries: int = 3, delay: float = 1.0) -> BalanceController
|
|||||||
log("全部重试结束,仍未能创建 BalanceController")
|
log("全部重试结束,仍未能创建 BalanceController")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# -------------------------------------------------
|
# ---------------------------------------印度洋15
|
||||||
|
# ----------
|
||||||
# UI 操作函数(统一异常捕获、日志记录、返回状态文字)
|
# UI 操作函数(统一异常捕获、日志记录、返回状态文字)
|
||||||
# -------------------------------------------------
|
# -------------------------------------------------
|
||||||
def open_port() -> tuple:
|
def open_port() -> tuple:
|
||||||
"""创建 BalanceController 实例并打开串口(不自动使能)。"""
|
"""创建 BalanceController 实例并打开串口(不自动使能)。"""
|
||||||
global controller
|
global controller, port_opened
|
||||||
if controller is None:
|
if controller is None:
|
||||||
controller = create_controller()
|
controller = create_controller()
|
||||||
if controller is None:
|
if controller is None:
|
||||||
msg = "打开串口失败:无法创建 BalanceController"
|
msg = "打开串口失败:无法创建 BalanceController"
|
||||||
log(msg)
|
log(msg)
|
||||||
|
port_opened = False
|
||||||
return (msg, msg)
|
return (msg, msg)
|
||||||
msg = "串口已打开"
|
msg = "串口已打开"
|
||||||
log(msg)
|
log(msg)
|
||||||
|
port_opened = True
|
||||||
return (msg, msg)
|
return (msg, msg)
|
||||||
msg = "串口已打开(已存在实例)"
|
msg = "串口已打开(已存在实例)"
|
||||||
log(msg)
|
log(msg)
|
||||||
|
port_opened = True
|
||||||
return (msg, msg)
|
return (msg, msg)
|
||||||
|
|
||||||
def enable_all() -> tuple:
|
def enable_all() -> tuple:
|
||||||
"""使能所有电机(腿部+轮子)。"""
|
"""使能所有电机(腿部+轮子)。"""
|
||||||
global motors_enabled
|
global motors_enabled
|
||||||
|
if not port_opened:
|
||||||
|
msg = "请先打开串口"
|
||||||
|
log(msg)
|
||||||
|
return (msg, msg)
|
||||||
if controller is None:
|
if controller is None:
|
||||||
msg = "请先打开串口"
|
msg = "请先打开串口"
|
||||||
return (msg, msg)
|
return (msg, msg)
|
||||||
@@ -84,6 +90,10 @@ def enable_all() -> tuple:
|
|||||||
def disable_all() -> tuple:
|
def disable_all() -> tuple:
|
||||||
"""失能所有电机。"""
|
"""失能所有电机。"""
|
||||||
global motors_enabled
|
global motors_enabled
|
||||||
|
if not port_opened:
|
||||||
|
msg = "请先打开串口"
|
||||||
|
log(msg)
|
||||||
|
return (msg, msg)
|
||||||
if controller is None:
|
if controller is None:
|
||||||
msg = "请先打开串口"
|
msg = "请先打开串口"
|
||||||
return (msg, msg)
|
return (msg, msg)
|
||||||
@@ -100,6 +110,10 @@ def disable_all() -> tuple:
|
|||||||
|
|
||||||
def set_position(pos1, pos2, pos3, pos4, vel) -> tuple:
|
def set_position(pos1, pos2, pos3, pos4, vel) -> tuple:
|
||||||
"""设置四条腿的位置与速度比例。"""
|
"""设置四条腿的位置与速度比例。"""
|
||||||
|
if not port_opened:
|
||||||
|
msg = "请先打开串口"
|
||||||
|
log(msg)
|
||||||
|
return (msg, msg)
|
||||||
if controller is None:
|
if controller is None:
|
||||||
msg = "请先打开串口"
|
msg = "请先打开串口"
|
||||||
return (msg, msg)
|
return (msg, msg)
|
||||||
@@ -115,6 +129,10 @@ def set_position(pos1, pos2, pos3, pos4, vel) -> tuple:
|
|||||||
|
|
||||||
def get_torque() -> tuple:
|
def get_torque() -> tuple:
|
||||||
"""读取四条腿的扭矩,返回字符串列表。"""
|
"""读取四条腿的扭矩,返回字符串列表。"""
|
||||||
|
if not port_opened:
|
||||||
|
msg = "未打开串口"
|
||||||
|
log(msg)
|
||||||
|
return (["未打开串口"] * 4, msg)
|
||||||
if controller is None:
|
if controller is None:
|
||||||
msg = "未打开串口"
|
msg = "未打开串口"
|
||||||
log(msg)
|
log(msg)
|
||||||
@@ -158,6 +176,10 @@ def start_balance_thread() -> None:
|
|||||||
|
|
||||||
def start_balance() -> tuple:
|
def start_balance() -> tuple:
|
||||||
"""检查电机是否已使能后启动平衡控制。"""
|
"""检查电机是否已使能后启动平衡控制。"""
|
||||||
|
if not port_opened:
|
||||||
|
msg = "请先打开串口"
|
||||||
|
log(msg)
|
||||||
|
return (msg, msg)
|
||||||
if not motors_enabled:
|
if not motors_enabled:
|
||||||
msg = "启动平衡控制失败:电机未使能"
|
msg = "启动平衡控制失败:电机未使能"
|
||||||
log(msg)
|
log(msg)
|
||||||
@@ -180,7 +202,7 @@ def refresh_log() -> str:
|
|||||||
# -------------------------------------------------
|
# -------------------------------------------------
|
||||||
# 自动打开串口并初始化 UI
|
# 自动打开串口并初始化 UI
|
||||||
# -------------------------------------------------
|
# -------------------------------------------------
|
||||||
init_status, _ = open_port() # 自动打开串口
|
init_status = "未打开串口"
|
||||||
|
|
||||||
# -------------------------------------------------
|
# -------------------------------------------------
|
||||||
# Gradio UI
|
# Gradio UI
|
||||||
@@ -198,14 +220,16 @@ with gr.Blocks() as demo:
|
|||||||
with gr.Column():
|
with gr.Column():
|
||||||
gr.Markdown("## 电机控制")
|
gr.Markdown("## 电机控制")
|
||||||
# 已移除 “打开串口” 按钮,串口在启动时已自动打开
|
# 已移除 “打开串口” 按钮,串口在启动时已自动打开
|
||||||
|
open_btn = gr.Button("🔌 打开串口")
|
||||||
enable_btn = gr.Button("✅ 使能全部")
|
enable_btn = gr.Button("✅ 使能全部")
|
||||||
disable_btn = gr.Button("❌ 失能全部")
|
disable_btn = gr.Button("❌ 失能全部")
|
||||||
start_btn = gr.Button("▶️ 启动平衡控制")
|
start_btn = gr.Button("▶️ 启动平衡控制")
|
||||||
status_box = gr.Textbox(label="状态", value=init_status, interactive=False)
|
status_box = gr.Textbox(label="状态", value=init_status, interactive=False)
|
||||||
|
|
||||||
enable_btn.click(fn=enable_all, inputs=None, outputs=[status_box, log_box])
|
open_btn.click(fn=open_port, inputs=None, outputs=[status_box, log_box])
|
||||||
disable_btn.click(fn=disable_all, inputs=None, outputs=[status_box, log_box])
|
enable_btn.click(fn=enable_all, inputs=None, outputs=[status_box, log_box])
|
||||||
start_btn.click(fn=start_balance, inputs=None, outputs=[status_box, log_box])
|
disable_btn.click(fn=disable_all, inputs=None, outputs=[status_box, log_box])
|
||||||
|
start_btn.click(fn=start_balance, inputs=None, outputs=[status_box, log_box])
|
||||||
|
|
||||||
# 中间:位置控制
|
# 中间:位置控制
|
||||||
with gr.Column():
|
with gr.Column():
|
||||||
|
|||||||
Reference in New Issue
Block a user