2026 M-Design设计竞赛 - 基于GD32嵌入式终端与Genesis具身智能仿真平台实现全链路机械臂仿真控制系统
该项目使用了GD32嵌入式终端与Genesis具身智能仿真平台,实现了机械臂仿真控制系统的设计,它的主要功能为:构建了一套"嵌入式人机交互终端—跨平台协议转换—物理仿真执行"的三层架构机械臂仿真控制系统。
标签
ROS
GD32
2026 M-Design
Genesis
具身智能
fyjh2023
更新2026-06-09
22

一、项目介绍

本项目基于兆易创新 GD32H759IMT6 微控制器(Cortex-M7 600MHz)构建了 LVGL v8.3.5 嵌入式图形交互终端,与 Linux PC 端 Genesis 物理仿真引擎通过 ROS 话题通信形成全链路闭环——在 GD32 触控屏上实现包含欢迎动画、六自由度关节控制、笛卡尔坐标显示与急停联动的六屏 GUI 系统,经 115200bps 串口协议上传关节增量与急停事件,由 Python TCP 网关解析并转换为标准 ROS 消息驱动 Genesis 中 YHRG S1 六自由度仿真机械臂执行 PD 位置控制(100Hz 仿真步进),同时将末端位姿与关节状态以 10Hz 频率回传至嵌入式终端实时显示,急停状态在全链路中同步传播并触发硬件 LED 指示与仿真引擎冻结,最终打通了“触控输入 → 串口传输 → ROS 消息转换 → 物理仿真执行 → 状态反馈回显”的端到端技术闭环,形成一套可复用的嵌入式前端 + 具身智能仿真后端的机械臂原型验证框架。本项目 Linux PC 部分的代码及后续优化工作将在 https://github.com/yjhsh/s1_genesis 同步开源。


任务方向:教育与创意互动——机械臂仿真控制演示系统(自命题)


工作内容.png


系统设计

2.1 整体架构

系统采用"嵌入式前端—协议转换层—仿真控制后端"的三层架构模式。

系统整体架构.png

2.2 嵌入式前端(硬件部分)

本项目以兆易创新 GD32H759I-EVAL 评估板 为嵌入式核心硬件平台,搭载 GD32H759IMT6 微控制器(ARM Cortex-M7 内核,主频 600MHz,内置 1MB SRAM 与 2MB Flash),通过 EXMC 总线外扩 32MB SDRAM 作为 LVGL 帧缓冲,驱动 4.3 寸 TFT LCD(480×272 RGB565)经 TLI 接口实现硬件图层叠加显示;人机交互采用 I2C 接口的 GT911 电容触摸 IC 捕获多点触控事件,USART0(115200bps)负责与上位机的全双工串行通信,PA6 引脚配置为推挽输出驱动急停指示 LED,构成一套独立运行的嵌入式人机交互终端。

引脚名

功能

连接至

PA9

USART0_TX

USB-TTL RX

PA10

USART0_RX

USB-TTL TX

PA6

LED2 (GPIO Output)

急停指示LED

PB6

I2C_SCL

GT911 触摸模块

PB7

I2C_SDA

GT911 触摸模块

2.3 协议转换层

异构系统间通过 ROS Noetic 作为分布式通信中间件,定义 /real_arm/joint_angles/real_arm/robot_state/real_arm/robot_status 三个标准话题,所有消息以 std_msgs/String 承载 JSON 结构化数据,实现 GD32 桥接节点( gd32_ros_bridge )与仿真控制节点( yhrg_s1_controller)间的发布/订阅解耦。串口接入采用 socat 构建虚拟串口桥接 —— 将远端 GD32 设备经 TCP 端口传入的串口数据流映射为Linux电脑本地 /dev/ttyVUSB0 伪终端设备,使得 GD32-ROS 桥接节点可通过标准 pyserial 接口透明访问远程物理硬件,消除了嵌入式设备与 Linux 主机间的物理连接距离约束,配合一键启动脚本 start_s1_system.sh 完成 roscoresocatgd32_ros_controlmain_mvc 四进程的自动化启动。

ROS交互.png

2.4 仿真控制后端

仿真后端采用 Genesis —— 新一代 GPU 加速物理仿真引擎,被广泛部署于具身智能与机器人策略学习领域。Genesis 内置高精度刚体动力学求解器与 PD 位置控制器,直接支持 URDF 标准模型描述文件加载,本项目中以 gs.init(backend=gs.gpu) 启用 GPU 后端,加载 YHRG S1 六自由度机械臂 URDF 模型,逐关节配置 PD 增益,以 0.01s 仿真时间步长执行 100Hz 物理步进;工程采用 MVC 架构 + 代理模式 将其封装为 ISimulatorProxy 抽象接口下的 GenesisSimulatorProxy 实现,实现仿真引擎与控制逻辑的完全解耦,便于替换或扩展。每 10 个仿真步发布一次机器人状态(位置、速度、力矩、末端位姿),形成 10Hz 的状态反馈流,将物理仿真运行结果实时回流至嵌入式终端。


Genesis仿真器的在线文档:https://genesis-world.readthedocs.io/zh-cn/latest/


三、活动指定厂家器件使用情况

3.1 元器件信息及用途

本项目使用了1个由Infineon生产的2N7002 N-MOS(产品链接 https://www.mouser.cn/ProductDetail/726-2N7002H6327XTSA2)。它的漏源极击穿电压 60 V,连续漏极电流 300 mA,栅源极阈值电压 1.5 V。它被用于搭建LED指示灯驱动电路,在系统的急停模式激活后,使 LED 处于最大亮度。


3.2 原理图、PCB和外壳设计

本项目使用立创EDA设计了一个 2N7002 N-MOS 驱动电路来控制 LED 。GD32 PA6 引脚连接模块的 EN 引脚,输入高电平,使 LED 处于最大亮度。此外,本项目使用立创EDA为模块设计了一个塑料外壳(PCB设计图中的绿线是3D外壳的边框),起到美观的作用。壳体上下两部分可直接合上,不需要借助工具或固定件。

PCB.png


四、嵌入式前端软件设计

4.1 整体介绍

本项目使用 Keil MDK-ARM (ARMCC v6) 工具链,目标芯片 GD32H759IMT6,使用 GUI-Guider 构建基于 LVGL v8.3.5 的图形界面,使用 TRAE 完成图形界面的内容定制。其中,LVGL 框架的移植参考 https://bbs.eeworld.com.cn/thread-1285869-1-1.html 的 Keil 工程代码,在此向该项目的作者表示感谢。


4.2 软件流程图

image.png

4.3 关键代码介绍

  • 机械臂 UI 全局状态: robot_ui_t。
typedef struct {
scrWelcome_ui_t scrWelcome; // 欢迎页UI对象
scrMonitor_ui_t scrMonitor; // 监控页UI对象
scrPos_ui_t scrPos; // 坐标页UI对象
scrShutdown_ui_t scrShutdown; // 关机页UI对象(代码保留,界面入口已移除)
scrLock_ui_t scrLock; // 锁屏页UI对象
scrStop_ui_t scrStop; // 急停页UI对象
float joint_angles[7]; // 7个关节角度/位移 [J1-J6:度, J7:mm]
float pos_x, pos_y, pos_z; // 末端执行器位置 (mm)
float quat_w, quat_x, quat_y, quat_z; // 四元数姿态
int32_t temperature; // 电机温度 (°C)(预留)
bool teach_mode_active; // 示教模式激活标志(预留)
} robot_ui_t;


  • 关节控制组件 (create_joint_control):每个关节(J1-J7)包含标签、增减按钮、进度条与数值显示。

元素

类型

功能

label

lv_label

显示关节名 "J1"~"J7"

btnMinus

lv_btn

减少按钮 "-",步进1°

bar

lv_bar

进度条 (0~100)

btnPlus

lv_btn

增加按钮 "+",步进1°

labelValue

lv_label

显示角度值,保留2位小数

static void create_joint_control(lv_obj_t *parent, int joint_num, int y_offset, 
                                  lv_obj_t **bar, lv_obj_t **label,
                                  lv_obj_t **btnMinus, lv_obj_t **btnPlus,
                                  lv_obj_t **labelValue)
{
    char buf[16];
    sprintf(buf, "J%d", joint_num);
   
    *label = lv_label_create(parent);
    lv_label_set_text(*label, buf);
    lv_obj_set_pos(*label, 5, y_offset + 5);
    lv_obj_set_style_text_color(*label, lv_color_hex(0x2f3243), LV_PART_MAIN|LV_STATE_DEFAULT);
    lv_obj_set_style_text_font(*label, &lv_font_montserratMedium_14, LV_PART_MAIN|LV_STATE_DEFAULT);
   
    *btnMinus = lv_btn_create(parent);
    lv_obj_set_pos(*btnMinus, 35, y_offset);
    lv_obj_set_size(*btnMinus, 28, 28);
    lv_obj_set_style_bg_color(*btnMinus, lv_color_hex(0x2f3243), LV_PART_MAIN|LV_STATE_DEFAULT);
    lv_obj_set_style_radius(*btnMinus, 5, LV_PART_MAIN|LV_STATE_DEFAULT);
    lv_obj_t *labelMinus = lv_label_create(*btnMinus);
    lv_label_set_text(labelMinus, "-");
    lv_obj_center(labelMinus);
    lv_obj_set_style_text_color(labelMinus, lv_color_hex(0xffffff), LV_PART_MAIN|LV_STATE_DEFAULT);
   
    *bar = lv_bar_create(parent);
    lv_bar_set_range(*bar, 0, 100);
    lv_bar_set_value(*bar, 50, LV_ANIM_OFF);
    lv_obj_set_pos(*bar, 68, y_offset + 6);
    lv_obj_set_size(*bar, 64, 16);
    lv_obj_set_style_bg_color(*bar, lv_color_hex(0xe0e0e0), LV_PART_MAIN|LV_STATE_DEFAULT);
    lv_obj_set_style_bg_color(*bar, lv_color_hex(0x2ad3ff), LV_PART_INDICATOR|LV_STATE_DEFAULT);
    lv_obj_set_style_radius(*bar, 5, LV_PART_MAIN|LV_STATE_DEFAULT);
   
    *btnPlus = lv_btn_create(parent);
    lv_obj_set_pos(*btnPlus, 137, y_offset);
    lv_obj_set_size(*btnPlus, 28, 28);
    lv_obj_set_style_bg_color(*btnPlus, lv_color_hex(0x2f3243), LV_PART_MAIN|LV_STATE_DEFAULT);
    lv_obj_set_style_radius(*btnPlus, 5, LV_PART_MAIN|LV_STATE_DEFAULT);
    lv_obj_t *labelPlus = lv_label_create(*btnPlus);
    lv_label_set_text(labelPlus, "+");
    lv_obj_center(labelPlus);
    lv_obj_set_style_text_color(labelPlus, lv_color_hex(0xffffff), LV_PART_MAIN|LV_STATE_DEFAULT);
   
    *labelValue = lv_label_create(parent);
    sprintf(buf, "%.2f", 0.0f);
    lv_label_set_text(*labelValue, buf);
    lv_obj_set_pos(*labelValue, 170, y_offset + 5);
    lv_obj_set_style_text_color(*labelValue, lv_color_hex(0x2f3243), LV_PART_MAIN|LV_STATE_DEFAULT);
    lv_obj_set_style_text_font(*labelValue, &lv_font_montserratMedium_14, LV_PART_MAIN|LV_STATE_DEFAULT);


  • 关节控制事件回调函数:以J1为例,按下“+”或“-”按键后,发送J1关节角度加减1的指令。
static void btn_j1_minus_cb(lv_event_t *e)
{
    lv_event_code_t code = lv_event_get_code(e);
    if (code == LV_EVENT_CLICKED || code == LV_EVENT_LONG_PRESSED_REPEAT) {
        lv_event_stop_bubbling(e);
        send_joint_increment_cmd("J1", -1);
    }
}


static void btn_j1_plus_cb(lv_event_t *e)
{
    lv_event_code_t code = lv_event_get_code(e);
    if (code == LV_EVENT_CLICKED || code == LV_EVENT_LONG_PRESSED_REPEAT) {
        lv_event_stop_bubbling(e);
        send_joint_increment_cmd("J1", +1);
    }
}


  • 上位机与MCU间的串口通信指令协议详见附录。


五、上位机 Linux 系统 ROS 消息转换与 Genesis 仿真控制

5.1 整体介绍

上位机系统采用 MVC(Model-View-Controller)架构,实现了从 GD32 微控制器硬件到 Genesis 物理仿真引擎的完整数据闭环。系统通过 ROS 作为中间件进行进程间通信,通过 socat 虚拟串口桥接实现硬件与软件的解耦。


一键启动脚本 start_s1_system.sh 按以下顺序启动4个核心进程:

序号

进程

终端

功能描述

1

roscore

xterm

ROS Master 节点,提供话题注册与通信基础设施

2

socat

后台

虚拟串口桥接,将远程 TCP 串口映射为本地 PTY 设备

3

gd32_ros_control.py

xterm

GD32-ROS 桥接节点,串口协议解析与 ROS 话题转发

4

main_mvc.py

xterm

Genesis 仿真主程序,MVC 架构的仿真控制循环


5.2 核心类设计

类名

设计模式

核心职责

SerialHandler

单例模式

管理 GD32 串口通信连接,后台线程读取数据,支持自动重连

ProtocolParser

解析 GD32 上行通知(关节增量、急停、示教模式),正则匹配

ROSInterface

ROS 话题发布/订阅,GD32-ROS 双向数据转发

GD32ROSControl

协调者

协调串口通信、协议解析和 ROS 发布/订阅,关节步长 0.01 rad

SimulationApplication

MVC-Controller

仿真应用主类,管理 Model-View-Controller 三层

ROSController

观察者

为仿真主程序提供 ROS 话题接口,支持 Real ROS / Mock 双模式

GenesisSimulatorProxy

代理模式

封装 Genesis 物理引擎 API,提供统一仿真器操作接口


5.3 ROS 节点详细说明

节点

源文件

话题方向

话题名

消息类型

频率

gd32_ros_bridge

gd32_ros_control.py

发布

/real_arm/joint_angles

std_msgs/String

事件驱动

gd32_ros_bridge

gd32_ros_control.py

发布

/real_arm/robot_status

std_msgs/String

事件驱动

gd32_ros_bridge

gd32_ros_control.py

订阅

/real_arm/robot_state

std_msgs/String

100Hz

yhrg_s1_controller

ros_controller.py

订阅

/real_arm/joint_angles

std_msgs/String

事件驱动

yhrg_s1_controller

ros_controller.py

订阅

/real_arm/robot_status

std_msgs/String

事件驱动

yhrg_s1_controller

ros_controller.py

发布

/real_arm/robot_state

std_msgs/String

~10Hz

ROS 话题消息格式详见附录。


5.4 数据处理与环境交互机制

  • 在 Linux 安装 socat 工具后,启动脚本通过它建立虚拟串口桥接,将远程 GD32 设备的 TCP 串口映射为本地 PTY 设备:


sudo socat pty,link=/dev/ttyVUSB0,raw,echo=0 tcp:$SOCAT_IP:7797 &

参数

说明

pty

创建伪终端设备

link

/dev/ttyVUSB0

符号链接路径

raw

原始数据模式

echo

0

禁用回显

tcp

$SOCAT_IP:7797

远端 TCP 地址和端口

  • GD32 到 Genesis 仿真器的关节命令流

image.png


  • Genesis 仿真器到 GD32 的状态反馈流

image.png


  • 急停状态传播

image.png


5.5 仿真控制循环

main_mvc.py,程序以约 100Hz(time.sleep(0.01))的频率持续运行。每次循环首先检查急停状态:若未急停,则通过 target_lock 线程安全地读取 ROS 回调更新的最新目标关节角度,并同步写入 RobotStateFlowPipeline;无论是否急停,都会执行 FlowPipeline.step() 驱动 Genesis 物理仿真前进一步(急停时目标位置冻结,当前指令仍可完成)。随后从仿真器读取当前关节位置、速度、力矩以及末端执行器的位置和四元数姿态,并以不同频率向外输出:每 10 步(~10Hz)通过 ros_controller.update_robot_state() 将机器人状态发布到 /real_arm/robot_state ROS 话题供 GD32 桥接端接收;每 100 步(~1Hz)记录末端位姿日志,并在距上次打印超过 1 秒时在终端刷新显示命令计数、平均延迟和关节位置等统计信息。循环通过 Ctrl+C 中断后,在 finally 块中安全停止 ROS 控制器,确保资源释放。


try:
    while True:
        if not self._estop_active:
            with target_lock:
                target = latest_target.copy()
            self._robot_state.set_target_positions(target)
            self._flow_pipeline.set_target_position(target)
       
        self._flow_pipeline.step()
       
        positions = self._simulator_proxy.get_joint_positions(self._robot, self._motor_dof_idx)
        velocities = self._simulator_proxy.get_joint_velocities(self._robot, self._motor_dof_idx)
        efforts = self._simulator_proxy.get_joint_efforts(self._robot, self._motor_dof_idx)
       
        ee_position = self._simulator_proxy.get_end_effector_position(
            self._robot, self._end_effector_link_name
        )
        ee_quaternion = self._simulator_proxy.get_end_effector_quaternion(
            self._robot, self._end_effector_link_name
        )
       
        if step_count % 100 == 0:
            logger.info(f"EE position: {ee_position}, EE quaternion: {ee_quaternion}")


        if step_count % 10 == 0:
            ros_controller.update_robot_state(
                positions=positions,
                velocities=velocities,
                efforts=efforts,
                end_effector_position=ee_position,
                end_effector_quaternion=ee_quaternion,
            )


        if step_count % 100 == 0:
            current_time = time.time()
            if current_time - last_stats_time >= 1.0:
                stats = ros_controller.get_statistics()
                print(f"\r[ROS] Commands: {stats['command_count']} | "
                        f"Latency: {stats['average_latency_ms']:.1f}ms | "
                        f"Position: {positions[0]:.3f}...", end="", flush=True)
                last_stats_time = current_time
       
        step_count += 1
        time.sleep(0.01)
   
except KeyboardInterrupt:
    logger.info("ROS simulation interrupted by user")
finally:
    ros_controller.stop()
    logger.info("ROS simulation ended")


六、功能展示

  • 嵌入式前端可视化界面

image.png

image.png

image.png


  • 嵌入式终端与 Genesis 仿真环境交互

image.png


七、项目总结

本项目采用三层解耦架构(嵌入式前端 / 协议转换层 / 仿真控制后端),应用 MVC + 代理模式封装 Genesis API,通过 socat 虚拟串口桥接突破物理连接距离限制,完美解决原先必须前往服务器所在场地进行系统联调的麻烦,实现了急停事件从硬件 LED 指示到 ROS 话题传播再到仿真引擎冻结的全链路联动。项目验证了“嵌入式 GUI 终端 + 具身智能仿真平台”这一异构系统集成方案的工程可行性,为机器人控制原型开发与算法验证提供了一套可复用的参考框架。


感谢电子森林和贸泽电子对本项目的支持,期待下一次的M-Design活动!






附录

A. 上位机与MCU间的指令协议表

下行指令(上位机 → MCU)

指令

格式

参数类型

返回格式

功能描述

SET_J1

SET_J1:<angle>

float

OK: J1=%.2f\r\n

设置关节1角度(-170°~170°)

SET_J2

SET_J2:<angle>

float

OK: J2=%.2f\r\n

设置关节2角度(0°~180°)

SET_J3

SET_J3:<angle>

float

OK: J3=%.2f\r\n

设置关节3角度(0°~180°)

SET_J4

SET_J4:<angle>

float

OK: J4=%.2f\r\n

设置关节4角度(-90°~87°)

SET_J5

SET_J5:<angle>

float

OK: J5=%.2f\r\n

设置关节5角度(-90°~90°)

SET_J6

SET_J6:<angle>

float

OK: J6=%.2f\r\n

设置关节6角度(-90°~90°)

SET_J7

SET_J7:<value>

float

OK: J7=%.2f\r\n

设置关节7位移(0~0.05mm)

SET_JOINTS

SET_JOINTS:<j1>,...,<j7>

7×float

OK: JOINTS=...\r\n

批量设置7个关节

SET_POS

SET_POS:<x>,<y>,<z>

3×float

OK: POS=%.2f,...\r\n

设置末端位置(mm)

SET_QUAT

SET_QUAT:<w>,<x>,<y>,<z>

4×float

OK: QUAT=...\r\n

设置四元数姿态

SET_TEMP

SET_TEMP:<value>

int

OK: TEMP=%d\r\n

设置电机温度(°C),暂未使用

GET_STATUS

GET_STATUS

多行文本

查询全部状态

HELP

HELP

多行文本

显示帮助信息

上行事件(MCU → 上位机,主动上报)

事件标识

输出格式

触发条件

PA6 急停指示指示灯

急停触发

ESTOP:TRIGGER\n

急停按钮点击

高亮度(高电平)

急停释放

ESTOP:RELEASE\n

解锁按钮点击

低亮度 (低电平)

关节增量

JOINT:Jx,INCREMENT:+/-d\n

关节+/-按钮

不变

B. ROS 话题消息格式

/real_arm/joint_angles 消息格式:

{
"joint_names": ["1joint", "2joint", "3joint", "4joint",
"5joint", "6joint", "7joint", "8joint"],
"positions": [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
"velocities": [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
"timestamp": 1234567890.123
}


/real_arm/robot_state 消息格式:

{
"name": "ros_publisher",
"joint_names": ["1joint", "2joint", "3joint", "4joint",
"5joint", "6joint", "7joint", "8joint"],
"positions": [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
"velocities": [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
"efforts": [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
"end_effector_position": [0.5, 0.0, 0.8],
"end_effector_quaternion": [1.0, 0.0, 0.0, 0.0],
"timestamp": 1234567890.123
}


/real_arm/robot_status 消息格式:

{
"estop": "TRIGGERED",
"teach_mode": "EXIT",
"timestamp": 1234567890.123
}




附件下载
s1_GD32H7_EVAL_ATK_LCD _GUIder.zip
GD32程序
s1_genesis_mvc_260429_final.zip
Linux服务器程序
ProPrj_LED扩展板_2026-05-29.epro
LED指示灯模块原理图、PCB、外壳设计
3DShell_PCB1.zip
LED指示灯模块外壳STL文件
团队介绍
本人负责项目整体设计,由TRAE完成C语言和Python代码编写,并协助整理项目报告。
团队成员
fyjh2023
评论
0 / 100
查看更多
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号