makemeahanzi 项目的 graphics.txt 是目前实现"机械臂写字"最完美的开源数据源之一。它存储的是中文字符的单线骨架(Skeleton),每一行数据都直接包含了汉字的笔顺和坐标点。
以下是如何在 C# 中解析这些数据并将其转化为机械臂"笔划点阵"的完整方案:
1. 数据结构解析
graphics.txt 中的每一行是一个 JSON 对象,核心字段是 strokes。
-
character: 汉字本身。 -
strokes: 一个字符串数组,每个字符串是一串 SVG 路径命令(如M 10 20 L 30 40...)。 -
medians: 这是你最需要的! 它存储了每一笔画的中心骨架点坐标集合。
2. C# 实现:从 JSON 到点阵轨迹
你需要使用 Newtonsoft.Json 或 System.Text.Json 来解析。
核心代码模型
public class HanziData
{
public string character { get; set; }
// medians 存储了每一笔的所有节点:[[[x,y],[x,y]], [[x,y],[x,y]]]
public List<List<List<int>>> medians { get; set; }
}
轨迹提取与 0.1s 采样逻辑
由于 medians 提供的点可能稀疏(转折点),而你要求"按点 0.1s 步进",我们需要进行线性插值,确保机械臂匀速移动。
C#
cs
public async Task DrawHanzi(HanziData data)
{
foreach (var stroke in data.medians) // 遍历每一笔
{
// 1. 抬笔移动到笔划起点 (Z 轴设为高位)
MoveTo(stroke[0][0], stroke[0][1], zRaise: 10);
// 2. 下笔 (Z 轴设为低位)
MoveTo(stroke[0][0], stroke[0][1], zRaise: 0);
foreach (var point in stroke) // 遍历笔划内的点
{
double targetX = point[0];
double targetY = point[1];
// 3. 逆运动学变换 (IK)
var angles = CalculateIK(targetX, targetY, 0);
// 4. 发送指令给机械臂
SendToRobot(angles);
// 5. 严格 0.1s 等待
await Task.Delay(100);
}
// 6. 笔划结束,抬笔
MoveTo(lastX, lastY, zRaise: 10);
}
}
3. 关键点:坐标映射与逆运动学
graphics.txt 的坐标通常在 1024 \\times 1024 的范围内,且 Y 轴向下。你需要将其映射到机械臂的物理空间。
坐标缩放公式
如果你的机械臂工作区域是 200\\text{mm} \\times 200\\text{mm}:
X_{robot} = \\frac{X_{data}}{1024} \\cdot 200
Y_{robot} = \\text{Offset} - (\\frac{Y_{data}}{1024} \\cdot 200)
3 轴机械臂逆运动学 (IK)
对于常见的水平 3 轴(SCARA 或 连杆结构),你需要根据连杆长度 L_1, L_2 计算角度。
4. 更好的优化方案
为了让写字更自然,建议在 C# 程序中加入以下逻辑:
-
等距插值 (Resampling):
medians里的两个点如果离得很远,0.1s 跳过去会很剧烈。建议在 C# 中预处理:如果两点距离 \> 2\\text{mm},则在中间补点,保证机械臂每 0.1s 只移动一小步。 -
缓冲区 (Buffering):
不要每 0.1s 发一条串口指令,因为 Windows 的
Task.Delay并不精准(有 \\pm 15\\text{ms} 误差)。更好的做法:在 C# 中一次性生成 50 个点发送给下位机(Arduino/ESP32),由下位机的定时器中断来严格控制 0.1s 的步进。
-
坐标转换矩阵:
利用
System.Numerics.Matrix3x2进行旋转和缩放,这样你可以轻松实现写大字、写小字或倾斜写字。
5. 总结你的开发步骤
-
下载
graphics.txt并将其作为资源文件载入 C#。 -
编写解析器 :根据输入的汉字找到对应的
medians数组。 -
数学变换:将 1024 坐标转为机械臂毫米坐标。
-
逆解计算:将毫米坐标转为电机脉冲/角度。
-
串口通信 :通过
SerialPort发送指令。