写字机器人——简易版demo全流程实现

基本思路:turtle画图——机械臂绘制

需要包含模块:
1、turtle绘图并记录坐标
2、坐标转换为机械臂角度
3、机械臂角度转为舵机的pwm值
4、将pwm值传入pca9685控制芯片,并执行

硬件部分:
需要3个舵机,分别为大臂、小臂、以及控制抬笔落笔(简称“抬落臂”,谐音“抬落笔”。。。)
在这里插入图片描述
核心计算:
1、坐标转为机械臂角度
2、角度转为pwm值(难)

(1)坐标转化为大、小臂的角度

灯灯大神图请添加图片描述

class Angle():
    def __init__(self,position):
        # 大臂与x正轴的夹角为alpha,小臂与大臂夹角为beta,根据终点坐标position,计算出夹角alpha和beta
        self.x,self.y = position
        # 大臂臂长OB,小臂BA
        self.OB = 50
        self.BA = 50
        self.OA = (self.x**2+self.y**2)**0.5
        # 机械臂默认只能向上绘画,且距离不应该超过大臂+小臂的长度之和
        assert self.y >= 0 and self.OA <= self.OB+self.BA,"the position out of the drawhand range!"
    # 计算大臂的alpha
    def get_alpha(self):
        # alpha = XOA-AOB,因此需要计算XOA和AOB
        XOA = atan(self.y/self.x)
        AOB = acos((self.OA**2+self.OB**2-self.AB**2)/(2*self.OA*self.OB))
        alpha = XOA-AOB
        return alpha
    # 计算小臂的beta
    def get_beta(self):
        # beta = 180-ABO,因此需要计算ABO
        ABO = acos((self.BA**2+self.OB**2-self.BA**2)/(2*self.BA*self.OB))
        beta = 180-ABO
        return beta
(2)角度转为pwm值

需要解决第1个问题:什么是pwm
请添加图片描述
PWM(Pulse Width Modulation),脉冲宽度调制,简称脉宽调制,即通过调整脉冲信号的宽度来实现对电机功率、舵机角度、灯的功率的调节

简单说,PWM值就是可以控制电路的数字。
原理:将角度转换为PWM值后,再传入pca9685中,在内部完成pwm值转为十六进制数,再将十六进制中的每一个数字转为二进制,存入对应的寄存器内。接着会将存储的二进制数据,用于控制高低电平电路,交给舵机去执行。

详细原理:
一般传入的pwm值,是一个数字,例如200,转化为十六进制就是,0xC8,并最终打包存入对应的寄存器后。寄存器内部再将0xC8转为二进制,则是11001000,根据之前提前设置的舵机频率(其实也就是pca9685根据舵机频率计算出这段pwm值的脉宽)
在这里插入图片描述
但以上解释都不重要,大概知道pwm值用于控制舵机运行即可。最重要的是下边:
pwm值和舵机角度之间为线性关系,一般0°到180°对应的pwm值为102到512。
一般会给舵机设置初始状态,初始状态可以设置为水平的如下图。
在这里插入图片描述
后续控制舵机进行一定角度的转动都需要基于该数值进行计算。
已知目标角度,计算相应的pwm的计算的公式(简称角度转pwm值)为:
object_pwm = init_pwm + angle*k
这个k值,是pwm值和舵机角度之间为线性关系的斜率,就相当于是,每度的单位pwm值,例如控制小臂舵机角度设置为5度,则转化为pwm值,则是init_pwm[2]+5*k。
一般按理说,相同规格的舵机,k值应该是一样的,一般0°到180°对应的pwm值为102到512,那么k值应该是(512-102)/180=2.73…
但是!!!使用这个k值去进行验证时,发现舵机转动的角度是不对的。
于是深刻怀疑,市面上的舵机不规范,没有统一标准的k值(也有可能pwm与角度的关系不是一元线性方程那样简单,但不管了)

所以,便独立对每个舵机进行测量运算,让每个舵机都转动90度后,记录下每个舵机对应的pwm值,然后计算出每个舵机的k值:pwm/90
最终得出了每个舵机的初始pwm值,以及k值
在这里插入图片描述
计算出的大小臂object_pwm保存在列表中,控制抬落臂的舵机,暂时不做改变。(简易版,是一笔画完,所以抬落臂只在开始画时落臂,在结束时抬臂)

class Pwm:
    def __init__(self,position):
        self.init_pwm = [330,185,390]
        self.k = [205/90,180/90,160/90]
        self.object_pwm = self.init_pwm
        self.leg = Angle(position)
    def get_pwm(self):
        leg_angles = self.leg.get_angles()
        # 计算控制大臂和小臂的舵机object_pwm
        for index in range(2):
            self.object_pwm[index+1] = self.init_pwm[index+1]+self.k[index]*leg_angles[index]
将转化好的pwm值,传入pca9685寄存器

这里涉及硬件简单的相关知识
首先是认识esp32主控芯片,和舵机之间,数据传输的途径。
首先,esp32是个主控芯片,但严格意义来说,不是类似于Arduino开发板。
作为硬件小白,简单认识esp32和Arduino的关系
esp32和Arduino的区别与关系

不重要,重要的是,esp32可以将计算出的pwm值,通过引脚传出去。
引脚是什么?就是esp32上的接线口,要将两根线分别接在SCL、和SDA引脚上,实现esp32与硬件的信息传递
在这里插入图片描述
在这里插入图片描述
这里简单了解几个硬件数据传输的概念,
I2C总线:I2C总线是由Philips公司开发的一种简单、双向二线制同步串行总线。它只需要两根线即可在连接于总线上的器件之间传送信息。
传送信息的两个线:SCL(控制线)、SDA(数据线)
在这里插入图片描述
在这里插入图片描述
硬件上,引脚连线SCL和SDA后,还要将I2C与pca9685进行绑定
这其中,esp32、I2C、引脚、pca9685、舵机之间的关系
硬件部分:
esp32是主控芯片,编程数据计算都在里边完成
引脚使用I2C总线,是通过两根线将esp32和舵机控制器pca9685,完成SCL和SDA的连接
pca9685与舵机连接,将PWM输出,控制舵机
编程部分:
I2C的实现,是通过machine库里,导入I2C、PIN两个模块

		
		from pca9685 import PCA9685
        from machine import Pin, I2C
        # 舵机控制
        self.i2c = I2C(sda=Pin(21),scl=Pin(22),freq=10000)  #i2c通讯,21和22是根据芯片的引脚标识所规定的
        self.pca = PCA9685(self.i2c)    #舵机对象(猜想:这里不是舵机对象,而是将i2c对象与pca9685进行绑定,由i2c向pca9685的对应寄存器写入数据)
        self.pca.freq(50)   #设置舵机的频率为50,这里的freq函数,主要用于设置pca9685内部的单位计时(详见pca9685.py解析)

舵机运行

舵机运行,是通过向pca9685的寄存器存储pwm值即可,这里只需要调用pca9685内的duty方法,传入pwm

    def servo_exc(self,servo_index,duty):
        '''传入舵机编号和pwm值(即duty-占空比)'''
        # self.pca.duty是传入十进制数值,而在pca的对象方法中,有self.pwm,自动转化数字为对应的十六进制,并自动写入pca9685的寄存器中
        # 每次在同一位置传入数据,该位置的寄存器都会更新。
        # duty中传入的舵机编号,是从0开始的,在pca9685的self.pwm方法中会自动根据编号,对应到相应的寄存器(寄存器内部不是从0开始,具体参见pca9685),
        self.pca.duty(servo_index,duty)

至此,整个写字机器人的核心部分拆分讲解已完成,开始根据模块组装重写代码
流程为:

turtle画图

#绘制并获取坐标

Paint.py——绘制简单的正方形

# 绘制简单的正方形
import turtle
pen = turtle.Pen()
for i in range(4): # 循环4次
	pen.forward(100) # 前进
	pen.left(360/4)	# 左转90度
turtle.done()

但要记录坐标,就不能这么简单了,需要将绘制的线段截取成一个个的点。
需要设置一个变量n,表示每次forward前进的线段,被分为n次获取坐标。再将获取的坐标,存入txt文档里。因此改变后的程序如下

# 绘制一个正方形,并将点坐标记录下来
import turtle
pen = turtle.Pen()
n = 30 # 人为设置
positions = [] # 存储坐标
# 绘制边长为100的正方形
for i in range(4):
    # 画长度为100的正方形单边
    for j in range(n):
        positions.append(pen.pos())
        pen.forward(100/n)
    pen.left(90)
# 坐标存入positions.txt文件中
with open("positions.txt", "w", encoding="utf-8") as file:
    file.write(str(positions))
turtle.done()
# 优化建议:根据线段长短,对n进行等比例的缩放,但是,暂时懒得搞了

机械臂绘制

# pos_to_angle——坐标转换成机械臂角度
# angle_to_pwm——角度转为舵机的pwm值
# esp32与pca9685的数据连接——I2C总线
# 将pwm值对应传入pca9685控制芯片中,舵机执行

calculate.py
将坐标转为角度,角度转为pwm放在calculate.py

from math import acos,atan
class Angle():
    def __init__(self):
        # 大臂臂长OB,小臂BA
        self.OB = 50
        self.BA = 50
        self.OA = self.OB+self.BA
        self.x,self.y = self.OA,0
        # 设置三个舵机的初始pwm值、k值属性、方向属性muti
        self.init_pwm = [330,185,390]
        self.k = [205/90,180/90,160/90]
        # 由于第2个舵机是反着装的,因此pwm值应当是与初始pwm相减,其实在pca9685的duty中有一个invert参数,可以很好的解决这个问题,但没用过
        self.multi = [1,1,-1]
        self.object_pwm = self.init_pwm
    # 数据预处理
    def pre_data(self,position):
        # 大臂与x正轴的夹角为alpha,小臂与大臂夹角为beta,根据终点坐标position,计算出夹角alpha和beta
        self.x, self.y = position
        self.OA = (self.x**2+self.y**2)**0.5
        # 机械臂默认只能向上绘画,且距离不应该超过大臂+小臂的长度之和
        assert self.y >= 0 and self.OA < self.OB+self.BA,"the position out of the drawhand range!"
    # 计算大臂的alpha
    def get_alpha(self):
        # alpha = XOA-AOB,因此需要计算XOA和AOB
        XOA = atan(self.y/self.x)
        AOB = acos((self.OA**2+self.OB**2-self.AB**2)/(2*self.OA*self.OB))
        alpha = XOA-AOB
        return alpha
    # 计算小臂的beta
    def get_beta(self):
        # beta = 180-ABO,因此需要计算ABO
        ABO = acos((self.BA**2+self.OB**2-self.BA**2)/(2*self.BA*self.OB))
        beta = 180-ABO
        return beta
    # 同时获取alpha和beta
    def get_angles(self,position):
        self.pre_data(position) # 对坐标数据进行预处理,算出self.OA值
        alpha = self.get_alpha()
        beta = self.get_beta()
        return alpha,beta
    # 获取舵机的最终pwm值
    def get_pwms(self,position):
        leg_angles = self.get_angles(position)
        # 计算控制大臂和小臂的舵机object_pwm,先暂时不管第一个舵机的pwm值
        for index in range(2):
            self.object_pwm[index+1] = self.init_pwm[index+1]+self.k[index]*leg_angles[index]*self.multi[index+1]
        return self.object_pwm

servo.py——伺服,可以说是I2C总线,对esp32与pca9685的连接,与执行

from pca9685 import PCA9685
from machine import Pin, I2C
class Servo:
    def __init__(self):
        # 舵机控制
        self.i2c = I2C(sda=Pin(21),scl=Pin(22),freq=10000)  #i2c通讯,21和22是根据芯片的引脚标识所规定的
        self.pca = PCA9685(self.i2c)    #舵机对象(猜想:这里不是舵机对象,而是将i2c对象与pca9685进行绑定,由i2c向pca9685的对应寄存器写入数据)
        self.pca.freq(50)   #设置舵机的频率为50,这里的freq函数,主要用于设置pca9685内部的单位计时(详见pca9685.py解析)
    # 传入寄存器后,舵机执行    
    def servo_exc(self,servo_index,duty):
        '''传入舵机编号和pwm值(即duty-占空比)'''
        # self.pca.duty是传入十进制数值,而在pca的对象方法中,有self.pwm,自动转化数字为对应的十六进制,并自动写入pca9685的寄存器中
        # 每次在同一位置传入数据,该位置的寄存器都会更新。
        # duty中传入的舵机编号,是从0开始的,在pca9685的self.pwm方法中会自动根据编号,对应到相应的寄存器(寄存器内部不是从0开始,具体参见pca9685),
        self.pca.duty(servo_index,duty)

main.py——将各个py文件综合组装

from calculate_topwn import *
from servo import *
import time
import Paint
hand = Angle()
servo = Servo()
if __name__=="__main__":
    with open("positions.txt", "r", encoding="utf-8") as file:
        text = file.read()
    positions = eval(text)  # 字符串转为列表
    for pos in positions:  # 落笔到抬笔的所有坐标
        pwms = hand.get_pwms(pos)
        # 两个舵机的角度转pwm
        for index in range(1,3):
            servo.servo_exc(index, pwms[index])
            # 0号舵机放下
            servo.servo_exc(0, pwms[0])
        time.sleep(0.05)
    # 0号舵机抬起
    servo.servo_exc(0, pwms[0] + 30)
    time.sleep(0.5)

整个demo的程序部分完成,但因是学习他人代码,并经过颅内高潮,不曾验证。仅做笔记

文档学习写字机安装还在学习中1、产品概览 AX4写字机框架采用2020工业铝型材,配合5MM亚克力面板,XY轴导向采用8mm直径光轴配合直线轴承运动,上下抬笔机构采用微型直线导轨系统。本套件XY轴行程297×210mm,标配42步进电机,SG90舵机,建议运行速度一分钟3000mm。本套件采用开源Arduino系统,配合相关软件可在纸质材料上写字画图等。本套件含包装重量3KG左右,纸箱外形尺寸57×21×6cm。本套件为USB接口,连接电脑才能工作,建议WIN7系统。 2、零部件明细 (1)机器框架 8mm光杆 450mm 2条、360mm 2条 2020欧标铝型材 421mm 1条、68mm 4条 LM8UU直线轴承 8个 亚克力 25片 F624法兰轴承 10个 同步轮 2个 同步带 1.5米 3D笔架 1个 扎带 25条 电线固定带 2条 直线导轨组件 1套 (2)电器控制 42步进电机2个 Arduino控制器1套 SG90舵机1个 电源1个 USB线1条 (3)螺丝清单 M2×6 十字螺丝=4 滑台 M2×10 十字螺丝=2 舵机 M2×16 十字螺丝=2 直线导轨 M3×8 十字螺丝=8 步进电机 M3×12 十字螺丝=8 亚克力固定 M3×20 十字螺丝=17 光轴固定+限位 M3×30 十字螺丝=4 电路板固定 M4×12 十字螺丝=8 光轴紧定 M4×30 十字螺丝=1 后滑轮 M4×25 手拧螺丝=1 笔架 M4×40 十字螺丝=4 十字滑轮 M5×10 十字螺丝=8 亚克力与铝型材 M6×12 十字螺丝=6 铝型材 M2 六角螺母=4 M3 六角螺母=29 M4 六角螺母=5 M4 方块螺母=9 M5 铝型材螺母=8 φ3×20 塑料隔离柱=4 φ4×2 塑料隔离柱=2 φ4×7 塑料隔离柱=8 φ3×20 塑料隔离柱=4(激光头配套) M3×30十字螺丝=4(激光头配套) 3、机架组装 (1)组装十字滑台
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值