你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

STM32与树莓派(上位机)交互控制机械臂

[复制链接]
STMCU 发布时间:2020-9-22 09:58
通常的机械臂都是由多路舵机组成,我使用的是某宝上(并不)常见的五自由度机械臂。尽管商家称它为六自由度。

这里使用STM32F407VGT6的6路PWM输出通道来控制6个舵机的运动,树莓派(上位机)通过USB转TTL模块与STM32进行串口通讯


PWM舵机控制原理

标准的 PWM 舵机有三条控制线,分别为:电源、地及信号线。

1_meitu_1.jpg


市面上大多数180°舵机需要的PWM波周期通常为20ms,高电平接收时间通常为0.5 ~ 2.5ms,对应舵机旋转角度为0 ~ 180°。用PWM波控制舵机时,只需将时钟的周期设置为20ms(50Hz),并且通过改变比较值pulse改变PWM波的高电平时间来控制舵机旋转的角度.


STM32CubeMx主要配置

对于STM32,我使用的是STM32CubeIDE + Mx 进行开发。时钟框图为默认配置,如图。


2_meitu_2.jpg


TIMER

由于需要控制6个舵机,我选择了TIM3(4路PWM输出)和TIM9(2路PWM输出)。
PWM输出的频率由时钟APB2决定,由时钟框图可知此处的APB2频率为16MHz;PWM波频率计算公式为:

W = APB2 / (PSC + 1)(ARR + 1)

其中PSC为分频系数,ARR为自动重装载值。这里我将PSE设置为39,ARR设置为7999。


3_meitu_3.jpg


注意,这里的计数模式(Counter Mode)不同时,会导致接下来比较值值相同时舵机的旋转方向不同。

由于PWM波高电平时间需控制在0.5 ~ 2.5ms内,所以各PWM通道的比较值(pulse)必须控制在200 ~ 1000之内; (8000 x 0.5 / 20 = 200)(8000 x 2.5 / 20 = 1000)

当舵机旋转角度为90°时,pulse值应为600。这里我将各个PWM输出通道的比较值均设置为600。


                                                             4_meitu_4.jpg

由于我手头上的机械臂中的一个舵机用于控制机械爪,其为90°舵机,所以在配置控制该舵机的PWM通道时,比较值范围仅能为200 ~ 600,中间值为400我使用的单片机为STM32F407VGT6,其TIM3和TIM9所对应的PWM输出通道分别为PA6, PA7, PB0, PB1和PE5, PE6。
这里只需将这些IO口设置为复用推挽输出以及上拉即可。


5_meitu_5.jpg


串口配置

这里我选择USART_2用作串口通讯,其Tx与Rx分别对应为PA2、PA3。


串口的配置如图所示。

6_meitu_6.jpg


这里使用异步通信模式,注意波特率等参数需与上位机相匹配。


7_meitu_7.jpg


PA2与PA3均设置为复用推挽输出,上拉。


中断控制

由于这里我只使用了串口接收中断,所以NVIC这里可以不用配置;若STM32除了控制机械臂外还有其余任务,则可能需要配置NVIC。


STM32CubeIDE代码实现

在STM32CubuMx配置完毕并生成工程之后,我们的主要任务只有设计上位机与单片机的通讯协议,以及完成串口接收中断函数即可。


通讯协议设计

我初步的设计为串口发送一次数据,控制单个舵机的角度;所以发送的数据中需要包括舵机的编号以及舵机的目标角度(或者由角度转换而成的比较值)。

在这里我没有设计通讯头以及对舵机转速的控制,因为这里我的串口只用于传输控制舵机的指令,以及我使用的是小型舵机,其转速并不是很快,可以满足我的要求,无需控制其速度。

设计思路如下:
上位机每次发送16位的数据(2个uint8_t类型,串口只能发送串口),舵机编号为0~5,发送的pulse值为 (200 ~ 1000) - 200,将舵机编号数值乘以1000加上pulse值减去200得到一个uint16_t类型的数值;再将其转换为十六进制进行发送。

例:对3号舵机进行操作,角度为45°。

数据处理:
3 x 1000 + (45° / 180°) x 800 = 3200;
3200转换为十六进制为:0x0C80。

STM32进行逆向操作得到原数据。


STM32代码实现

由于串口发送的是两个uint8_t类型的数据,所以在解码之前还需将两个uint8_t类型数据转换为一个uint16_t类型的数据。

在打开USART2的接收中断时,设置为接受到两个uint8_t类型的数据进入接收中断,以便于在接收中断处理函数中进行数据处理。


  1. uint8_t pulse[2];
  2. HAL_UART_Receive_IT(&huart2, pulse, sizeof(pulse));
复制代码


串口接受中断函数代码如下:


  1. void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
  2. {
  3.         if(huart->Instance == USART2)
  4.         {
  5.                 uint8_t rec0, rec1;

  6.                 // 获取接收到的两个字节的数据
  7.                 rec0 = *((huart->pRxBuffPtr) - 2);
  8.                 rec1 = *((huart->pRxBuffPtr) - 1);

  9.                 uint16_t numPart[2], armControl, arm targetPulse;

  10.                 // 通过移位与强制转换得到uint16_t类型数据
  11.                 numPart[0] = (uint16_t) rec0;
  12.                 numPart[1] = (uint16_t) rec1;
  13.                 armControl = ((numPart[0] << 8) | numPart[1]);

  14.                 // 逆向解码
  15.                 arm = armControl / 1000;  // 舵机编号
  16.                 targetPulse = armControl % 1000 + 200;        // 目标比较值

  17.                 // 通过switch语句改变指定PWM通道的比较值
  18.                 switch(arm)
  19.                 {
  20.                         case 1:
  21.                                 __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, targetPulse);
  22.                                 break;

  23.                         case 2:
  24.                                 __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_2, targetPulse);
  25.                                 break;

  26.                         case 3:
  27.                                 __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_3, targetPulse);
  28.                                 break;

  29.                         case 4:
  30.                                 __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_4, targetPulse);
  31.                                 break;

  32.                         case 5:
  33.                                 __HAL_TIM_SET_COMPARE(&htim9, TIM_CHANNEL_1, targetPulse);
  34.                                 break;

  35.                         case 6:
  36.                                 __HAL_TIM_SET_COMPARE(&htim9, TIM_CHANNEL_2, targetPulse);
  37.                                 break;
  38.                 }
  39.         }
  40. }
复制代码

在进入while循环之前还需将PWM通道使能;


  1.   HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1);
  2.   HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_2);
  3.   HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_3);
  4.   HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_4);
  5.   HAL_TIM_PWM_Start(&htim9, TIM_CHANNEL_1);
  6.   HAL_TIM_PWM_Start(&htim9, TIM_CHANNEL_2);
复制代码


并且在USART2_IRQHandler函数中使能串口接收中断:


  1. void USART2_IRQHandler(void)
  2. {
  3.   /* USER CODE BEGIN USART2_IRQn 0 */

  4.   /* USER CODE END USART2_IRQn 0 */
  5.   HAL_UART_IRQHandler(&huart2);
  6.   /* USER CODE BEGIN USART2_IRQn 1 */
  7.   HAL_UART_Receive_IT(&huart2, pulse, sizeof(pulse));
  8.   /* USER CODE END USART2_IRQn 1 */
  9. }
复制代码


测试

在测试时我使用的环境是安装有Ubuntu 18.04的树莓派,串口调试工具为CuteCom;由5V锂电池为单片机与舵机(机械臂)供电,每次手动发送两个字节的数据,十进制转十六进制在科学计算器上进行。机械臂的运动符合预期。


经过测试,同时发送12个字节的数据,机械臂也能够正常运行。


这里需要注意,在接线时,舵机的地线必须与单片机共地。


之后就可以将上位机串口发送的代码写入C++或者Python程序,通过上位机控制机械臂。



收藏 评论0 发布时间:2020-9-22 09:58

举报

0个回答

所属标签

STM32团队

意法半导体微控制器和微处理器拥有广泛的产品线,包含低成本的8位单片机和基于ARM® Cortex®-M0、M0+、M3、M4、M33、M7及A7内核并具备丰富外设选择的32位微控制器及微处理器


最新内容

相似技术帖

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
微信公众号二维码 微信公众号
手机版二维码 手机版