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

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

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

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

+ S- u2 w" {: P! G* }0 ]* K

PWM舵机控制原理  p- t6 o3 ]2 c6 U3 u
: C9 W8 \  ^+ r: v6 U2 W' X7 `

标准的 PWM 舵机有三条控制线,分别为:电源、地及信号线。
9 Q4 c+ N8 O5 r

1_meitu_1.jpg
: \$ ^; S( c- f7 c0 ?2 v1 H
- s0 H" M: b( S( l
市面上大多数180°舵机需要的PWM波周期通常为20ms,高电平接收时间通常为0.5 ~ 2.5ms,对应舵机旋转角度为0 ~ 180°。用PWM波控制舵机时,只需将时钟的周期设置为20ms(50Hz),并且通过改变比较值pulse改变PWM波的高电平时间来控制舵机旋转的角度.


$ {! R( I. N( L% E" w8 U2 m

STM32CubeMx主要配置8 L1 H' @& ]4 l! N' j( L, l

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


1 `2 n5 d$ W/ Q5 @1 r7 z) z, j

2_meitu_2.jpg

% m' y$ ]# H0 Q: V; E& p4 U
. r; C7 e( n( ^% J( C

TIMER& l" N+ v1 X: |; X; S, @

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

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

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


1 m! g9 t5 }4 O2 \6 c

3_meitu_3.jpg

& g* R4 H$ L0 F1 }( Q% N8 p4 d1 b' ^1 x$ q
注意,这里的计数模式(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。


! \( s- c# j! v/ i% Z

                                                             4_meitu_4.jpg

- d6 r3 W( h; E/ c/ C
由于我手头上的机械臂中的一个舵机用于控制机械爪,其为90°舵机,所以在配置控制该舵机的PWM通道时,比较值范围仅能为200 ~ 600,中间值为400我使用的单片机为STM32F407VGT6,其TIM3和TIM9所对应的PWM输出通道分别为PA6, PA7, PB0, PB1和PE5, PE6。
* p9 g/ I$ |2 u5 u6 ~8 J8 K/ g这里只需将这些IO口设置为复用推挽输出以及上拉即可。


  `+ h" J; O: a; T0 D: i6 i

5_meitu_5.jpg

! R7 G7 M! r9 o. ?) P% r! |  y2 d7 j3 r/ U1 _* T* c

串口配置
2 c' P5 n# F1 L5 y9 m+ r# j

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

9 ?4 a1 t# ]" D# ?6 H. y1 g

串口的配置如图所示。
/ w) C# m( {8 {$ U3 b" q
6_meitu_6.jpg

( j- A5 }) q  H3 k2 U% |! {/ n& E: l2 }
这里使用异步通信模式,注意波特率等参数需与上位机相匹配。

6 ^1 T* L+ T3 x4 c0 G6 l! T; ]

7_meitu_7.jpg
! N  h+ a% ?: H; f
6 I$ t  B: U, H; A2 ?8 A3 P
PA2与PA3均设置为复用推挽输出,上拉。


" v" R; P& L# l. @% g( ^

中断控制+ k2 M; A6 I' q* J5 Y

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

3 A) b4 h1 i5 o* k/ ~9 @1 o

STM32CubeIDE代码实现- N% ~/ ~7 [  J7 |3 M0 s) q

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

9 I+ P6 `2 ^" H2 f4 G1 v

通讯协议设计
7 c3 F* F2 g# T: x

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

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

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

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

数据处理:  O6 a6 [5 N. Y8 _
3 x 1000 + (45° / 180°) x 800 = 3200;0 F2 n( D" C5 J+ T9 k  ?& B, }' A, p
3200转换为十六进制为:0x0C80。

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

6 m( h% J* U% @

STM32代码实现

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

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

/ Y: d) ~$ T# T& N3 g
  1. uint8_t pulse[2];6 X9 W; r/ |/ @2 H
  2. HAL_UART_Receive_IT(&huart2, pulse, sizeof(pulse));
复制代码

: g6 f( e$ u1 ?) B4 w7 g# j


9 g$ w4 w3 C( v8 n2 p

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

% V; D  j8 {- D0 l" k

  1. void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)" N6 T) ~4 _) n9 S, i/ I, Y6 w
  2. {
    . L) K- O# A$ W0 }5 ^
  3.         if(huart->Instance == USART2)0 x- E. t* J; L) _" d& |
  4.         {
    . R7 X  a9 A4 T; T2 \3 C
  5.                 uint8_t rec0, rec1;
    . g1 j( k1 p. J4 v! n) ~4 V

  6. . @0 P- |& k+ g3 o* ~5 T
  7.                 // 获取接收到的两个字节的数据: {7 `7 X4 T9 R# r0 K- ]
  8.                 rec0 = *((huart->pRxBuffPtr) - 2);
    7 v* M$ N- O" `3 X' f. \
  9.                 rec1 = *((huart->pRxBuffPtr) - 1);6 C# p2 a  R4 j- O5 L1 K4 k1 T3 j' [
  10. 2 R4 B7 l' l! L% @; L0 w9 K) @
  11.                 uint16_t numPart[2], armControl, arm targetPulse;$ \2 [" O! A" x& a. H5 q
  12. 1 A' b' ^% k* S, a: |# J7 S
  13.                 // 通过移位与强制转换得到uint16_t类型数据
    / n# u; [* J8 \, B8 @* w9 ~, d
  14.                 numPart[0] = (uint16_t) rec0;2 ]# V/ r% B4 z/ o
  15.                 numPart[1] = (uint16_t) rec1;6 ~- m5 N3 ]) v% ^
  16.                 armControl = ((numPart[0] << 8) | numPart[1]);3 R8 J5 K6 _6 x% w' q
  17. 9 S% y+ |9 i5 E& K6 H
  18.                 // 逆向解码' g  c+ D: X) `9 ~* y/ Z" g6 a2 g
  19.                 arm = armControl / 1000;  // 舵机编号
      ~" `7 r8 D: Q' X8 A3 ~
  20.                 targetPulse = armControl % 1000 + 200;        // 目标比较值+ m. s! t' O& Q- a$ i

  21. 9 {1 }' B1 u5 v
  22.                 // 通过switch语句改变指定PWM通道的比较值
    - y8 l1 B8 @5 [
  23.                 switch(arm)- F4 d- J: m6 G7 p3 R  ~) f7 N
  24.                 {
    7 B2 j) j4 M; X8 m
  25.                         case 1:
    8 B  K3 O3 a- p& b
  26.                                 __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, targetPulse);( v4 {+ ^: K: F
  27.                                 break;
    . ?8 `: u* X) Z% T$ {! f

  28. ( B' B9 f2 ^6 {- b1 g9 `1 F+ W8 l
  29.                         case 2:
    , ?5 v# u" L8 _8 ]
  30.                                 __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_2, targetPulse);8 ]3 b+ c7 R% ?4 N: @' ~( \9 Z
  31.                                 break;
    ' n; K8 v7 |8 E$ d/ {

  32. 3 l; V$ A7 F( t
  33.                         case 3:
    , U" p( t1 [; \' ^* u
  34.                                 __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_3, targetPulse);
    ; t$ i! s# p2 F4 b# I4 F& m8 {
  35.                                 break;
    - j( U+ W+ l2 @, z

  36. 7 H  Z0 s9 ^4 e
  37.                         case 4:
    4 e4 I+ E2 O5 m
  38.                                 __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_4, targetPulse);
    * i' \! _3 [$ j4 q( K
  39.                                 break;
    1 I  ~0 T2 A" ?8 P

  40. ; M5 _% W0 A/ q$ t$ Y, F8 f
  41.                         case 5:9 z4 Q7 D1 J5 {; |. X) L0 k0 e
  42.                                 __HAL_TIM_SET_COMPARE(&htim9, TIM_CHANNEL_1, targetPulse);
    $ Y& A, E9 |; _+ W
  43.                                 break;; v$ U, q; i, d5 U+ N
  44. ! L- w7 f) @1 T$ d7 F1 L3 z4 q6 a
  45.                         case 6:3 i' p. ~7 f( r
  46.                                 __HAL_TIM_SET_COMPARE(&htim9, TIM_CHANNEL_2, targetPulse);
    1 a7 y( f& W( M& F1 c
  47.                                 break;: T$ C! ~# b' n1 u4 a! f
  48.                 }1 j) V' d/ S/ B0 }
  49.         }
    . ^1 {3 B$ ?3 ?. C+ N8 w
  50. }
    , L2 k2 \) S) }+ T6 S
复制代码
8 u- N: \) G6 n; q  A: E8 `; g

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


8 Q& Y2 n2 i7 Z( h
  1.   HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1);
      N* q7 E& [) J+ Q" S/ \+ X
  2.   HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_2);
    % o! ~, P7 n& R7 i& @
  3.   HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_3);
    1 j( T1 ~. E2 O, [! n
  4.   HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_4);: b. k2 X9 A8 ~/ _& |$ F
  5.   HAL_TIM_PWM_Start(&htim9, TIM_CHANNEL_1);
    7 m3 I4 u6 R3 n! V$ P* ^! X
  6.   HAL_TIM_PWM_Start(&htim9, TIM_CHANNEL_2);
复制代码

* J7 j6 n/ Q9 {


, l, q) o- c. n( l4 V8 P& ?

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

4 ]" S' f( K1 v* n1 z

  1. void USART2_IRQHandler(void)
    5 A% \% M) }. `( F$ P6 V7 `4 v
  2. {
    - k" S+ Y0 u7 _7 \
  3.   /* USER CODE BEGIN USART2_IRQn 0 */
    8 u/ x6 ^8 D+ P$ I0 ]) e

  4. 1 k; Z7 ?% E: F9 Q1 O* ^
  5.   /* USER CODE END USART2_IRQn 0 */
    & d: ~+ c- v0 _( V. b: u0 o
  6.   HAL_UART_IRQHandler(&huart2);# M7 `# N, a% _; D* w- q7 Q) G2 |
  7.   /* USER CODE BEGIN USART2_IRQn 1 */
    1 g* v# R  H6 G3 A8 D
  8.   HAL_UART_Receive_IT(&huart2, pulse, sizeof(pulse));2 w$ Q0 i, X3 Z& Q, W7 j# o: u
  9.   /* USER CODE END USART2_IRQn 1 */* M* g* ^  y3 z; m2 o5 W  c% y
  10. }
    9 l$ G; ?4 k. g  @
复制代码
4 r) J+ A" a* I6 e

5 }6 u5 t7 v6 X测试! j6 D8 n  t( K  }  e

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

) e( f& x) o* n: f* t: ], V* F

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

, p, n7 D  M% `& T" K) ~

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

5 r( @1 ~' I$ c. Y% P+ f7 C6 A

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

+ o! R3 F9 o+ ]/ \2 C
# b& e: C, w0 B( e. L3 d- {  L* ]9 B
赞 收藏 评论0 发布时间:2020-9-22 09:58

举报

0个回答
关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版