前言
近期回校上最后一门课,刚好是做机械臂有关的题目,所以写文记录一下。主要实现的是可以自动识别获取快递位置,机械臂可以抓取快递,以及根据自动识别快递上的条形码获得目的地点,机械臂可以将快递抓取并移动到目的地点,此外就是还可以通过上位机来控制机械臂,上位机可以是PC,也可以是另一部STM32,需要有图形化界面。
题目分析
自动识别获取快递位置以及识别快递上的条形码需要用到摄像头模块,需要解决STM32和摄像头模块之间的通信问题,以及快递形状。通过上位机来控制机械臂则需要使用到蓝牙或者WIFI模块,图形化界面可以使用物联网云平台来开发,这里用的是阿里云。如果使用另一部32进行控制,则还可在另一部32上做界面。
但购买的机械臂是已经组装完毕的,且上面带有STM32系统板,型号为STM32F103C8T6,只引出了串口3的接口,也就是说如果要连接摄像头模块,就无法与上位机进行通信。所以我打算将该机械臂作为一个下位机来接收命令,上位机采用另一部STM32来做,上位机来处理串口接收到的来自云平台或者摄像头的数据,然后算出各个舵机的PWM占空比重装载值,通过蓝牙模块发送到下位机机械臂,以此进行控制。
之前的博文已经完成了机械臂控制程序(下位机)的编写,并通过蓝牙以及串口调试工具能实现对机械臂的控制,本篇主要是完成上位机程序的编写,使机械臂可以接收来自上位机的指令并做出相应的动作,方法还是采用串口通信,使用蓝牙模块连接,然后就是可以让这个上位机能连接阿里云平台,这样就可以在阿里云平台上实时看到各舵机的信息,以及发出控制指令,做出控制。
之前博文的传送门如下
【STM32实战】机械臂快递分拣系统(一)——机械臂控制程序(下位机)编写
由于需要用到蓝牙模块,先介绍一下怎么将两个蓝牙模块配对。
蓝牙模块的使用
蓝牙模块就是一种透传设备,串口给它发送什么,它就接收什么,然后它将接收到的数据通过某种协议发送到跟它配对的蓝牙上,这个蓝牙将这些数据解析,最后还原成最初的未经处理的数据,这就是透明传输意思。这跟使用杜邦线将两个模块串口的RT对接是一样的。
推荐购买的蓝牙模块是这种带有按键的,按下按键然后接到电脑(通电)即可进入AT模式,设置蓝牙配对。
配对方法也很简单,接CH340模块(需要注意RT对接),在按键按下的状态,将CH340接入电脑USB口,即可让蓝牙进入AT模式,一般此模式下蓝牙模块的波特率为38400,在串口工具上发送命令AT,勾选发送新行,即可看到窗口返回OK。
然后可以使用AT命令设置蓝牙名称AT+NAME=Bluetooth-Marster,然后点击发送,这里设置名称为Bluetooth-Marster,另一个蓝牙的名称可以设置为Bluetooth-Slave。
然后设置蓝牙配对码AT+PSWD=0425,然后点击发送,这里设置为0425,另一个蓝牙的配对码也要是这个,才能成功配对。
然后设置工作模式为主机模式,命令是AT+ROLE=1,然后点击发送,另一个蓝牙需要设置为从机模式,命令就是AT+ROLE=0
然后配置蓝牙串口参数AT+UART=115200,0,0,然后点击发送即可,两个蓝牙都要设置一样。
然后设置一下模式为无需地址绑定模式,这样只要配对码一样就能配对成功了。命令AT+CMODE=1,然后发送即可。
蓝牙模块主机这就配置完毕了,接下来配置从机,也就是上面所说的另一个蓝牙。
这里因为我的两个蓝牙不是一样的,所有前面报错。这个蓝牙设置配对码需要加上冒号,因此推荐买蓝牙模块的时候最好是买一样的。
然后设置为从机模式
设置波特率115200
设置任意地址绑定模式
然后就将两个CH340模块拔掉再插入电脑,即可在串口工具上测试蓝牙能否配对成功了。
需要注意的是现在的波特率就不是38400了。
这里发送111,可以看到另一个蓝牙成功接收到了,说明配对成功了。
上位机程序的编写
这里提供一下工程模板源码、各类需要用到的库源码、以及我配置好的工程源码,一方面是方便大家将库用到自己的工程里,一方面是方便不想配置想先上手玩一玩的人的需求。资源链接如下,我设置的是0积分免费下载,如果不能下载给我留言吧!
STM32机械臂控制程序(上位机)
然后就是主要介绍一下我是怎么实现的,首先在 main.c 中将这些库引入(怎么将库加入工程之前也已经说过很多次了,这里就不再解释了)
#include "SysTick.h"
#include "sys.h"
#include "string.h"
#include "timer.h"
#include "usart.h"
#include "uartconfig.h"
#include "uartprotocol.h"
#include "ui.h"
#include "lcd.h"
#include "key.h"
#include "wifi.h"
#include "mqtt.h"
#include "iot.h"
然后就是一些变量定义,这里先是定义一下任务有关的变量,task_num会在定时器中断触发的时候自增,直到值大于task_max后会清零,而task_flag会在定时器中断触发的时候被置为1,在任务完成的时候被置为0,这样就可以根据task_num的值执行不同的任务,比如0的时候执行任务1,1的时候执行任务2,task_flag则是控制每一次只执行一次任务。
// 定时器任务队列参数定义
uint8_t task_num = 0; // 任务序号
uint8_t task_flag = 0; // 任务完成标志 0完成 1未完成
uint8_t task_max = 3; // 任务序号最大值
然后就是串口通信相关结构体定义,这部分服务于串口通信的,之前博文(串口通信)中有介绍怎么用,想深刻理解的可以点我主页去找相应的博文看。
// 串口通信相关结构体定义
DataTransmit data_transmit_uart1; // 声明全局结构体 data_transmit_uart1
DataReceive data_receive_uart1; // 声明全局结构体 data_receive_uart1
DataTransmit data_transmit_uart3; // 声明全局结构体 data_transmit_uart3
DataReceive data_receive_uart3; // 声明全局结构体 data_receive_uart3
TargetProperty tstm32; // 声明全局结构体 tstm32
TargetProperty rk210; // 声明全局结构体 rk210
TargetProperty rstm32; // 声明全局结构体 rstm32
然后是定义一个变量,控制一下是否连接阿里云。主要原因就是连接阿里云太慢,然后我要调LCD屏幕上显示的参数位置的时候,每一次都需要等连接阿里云,不等就要注释相对应的代码,很麻烦,所以我就定义一个变量来控制是否连接,这样调试LCD屏上显示的参数位置的时候就很方便了。
// ui 相关参数
uint8_t ui_flag = 0; // ui 调试模式标志 0 开启 不连接阿里云 1 关闭 连接阿里云
然后是定义一下服务于按键的相关参数。keycode是按键码,这里我用的按键程序是正点原子的,按下板子上不同的按键会获得不同的按键码,我将这个按键码用keycode保存,这样就可以知道每次我按下的是什么键值。sw则是功能选择标志,sw_max是功能选择最大值,因为是6个舵机,所以该最大值是5,sw在某个按键按下的时候值会自增,直到超过5后清零,sw的值为0的时候,可以通过另外两个按键,根据key_servo的值,将舵机1的占空比重装在值进行加减key_servo的值的操作。以此类推,sw的值为1就是控制舵机2,为2就是控制舵机3……,每次加减操作舵机占空比重装载值会变化50。
// 按键相关参数定义
uint8_t keycode = 0; // 按键码 按下按键此变量将产生改变
uint8_t sw = 0; // 功能选择标志位
uint8_t sw_max = 5; // 功能选择最大值
uint16_t key_servo = 50; // 按键控制舵机占空比步进
然后就是机械臂抓取参数定义,get是抓取计数器,抓取任务每一次进行的时候这个值都会自增,get_time控制的是机械臂抓取物体到移动物体再到放下物体这中间每一个动作的间隔。get_flag是抓取标志,这个值开始是0,到机械臂移动到物体的位置的时候被置为1,然后机械臂就开始抓取物体,get_down是保存抓取物体时候的二号舵机占空比值,因为放下物体的时候要将物体放到桌面再松开爪子。
// 机械臂抓取调试
uint16_t get = 0; // 抓取计数器
uint16_t get_flag = 0; // 0 不抓取 1 开始抓取
uint16_t get_down = 0; // 保存抓取物体时二号舵机占空比 服务于放下物体
uint16_t get_time = 3; // 抓取任务周期
然后就是相关函数声明,c语言中函数写在后面的,但被前面函数调用到的,需要在最前面声明,才能正常调用。
// 相关函数声明
void System_Init(void); // 系统初始化函数
void Run_Task(void); // 运行任务函数
void Send_ALY(void); // 发送参数到阿里云函数
void Key_Dispose(void); // 按键处理函数
void RobControl(TargetProperty *rk210); // 机械臂控制函数
接下来就是主函数
// 主函数
int main(void)
{
System_Init(); // 系统初始化
while(1)
{
Run_Task(); // 任务开始运行
}
}
主函数里面初始化函数主要完成的是延时函数初始化、中断分组设置、各串口初始化、LCD初始化、定时器初始化、MQTT连接阿里云、串口通信相关结构体初始化。
// 系统初始化函数
void System_Init(void)
{
SysTick_Init(72); // 延时函数初始化 时钟72MHz
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 设置NVIC中断分组2 2位抢占优先级 2位响应优先级 即可设置 0-3 抢占优先级 0-3 子优先级 2位就是2的2次方
uart2_init(115200); // 串口2 初始化 波特率 115200
LCD_Init(); // LCD初始化
if(ui_flag != 0) // 如果不为 ui 调试模式
{
MQTT_Init(); // MQTT初始化 连接阿里云
}
Init_TIM3(9999,719,0,1); // 720分频 计数 10000 频率 10 Hz 抢占优先级0 子优先级1
Init_UART1(115200,1,1); // 串口1 初始化 波特率115200 抢占优先级1 子优先级1
Init_UART3(115200,1,1); // 串口3 初始化 波特率115200 抢占优先级1 子优先级1
KEY_Init(); // 按键初始化
ui(); // 界面静态信息显示
Data_Transmit_Init(&data_transmit_uart1,0xFC,0xAA,7); // 设置发送给 K210 的帧头 FC AA 有效数据长度 7
Data_Receive_Init(&data_receive_uart1,0xFC,0xAA); // 设置接收自 K210 的帧头 FC AA
Data_Transmit_Init(&data_transmit_uart3,0xFC,0xAA,12); // 设置发送给 STM32 的帧头 FC AA 有效数据长度 12
Data_Receive_Init(&data_receive_uart3,0xFC,0xAA); // 设置接收自 STM32 的帧头 FC AA
Target_Init(&tstm32);