多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

14岁少年四年造机械臂:Rust重写驱动与3D打印避坑指南

14岁少年四年造机械臂:Rust重写驱动与3D打印避坑指南 1. 一个14岁少年的四年硬核长跑到底在折腾什么先把这件事的轮廓说清楚。一个14岁的少年花了整整四年时间从零开始做了一台机械臂。中间经历过3D打印件反复开裂、结构推倒重来、电路板画了又废最后用Rust重写了底层驱动才算真正跑通。这个项目里塞满了几个硬核关键词Rust、机械臂、3D打印、PCB设计、嵌入式开发。任何一个单独拎出来都够一个成年人啃上大半年他全给串起来了。我第一眼看到这个标题的时候反应不是哇天才而是这孩子踩的坑得有多少。因为我自己做过类似的事——用3D打印件搭结构、画PCB、写固件驱动舵机太清楚这条路上每一步会卡在哪里。3D打印的PLA件在舵机扭矩下会蠕变、会层间剥离自己画的PCB第一版大概率有封装画反的、有走线太细扛不住电流的嵌入式固件从裸机寄存器到RTOS再到用Rust重写每一次都是认知升级。所以这篇东西不是要复述少年天才的故事而是把这个项目拆开看看一个从零起步的人在机械臂这个方向上到底要跨过哪些技术门槛每个门槛背后的原理是什么有哪些坑是可以提前避开的。适合谁看适合正在做机械臂毕业设计的学生、想入门嵌入式机械结构的爱好者、以及任何对用Rust写硬件驱动这件事好奇的开发者。不管你是刚买了第一台3D打印机的新手还是已经能画四层板的老手这里面的拆解逻辑和避坑经验都能对上号。我打算按这个项目实际推进的顺序来拆先讲整体方案怎么定、为什么这么定再讲机械结构和3D打印件的核心细节然后是PCB设计和嵌入式驱动的实现最后是Rust重写驱动这件事到底解决了什么问题以及整个过程中最容易翻车的地方。2. 整体方案设计与技术选型拆解2.1 为什么是3D打印总线舵机自绘PCB这条路线做机械臂第一步不是画图是定路线。市面上常见的方案大概分三种工业级谐波减速器方案、步进电机同步带方案、舵机直驱方案。一个14岁少年在四年前起步预算有限、加工条件有限选舵机直驱几乎是必然的。但舵机直驱有个致命问题普通PWM舵机的角度反馈是假的——它只知道我收到了一个PWM信号不知道我实际转到了哪里。机械臂要做得准必须知道每个关节的真实角度。所以这里的关键选型是总线舵机。总线舵机内部集成了磁编码器或电位器反馈能通过串口协议读回当前角度、负载、温度等参数。这就把开环控制变成了闭环控制的硬件基础。常见的总线舵机协议有半双工串口单线和全双工串口双线两种前者省线但时序要求高后者接线多但调试简单。对于一个自学起步的项目我建议优先选全双工串口的总线舵机因为半双工的单线时序在软件层面很容易出问题尤其是用Rust这种对时序敏感的语言写驱动时半双工切换收发方向的时机稍微偏一点就丢包。结构件用3D打印理由更直接FDM打印机能做出复杂几何形状成本低迭代快。但3D打印件有个绕不开的物理特性——各向异性。FDM打印的层与层之间是靠热熔粘结的Z轴方向的强度通常只有XY方向的30%到50%。这意味着如果你把一个受弯的零件按层纹垂直于受力方向来打印它很容易从层间裂开。机械臂的关节连接件、舵机支架恰恰是受力最复杂的地方所以打印方向的选择比打印参数本身更重要。PCB自绘这件事很多人会觉得买个现成的驱动板不就行了。但总线舵机通常需要独立的供电和信号分配市面上的通用板要么接口对不上要么供电能力不够。自己画一块板把电源分配、电平转换、舵机接口、主控最小系统全集成上去反而更省事。而且画PCB这件事本身就是嵌入式开发的核心技能之一早画早踩坑早成长。2.2 主控选型的逻辑从STM32到Rust支持的芯片四年前起步主控大概率是从STM32F103这类经典芯片入门的资料多、教程多、社区大。但后来要用Rust重写驱动芯片选型就得重新考虑。Rust的嵌入式生态embedded-hal、embassy等对芯片的支持程度差异很大。STM32系列有stm32f1xx-hal、stm32f4xx-hal这些成熟的crate但不同型号的HAL成熟度不一样。相比之下CH32系列比如CH32V307、CH32F103因为国产替代的浪潮Rust社区的支持也在快速跟进而且价格有优势。如果让我现在重新选我会优先考虑两类芯片一类是Rust生态已经非常成熟的STM32F4/F7系列另一类是esp32系列esp-hal crate支持得不错而且自带无线功能方便后续扩展。选芯片的时候不要只看能不能跑Rust要看这个芯片的Rust HAL有没有实现你要用的外设——比如你要用DMA串口、要用硬件定时器输出PWM、要用ADC读电流这些外设在Rust HAL里的实现程度直接决定你后面写驱动是顺畅还是痛苦。提示选主控时先去docs.rs搜一下对应的HAL crate看看它的文档完整度和最近更新日期。一个半年没更新的HAL大概率有未修复的bug。2.3 通信架构为什么最终会走向上位机下位机分离机械臂的控制逻辑分两层一层是运动学解算和轨迹规划这需要浮点运算和一定的算力另一层是实时驱动舵机、读编码器、做电流保护这需要硬实时。把这两层塞进一颗MCU里要么算力不够要么实时性被拖累。所以合理的架构是上位机负责规划下位机负责执行。上位机可以是一台电脑、一块树莓派甚至是一个跑Rust的桌面程序。下位机就是那块自绘PCB上的MCU通过串口或CAN总线和上位机通信。上位机算出每个关节的目标角度打包成协议帧发给下位机下位机解析后驱动舵机同时把舵机的实际角度、电流、温度回传。这个架构的好处是上位机可以用Rust写复杂的运动学算法下位机只需要用Rust写一个精简的实时循环两边通过一个定义清晰的串口协议解耦。这里有个经验协议帧一定要带校验和帧头帧尾。我见过太多项目因为串口丢一个字节导致整个机械臂乱动。最简单的做法是帧头用两个固定字节比如0xAA 0x55帧尾加一个CRC8或累加和校验。别省这一步省下来的调试时间远超你写校验的时间。3. 机械结构与3D打印件的核心细节3.1 打印方向决定零件寿命一个被90%新手忽略的问题前面提到FDM打印件的各向异性这里展开说。假设你打印一个舵机支架它是一个L形零件舵机的扭矩会让L形的拐角处承受弯矩。如果你把零件平躺着打印层纹水平那么拐角处的层间结合面正好垂直于弯矩方向受力时层与层之间被撕开非常容易断。正确的做法是把零件竖起来打印让层纹方向平行于受力方向这样受力时是拉伸而非剥离层间结合面强度能提升一倍以上。但竖着打印又带来新问题悬垂面需要支撑支撑拆除后表面粗糙而且打印时间变长。所以实际做法是分件打印后期组装。把复杂的L形支架拆成两个简单的块各自用最优方向打印再用螺丝和定位销组装起来。这样既保证了强度又避免了支撑。这个思路在机械臂结构设计里非常通用——能用螺丝连接的就不要用一体打印的复杂悬垂结构。还有一个细节打印填充率不是越高越好。机械臂零件需要的是表层强度和局部加强100%填充既浪费材料又增加重量。我的经验是外壳用4到5层壁厚shell填充率20%到30%的gyroid或cubic模式然后在螺丝孔周围和受力集中区域用局部修改器提高填充率到60%以上。这样零件的整体强度接近实心但重量轻了三分之一对舵机的负载也小。3.2 舵机选型与扭矩计算别让机械臂抬不起头机械臂的每个关节需要多大扭矩的舵机是可以算出来的。假设机械臂最远端要抓取一个200克的物体机械臂全长40厘米那么肩部关节承受的力矩大约是物体重量×力臂 机械臂自身重量×重心力臂。粗略估算200克×40厘米 8000克·厘米再加上臂身自重假设500克重心在20厘米处 10000克·厘米合计约18000克·厘米也就是18千克·厘米。考虑到安全系数1.5到2倍肩部舵机至少需要30千克·厘米的扭矩。但这里有个陷阱舵机标称扭矩是在特定电压下的堵转扭矩实际工作扭矩通常只有标称值的60%到70%。而且舵机在接近堵转时电流急剧上升发热严重寿命骤降。所以选型时要把标称扭矩打个六折来用。另外总线舵机的扭矩和价格不是线性关系20千克·厘米和30千克·厘米的舵机价格可能差一倍但扭矩只多50%。所以合理的做法是大关节用大扭矩舵机小关节用小扭矩舵机通过减速结构比如同步带或齿轮来放大扭矩。注意总线舵机的供电电压通常是6V到12V不同型号不一样。如果你用锂电池直接供电比如3S锂电满电12.6V一定要确认舵机能承受这个电压否则上电就烧。稳妥的做法是加一级DC-DC降压到舵机额定电压。3.3 机械臂偏差的来源与补偿思路标题里提到机械臂偏差这是所有做机械臂的人都会遇到的问题。偏差来源主要有四个一是舵机的角度反馈精度总线舵机通常是0.1度到1度分辨率二是机械结构的间隙3D打印件的孔和轴之间总有间隙螺丝连接处也有微小位移三是重力导致的弹性变形3D打印件在负载下会弯曲四是运动学模型的误差连杆长度测量不准、关节零点标定不准。前两个是硬件层面的能通过加工精度和装配工艺改善但无法完全消除。后两个是软件层面的可以通过标定和补偿来减小。一个实用的做法是在每个关节的零点位置做一次标定。具体操作是让机械臂缓慢运动到机械限位记录此时舵机的读数作为零点偏移然后在运动学解算时把这个偏移加进去。这个操作能消除大部分零点漂移导致的系统性偏差。对于重力变形如果要求不高可以在运动学模型里加一个负载补偿系数根据当前负载和姿态估算变形量反向修正目标角度。如果要求高那就得上闭环视觉反馈了——用摄像头看末端执行器的实际位置和理论位置对比实时修正。这个方案复杂度高但精度能提升一个数量级。4. PCB设计与嵌入式驱动的实现4.1 自绘PCB的完整流程与关键规则画一块能用的机械臂驱动板流程大概是原理图设计→元件选型→PCB布局→布线→打样→焊接→调试。每一步都有坑。原理图阶段最容易犯的错是电源树没理清。机械臂驱动板通常有多路电源主控3.3V、舵机6V到12V、可能还有传感器5V。如果直接用一颗LDO从舵机电压降到3.3V舵机一动作电流突变3.3V就会被拉垮主控复位。正确做法是主控电源和舵机电源分开舵机电源用独立的DC-DC主控电源用另一颗LDO或DC-DC中间用磁珠或电感隔离。这样舵机的电流波动不会影响主控。PCB布局阶段核心原则是大电流路径要短而粗。舵机的供电走线如果电流是5A铜厚1盎司那么走线宽度至少要2毫米以上经验值1盎司铜厚下1毫米线宽大约能扛1A但温升会比较高稳妥起见按0.5A每毫米算。如果板子空间不够可以在走线上开窗加锡或者直接用铜排。信号线则要远离大电流路径避免串扰。布线阶段晶振和复位电路要靠近主控晶振走线要短且包地。串口线如果走线长要加匹配电阻或共模电感。舵机接口的PWM或串口信号线最好串一个22欧姆到100欧姆的电阻抑制振铃。提示第一次画板所有元件的封装一定要对着数据手册的推荐焊盘尺寸画不要凭感觉。我见过太多人因为封装画小了0.2毫米导致芯片焊不上去。4.2 嵌入式驱动的分层设计从寄存器到Rust抽象嵌入式驱动的写法从底层到上层大概分四层寄存器操作层、外设抽象层、设备驱动层、应用逻辑层。用Rust写的时候embedded-hal提供了外设抽象层的trait定义比如OutputPin、PwmPin、Serial。你的舵机驱动只需要实现这些trait就能在不同芯片之间移植。具体到总线舵机的驱动核心是串口收发。总线舵机通常用半双工或全双工串口波特率常见的是115200或1000000。用Rust写的时候可以用embedded-io或embedded-hal的串口trait配合DMA收发。DMA的好处是CPU不用轮询可以腾出时间做运动学解算。但DMA的坑在于缓冲区管理——如果DMA还在发送上一帧数据你就往缓冲区写新数据数据会被覆盖。所以要用双缓冲或者环形缓冲并且用中断或async来同步。Rust的async在嵌入式里的用法和桌面端不太一样。embassy框架提供了async运行时可以用async fn写串口收发用await等待DMA完成。这样代码写起来像同步代码但实际是异步执行的。比如async fn send_servo_command(uart: mut Uart, id: u8, angle: f32) - Result(), Error { let frame build_frame(id, angle); uart.write_all(frame).await?; uart.flush().await?; Ok(()) }这段代码看起来简单但背后embassy帮你处理了DMA中断、任务调度、缓冲区管理。用async写嵌入式驱动最大的好处是逻辑清晰不用在中断和主循环之间来回跳。4.3 舵机控制协议解析与角度换算总线舵机的控制协议通常是这样的帧头2字节 ID1字节 指令长度1字节 指令1字节 参数N字节 校验1字节。比如让ID为3的舵机转到90度参数就是目标角度的原始值。舵机的角度分辨率通常是0.1度或0.24度所以90度对应的原始值可能是900或375取决于型号。这里有个容易出错的地方角度范围和零点定义。不同舵机的角度范围不一样有的是0到180度有的是0到270度还有的是-90到90度。而且舵机的0度位置不一定是你机械结构上的零位。所以驱动层要做一个映射把机械臂运动学算出来的关节角度比如-90到90度映射到舵机的原始值范围。这个映射关系要在标定阶段确定并且写死在配置里。// 角度映射示例机械角度 -90..90 映射到舵机原始值 0..1000 fn angle_to_raw(angle: f32, min_angle: f32, max_angle: f32, min_raw: u16, max_raw: u16) - u16 { let ratio (angle - min_angle) / (max_angle - min_angle); (min_raw as f32 ratio * (max_raw - min_raw) as f32) as u16 }这个函数看起来简单但min_angle、max_angle、min_raw、max_raw这四个参数必须通过实际标定得到不能照抄别人的值。标定方法手动把关节推到机械限位读舵机原始值记录再推到另一个限位记录。两个原始值对应两个机械角度线性映射就出来了。5. Rust重写驱动到底解决了什么问题5.1 从C到Rust不是跟风是踩过坑之后的选择很多人问为什么非要用Rust重写C不是挺好的吗这个问题要分两面看。C在嵌入式领域统治了几十年工具链成熟、资料多、性能好。但C有个致命问题内存安全和并发安全全靠程序员自觉。机械臂这种多任务系统串口中断、定时器中断、主循环、DMA回调多个执行流共享数据一个野指针或者一个竞态条件轻则数据错乱重则舵机乱转打坏结构。Rust的所有权系统和借用检查器在编译期就能消除数据竞争和空指针。比如你要在中断和主循环之间共享一个舵机角度数组C里你得自己加锁或者用原子操作稍不注意就出bug。Rust里你用Mutexembassy提供的异步Mutex或者RefCell配合中断安全抽象编译器会强制你正确处理共享。这不是更安全的口号是实打实减少了调试时间。另一个原因是Rust的包管理和抽象能力。C里你要复用一段串口驱动代码得手动拷贝或者做成库依赖管理靠Makefile。Rust里一个cargo add就能引入一个经过社区验证的HAL crate而且trait抽象让驱动可以跨芯片复用。比如你从STM32换到CH32只要新芯片的HAL实现了相同的trait你的舵机驱动代码几乎不用改。5.2 Rust嵌入式开发的环境搭建与工具链Rust嵌入式开发的环境搭建比桌面端稍微麻烦一点但也就几步。首先装Rust工具链用rustup。然后装目标平台的编译目标比如STM32F4是thumbv7em-none-eabihfCH32V是riscv32imac-unknown-none-elf。然后装调试工具比如probe-rs或者openocd。最后在项目里配置.cargo/config.toml指定target和runner。rustup target add thumbv7em-none-eabihf cargo install probe-rs-toolsprobe-rs是我强烈推荐的调试工具它支持烧录、调试、RTT日志输出而且和VSCode集成得很好。你可以在VSCode里装probe-rs-debugger插件直接打断点、看变量、单步执行。这比用OpenOCDGDB的命令行调试舒服太多了。注意用Rust写嵌入式no_std是常态。这意味着你不能用标准库的Vec、String、HashMap。要用heapless提供的固定容量容器或者alloc配合自定义分配器。刚开始会不习惯但习惯了之后你会发现固定容量的数据结构反而让内存使用更可控。5.3 用Rust写舵机驱动的实操代码拆解下面是一个用embassy写总线舵机驱动的核心结构。先定义舵机结构体包含串口、ID、角度范围等pub struct BusServod, U: embedded_io_async::Write embedded_io_async::Read { uart: U, id: u8, min_raw: u16, max_raw: u16, min_angle: f32, max_angle: f32, }然后实现发送指令的方法impld, U BusServod, U where U: embedded_io_async::Write embedded_io_async::Read, { pub async fn set_angle(mut self, angle: f32) - Result(), ServoError { let raw self.angle_to_raw(angle); let frame self.build_move_frame(raw); self.uart.write_all(frame).await.map_err(|_| ServoError::Uart)?; Ok(()) } fn angle_to_raw(self, angle: f32) - u16 { let clamped angle.clamp(self.min_angle, self.max_angle); let ratio (clamped - self.min_angle) / (self.max_angle - self.min_angle); (self.min_raw as f32 ratio * (self.max_raw - self.min_raw) as f32) as u16 } fn build_move_frame(self, raw: u16) - heapless::Vecu8, 16 { let mut frame heapless::Vec::new(); frame.extend_from_slice([0xAA, 0x55]).unwrap(); frame.push(self.id).unwrap(); frame.push(0x04).unwrap(); // 长度 frame.push(0x01).unwrap(); // 移动指令 frame.extend_from_slice(raw.to_le_bytes()).unwrap(); let checksum frame[2..].iter().fold(0u8, |acc, b| acc.wrapping_add(b)); frame.push(checksum).unwrap(); frame } }这段代码里heapless::Vecu8, 16是固定容量16字节的向量不会动态分配内存。angle_to_raw做了钳位和线性映射。build_move_frame构造协议帧并计算校验和。整个驱动不依赖任何动态内存可以在no_std环境里跑。读取舵机角度的方法类似发送读取指令后等待回复解析回复帧里的角度值。这里要注意超时处理——如果舵机没回复不能死等要设一个超时返回错误。embassy的with_timeout可以很方便地实现let result embassy_time::with_timeout( Duration::from_millis(10), self.uart.read(mut buf) ).await;5.4 多关节同步控制与轨迹插补机械臂有多个关节如果逐个发送指令关节之间会有时间差导致末端轨迹不是直线。要解决这个问题有两个思路一是广播指令总线舵机通常支持广播ID一条指令让所有舵机同时开始运动二是轨迹插补上位机把一条直线轨迹离散成很多个小点每个点对应一组关节角度然后以固定周期比如10毫秒连续下发。广播指令的优点是同步性好缺点是所有舵机必须同时到达目标如果某个关节路径上有障碍没法单独调整。轨迹插补的优点是灵活可以做避障和速度规划缺点是对通信带宽和实时性要求高。实际项目里通常是两者结合粗定位用广播精跟踪用插补。插补的算法不复杂最简单的线性插补就是起点角度A终点角度B插补周期T总时间t那么第n个周期的角度是A (B-A) * n*T/t。但线性插补在关节空间是直线在笛卡尔空间不是直线。如果要求末端走直线得用逆运动学把笛卡尔空间的直线离散成关节空间的角度序列。这个计算量不小所以放在上位机做。6. 常见问题与排查技巧实录6.1 3D打印件开裂与变形的排查思路打印件开裂是最常见的问题。排查顺序是先看裂口位置和方向如果裂口沿着层纹方向那是层间结合力不足解决办法是提高打印温度比如PLA从200度提到215度、降低层高从0.2毫米降到0.15毫米、提高挤出量流量从100%提到103%。如果裂口是斜的那可能是零件受力方向设计不合理需要改打印方向或增加加强筋。变形的问题通常是冷却不均导致的。大平面零件容易翘边解决办法是加裙边brim、降低热床温度PLA从60度降到50度、关闭前几层的风扇。如果零件在打印过程中就变形可能是环境温度太低可以给打印机加个罩子。提示机械臂的受力件打印完后最好做一次退火处理。PLA在60到70度的烤箱里放一小时然后缓慢冷却能显著提高结晶度层间强度能提升20%以上。但退火会导致零件收缩尺寸精度会变差所以退火前要留好公差。6.2 舵机抖动、丢步、发热的典型原因舵机抖动通常有三个原因一是电源供电不足舵机一动作电压就掉导致内部控制器复位二是信号干扰串口线太长或者和电机线捆在一起三是舵机内部齿轮磨损间隙变大。排查方法先用示波器看舵机供电电压如果动作时掉超过0.5V就是电源问题如果电压稳就把信号线单独走远离电机线如果还抖换个舵机试试。丢步舵机没转到目标角度通常是负载超过舵机扭矩或者舵机内部保护了。总线舵机通常有堵转保护电流超过阈值就断电。如果频繁丢步要么换大扭矩舵机要么检查机械结构有没有卡涩。发热是舵机的大敌。舵机在接近堵转时效率极低大部分电能变成热量。如果舵机外壳温度超过60度就要检查是不是负载太大或者运动轨迹不合理。一个实用的技巧是在运动轨迹里加入减速段让舵机在接近目标时降速减少冲击和发热。6.3 串口通信丢包与校验失败的排查串口丢包是嵌入式开发的家常便饭。排查步骤先确认波特率是否匹配两边差一点都会丢包再看地线是否共地不共地的话信号电平没有参考通信必挂然后看线长串口线超过30厘米就容易受干扰最好用双绞线或者屏蔽线最后看软件DMA缓冲区是否够大中断优先级是否合理。校验失败通常是数据在传输过程中被干扰了。解决办法降低波特率从1000000降到115200、加校验和、加超时重传。如果环境电磁干扰强可以在串口线上加共模电感或者TVS管。问题现象可能原因排查方法解决措施舵机抖动电源不足示波器看供电电压加大电容或换DC-DC舵机丢步负载过大测电流是否超阈值换大扭矩舵机或加减速串口丢包波特率不匹配核对两边配置统一波特率校验失败电磁干扰检查线缆走向加屏蔽或降波特率打印件开裂层间结合弱看裂口方向提高温度或改打印方向6.4 Rust嵌入式开发的常见编译错误与解决Rust嵌入式开发最常遇到的编译错误是no_std环境下的alloc缺失。比如你想用Vec编译器会报cannot find macro vec in this scope。解决办法是引入heapless或者配置alloc和全局分配器。另一个常见错误是trait未实现比如你调用了uart.write_all()但编译器说Writetrait未实现那是因为你没有use embedded_io_async::Write。还有一个坑是中断处理函数的命名和属性。不同芯片的中断向量表不一样STM32用#[interrupt]属性CH32用#[riscv_rt::entry]或者#[interrupt]。写中断处理函数时要注意不能在中断里做耗时操作也不能在中断里调用可能阻塞的async函数。如果需要在中断和async任务之间通信用embassy_sync::channel或者Signal。注意Rust的借用检查器在中断和主循环共享数据时会很严格。如果编译器报cannot borrow as mutable more than once不要急着用unsafe绕过先想想能不能用Mutex或者RefCell重构。unsafe能解决编译问题但解决不了逻辑问题。7. 四年长跑里最值得分享的几条经验做这个项目四年最大的体会不是某个技术点多难而是系统复杂度会以你想象不到的速度膨胀。一开始只想做个能动的机械臂做着做着发现要画PCB画完PCB发现要写驱动写完驱动发现要搞运动学搞完运动学发现要标定标定完发现要补偿……每一步都是前一步的必要条件但每一步又都能独立成为一个大坑。如果让我给后来者一条建议那就是先做减法再做加法。第一版机械臂不要追求六轴先做三轴不要追求闭环先做开环不要追求Rust先用Arduino跑通。等三轴开环能动了再加第四轴、第五轴再换闭环舵机再用Rust重写。每一步只引入一个新变量这样出问题的时候你能快速定位是哪个变量导致的。另一个体会是文档和版本管理要趁早。四年前画的PCB如果没有原理图和BOM现在根本没法维护。每次改结构、改电路、改代码都要记下来改了什么、为什么改。我用的是Git管理代码和PCB文件用Markdown记设计决策。这个习惯在项目后期帮了大忙——当你要回头改一个两年前的设计时没有文档就是灾难。最后说一个具体的技巧机械臂的零点标定不要只做一次。3D打印件会蠕变螺丝会松动舵机零点会漂移。我的做法是每次上电后自动执行一次回零动作——让每个关节缓慢转到机械限位记录当前读数作为零点。这个动作只需要几秒钟但能消除大部分累积误差。回零的时候速度要慢力矩要限制避免撞限位时冲击太大。这个项目后续还可以往几个方向扩展一是加视觉反馈用摄像头做闭环二是加力控在末端执行器上加力传感器做柔顺控制三是把上位机做成图形界面用Rust的egui或tauri写一个示教器。每一个方向都是一个新的技术栈但底层的那套结构电路驱动算法的框架是不变的。把框架搭稳了上面加什么都是水到渠成的事。
返回列表