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

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个回答

所属标签

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