蓝牙通讯原理
启智模块机器人控制器具备BLE4.0蓝牙无线通讯能力,可用来与智能手机等蓝牙设备进行数据交互。启智控制器面板右下角有一个蓝牙状态指示灯,用于指示蓝牙模块的工作状态。蓝牙模块处于待机状态(未配对连接)指示灯频闪,蓝牙模块处于连接状态时指示灯常亮。
启智模块机器人的蓝牙功能是使用额外模块实现的,该蓝牙模块与主芯片STM32是通过UART连接,占用了STM32处理器的USART2接口,具体电路图如下:
在这个实验里,使用Android智能手机为上位机和机器人控制器进行通讯,操作软件为套件资源内附带的APP“启智蓝牙通讯”,安装文件名为“WPBPanel.apk”,可以将其拷贝到Android手机内进行安装。实验时,Android手机和控制器通过蓝牙进行连接,从手机端的“启智蓝牙通讯”APP向控制器发送数据,编写程序让控制器接收到数据后显示在液晶屏上并向手机返送数据。手机端的“启智蓝牙通讯”APP接收到控制器返送的数据会显示在手机界面上。
STM32的蓝牙接口设置十分繁琐,为了方便用户使用,我们将蓝牙功能进行了函数封装,提供了简洁易用的接口。在蓝牙数据的接收功能上,用户只需要定义一个接收中断函数,然后将其与控制器的蓝牙接收中断进行关联即可。在蓝牙数据的发送功能上,提供了两个发送函数,一个用于发送单个字节数据,另一个用于发送字符串数据。
实验代码中将会用到启智控制器的部分库函数:
设置接收中断函数
void BLE_SetHandler(void (*HandlerName)(u8))
HandlerName —— 蓝牙接收中断函数的函数名称;
返回值:空。
设置控制器的蓝牙设备名称
void BLE_SetName(char Name[])
Name —— 控制器新的蓝牙设备名称;
返回值:空。
发送单个字节数据
void BLE_SendChar(unsigned char Value)
Value —— 需要从蓝牙发送出去的单字节数据;
返回值:空。
发送字符串
void BLE_SendString(char String[])
String —— 需要从蓝牙发送出去的字符串;
返回值:空。
2、蓝牙通讯代码
2.1 进行这个实验前,需要确认Android智能手机已经安装了APP“启智蓝牙通讯”(随机器发货的软件已经包含)。
2.2 按照实验一的方法建立一个新的工程,工程名为“16_BLE”。
2.3 将后台库源码文件导入后,在USER目录下的main.c文件中,编写如下代码:
#include "Wp_Sys.h"
int recv = 0; int count = 0;
void ble_handler(u8 data) { recv = data; count ++;
BLE_SendChar(count); }
int main(void) { BLE_SetHandler(ble_handler);
WPB_Init();
BLE_SetName("WPB_10"); while(1) { OLED_String(0, 0, "BLE"); OLED_String(0, 1, "recv="); OLED_Int(6, 1, recv, 3); OLED_String(0, 2, "count="); OLED_Int(7, 2, count, 3);
DelayMs(50); } } |
(1)代码开头先include系统函数头文件“Wp_Sys.h”。
(2)定义一个int型变量recv,后面会用来存储控制器接收到的从手机端发来的最新一个字节数据。再定义一个int型变量count,初值为0,后面会用来记录控制器接收到的数据累计字节数。
(3)定义一个名为ble_handler的函数,其参数为data,参数变量类型为u8,这就是本实验所使用的蓝牙接收中断的响应函数。该响应函数会在控制器每接收到一个字节新数据时自动触发,在这个中断响应函数的参数data即为本次接收到的数据,数据长度为一个字节。在这个例子程序里,我们将接收到的数据data赋值给recv,后面会在主函数里将这个recv显示到控制器液晶屏上。然后对count进行一次累加,后面也会在主函数里将这个count显示到控制器液晶屏上,用于统计接收到的数据量。最后,调用BLE_SendChar()函数,将count的数值返送给手机,手机的“启智蓝牙通讯”会显示这个数值。
(4)在主体函数main函数中,调用函数BLE_SetHandler()将前面定义的ble_handler指定为蓝牙接收中断函数。需要特别注意的是:这步操作需要放置在系统初始化操作之前,否则无效。
(5)调用系统初始化函数“WPB_Init()”,进行系统初始化操作。
(6)在我们第一次运行实验程序时,需要调用BLE_SetName来给启智控制器的蓝牙起个名字,这样我们才好在蓝牙配对的环节中快速找到我们的设备。否则启智控制器会保持上一次的蓝牙名字,有可能和别的启智控制器重名,也可能是一个我们并不知道的名字,无法与其配对。例子代码里给启智控制器的蓝牙起名为“WPB_10”,同学们需要做一下修改,否则容易和附近其他启智控制器的蓝牙名称重复。这个蓝牙名称的设置函数调用后,需要重启一下才能生效,只需要调用一次就能保存在启智控制器里。所以我们只需要第一次编译下载程序的时候调用就好,之后再修改程序可以将这一句注释掉。
(7)构建一个while循环,以便程序能够持续显示recv和count的最新数值。
(8)在while循环中,先调用OLED_String()函数将“BLE”显示在OLED液晶屏第一行。再调用OLED_String()函数将“recv=”显示在OLED液晶屏第二行的开头位置,接着调用函数OLED_Int()函数将recv变量数值显示在recv=”的后面(横向偏移6个字符位置),recv中保存的是蓝牙最新接收到的一个字节的数值。再调用OLED_String()函数将“count=”显示在OLED液晶屏第三行的开头位置,接着调用函数OLED_Int()函数将count变量显示在“count=”后面(横向偏移7个字符位置),count中保存的是蓝牙总共接收到的数据量统计总和,单位为字节。
(9)执行一个DelayMs(50)函数,让循环暂停50毫秒。这样前面的两个数值能够在OLED屏幕上保持50毫秒,不至于太闪烁无法阅读。
(10)While循环结束,上述操作在循环里周而复始的重复运行。
2.4 代码编写完成后,编译看是否能够通过。
2.5 将控制器和电池连接,同时通过下载器和电脑连接。
2.6 打开控制器电源,让控制器上电。
2.7 在Keil uVision4工具栏里点击“LOAD”下载按钮,将编译好的程序下载到控制器里。
2.8 程序下载完成后,需要手动关闭启智控制电源,然后再打开。让内部的蓝牙模块重新上电,这样才能让代码中设置的蓝牙名称生效。
2.9 在Android手机端打开APP“启智蓝牙通讯”。
此时可以看到“启智蓝牙通讯”的界面:左上角是信息提示区,刚启动时会提示“搜索并连接蓝牙设备”。右上角是一个“搜索设备”按钮,点击后可以搜索附近的蓝牙设备。中间的区域是接收信息的显示区,刚启动时为空。最下面是数据发送栏,包含一个输入框和一个“发送”按钮。
2.10 点击“启智蓝牙通讯”的界面右上角的“搜索设备”按钮。此时屏幕中间会弹出一个列表,等待一会,它会搜索到我们的启智控制器设备“WPB_10”。
点击设备列表中的“WPB_10”,自动跳转回到主界面。此时左上角信息区提示“成功连接 - WPB_10”,同时显示该设备的MAC地址,说明手机和启智控制器蓝牙配对成功。配对成功后,启智控制器内部的蓝牙模块会给STM32发送一条配对信息,这时候能看到启智控制器的OLED屏幕上count数值增加7,说明蓝牙模块给STM32发送的配对信息为7个字节。
2.11 在APP“启智蓝牙通讯”界面最下面的数据发送栏输入框中,填入数值“01”,点击右侧的“发送”按钮。启智控制器会接收到这个数值,其OLED屏幕上recv数值会变为“001”,即刚才手机发送下来的数据。同时count增加了1,变成“008”。启智控制器会把count的数值通过蓝牙连接返送给Android手机,所以APP“启智蓝牙通讯”的接收显示区会显示接收到的数值“08”,在数值的前面是接收到这个数值的时间。需要注意的是,在APP“启智蓝牙通讯”里发送和接收的都是十六进制数据,而启智控制器的OLED上显示的数值都是十进制。所以当交互的数值超过9时,需要在脑子里做一下转换。
2.12 在手机端“启智蓝牙通讯”里不停的发送蓝牙数据给控制器,启智控制器里的count数值会不断累加,并返送到手机端“启智蓝牙通讯”,显示在该APP的接收数据区内。
3、蓝牙通讯实验结果
3.1 程序启动后,启智控制器的OLED屏幕会显示第一行字符串“BLE”、第二行“recv= 000”和第三行“count= 000”。
3.2 在手机端“启智蓝牙通讯”手动发送数字“01”之后,启智控制器的OLED屏幕上的第二行数字recv会变成“001”,即我们发下去的数值。第三行数字count累加1,表明控制器又多接收了1个字节的数据。
3.3 在手机端“启智蓝牙通讯”的下方发送栏把数字“01”变成其他数值,点击“发送”按钮。可以发现启智控制器的OLED屏幕上的recv会变化,同时count不停累加。手机端“启智蓝牙通讯”接收到启智控制器返送的count数值。
二、手机蓝牙遥控
1、基于麦克纳姆轮底盘蓝牙遥控程序的硬件组装
1.1 结构图
启智模块机器人套件标配了4个伺服电机模块,由于伺服电机模块采用RS485总线通讯, 所以每个伺服电机模块均有一个独立的ID号。伺服电机模块的供电和通讯功能集成在一个航插接口,使用时通过通讯线缆连接控制器 的RS485接口即可。控制器的两个RS485接口也是并联关系,插接任意接口都可以。
1.2 安装视频
1.3 麦克纳姆轮组装方式
需要注意的是,麦克纳姆轮底盘的轮子有两种,主轮上的小轮安装方向不一样。其中左前轮和右后轮的轮子类型一致,小轮的排布形状如下:
右前轮和左后轮的类型一致,小轮的排布形状如下:
拼装完成后对比图:
四个麦克纳姆轮轮组的电机模块都有各自的ID,需要按照特定的顺序进行排布。
2、蓝牙遥控实验原理
在前面的实验中,已经学习了蓝牙通讯的实现以及各种底盘的控制,在这一节实验中,我们将会进行一个综合实验,将蓝牙通讯和机器人底盘的运动学逆解结合起来,使用智能手机通过蓝牙传输控制指令,无线遥控机器人进行运动。
手机遥控功能的实现思路是:使用Android智能手机为上位机和机器人控制器进行通讯,操作软件为套件资源内附带的APP“启智蓝牙遥控”,安装文件名为“WPBCtrl.apk”,可以将其拷贝到Android手机内进行安装。这个APP在手机的触摸屏上构建左右两个十字坐标系,左侧坐标系控制机器人的平移,即X和Y轴上的位移;右侧坐标系用于控制机器人的机器人的旋转。操作者通过触碰这两个坐标系内的滑动点来控制机器人的运动。
为了将手机触摸屏的操控数据传达到机器人,实验中将会构建一个简单的通讯协议。协议中只携带左侧坐标系的X和Y轴的偏移数值,以及右侧坐标系的X轴偏移数值(旋转量是一个一维向量,所以只用一个轴的数据)。启智控制器在接收到符合这个协议的蓝牙指令后,将会对其进行解析,将上述的三个控制量还原出来。然后像前面的底盘运动学逆解实验一样,从这三个控制量反解出电机的转速值,驱动底盘进行相应的移动。
这里我们设计一个6个字节的简单协议:
上述协议中,Move_x、Move_y和Turn都将采用一个字节数据,其代表的含义是手机屏幕上触控点在各坐标系里的偏移量。因为字节类型的数据没有正负,所以我们把触控点位于负方向最大时偏移量值为0,触控点位于坐标系中心原点时偏移量值为100,触控点位于坐标系正方向最大时偏移量取值为200,这样正负方向都定义了100的偏移量分辨率,所以偏移量的取值范围为(0~200)。
具体的情况举例如下:
往期公众号实验已经推导过麦克纳姆轮全向底盘的运动学逆解方程组,其四个轮子的转速关系如下所示:
实验代码中将会用到启智控制器的部分库函数:
设置接收中断函数
void BLE_SetHandler(void (*HandlerName)(u8))
HandlerName —— 蓝牙接收中断函数的函数名称;
返回值:空。
设置控制器的蓝牙设备名称
void BLE_SetName(char Name[])
Name —— 控制器新的蓝牙设备名称;
返回值:空。
设置电机速度
void Motor_SetSpeed(u8 MotorID, float MotorSpeed)
MotorID —— 电机ID号,范围1~4;
MotorSpeed —— 设置的转速值,精度0.1,单位为“转/分”。
这条函数调用后,控制器只是把速度值发给电机模块,而电机模块并未立刻执行,需要等待调用Motors_Action()后才执行新的速度值。
返回值:空。
电机执行设置的速度
void Motors_Action(void)
命令所有电机模块执行新的速度值。
返回值:空。
3、蓝牙遥控代码
3.1 进行这个实验前,需要确认Android智能手机已经安装了APP“启智蓝牙遥控”。
3.2 按照实验一的方法建立一个新的工程,工程名为“17_RemoteControl”。
3.3 打开USER目录下的main.c文件,编写如下代码:
#include "Wp_Sys.h"
float Rw = 0.03; float Pi = 3.1415926; float a = 0.150; float b = 0.095;
void Mecanum(float Vx, float Vy, float Wz) { float w1,w2,w3,w4,wx,wy,wt; wx = ((Vx/Rw)/(2*Pi))*60; w1 = -wx; w2 = wx; w3 = wx; w4 = -wx;
wy = ((Vy/Rw)/(2*Pi))*60; w1 += wy; w2 += wy; w3 -= wy; w4 -= wy;
wt = ((Wz*(a+b)/Rw)/(2*Pi))*60; w1 += wt; w2 += wt; w3 += wt; w4 += wt;
Motor_SetSpeed(1, w1); Motor_SetSpeed(2, w2); Motor_SetSpeed(3, w3); Motor_SetSpeed(4, w4); Motors_Action(); }
int count = 0; u8 last_recv = 0; bool bFrameStart = false; u8 recv_buffer[6]; float move_x = 0; float move_y = 0; float turn = 0;
void ble_handler(u8 data) { if(bFrameStart == false) { if(last_recv == 0x55 && data == 0xaa) { bFrameStart = true; recv_buffer[0] = 0x55; recv_buffer[1] = 0xaa; count = 2; } } else { recv_buffer[count] = data; count ++; if(count >= 6) { move_x = (recv_buffer[3]-100) * 0.005; move_y = (recv_buffer[4]-100) * 0.005; turn = (recv_buffer[5]-100) * 0.01;
OLED_Float(9, 1, move_x, 2, 2); OLED_Float(9, 2, move_y, 2, 2); OLED_Float(9, 3, turn, 2, 2);
Mecanum(move_x, move_y, turn);
bFrameStart = false; } } last_recv = data; }
int main(void) { BLE_SetHandler(ble_handler);
WPB_Init();
BLE_SetName("WPB_10"); OLED_String(0, 0, "Remote"); OLED_String(0, 1, "move_x ="); OLED_String(0, 2, "move_y ="); OLED_String(0, 3, "turn ="); while(1) { DelayMs(50); } } |
(1)代码开头先include系统函数头文件“Wp_Sys.h”。
(2)定义四个定值变量:第一个a是麦克纳姆轮触地点到车体中心的横向距离,这里为0.15米;第二个b是麦克纳姆轮触地点到车体中心的纵向距离,这里为0.095米;第三个Rw为橡胶轮半径,这里为0.03米;第四个Pi为圆周率系数。
(3)定义一个函数void Mecanum(float Vx, float Vy, float Wz)。第一个参数Vx是机器人沿水平X轴的速度分量,第二个参数Vy是机器人沿水平Y轴的速度分量,第三个参数Wz是机器人旋转的速度。这个函数会根据这两个输入值解算出四个电机应该输出的转速,并驱动四个电机执行这个计算结果。
(4)在void Mecanum()函数里,可以看到代码分为四个部分:第一部分是根据Vx计算四个电机速度w1、w2、w3和w4,计算的公式参照前面方程组。需要注意的是按照公式计算出来的数值结果单位为(弧度/秒),而启智机器人的电机模块驱动函数的输入参数是(转/分)。所以代码里能看到对计算结果进行了一个换算,先除以2*Pi,单位变成(转/秒),再乘以60,单位变成(转/分)。第二部分代码是根据Vy计算机器人电机速度,公式参照前面的方程组。同样的,计算完毕后还需要进行一次换算,把单位从(弧度/秒)换算成(转/分)。第三部分代码是根据Wz计算机器人电机速度,公式参照前面的推导结果。同样的,计算完毕后还需要进行一次换算,把单位从(弧度/秒)换算成(转/分)。上述换算后的结果,直接累计到前面的电机速度w1、w2、w3和w4里。第四部分代码是执行,调用函数Motor_SetSpeed()为四个电机设置目标转速,再调用函数Motors_Action()指示电机开始执行目标转速。
(5)定义一个名为ble_handler的函数,其参数为data,参数变量类型为u8,这就是本实验所使用的蓝牙接收中断的响应函数。该响应函数会在控制器每接收到一个字节新数据时自动触发,在这个中断响应函数的参数data即为本次接收到的数据,数据长度为一个字节。在这个例子程序里,我们会对接收到的数据进行记录和分析,当收到相邻的两个字节为“0x55 0xaa”时,我们认为这是控制协议的包头,开始对接收到的后续数据进行缓存,存到数组recv_buffer[6]里。当累计接收满6个字节时,就将接收缓存数组的后面三个字节提取出来作为控制量。这三个控制量需要进行一些换算才能作为速度进行底盘速度解算。对三个控制量进行值范围调整,将其从(0~200)的范围调整到(-100~100)。然后为调整后的值乘上一个系数,即可变成速度,这里平移控制偏移量的系数为0.005,旋转控制偏移量系数为0.01,这两个系数可以根据需要进行修改。计算后的三个速度值调用OLED_Float()函数将其显示在启智控制器的液晶屏上。最后调用Mecanum(move_x, move_y, turn)将三个速度值解析成四个电机的转速并下发给电机模块执行。
(6)在主体函数main函数中,调用函数BLE_SetHandler()将前面定义的ble_handler指定为蓝牙接收中断函数。需要特别注意的是:这步操作需要放置在系统初始化操作之前,否则无效。
(7)调用系统初始化函数“WPB_Init()”,进行系统初始化操作。
(8)在我们第一次运行实验程序时,需要调用BLE_SetName来给启智控制器的蓝牙起个名字,这样我们才能在蓝牙配对的环节中快速找到我们的设备。否则启智控制器会保持上一次的蓝牙名字,有可能和别的启智控制器重名,也可能是一个我们并不知道的名字,无法与其配对。例子代码里给启智控制器的蓝牙起名为“WPB_10”,同学们需要做一下修改,否则容易和附近其他启智控制器的蓝牙名称重复。这个蓝牙名称的设置函数调用后,需要重启一下才能生效,只需要调用一次就能保存在启智控制器里。所以我们只需要第一次编译下载程序的时候调用就好,之后再修改程序可以将这一句注释掉。
(9)调用OLED_String()函数将实验名和三个速度值的名称显示在启智控制器的液晶屏上,放在三个值的前面。
(10)构建一个while循环,让程序能持续运行,以便蓝牙接收中断函数能够正常工作。
3.4 代码编写完成后,编译确认是否能够通过。
3.5 编译通过以后将启智控制器和电池连接,同时通过下载器和电脑连接。
3.6 在Keil uVision4工具栏里点击“LOAD”下载按钮,将编译好的程序下载到控制器里。当Keil uVision4左下角状态栏里的下载进度进行完成后,控制器会重新启动,屏幕显示开机信息,稍等片刻,程序开始运行。
3.7 将启智控制器断电,拔掉下载线,然后再打开。让内部的蓝牙模块重新上电,这样才能让代码中设置的蓝牙名称生效。
3.8 在Android手机端打开APP“启智蓝牙遥控”。
此时可以看到“启智蓝牙遥控”的界面:左上角是信息提示区,刚启动时会提示“搜索并连接蓝牙设备”。右上角是一个“搜索设备”按钮,点击后可以搜索附近的蓝牙设备。中间的区域是两个控制机器人的直角坐标系,每个坐标系的中心原点有一个黄色的触控点。
3.9 点击“启智蓝牙遥控”APP的界面右上角的“搜索设备”按钮。此时屏幕中间会弹出一个列表,等待一会,它会搜索到我们的启智控制器设备“WPB_10”。
点击设备列表中的“WPB_10”,自动跳转回到主界面。此时左上角信息区提示“成功连接 - WPB_10”,同时显示该设备的MAC地址,说明手机和启智控制器蓝牙配对成功。
3.10 手机和启智机器人蓝牙配对成功之后,我们便可以通过触摸滑动手机屏幕上的两个黄色触控点来控制机器人移动。
4、实验结果
机器人的触控点位置和对应的机器人动作:
更多产品了解
欢迎扫码加入云巴巴企业数字化交流服务群
产品交流、问题咨询、专业测评
都在这里!
2022-06-17 17:43:30
2022-11-24 09:55:55
2022-11-22 09:52:24
2024-03-27 13:49:09
2022-11-21 11:15:13
2022-11-21 15:48:43
甄选10000+数字化产品 为您免费使用
申请试用
评论列表