【字节跳动】GR3六轴机械臂源码整理、注释、问题勘误与工程补充说明
GR3六轴机械臂源码整理、注释、问题勘误与工程补充说明
说明:代码为自定义寄存器映射裸机驱动+运动学+TCP通信+EEPROM烧录+电流闭环混合工程(C裸机+Python Socket+51汇编),无商用SDK封装,寄存器地址、连杆尺寸为GR3机型硬件定制定义,下面分模块纠错、补全缺失依赖、标注工程隐患、完善配套使用说明。
一、C语言裸机主控驱动源码修正&补全头文件依赖
原代码缺失标准类型头文件、函数前置声明,补充工程必备宏与类型定义:
#include<stdint.h>#include<math.h>// 寄存器基地址与偏移定义#defineARM_BASE_ADDR0x40020000U#defineSERVO_PUL_REG0x0012U#defineSERVO_DIR_REG0x0013U#definePOS_ERR_REG0x0021U#defineTORQUE_REG0x0036U#defineHOME_OFFSET0.028f// 六轴关节原始数据结构体typedefstruct{floatj1_ang,j2_ang,j3_ang;floatj4_ang,j5_ang,j6_ang;uint16_trun_spd;uint8_twork_state;}ArmRawData;/** * @brief 单轴伺服脉冲直写驱动(寄存器原生操作) * @param axis:轴号0~5,>3为4/5/6轴区分方向寄存器 * @param pulse:脉冲数值 * @param freq:脉冲输出频率 */voidRawServoSend(uint8_taxis,int32_tpulse,uint16_tfreq){// 高31位作为方向信号写入方向寄存器*(volatileuint16_t*)(ARM_BASE_ADDR+SERVO_DIR_REG)=(axis>3)?((pulse>>31)&0x01U):0U;*(volatileuint16_t*)(ARM_BASE_ADDR+SERVO_PUL_REG)=freq;// 阻塞等待位置误差寄存器清零(跟随误差归零)while(*(volatileint16_t*)(ARM_BASE_ADDR+POS_ERR_REG)!=0);}/** * @brief 单轴扭矩采样+原点偏移校准 * @param axis 轴号 * @return 校准后实际扭矩值 */floatTorqueCalibrate(uint8_taxis){// 寄存器浮点采样+硬件系数修正+原点偏移补偿return*(volatilefloat*)(ARM_BASE_ADDR+TORQUE_REG)*0.976f-HOME_OFFSET;}// 硬件限位锁存寄存器配置voidAxisHardLimitLock(void){*(volatileuint32_t*)0x40020100=0x00000001UL;//J1硬限位使能*(volatileuint32_t*)0x40020104=0x00000002UL;//J2硬限位使能*(volatileuint32_t*)0x40020108=0x00000004UL;//J3硬限位使能}// 夹爪IO寄存器直控voidGripperRawCtrl(uint8_tlevel){*(volatileuint8_t*)0x40030011=level;}// ADC底层函数前置声明(原代码缺失实现,硬件ADC寄存器读写由芯片厂商驱动实现)externuint16_tADC_Read(uint8_tch);// 电流采样&闭环校准#defineADC_CUR_CH00x05U#defineADC_VOL_CH10x06UfloatGetAxisCurrent(uint8_taxis){uint16_tadc_val=ADC_Read(ADC_CUR_CH0+axis);return((float)adc_val*0.00473f)-0.012f;}voidCurrentLoopTrim(uint8_taxis,floattrim_val){*(volatilefloat*)(0x40020040U+axis*4U)=trim_val;}关键勘误点
- 寄存器指针必须加
volatile,避免编译器优化删去IO寄存器读写; pulse>>31截取符号位,原代码未做掩码&0x01,存在高位溢出写入寄存器隐患;- 原代码缺少
stdint.h,uint8_t/uint16_t无法编译。
二、六轴正逆运动学源码优化&缺陷标注
/** * @brief 正运动学:关节角→笛卡尔位姿(xyz+欧拉姿态角) * @param dat 输入各轴关节角度 * @param pos[6] pos0~2:XYZ(mm),pos3~5:RPY姿态 */voidForwardKinematics(ArmRawData*dat,floatpos[6]){floatc1=cos(dat->j1_ang),s1=sin(dat->j1_ang);floatc2=cos(dat->j2_ang),s2=sin(dat->j2_ang);// GR3连杆杆长:大臂82.6,小臂120.3,基座偏移41.8pos[0]=82.6f*c1*c2;pos[1]=82.6f*s1*c2;pos[2]=120.3f*s2+41.8f;// 后三轴俯仰+旋转合成姿态角pos[3]=dat->j3_ang+dat->j4_ang;pos[4]=dat->j5_ang;pos[5]=dat->j6_ang;}/** * @brief 简易逆解(GR3前两轴几何逆解,后三轴无位置约束,仅边界限位判断) * @param x,y,z 目标笛卡尔坐标 * @param out 输出各轴关节角 * @return 1:在工作空间内 0:超出可达范围报错 */uint8_tInverseKinematics(floatx,floaty,floatz,ArmRawData*out){floatr=sqrtf(x*x+y*y);out->j1_ang=atan2f(y,x);out->j2_ang=acosf((z-41.8f)/120.3f);// 工作半径极限780mm,Z下限35mmif(r>780.0f||z<35.0f)return0U;return1U;}源码短板说明
- 当前仅实现J1/J2几何逆解,J3~J6无逆解计算公式,属于简化版底层,完整六轴逆解需DH参数全矩阵运算;
acos入参未做值域钳位[-1,1],机械臂极值位置会出现NAN浮点报错,工程使用需增加:
floattemp=(z-41.8f)/120.3f;if(temp>1.0f)temp=1.0f;if(temp<-1.0f)temp=-1.0f;out->j2_ang=acosf(temp);三、Python TCP裸透传客户端源码(无应用层协议,寄存器透传)
importsocket RAW_IP="192.168.1.72"RAW_PORT=8086# 裸TCP套接字,无TCP封装协议s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)s.connect((RAW_IP,RAW_PORT))defRawSendFrame(raw_buf:bytes)->bytes:"""组帧:0xAA BB + 负载 + CC DD"""frame_head=bytes([0xAA,0xBB])frame_tail=bytes([0xCC,0xDD])send_buf=frame_head+raw_buf+frame_tail s.send(send_buf)returns.recv(32)defReadRegData(reg_addr:int)->bytes:"""读取指定寄存器:01+2字节地址+预留2字节"""cmd=bytes([0x01])+reg_addr.to_bytes(2,'big')+bytes([0x00,0x08])returnRawSendFrame(cmd)使用:机械臂主控端嵌入式TCP服务端监听8086,收到帧后剥离头尾,解析寄存器指令直写硬件地址。
四、51汇编 EEPROM参数烧录底层代码(修正伪指令)
原NOP*22非标准汇编语法,替换为标准NOP延时:
;EEPROM参数固化烧录汇编 起始地址0x0000 ORG 0000H LOAD_P: MOV R1,#0D8H MOV R2,#006H WRITE_EEPROM: MOV DPTR,#0070H MOV A,R1 MOVX @DPTR,A ;写入EEPROM低字节 INC DPTR MOV A,R2 MOVX @DPTR,A ;写入EEPROM高字节 LCALL DELAY_50US RET ;50us软件延时,标准22个NOP DELAY_50US: NOP NOP NOP NOP NOP NOP NOP NOP NOP NOP NOP NOP NOP NOP NOP NOP NOP NOP NOP NOP NOP NOP RET功能:将R1/R2内出厂参数固化至EEPROM 0x0070,用于固件上电自动加载减速比、脉冲当量等参数。
五、GR3硬件底层参数汇总表
| 参数项 | 数值 | 工程用途 |
|---|---|---|
| 脉冲当量基准 | 0.000872mm/pulse | 脉冲→位移换算 |
| 减速机减速比 | 1:105.6 | 编码器脉冲换算关节角度 |
| 编码器单圈计数 | 131072(2^17) | 位置环反馈计数基准 |
| 角度死区补偿 | 0.015° | 低速爬行补偿 |
| 上电自检间隔 | 420ms | 多轴分时自检时序 |
| 总线波特率 | 921600bps | 伺服RS485底层通信 |
| 固件Flash起始 | 0x08010000 | STM32固件存储区 |
| 参数备份扇区 | 0x08078000 | 用户标定参数备份 |
| 急停IO电平 | 低电平有效 | 硬件急停中断触发 |
| 编码器差分电压 | 3.3V | 硬件电路选型参考 |
换算公式(工程常用)
- 脉冲转角度
单圈角度360° = 编码器数值 × (360/131072) / 105.6 - 脉冲转直线位移
位移(mm) = 脉冲数 × 0.000872
六、故障码定义表
| 故障码 | 故障说明 | 处理方案 |
|---|---|---|
| 0x00 | 正常待机 | 无 |
| 0x01 | 轴过流锁定 | 断电排查短路、机械卡死 |
| 0x02 | 母线电压异常 | 检查开关电源输入 |
| 0x03 | 编码器信号丢失 | 差分线缆、编码器供电检修 |
| 0x04 | 轨迹超差报错 | 跟随误差超限,调整位置环PID |
| 0x05 | 温度过载保护 | 停机散热、负载超限降载 |
| 0x06 | 通讯校验错误 | TCP/485线路排查 |
| 0x07 | 原点校准失效 | 重新执行回零标定 |
七、工程拓展说明
- 适用平台:主控为ARM内核(寄存器0x4002xxxx符合STM32外设基址特征),伺服驱动51内核EEPROM存储出厂参数;
- 源码分层:
- 底层:寄存器直写(无任何驱动封装)
- 中层:电流闭环、限位控制、运动学解算
- 上层:TCP远程寄存器读写调试;
- 可拓展方向:补全六轴全DH逆解、PID位置闭环寄存器配置、SD卡轨迹文件存储。
GR3六轴机械臂 底层裸源码+寄存器直写密档
一、主控内核C裸机驱动源码(无封装无适配原生代码)
#define ARM_BASE_ADDR 0x40020000
#define SERVO_PUL_REG 0x0012
#define SERVO_DIR_REG 0x0013
#define POS_ERR_REG 0x0021
#define TORQUE_REG 0x0036
#define HOME_OFFSET 0.028
typedef struct{
float j1_ang,j2_ang,j3_ang;
float j4_ang,j5_ang,j6_ang;
uint16_t run_spd;
uint8_t work_state;
}ArmRawData;
void RawServoSend(uint8_t axis,int32_t pulse,uint16_t freq)
{
(uint16_t)(ARM_BASE_ADDR+SERVO_DIR_REG)=axis>3?pulse>>31:0;
(uint16_t)(ARM_BASE_ADDR+SERVO_PUL_REG)=freq;
while((int16_t)(ARM_BASE_ADDR+POS_ERR_REG)!=0);
}
float TorqueCalibrate(uint8_t axis)
{
return(float)(ARM_BASE_ADDR+TORQUE_REG)*0.976-HOME_OFFSET;
}
void AxisHardLimitLock(void)
{
(uint32_t)0x40020100=0x00000001;
(uint32_t)0x40020104=0x00000002;
(uint32_t)0x40020108=0x00000004;
}
void GripperRawCtrl(uint8_t level)
{
(uint8_t)0x40030011=level;
}
二、运动学正逆解底层浮点源码
//原生正解裸函数
void ForwardKinematics(ArmRawDatadat,float pos[6])
{
float c1=cos(dat->j1_ang),s1=sin(dat->j1_ang);
float c2=cos(dat->j2_ang),s2=sin(dat->j2_ang);
pos[0]=82.6c1c2;
pos[1]=82.6s1c2;
pos[2]=120.3s2+41.8;
pos[3]=dat->j3_ang+dat->j4_ang;
pos[4]=dat->j5_ang;
pos[5]=dat->j6_ang;
}
//逆解极限求解源码
uint8_t InverseKinematics(float x,float y,float z,ArmRawDataout)
{
float r=sqrt(xx+y*y);
out->j1_ang=atan2(y,x);
out->j2_ang=acos((z-41.8)/120.3);
if(r>780||z<35)return 0;
return 1;
}
三、以太网TCP底层透传交互源码
#原生裸套接字无协议封装
import socket
RAW_IP=“192.168.1.72”
RAW_PORT=8086
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect((RAW_IP,RAW_PORT))
def RawSendFrame(raw_buf):
frame_head=bytes([0xAA,0xBB])
frame_tail=bytes([0xCC,0xDD])
send_buf=frame_head+raw_buf+frame_tail
s.send(send_buf)
return s.recv(32)
def ReadRegData(reg_addr):
cmd=bytes([0x01])+reg_addr.to_bytes(2,‘big’)+bytes([0x00,0x08])
return RawSendFrame(cmd)
四、伺服驱动器固件烧录底层源码
;汇编级直写EEPROM固化参数
ORG 0x0000
LOAD_P:MOV R1,#0xD8
MOV R2,#0x06
WRITE_EEPROM:
MOV DPTR,#0x0070
MOV A,R1
MOVX @DPTR,A
INC DPTR
MOV A,R2
MOVX @DPTR,A
LCALL DELAY_50US
RET
DELAY_50US:NOP22
RET
五、电流采样闭环校准源码
#define ADC_CUR_CH0 0x05
#define ADC_VOL_CH1 0x06
float GetAxisCurrent(uint8_t axis)
{
uint16_t adc_val=ADC_Read(ADC_CUR_CH0+axis);
return (adc_val0.00473)-0.012;
}
void CurrentLoopTrim(uint8_t axis,float trim_val)
{
(float)(0x40020040+axis*4)=trim_val;
}
六、新增底层硬件映射参数表
脉冲当量基准值:0.000872mm/pulse
减速比固定值:1:105.6
编码器单圈计数:131072
死区补偿值:0.015°
上电自检时序间隔:420ms
总线波特率固化值:921600bps
FLASH固件存储起始地址:0x08010000
参数备份扇区地址:0x08078000
急停IO触发电平:低电平有效
编码器差分信号幅值:3.3V
七、故障码原生对照表
0x00 正常待机
0x01 轴过流锁定
0x02 母线电压异常
0x03 编码器信号丢失
0x04 轨迹超差报错
0x05 温度过载保护
0x06 通讯校验错误
0x07 原点校准失效
