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

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

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

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


: F4 s; c( T3 ]& }! T

PWM舵机控制原理+ s7 l; G" T  v2 N

1 U1 y% y  W' s' t

标准的 PWM 舵机有三条控制线,分别为:电源、地及信号线。" g2 ?& u: @9 m# }( i/ A

1_meitu_1.jpg
6 [. J4 L3 Y* V1 Y/ A
! B0 F  e0 Z% z5 U  D+ C: ~
市面上大多数180°舵机需要的PWM波周期通常为20ms,高电平接收时间通常为0.5 ~ 2.5ms,对应舵机旋转角度为0 ~ 180°。用PWM波控制舵机时,只需将时钟的周期设置为20ms(50Hz),并且通过改变比较值pulse改变PWM波的高电平时间来控制舵机旋转的角度.


; Q+ r4 X/ O" S% L/ a/ {2 {" D) D6 G1 K

STM32CubeMx主要配置1 ?# R# K7 |/ s- r* {; }

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

, V; t& ^, Y$ }" ~/ [3 j1 U% q1 U

2_meitu_2.jpg
$ z  d! n- l4 q
/ m! x/ F' x- k0 v4 b

TIMER+ u/ _. ^% B* a2 R( ^

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

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

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


2 \' X4 d3 a1 h% g4 f8 {

3_meitu_3.jpg

  A0 Q$ Z, M4 J7 t, i% G
+ T* S3 X! C( z3 C! X
注意,这里的计数模式(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。


: n7 ~3 W9 R5 S

                                                             4_meitu_4.jpg

7 P9 L/ ?5 h5 R( b! x
由于我手头上的机械臂中的一个舵机用于控制机械爪,其为90°舵机,所以在配置控制该舵机的PWM通道时,比较值范围仅能为200 ~ 600,中间值为400我使用的单片机为STM32F407VGT6,其TIM3和TIM9所对应的PWM输出通道分别为PA6, PA7, PB0, PB1和PE5, PE6。
# [% ]* i2 L2 R) z+ y/ b* k这里只需将这些IO口设置为复用推挽输出以及上拉即可。

% g! f2 G* u8 o0 O

5_meitu_5.jpg

0 |* R  U- l# Q* L0 M8 ?3 [7 a
8 s5 j7 l8 C" _& U

串口配置6 U$ s; t( i& k: B( ~7 e9 ^; y

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


; R7 u" L8 @- P2 t; i( E4 Z

串口的配置如图所示。
9 v1 m4 \% D3 l$ g$ y; C
6_meitu_6.jpg
* t: V: X# A$ Y- ?
5 E: Z  ^" r3 n- s/ Z
这里使用异步通信模式,注意波特率等参数需与上位机相匹配。


8 I, w1 M2 P0 ~/ H

7_meitu_7.jpg
& P7 ]( o  Y+ u/ Z. ]3 i5 Q
1 X' ?0 S" _1 U3 e5 ~5 W6 N
PA2与PA3均设置为复用推挽输出,上拉。


' J  [0 ^) r; d) t7 U4 u

中断控制1 V+ g' Y. p3 u1 U' s$ H3 [

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

1 x; A2 O0 p! t( d7 c. Q

STM32CubeIDE代码实现! U& ]1 X! E! o7 ]8 J' E9 u

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

9 i) }5 O* d5 ^, s

通讯协议设计1 o' ]8 ]* c9 O) ~* ^7 \1 ~6 \

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

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

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

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

数据处理:
9 p0 E1 p% G- w3 x 1000 + (45° / 180°) x 800 = 3200;# O3 Q1 F7 |1 z2 j4 F
3200转换为十六进制为:0x0C80。

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


; X8 m! l, t# E

STM32代码实现

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

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

9 d# P, B' T& P: E1 t
  1. uint8_t pulse[2];, A5 n6 t1 g4 ]! A, e
  2. HAL_UART_Receive_IT(&huart2, pulse, sizeof(pulse));
复制代码
" F* {% r& Q) D

) x- x) Y' O4 _. W4 X. b( S

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


9 @' D8 k; M) ?! P4 x9 E+ \& J

  1. void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
    ) ], a6 p" o5 E9 q" M& [
  2. {$ Z+ D- d* v  e) H
  3.         if(huart->Instance == USART2)
    : l' y6 K( D7 ^4 v+ j0 Z% l  J
  4.         {
    6 [! }7 T7 D) ~
  5.                 uint8_t rec0, rec1;1 @3 c& @! H4 T7 n& _6 ]# N- e* ~

  6. ) t7 D: j) b% U( M2 G3 l. M
  7.                 // 获取接收到的两个字节的数据. ~3 Q; ]# P% \8 Z
  8.                 rec0 = *((huart->pRxBuffPtr) - 2);
    & {. A! W# ^* R1 x0 W5 s2 T+ f, @) w' b/ W
  9.                 rec1 = *((huart->pRxBuffPtr) - 1);2 t3 s! j2 @" \9 m

  10. # {) y' [& u$ }% g" I! W
  11.                 uint16_t numPart[2], armControl, arm targetPulse;: N- F, {0 W9 R+ b- ?
  12. 2 ]. M: K2 q2 ?0 h5 B0 x: s
  13.                 // 通过移位与强制转换得到uint16_t类型数据
    ; E7 o; B' b% w6 J3 k
  14.                 numPart[0] = (uint16_t) rec0;
    & ^2 I# P9 |/ s5 |% h
  15.                 numPart[1] = (uint16_t) rec1;
    ! ?/ y4 Y& E7 S
  16.                 armControl = ((numPart[0] << 8) | numPart[1]);
    2 }+ o+ v/ ?2 T- A$ u
  17. 7 Y4 U+ h8 u4 _
  18.                 // 逆向解码5 [4 ~5 X& ^* [  f) [8 W
  19.                 arm = armControl / 1000;  // 舵机编号2 C$ |4 A6 W# t4 T0 a( t+ H) J  l
  20.                 targetPulse = armControl % 1000 + 200;        // 目标比较值* B! K, L& o' }8 Y6 g0 W' v1 j

  21. % o( b7 I; n& v4 J& q% _( t
  22.                 // 通过switch语句改变指定PWM通道的比较值
    ' }5 k$ o. i3 U3 B
  23.                 switch(arm)1 j7 l! o5 i% @  {# v% U
  24.                 {
    # B7 y9 I" l! i) S
  25.                         case 1:7 Y2 J7 r" ~! T9 y& P
  26.                                 __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, targetPulse);; T6 G* J' \# a- `8 C+ Q' S! E7 e
  27.                                 break;8 x6 [. x% D4 |0 `2 _* y
  28. 9 `7 r. {+ Z5 i& v# `0 B
  29.                         case 2:6 f! `( R: j, V) D
  30.                                 __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_2, targetPulse);
    : M* C, M) ]# ?% b7 ~$ Z  P
  31.                                 break;, x, t5 B0 Y; h% U- C) t* R
  32. 3 k0 H  ^7 S+ H% ~  h
  33.                         case 3:
    ' t9 m. ?+ c7 }
  34.                                 __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_3, targetPulse);
    2 o! W1 Q: G2 w9 c, d3 {7 V6 j7 V
  35.                                 break;
    ( V$ @0 n1 p' S- P: m( V7 k- ]

  36. % H8 n0 K+ g9 m2 L* d0 Q/ x
  37.                         case 4:
    9 d2 P4 T/ B7 Y" g
  38.                                 __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_4, targetPulse);6 t, h8 p- B# I/ U, F
  39.                                 break;& O1 w% c- y' H- k
  40. : C% a. \0 D1 m6 y7 s
  41.                         case 5:) I) m: F; n3 d, O; E" X
  42.                                 __HAL_TIM_SET_COMPARE(&htim9, TIM_CHANNEL_1, targetPulse);/ i% B4 n( [- F$ N9 q- l( d
  43.                                 break;
    & r: ]' d8 T% J, q  v% P0 Z
  44. ) t5 z# c) F+ B. Q, z2 l4 C* Z2 ]! J
  45.                         case 6:
    ' j  L6 i- x) {7 a3 }* Q' \4 @
  46.                                 __HAL_TIM_SET_COMPARE(&htim9, TIM_CHANNEL_2, targetPulse);+ W+ \3 @) U/ [, e! [
  47.                                 break;7 X, Y) E# U, T6 _, O' L
  48.                 }8 F: l0 x: A6 S8 x7 @3 |1 k
  49.         }5 [- V' O2 o# N0 H2 o7 W, h& i! s
  50. }" X. }, @) M1 l$ S6 v) r
复制代码

* ^7 T) ^+ z' `. ]! l7 z7 v0 s. Y

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


3 P; D# Z5 ?7 I) ~& A/ g+ b. R; B8 @
  1.   HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1);/ T! K. J. @) |2 Y/ F
  2.   HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_2);: {; ]2 G, S9 Z5 l3 |
  3.   HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_3);
    - b2 t/ F  i2 d7 U9 t
  4.   HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_4);
    4 b: Q% w: L" n" O
  5.   HAL_TIM_PWM_Start(&htim9, TIM_CHANNEL_1);
      V, [, G' n7 i1 _+ J# K
  6.   HAL_TIM_PWM_Start(&htim9, TIM_CHANNEL_2);
复制代码
9 R/ r* F+ n9 x4 \


) f/ f; R. _: c8 W! [1 D: W1 m) \5 h

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

0 p% a7 K' d( s7 V

  1. void USART2_IRQHandler(void)
    5 P+ f, u! g1 @* f
  2. {' [& s% C, _6 |; i
  3.   /* USER CODE BEGIN USART2_IRQn 0 */2 [4 ^) U6 M& ~7 L7 O4 k
  4. " {) g6 d; S% v8 F; _" E( A
  5.   /* USER CODE END USART2_IRQn 0 */
    $ t0 J' J3 \# q4 {+ D1 R/ ^+ @
  6.   HAL_UART_IRQHandler(&huart2);
    3 R5 S* I' U: b7 b' {6 I
  7.   /* USER CODE BEGIN USART2_IRQn 1 */
    ! {& \. y( Z7 f% m
  8.   HAL_UART_Receive_IT(&huart2, pulse, sizeof(pulse));
    . D4 b6 `8 _$ z) _6 I3 ?
  9.   /* USER CODE END USART2_IRQn 1 */* X: |8 G9 u7 f5 k
  10. }
    4 u( d" `+ p/ {/ T! w& j2 C
复制代码
: {1 X  p2 d% l

6 V1 c/ k9 e% m5 N3 P测试
1 G5 ~0 ^' f5 y+ T8 @% @4 r

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

2 l- \) \8 q4 c' x$ g

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

! B9 p. E4 S. J

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

$ i9 F3 Y5 s- T3 g! o/ X! t' L% n( F

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


. r7 A9 Y. B" [5 W: k7 C" ^1 t$ \+ H! J& X# x8 n
收藏 评论0 发布时间:2020-9-22 09:58

举报

0个回答

所属标签

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