上一节我们说到了机械臂的关节与关节舵机驱动程序的基本原理;这一节我们将使用STM32单片机来实现机械臂运动的编程。

  

0. 本节基本内容

    本节主要介绍了如何利用单片机蓝牙-串口中断控制6路舵机转角;具体涉及的技术点为:

  1. 配置STM32单片机的Timer3与Timer8比较输出
  2. 配置STM32的串口中断,基于中断传输帧格视的数据
  3. 配置HC-05蓝牙,并与笔记本上的蓝牙调试器进行配对
  4. 使用笔记本电脑通过蓝牙控制机械臂执行一些固定动作

    本节的操作需要准备以下硬件:

  1.  一台机械臂
  2. 一个机械臂的驱动小半
  3. 蓝牙HC-05模块
  4. 串口转USB小板(建议用CH340)
  5. 一台带有蓝牙功能的笔记本电脑(或者外接一个蓝牙)

  (本教程中涉及到的这些小工具和硬件,可以通过文末的联系方式  联系我来要链接)

1. 写单片机程序的准备工作

    首先在我的舵机主控板上,舵机对应的单片机IO口如下:

   

    也就是说,前四个舵机对应着Timer3的四个通道,后四个舵机对应着Timer8的四个通道。

    为了使我们的电脑与单片机进行通信,我们还需要用到串口;在这里我们使用单片机的串口4(UART4),电路连接如图所示,其中TX对应PC10引脚,RX对应PC11引脚:

   

    我们现在配置单片机的工程,具体的配置方法可以参考我之前的教程,或者点击这个链接下载现成的工程文件。

    在这个工程中,我们需要完成两个重要的驱动文件,第一个是舵机的驱动(servo.c),第二个是串口的驱动(usart.c)

    2. 舵机驱动

     舵机驱动在上一章讲过,这次我们来细化一下

    首先,既然利用了Timer3与Timer8,我们首先就把这两个Timer的初始化过程封装成一个函数:

void Servo_Init(void)
{
	// 初始化PWM定时器
	TIM3_Init();  // 在这个函数里添加上一章初始化Timer3的代码  
	TIM8_Init();  //在这个函数里添加上一章初始化Timer8的代码
}

    然后,我们再来封装一个驱动机械臂各个舵机的函数,名字就起为void Set_Servo_PWM(uint8_t channel, uint16_t pwm),其中channel表时要控制第几个舵机,pwm表示要对这路舵机输出的占空比:

void Set_Servo_PWM(uint8_t channel, uint16_t pwm)
{
	TIM_TypeDef *tim;
	
	if(channel > 8)
	{
		return;
	}
	if(pwm > 2500)
	{
		return;
	}
	else if(pwm < 500)
	{
		return;
	}
	
	if(channel > 4)
	{
		tim = TIM8;
		channel -= 4;
	}
	else
	{
		tim = TIM3;
	}
	
	switch(channel)
	{
		case 1: tim->CCR1 = pwm; break;
		case 2: tim->CCR2 = pwm; break;
		case 3: tim->CCR3 = pwm; break;
		case 4: tim->CCR4 = pwm; break;
		default:
			break;
	}
}

    接下来,我们在main函数中添加一系列固定的动作状态,看看舵机能不能按照我们预定义的动作来运动:

    

while(1)
	{
		for(i=4;i>0;i--)
		{
			Set_Servo_PWM(i,1500);
			delay_ms(200);
		}
		USART_SendData(UART4,'a'); //Debug
		for(i=4;i>0;i--)
		{
			Set_Servo_PWM(i,1300);
			delay_ms(200);
		}
		USART_SendData(UART4,'b'); //Debug
	} 

  执行完这些操作后,通电,我们就能够看到一个妖娆且抖动的机械臂

3. 使用蓝牙-串口将机械臂无线连接到电脑

3.1 配置蓝牙芯片

    我们使用HC-05的蓝牙芯片来进行与电脑之间的通讯,蓝牙芯片共有VCC,GND,RX,TX四个引脚,其中RX和TX应当分别连接单片机串口的TX和RX:

    蓝牙RX----单片机TX       蓝牙TX----单片机RX

    在使用之前,我们首先对蓝牙进行一些基本的配置,具体操作为把蓝牙芯片通过USB-TTL串口转接小班连接到电脑。 同时我这里有个HC-05的一键配置软件,通过这个软件对蓝牙进行基本的配置(比如配置蓝牙名称,波特率等等),详细的配置方法请参考:外部链接

3.2 配置单片机和电脑端

    蓝牙配置好后便相当于透传模块(可以理解为此时蓝牙就是一台无线的USB-TTL转接小板);我们打开电脑(以Win10为例),可以在电脑端看到蓝牙芯片的名字;连接这个蓝牙后,在蓝牙选项里查看设备对应的COM口,这个COM就是电脑与机械臂的通讯端口。

4. 基于串口数据协议实现舵机控制协议的传输

    关于串口通讯的基本理论可看我以前的教程

    因为舵机的控制还是相对简单的,所以我们封装的数据也可以简单一些;我们统一一个数据包的长度为8个字节;其中两个字节为包头部,都用0xff来标识;而后六个字节就是机械臂每个关节所处的角度,127代表正中心,0和255分别代表极限值。当然这种方法控制的精度不是很高,但是对于一个脑电/肌电驱动的机械臂,已经是很够用了。

    上位机下发的数据如下:

地址偏移 0 1 2 3 4 5 6 7
字节内容 0xff(固定值) 0xff(固定值) 第一个舵机角度 。。。 。。。 。。。 。。。 第六个舵机角度

    所以,我们来在单片机的串口中断里每读取8个字节后进行一次处理,即可实现对机械臂控制相关的通讯。

void UART4_IRQHandler()
{
	static uint8_t temp[8];
	static uint8_t addr = 0;
	if(USART_GetITStatus(UART4, USART_IT_RXNE)!=RESET)
	{
		temp[addr]=USART_ReceiveData(UART4);
		addr++;
		if(addr>=8)
		{
			/*开始进行一个数据包的处理*/
			if((temp[0]==0xff)&&(temp[1]==0xff))
			{
				Set_Servo_PWM(1,temp[2]*7+611);   //按比例调整舵机参数(127对应1500,0对应611,255对应2396)
				Set_Servo_PWM(2,temp[3]*7+611);
				Set_Servo_PWM(3,temp[4]*7+611);
				Set_Servo_PWM(4,temp[5]*7+611);
				Set_Servo_PWM(5,(255-temp[6])*7+611);		//因为我的5号舵机装反了,所以修改一下方向
				Set_Servo_PWM(6,temp[7]*7+611);
				USART_SendData(UART4,VV);			//VV是一个自定义的反馈数据
			}
			else
			{
				USART_SendData(UART4,0xff);
			}
			addr=0;		//等待接收下一个数据包
		}
	}
}

5. 写一个简单的上位机实现电脑控制机械臂运动

    接下来我们可以按照制定的协议来控制机械臂的运动,用一个Python脚本即可,脚本的核心是一个Python的类,使用这个类可以向机械臂发送其运动相关指令。

import serial
import numpy as np
import threading
import time
class Serial_Arm:
    def __init__(self,port='COM9'):
        self.ser=serial.Serial(port,115200,timeout=3.0)
        self.vals=np.ones(8,dtype=np.uint8)*127
        self.vals[0] = 255
        self.vals[1] = 255
        self.volt=1.0
    def Set_Arm_Values(self,dat):
        if(len(dat))!=6:
            print('机械臂关节数量输入错误!')
            return
        vals=[]
        for i in dat:
            val = int(i * 127 / np.pi + 127)
            if val < 0: val = 0
            if val > 255: val = 255
            vals.append(val)
        self.vals[2:8]=vals
        pPush=bytes(self.vals)
        self.ser.write(pPush)
        self.volt=ord(self.ser.read(1))

    def Set_Arm_Joint(self,joint,val):
        val=int(val*127/np.pi+127)
        if val<0:val=0
        if val>255:val=255
        self.vals[joint+2]=val
        print(self.vals)
        pPush = bytes(list(self.vals))
        print(pPush)
        self.ser.write(pPush)
        self.volt = ord(self.ser.read(1))
        print(self.volt)
if __name__=='__main__':
    s=Serial_Arm()




    然后使用Pygame配合键盘,机械臂就动起来了~

    下面是一个我让机械臂按电脑播放音频节奏抖动的小视频~

https://www.bilibili.com/video/BV1VT4y1d7cG?t=0.0

本章小结

    本小节主要介绍了机械臂控制的相关原理性代码,上位机-下位机通讯方案,基于本小节的内容,底层的硬件相关内容已经基本可以完成;下一节我们将开始人体肌肉电/脑电的采集和分析介绍,以及脑控机械臂仿真环境的搭建。

联系作者