对于STM32F103三轴机械臂控制器进行基本功能测试-关节角度读取

 

01 机械臂调试


1.简介

基于STM32对于三轴机械臂控制器设计 中对应的控制电路读取肩部和肘部两个 角度编码器 ST-3806-15-RS 数据进行调试。

2.接口电路1

▲ 角度读取的相关电路和PCB

▲ 角度读取的相关电路和PCB

2.单片机软件

单片机软件所在目录:

D:\zhuoqing\window\ARM\IAR\STM32\Application\XQWF\2020\CNTSTM103\Src\main.c

 

02 基本调试2


1. UART2输入输出

通过UART2发送字符,测试在MAX485的输出波形显示互补的信号。

2.建立ST3806文件

角度编码器 ST-3806-15-RS 拷贝 ST3806的头文件和C文件并建立相应的文件,加入Project。 具体文件内容参见附录。

注意:Control中调用ST3806Init()初始化的时候,需要注意到它的输入参数应该设置为1。

通过下面的过程,可以测量读取两个角度值所需要的缠绵大约在3 ~ 4ms。

    if(++nShowCount >= 500) {
        nShowCount = 0;
        int nBegin = HAL_GetTick();
        unsigned int nNumber1 = ShoulderAngle(); //ST3806ReadNumber(ST3806_CHANNEL_1);
        unsigned int nNumber2 = ElbowAngle();  //ST3806ReadNumber(ST3806_CHANNEL_2);
        int nEnd = HAL_GetTick();
        printf("%d %d %d\r\n", nNumber1, nNumber2, nEnd-nBegin);
    }

3.读取两个关节的最大值和最小值

从顶部看肩关节和肘关节的角度极值分别为:

(1)肩关节角度极值范围
  • 顺时针: 11157
  • 逆时针: 23376

▲ 顺时针旋转,从左90°旋转刀右90°

▲ 顺时针旋转,从左90°旋转刀右90°

(2)肘关节角度极值范围
  • 顺时针: 23913
  • 逆时针: 27646

▲ 顺时针旋转,从左135°旋转刀右135°

▲ 顺时针旋转,从左135°旋转刀右135°

4.角度左右补偿

(1)目标

由于传感器一周的分辨率是15bit,角度是以 θ T = 0 x 8000 \theta _T = 0x8000 θT=0x8000为周期重复的。所以机械臂在旋转过程中它的数值可能会从0x8000→0,或者0→0x8000突变。

通过角度增加一个偏置 θ o f f s e t \theta _{offset} θoffset,使得修正后的角度,在机械臂旋转范围内,不会出现上述的突变。实际的角度通过如下公式计算:
θ n e w = ( θ + θ o f f s e t ) % 0 x 8000 \theta _{new} = \left( {\theta + \theta _{offset} } \right)\% 0x8000 θnew=(θ+θoffset)%0x8000

(2)获得偏移量

如何获得最佳的偏移量 θ o f f s e t \theta _{offset} θoffset使得输出的角度值范围能够位于0x0 ~ 0x8000的中间位置0x4000呢?

下面给出了角度偏移量 θ o f f s e t = Δ A \theta _{offset} = \Delta A θoffset=ΔA的计算公式。

#!/usr/local/bin/python
# -*- coding: gbk -*-
#============================================================
# TEST1.PY                     -- by Dr. ZhuoQing 2020-12-21
#
# Note:
#============================================================
from headm import *
from tsmodule.tsstm32       import *
def deltaA(amin, amax):
    amax = amax if amax >= amin else 0x8000+amax
    a0 = (amin + amax) // 2
    delA = 0x4000-a0 if a0 <= 0x4000 else 0xc000-a0
    return delA
#------------------------------------------------------------
data = stm32memo(1)
delA = deltaA(data[0], data[-1])
newdata = [(a + delA) % 0x8000 for a in data]
printf('DeltaA:%d'%delA)
printf(data, newdata)
plt.plot(data, label='Origin')
plt.plot(newdata, label='Modifier')
plt.xlabel("Sample")
plt.ylabel("Angle")
plt.grid(True)
plt.tight_layout()
plt.legend(loc='upper right')
plt.show()
#------------------------------------------------------------
#        END OF FILE : TEST1.PY
#============================================================
(3)测试数值

对于肩关节: Δ A = 15176 \Delta A = 15176 ΔA=15176

▲ 肩关节经过修正后的角度变化范围

▲ 肩关节经过修正后的角度变化范围

对于肘关节的数据: Δ A = 7433 \Delta A = 7433 ΔA=7433
▲ 肘关节角度补偿前和补偿后的曲线

▲ 肘关节角度补偿前和补偿后的曲线

 

03 程序接口


下面分别是在control.h, control.c下的两个接口程序:


//------------------------------------------------------------------------------
#define ARM_SHOULDER_OFFSET     15176
#define ARM_ELBOW_OFFSET        7433

unsigned int ShoulderAngle(void);
unsigned int ElbowAngle(void);

//------------------------------------------------------------------------------
unsigned int ShoulderAngle(void) {
    unsigned int nAngle;
    nAngle = ST3806ReadNumber(ST3806_CHANNEL_1);
    return (nAngle + ARM_SHOULDER_OFFSET) & 0x7fff;
}

unsigned int ElbowAngle(void) {
    unsigned int nAngle;
    nAngle = ST3806ReadNumber(ST3806_CHANNEL_2);
    return (nAngle + ARM_ELBOW_OFFSET) & 0x7fff;    
}

 

※ 附录


1.ST3806角度传感器的读写程序

/*
**==============================================================================
** ST3806.C:             -- by Dr. ZhuoQing, 2020-09-14
**
**==============================================================================
*/

#include "stm32f1xx_hal.h"
#include <stdio.h>
#include <math.h>
#include <string.h>
#include "stm32f1xxa.h"

//------------------------------------------------------------------------------
#define ST3806_GLOBALS        1              // Define the global variables
#include "ST3806.H"

//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
void ST3806Init(unsigned char ucAddress) {  // Default ucAddress should be 1
    OFF(ST3806_DIR1_PIN);
    OUT(ST3806_DIR1_PIN);
    OFF(ST3806_DIR2_PIN);
    OUT(ST3806_DIR2_PIN);
    
    g_ucST3806Address = ucAddress;    
    g_ucST3806Channel = ST3806_CHANNEL_1;
}

//------------------------------------------------------------------------------
void UART2ClearBuffer(void) {
    while(1) {
        if(!UART2_CANRECE) return;
        unsigned char ucChar;
        UART2ReceChar(&ucChar);
    }

}

//------------------------------------------------------------------------------
void ST3806SetChannel(unsigned char ucChannel) {
    g_ucST3806Channel = ucChannel;
    OFF(ST3806_DIR1_PIN);
    OFF(ST3806_DIR2_PIN);
}

void ST3806DirON(void) {
    if(g_ucST3806Channel == ST3806_CHANNEL_1) {
        OFF(ST3806_DIR2_PIN);
        ON(ST3806_DIR1_PIN);
    } else {
        OFF(ST3806_DIR1_PIN);
        ON(ST3806_DIR2_PIN);        
    }

}

void ST3806DirOFF(void) {
    OFF(ST3806_DIR2_PIN);
    OFF(ST3806_DIR1_PIN);
}

//------------------------------------------------------------------------------
unsigned int ModbusCRC(unsigned char * pByte, int nLength) {
    unsigned int nCRC;
    unsigned int i;
    unsigned char ucByte, j;
    
    nCRC = 0xffff;
    
    for(i = 0; i < nLength; i ++) {
        ucByte = *(pByte + i);
        nCRC ^= (unsigned int)ucByte;
        
        for(j = 0; j < 8; j ++) {
            if((nCRC & 0x1) != 0) {
                nCRC >>= 1;
                nCRC ^= 0xA001;
            } else nCRC >>= 1;
        }        
    }
            
    return nCRC;
}

//------------------------------------------------------------------------------
void ST3806AppendCRC(unsigned char ucLength) {
    unsigned int nCRC;
    nCRC = ModbusCRC(g_ucST3806Buffer, ucLength);
    g_ucST3806Buffer[ucLength + 1] = (unsigned char)(nCRC >> 8);
    g_ucST3806Buffer[ucLength] = (unsigned char)nCRC;
}

//------------------------------------------------------------------------------
void ST3806SendBuffer(unsigned char ucLength) {
    unsigned char i;

    ST3806AppendCRC(ucLength);
    
    ST3806DirON();
    
    for(i = 0; i < ucLength + 2; i ++) {
        UART2SendChar(g_ucST3806Buffer[i]);
    }
     
    ST3806DirOFF();
    
    UART2_CLEAR;

}

//------------------------------------------------------------------------------
unsigned char ST3806SetBaud115200(void) {
    unsigned char i, c;
    
    g_ucST3806Buffer[0] = g_ucST3806Address;
    g_ucST3806Buffer[1] = 0x10;
    g_ucST3806Buffer[2] = 0x0;
    g_ucST3806Buffer[3] = 0x3;
    g_ucST3806Buffer[4] = 0x0;
    g_ucST3806Buffer[5] = 0x1;
    g_ucST3806Buffer[6] = 0x2;
    g_ucST3806Buffer[7] = 0x0;
    g_ucST3806Buffer[8] = 0x5;
    
    ST3806SendBuffer(9);
    
    for(i = 0; i < 100; i ++) {
        if(UART2ReceChar(&c) == 0) break;
    }
    
    if(i < 100)
        g_ucST3806Buffer[0] = c;
    else return 1;

/*        
    for(i = 1; i < 8; i ++) {
        if(UART2ReceChar(&c)) return 10+i;
        g_ucST3806Buffer[i] = c;
    }
*/   
    return 0;
}

//------------------------------------------------------------------------------
unsigned char ST3806ReadData2Buffer(void) {
    unsigned char i, c;
    
    g_ucST3806Buffer[0] = g_ucST3806Address;
    g_ucST3806Buffer[1] = 0x3;
    g_ucST3806Buffer[2] = 0x0;
    g_ucST3806Buffer[3] = 0x0;
    g_ucST3806Buffer[4] = 0x0;
    g_ucST3806Buffer[5] = 0x1;
    ST3806SendBuffer(6);
        
    g_ucST3806Buffer[3] = 0x0;
    g_ucST3806Buffer[4] = 0x0;
    g_ucST3806Buffer[5] = 0x0;
    g_ucST3806Buffer[6] = 0x0;
    
    for(i = 0; i < 10; i ++) {
        if(UART2ReceChar(&c) == 0) break;
    }
    
    if(i < 10)
        g_ucST3806Buffer[0] = c;
    else return 1;
        
    for(i = 1; i < 7; i ++) {
        if(UART2ReceChar(&c)) return 1;
        g_ucST3806Buffer[i] = c;
    }
    
    return 0;

}

//------------------------------------------------------------------------------
unsigned char ST3806ReadConfig2Buffer(void) {
    unsigned char i, c;
    
    g_ucST3806Buffer[0] = g_ucST3806Address;
    g_ucST3806Buffer[1] = 0x3;
    g_ucST3806Buffer[2] = 0x0;
    g_ucST3806Buffer[3] = 0x0;
    g_ucST3806Buffer[4] = 0x0;
    g_ucST3806Buffer[5] = 0x3;
    ST3806SendBuffer(6);
        
    for(i = 0; i < 10; i ++) {
        if(UART2ReceChar(&c) == 0) break;
    }
    
    if(i < 10)
        g_ucST3806Buffer[0] = c;
    else return 1;

    for(i = 1; i < 11; i ++) {
        if(UART2ReceChar(&c)) return 1;
        g_ucST3806Buffer[i] = c;
    }
    
    return 0;
}

//------------------------------------------------------------------------------
unsigned int ST3806Buffer2Number(void) {
    unsigned int nNumber;
    nNumber = g_ucST3806Buffer[3];
    nNumber = (nNumber << 8) + g_ucST3806Buffer[4];
    return nNumber;
}

//------------------------------------------------------------------------------
unsigned int ST3806ReadNumber(unsigned char ucChannel) {    
    ST3806SetChannel(ucChannel);
    ST3806ReadData2Buffer();
    return ST3806Buffer2Number();
}

//==============================================================================
//                END OF THE FILE : ST3806.C
//------------------------------------------------------------------------------
/*
**==============================================================================
** ST3806.H:            -- by Dr. ZhuoQing, 2020-12-18
**
**  Description:
**
**==============================================================================
*/
#ifndef __ST3806__
#define __ST3806__
//------------------------------------------------------------------------------
#ifdef ST3806_GLOBALS
   #define ST3806_EXT
#else
   #define ST3806_EXT extern
#endif // ST3806_GLOBALS
//------------------------------------------------------------------------------
#define ST3806_ADDRESS          1

#define ST3806_DIR1_PIN         GPIOB,7
#define ST3806_DIR2_PIN         GPIOB,6

//==============================================================================
void ST3806Init(unsigned char ucAddress);         // ucAddress: Default is 1

#define ST3806_CHANNEL_1        0
#define ST3806_CHANNEL_2        1

ST3806_EXT unsigned char g_ucST3806Channel;
void ST3806SetChannel(unsigned char ucChannel);
void ST3806DirON(void);
void ST3806DirOFF(void);

//------------------------------------------------------------------------------
#define ST3806_BUFFER           16

ST3806_EXT unsigned char g_ucST3806Buffer[ST3806_BUFFER];
ST3806_EXT unsigned char g_ucST3806Address;

unsigned int ModbusCRC(unsigned char * pByte, int nLength);
void ST3806AppendCRC(unsigned char ucLength);

//------------------------------------------------------------------------------
void ST3806SendCharBuffer(unsigned char ucLength);

unsigned char ST3806ReadData2Buffer(void);
unsigned char ST3806ReadConfig2Buffer(void);

unsigned int ST3806Buffer2Number(void);

//------------------------------------------------------------------------------
unsigned int ST3806ReadNumber(unsigned char ucChannel); // Return about 2ms(&115200bps)

unsigned char ST3806SetBaud115200(void);

//------------------------------------------------------------------------------
#define UART2_CLEAR         UART2ClearBuffer()
void UART2ClearBuffer(void);

//==============================================================================
//             END OF THE FILE : ST3806.H
//------------------------------------------------------------------------------
#endif // __ST3806__

■ 相关文献链接:


  1. 接口板上的电路AD工程文件:AD\XQWF\2020\机械臂\CNTINTERFACE.SchDoc ↩︎

  2. 调试工程文件:STM32工程文件:STM32\Application\XQWF\2020\CNTSTM103\ ↩︎

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

卓晴

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值