不会吧不会吧,不会还有人不知道SmallArmRobot机械臂的吧~

不过不知道也没关系,看完了图片,你会心动的!

看起来是一个相当帅气的桌面级机械臂,而且还是步进电机的机械臂。这个机械臂和古月大大的Anno机械臂觉得有的一拼,所以你懂得~

话不多说,先看效果!!!

看起来效果是相当的nice!这里的模型功能包是我使用雪铭大佬的资料,大家可以古月学院找到相关的课程内容,这个课程还是免费的哦~传送门

当然啦,我肯定不会只是甘心来玩玩仿真的,要搞就搞一波大的,撸起袖子就是造!

这一大堆的飞线请自动忽略,还有就是不要问我为啥视频是快手的,俺毕竟能力有限,剪辑视频的活还搞不来~

咱们现在就开始来造这个SmallArmRobot机械臂!

造这个机械臂首先肯定要有实物,这个机械臂的模型在网上大家是可以下载。我在这里为大家提供百度网盘的下载链接。https://pan.baidu.com/s/1nIB4dy-eBiX7VpYNtvFQFA提取码:a7mk --来自百度网盘超级会员V4的分享

大家下载知乎可以通过3D打印的方式来做这个机械臂,同时你还需要准备一大堆型号超多的螺丝,工具什么的也要准备好,还有就是准备六个步进电机,2个57步进电机、1个42步进电机、2个28步进电机和1个20步进电机。组装的教程及相关的BOM表,都在链接里面为大家提供。

当然,大家也可以小资一把,直接从咸鱼采购一个机械臂实物,现在咸鱼的价格差不多是在600到800之间,只有机械臂主体就可以的,驱动板我是重新设计了的(开源大佬提供的使用有些难受)。有条件的小伙伴可以把侧板和底板换成不锈钢板,看起来不仅更帅气,而且也增加了配重和稳定性。有的大佬做的是这样的,我看了着实羡慕~

反正不管怎么样,搞个机械臂实物出来就对了!SmallArmRobot机械臂的成本相对来说还是比较搞的,毕竟六个步进电机已经给摆在这了,3D打印件更是一大堆~

然后就是我们的驱动板,开源大大们使用的是Arduino Mega的3D打印机的板子,各式各样的的飞线,虽然有外壳,但是我个人觉得还是比较乱,所以我将整个驱动板进行了全新的设计~

还是使用Arduino Mega2560开发板,做了一块扣在上面的扩展板。电机驱动使用了3个DRV8825和3个TB6600,DRV8805驱动的小电机的位置,我将控制细分的引脚通过Arduino Mega的IO口进行控制,实现软件控制细分的效果(TB6600我也想这样玩,但是总不能把TB6600拆开飞出来几条线吧~)

DRV8825驱动这里,我将EN引脚通过10K的电阻进行上拉,EN默认一直会处于高电平状态,同时我将细分引脚(M0、M1、M2)通过10K的电阻进行下拉,细分引脚一直会默认处于低电平状态,在12V和GNG之间,使用了一个35V 100uF电容和一个0.1uF电容进行并联进行滤波,提升电机驱动电压的稳定,在5V个GND之间使用了一个0.1uF的电容进行滤波,保证模块信号电压稳定。其余引脚全部引出。

这里为大家提供DRV8825细分表一份。

TB6600的驱动相对就简单了的,只是简单的引出现即可。这里,也是大家刚刚看到一大堆飞线的位置(肯定比他们的少)。

这里是我们的供电电路,机械臂工作电压为12V,这是我使用了XL4016的IC进行12V转5V 8A的功能。别问我为啥留的5V 8A的,比较Jetson Nano彻底跑起来都差不多5V 5A,要是这时候再有个来充手机电的,我觉得5V 8A很有必要!

这里使用了一个5.5-2.1的DC头作为12V供电接口,接开关电源的输出。在12V位置使用470uF和1uF电容并联的方式进行滤波;VC是内测电压调整,这里哦我们使用个1uF接到12V即可;FB是分压反馈引脚,检测当前的输出电压并进行调整;SW为功率输出引脚。

在SW功率输出后,我们需要使用一个快恢复二极管20100、一个47uH电感和一个1000uF的电容进行高频滤波处理,1uF电容作为输出滤波。

10K电阻和3.3K电阻作为分压检测使用,FB的检测电压是1.25V,10K电阻接5V,3.3K电阻接GND,则两电阻之间和GND的电压差值为5V*3.3/(3.3+10)≈1.25V(1:4的比例),当输出电压不足或者过高的情况下,XL4016会自动进行调整。

在使用的当中,有很多IO口并没有使用到,我们将按照3P的接线模式(S、V、G,信号线、供电正极、供电负极)预留出来,便于外接其他的传感器,比如气泵吸盘、舵机等等。

同时串口、IIC也均以4P的接线模式留出,可用于外接其他外界设备。

驱动板也预留出了两个USB供电接口,可以给大家的手机、充电宝等等充电。

我们所有IO相关的引脚功能如下(怕上面的图大家看不清楚)

很舒服,把模拟口、PWM和功能引脚更留出来了的,虽然SPI没能留出实在是有点遗憾。引脚功能相对比较工整,那么PCB布线的难度实在是一把辛酸史~

尽量保证布局的工整,留出了5个没有用到的IO口,大家可以再机械臂的末端介个气泵之类的控制啥的,两片铜皮是5V和GND,需要用5V供电直接焊上去。PCB留出了复位引脚,这里你可以接一个按键,当机械臂抽风(其实是你的代码写的抽风)的时候,直接让Arduino Mega进行复位。两个USB给Jetson Nano或者树莓派供电就很nice,给手机充电也是问题不大。

在供电的部分,我是使用了一个12V 8A的UPS模块,自己做了一块12V 3S的电池,六个步进电机功率实在是有点大,不过应该不会超过100W,12V 8A应该是够用的。

大家将机械臂和驱动板连接好之后,就可以开始程序的测试,先在Arduino写代码,测试每个电机运行是否存在问题,没有问题的话,就开始开发我们的ROS机械臂。

ROS的部位我们有三个功能包,分别是smallarmrobot_description模型功能包、smallarmrobot_moveit_config功能包、smallarmrobot_driver驱动功能包组成。其中smallarmrobot_description功能包是从雪铭大佬的的leaf_description修改得到的,大家可以自己在古月居搜寻一下。而smallarmrobot_moveit_config功能包是通过moveit_setup_assistant配置得到,这里不熟悉的同学可以选择古月学院下的《MoveIt可视化配置及仿真指南》教程进行学习,这里我不做过多的介绍。

在ROS的机械臂开发当中,关于机械臂关节角度的数值是在joint_stats话题的position数据当中,通过订阅joint_state话题的的position数据获取机械臂当前各个关节的弧度制,将其转换为角度值,并驱动步进电机执行到对应的位置即可。具体代码如下。

#!/usr/bin/env python
#coding:utf-8

#导入rospy库
import rospy

#joint_state的msg是属于sensor
from sensor_msgs.msg import JointState

#调用StandardFirmata协议
from pyfirmata import ArduinoMega,util

#导入时间函数
import time

#导入IO配置函数,自定义
from IO_config import *

#步进电机驱动引脚声明
Joint_STEP = [Joint1_STEP,Joint2_STEP,Joint3_STEP,Joint4_STEP,Joint5_STEP,Joint6_STEP]  #脉冲引脚声明
Joint_DIR = [Joint1_DIR,Joint2_DIR,Joint3_DIR,Joint4_DIR,Joint5_DIR,Joint6_DIR] #方向引脚声明
Joint_EN = [Joint1_EN,Joint2_EN,Joint3_EN,Joint4_EN,Joint5_EN,Joint6_EN]    #使能驱动引脚声明

#减速比系数声明,这里有减速带,需要重新计算
joint_pro = [joint1_pro,joint2_pro,joint3_pro,joint4_pro,joint5_pro,joint6_pro] 

#标记量声明
joint_pul_flag = [joint1_pul_flag,joint2_pul_flag,joint3_pul_flag,joint4_pul_flag,joint5_pul_flag,joint6_pul_flag]  #脉冲标记
joint_dir_flag = [joint1_dir_flag,joint2_dir_flag,joint3_dir_flag,joint4_dir_flag,joint5_dir_flag,joint6_dir_flag]  #方向标记
joint_value = [joint1_value,joint2_value,joint3_value,joint4_value,joint5_value,joint6_value]   #当前角度值

#脉冲限位
joint_min_pul = [joint1_min_pul,joint2_min_pul,joint3_min_pul,joint4_min_pul,joint5_min_pul,joint6_min_pul] #最小脉冲值
joint_max_pul = [joint1_max_pul,joint2_max_pul,joint3_max_pul,joint4_max_pul,joint5_max_pul,joint6_max_pul] #最大脉冲值

#通过StandardFirmata协议来实例化一个ArduinoMega的对象board,传入参数端口号和波特率,默认8N1模式
board = ArduinoMega("/dev/ttyUSB0",baudrate=115200)

#使能驱动处于低电平
board.digital[Joint1_EN].write(0)
board.digital[Joint2_EN].write(0)
board.digital[Joint3_EN].write(0)
board.digital[Joint4_EN].write(0)
board.digital[Joint5_EN].write(0)
board.digital[Joint6_EN].write(0)

'''
函数名称:value_driver
函数功能:控制对应的关节旋转到指定的角度值
输入参数:joint为关节编号,angle为该关节目标角度值
'''
def value_driver(joint,angle):
    #如果当前的脉冲迭代超出可运行范围,步进电机不执行,输出提示信息。这里是为了避免过转导致机械臂卡住
    if joint_pul_flag[joint] < joint_min_pul[joint] - 30 or joint_pul_flag[joint] > joint_max_pul[joint] + 30:
        print("ERROR.Beyond the limit...")
    else:
        diff_value = 0  #角度差值变量声明
        step = 0    #需要的部署变量声明
        
        #如果目标角度angle小于当前角度值
        if angle < joint_value[joint]:     
            joint_dir_flag[joint] = 0   #对应关节方向标记为0
            board.digital[Joint_DIR[joint]].write(0)    #方向引脚为低电平
            diff_value = joint_value[joint] - angle #计算角度差值
            print("角度值减小.") #输出提示信息
        else:
            joint_dir_flag[joint] = 1   #对应关节方向标记为1
            board.digital[Joint_DIR[joint]].write(1)    #方向引脚为高电平
            diff_value = angle - joint_value[joint] #计算角度差值
            print("角度值增加.") #输出提示信息
        print("角度差值为:" + str(diff_value))   #输出计算好的角度差值
        
        step = int(diff_value/joint_pro[joint]) #步数=角度值/减速比
        print("需要脉冲数量:" + str(step) + ".")  #输出提示信息,需要的脉冲数量
        
        #发送指定数量的脉冲进行驱动执行
        while step > 0:
            board.digital[Joint_STEP[joint]].write(1)
            time.sleep(0.01)
            board.digital[Joint_STEP[joint]].write(0)
            time.sleep(0.01)
            step = step - 1
        print("脉冲执行完毕.")    #输出提示信息
        
        step = int(diff_value/joint_pro[joint])
        if joint_dir_flag[joint] == 0:
            joint_pul_flag[joint] = joint_pul_flag[joint] - step    #更新当前脉冲标识
        else:
            joint_pul_flag[joint] = joint_pul_flag[joint] + step
        print("脉冲计数迭代完毕.当前脉冲数为" + str(joint_pul_flag[joint]) + ".") #输出提示信息,脉冲累计量
        
        joint_value[joint] = angle  #角度值迭代
        print("角度迭代完毕.当前角度为" + str(joint_value[joint]) + ".")   #输出提示信息,当前角度值

'''
函数名称:callback
函数功能:作为回调函数执行
'''		
def callback(data):
	joint6_angle = data.position[0]*360/6.28+90 #对应的关节弧度值转角度值值转
    #判断当前角度值是否越界
	if joint6_angle<0:
        #如果当前角度值小于0,则角度值为0,避免出现负数
		joint6_angle = 0   
	elif joint6_angle>180:
        #如果当前角度值大于180,则角度值为180,避免出现角度超出
		joint6_angle = 180
	rospy.loginfo(rospy.get_caller_id() + ']--->Joint6 Angle :%d', joint6_angle)    #ros下输出提示信息
	#value_driver(5,joint6_angle)   #角度值执行
    
    #下同
	joint5_angle = data.position[1]*360/6.28+90
	if joint5_angle<0:
		joint5_angle = 0
	elif joint5_angle>180:
		joint5_angle = 180
	rospy.loginfo(rospy.get_caller_id() + ']--->Joint5 Angle :%d', joint5_angle)
	value_driver(4,joint5_angle)

	joint4_angle = data.position[2]*360/6.28+90
	if joint4_angle<0:
		joint4_angle = 0
	elif joint4_angle>180:
		joint4_angle = 180
	rospy.loginfo(rospy.get_caller_id() + ']--->Joint4 Angle :%d', joint4_angle)
	value_driver(3,joint4_angle)

	joint3_angle = data.position[3]*360/6.28+90
	if joint3_angle<0:
		joint3_angle = 0
	elif joint3_angle>180:
		joint3_angle = 180
	rospy.loginfo(rospy.get_caller_id() + ']--->Joint3 Angle :%d', joint3_angle)
	value_driver(2,joint3_angle)

	joint2_angle = data.position[4]*360/6.28+90
	if joint2_angle<0:
		joint2_angle = 0
	elif joint2_angle>180:
		joint2_angle = 180
	rospy.loginfo(rospy.get_caller_id() + ']--->Joint2 Angle :%d', joint2_angle)
	value_driver(1,joint2_angle)

	joint1_angle = data.position[5]*360/6.28+90
	if joint1_angle<0:
		joint1_angle = 0
	elif joint1_angle>180:
		joint1_angle = 180
	rospy.loginfo(rospy.get_caller_id() + ']--->Joint1 Angle :%d', joint1_angle)
	value_driver(0,joint1_angle)
	
def driver():
	rospy.init_node('SmallArmRobot_Driver', anonymous=True) #初始化节点,命名为SmallArmRobot_Driver
	rospy.Subscriber('joint_states', JointState, callback)  #订阅joint_states话题,类型为JointState,当订阅到该话题执行callback函数
	rospy.spin()

if __name__ == '__main__':
	driver()
board.exit()

这里需要注意的是关于value_driver函数功能的理解。

这是我之前在验证的时候所截图的数据,value_dirver的作用就是将步进电机处理成舵机的方式,给角度值就转动到指定的角度值。目前我设计的算法存在的误差很大,而且细分引脚的动态切换效果也没有开发出来,这也是下一阶段我要完成的任务。大家也可以去浏览下SmallArmRobot官方提供的驱动代码,精度还是较高的(俺比较菜,没咋看懂)。

# -*- coding: utf-8 -*-
"""
Created on Fri Oct 16 19:37:56 2020

@author: 嘉
"""

''''''''''''''''''''''''
'Arduino Mega引脚IO声明'
''''''''''''''''''''''''
Joint1_DIR = 51
Joint1_STEP = 50
Joint1_MS3 = 49
Joint1_MS2 = 48
Joint1_MS1 = 47
Joint1_EN = 46

Joint2_DIR = 45
Joint2_STEP = 44
Joint2_MS3 = 43
Joint2_MS2 = 42
Joint2_MS1 = 41
Joint2_EN = 40

Joint3_EN = 39
Joint3_MS1 = 38
Joint3_MS2 = 37
Joint3_MS3 = 36
Joint3_STEP = 35
Joint3_DIR = 34

Joint4_EN = 32
Joint4_DIR = 31
Joint4_STEP = 30

Joint5_EN = 28
Joint5_DIR = 27
Joint5_STEP = 26

Joint6_EN = 24
Joint6_DIR = 23
Joint6_STEP = 22

''''''''''''''
'减速比例系数'
''''''''''''''
joint1_pro = 1.8
joint2_pro = 0.8
joint3_pro = 0.6
joint4_pro = 0.375
joint5_pro = 0.42
joint6_pro = 0.375

''''''''''''''
'标记变量声明'
''''''''''''''
joint1_pul_flag = 0
joint1_dir_flag = 0
joint1_value = 0

joint2_pul_flag = 105
joint2_dir_flag = 0
joint2_value = 90

joint3_pul_flag = 150
joint3_dir_flag = 0
joint3_value = 90

joint4_pul_flag = 230
joint4_dir_flag = 0
joint4_value = 90

joint5_pul_flag = 210
joint5_dir_flag = 0
joint5_value = 90

joint6_pul_flag = 240
joint6_dir_flag = 0
joint6_value = 90

''''''''''''''
'限位标记声明'
''''''''''''''
joint1_min_pul = 0
joint1_max_pul = 200

joint2_min_pul = 0
joint2_max_pul = 210

joint3_min_pul = 0
joint3_max_pul = 300

joint4_min_pul = 0
joint4_max_pul = 460

joint5_min_pul = 0
joint5_max_pul = 420

joint6_min_pul = 0
joint6_max_pul = 480

这个位置是我们的IO_config文件的内容,主要是一些参数的定义。里面涉及到的减速比的计算大家可以推算一下,其实就是一个脉冲在当前的减速履带下可以让机械臂转到多少度,可能一个脉冲看不出角度。我是让对应关节分别在0度、90度、180度等情况下,统计需要多少个脉冲,做了一个除法来获取了减速比。步数=角度值/减速比

大家可以来运行下smallarmrobot_description模型功能包,通过进度条来控制SmallArmRobot的各个关节,也可以在smallarmrobot_moveit_config功能包的demo.launch下来体验一下运动规划,大牛们也可以来自己使用moveit编程接口来控制机械臂~

欢迎大家就步进电机控制的算法做出自己的设计,也欢迎大家在已有的基础上进行自己的创意开发。共同学习,共同进步!

相关资料可通过一下链接下载,链接失效请联系古月居公众号!

链接:https://pan.baidu.com/s/1cEUfMHaa4bICSXhsn6B4xg 
提取码:b647 
--来自百度网盘超级会员V4的分享