找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 248|回复: 1
打印 上一主题 下一主题
收起左侧

Arduino控制小米微电机(基于MCP2515)运动

[复制链接]
跳转到指定楼层
楼主
一、前言
最近接触到一个项目需要用到小米电机,本来是想用STM32进行控制,但是必须得使用Arduino进行开发,于是尝试了一下。小米电机是基于CAN通讯的,但恰好Arduino不带CAN通讯协议,于是难度上了一个档次。在查阅了相关资料后,偶然间看到B站某位大佬的视频,得到了启发,在此感谢这位大佬,并成功实现了目标功能,链接如下:www.bilibili.com/video/BV1P64y1p7hx/

二、准备
硬件:
1、MCP2515 SPI2CAN转换器 *1
2、小米微电机 *1
3、电机转接头 *1
4、Arduino Nano *1
5、转接线 *若干
6、usb to can转换头

知识储备:
1、掌握Arduino开发,能读懂程序
2、了解CAN协议
3、了解小米微电机相关参数,电机运转方式

三、接线及库的准备
参考一篇文章的CAN协议进行接线,链接如下:gitcodecom/autowp/arduino-mcp2515/overview?utm_source=csdn_github_accelerator&isLogin=1



注意,小米电机供电为24V,电流在额定电流左右即可。具体相关参数在小米电机手册中查看。CAN通讯线最好双绞。

并下载相关库函数,导入我们的library文件夹中
CAN库需要我们另外下载,下载渠道很多,这里贴一个下载链接:githubcom/sandeepmistry/arduino-CAN

四、电机ID
使用usb2can转换头连接电脑,并进行如下设置:




五、运行代码xm_motor.cpp

  1. #include "xm_motor.h"
  2. #include <Arduino.h>
  3. MCP2515 mcp2515(10);
  4. struct can_frame canMsg;
  5. uint32_t ExtId;             //定义can扩展id
  6. uint8_t rx_data[8];         //接收数据
  7. uint32_t Motor_Can_ID;      //接收数据电机ID
  8. static uint8_t byte_ls[4];  //转换临时数据
  9. uint8_t tx_data[8];         //can写入的数据

  10. int filter(int queue[], char n)  //数值过滤
  11. {
  12.   int sum = 0;
  13.   byte i;
  14.   int maxsz = queue[0];  //寻找最大值
  15.   int minsz = queue[0];  //寻找最小值
  16.   for (i = 0; i < n; i++) {
  17.     if (maxsz < queue[i]) { maxsz = queue[i]; }  //寻找最大值和最小值
  18.     if (minsz > queue[i]) { minsz = queue[i]; }
  19.   }

  20.   for (i = 0; i < n; i++) {
  21.     sum += queue[i];
  22.   }
  23.   sum = sum - maxsz - minsz;  //去除最大值和最小值
  24.   return (sum / (n - 2));
  25. }  //数值过滤
  26. int check(byte ao_port, byte n)  //采样
  27. {

  28.   int check_date[n];  //定义采样数组
  29.   for (byte i = 0; i < n; ++i) {
  30.     check_date[i] = analogRead(ao_port);  //获得指定传感器数据
  31.   }

  32.   int vvvv = filter(check_date, n);
  33.   return vvvv;

  34. }  //采样
  35. void xm_can_start() {

  36.   mcp2515.reset();
  37.   mcp2515.setBitrate(CAN_1000KBPS, MCP_8MHZ);
  38.   mcp2515.setNormalMode();
  39.   delay(4);
  40. }
  41. static uint8_t* Float_to_Byte(float f)  //float分解成四个byte数据
  42. {
  43.   unsigned long longdata = 0;
  44.   longdata = *(unsigned long*)&f;
  45.   byte_ls[0] = (longdata & 0xFF000000) >> 24;
  46.   byte_ls[1] = (longdata & 0x00FF0000) >> 16;
  47.   byte_ls[2] = (longdata & 0x0000FF00) >> 8;
  48.   byte_ls[3] = (longdata & 0x000000FF);
  49.   return byte_ls;
  50. }
  51. static float uint16_to_float(uint16_t x, float x_min, float x_max, int bits)  //把uint 16位数据变成浮点数 用在接受数据的处理上
  52. {
  53.   uint32_t span = (1 << bits) - 1;
  54.   float offset = x_max - x_min;
  55.   return offset * x / span + x_min;
  56. }
  57. static int float_to_uint(float x, float x_min, float x_max, int bits)  //把浮点数转换成uint_16 用在位置 扭矩 上面
  58. {
  59.   float span = x_max - x_min;
  60.   float offset = x_min;
  61.   if (x > x_max) x = x_max;
  62.   else if (x < x_min) x = x_min;
  63.   return (int)((x - offset) * ((float)((1 << bits) - 1)) / span);
  64. }
  65. void exid_count(uint8_t Communication_Type, uint16_t msid, uint8_t can_id)  //计算扩展ExtId,Communication_Type通信类型,msid主canid,
  66. {
  67.   uint8_t msid_l = msid;
  68.   uint8_t msid_h = msid >> 8;
  69.   uint32_t di_data = ((0xFFFFFFFF & Communication_Type) << 24) | 0x00FFFFFF;  //求出高32位
  70.   uint32_t di_datab = ((0xFFFFFFFF & msid_h) << 16) | 0xFF00FFFF;             //求出高32位
  71.   uint32_t di_datac = ((0xFFFFFFFF & msid_l) << 8) | 0xFFFF00FF;              //求出高32位
  72.   uint32_t di_datad = (0xFFFFFFFF & can_id) | 0xFFFFFF00;                     //求出高32位
  73.   ExtId = (di_data & di_datab & di_datac & di_datad);
  74. }  //计算扩展ExtId,Communication_Type通信类型,msid主canid,

  75. void data_count_dcs(uint16_t Index, float Value, char Value_type) {
  76.   //计算can在 单参数写入,通信类型 12下发送的8位数据,Index 是命令类型0: 运控模式1: 位置模式2: 速度模式3: 电流模式Value是0 值,Value_type是数据类型,浮点数用f非浮点用s
  77.   //速度数值要 注明浮点数 f ,
  78.   //写入扭矩 n


  79.   canMsg.data[0] = Index;
  80.   canMsg.data[1] = Index >> 8;
  81.   canMsg.data[2] = 0x00;
  82.   canMsg.data[3] = 0x00;
  83.   if (Value_type == 'f') {
  84.     Float_to_Byte(Value);
  85.     canMsg.data[4] = byte_ls[3];
  86.     canMsg.data[5] = byte_ls[2];
  87.     canMsg.data[6] = byte_ls[1];
  88.     canMsg.data[7] = byte_ls[0];
  89.   } else if (Value_type == 's') {
  90.     canMsg.data[4] = (uint8_t)Value;
  91.     canMsg.data[5] = 0x00;
  92.     canMsg.data[6] = 0x00;
  93.     canMsg.data[7] = 0x00;
  94.   }
  95. }  //计算can在 单参数写入,通信类型 12下发送的8位数据,Index 是命令类型Value是值,Value_type是数据类型,浮点数用f非浮点用s
  96. void data_count_zero()  //can数据置零
  97. {
  98.   canMsg.data[0] = 0x00;
  99.   canMsg.data[1] = 0x00;
  100.   canMsg.data[2] = 0x00;
  101.   canMsg.data[3] = 0x00;
  102.   canMsg.data[4] = 0x00;
  103.   canMsg.data[5] = 0x00;
  104.   canMsg.data[6] = 0x00;
  105.   canMsg.data[7] = 0x00;
  106. }  //can数据置零

  107. void motor_enable(uint8_t id = 1)  //电机使能 电机canid
  108. {
  109.   exid_count(3, Master_CAN_ID, id);
  110.   canMsg.can_id = ExtId | CAN_EFF_FLAG;
  111.   canMsg.can_dlc = 8;
  112.   data_count_zero();
  113.   mcp2515.sendMessage(MCP2515::TXB1, &canMsg);
  114.   delay(4);
  115. }  //电机使能 电机canid
  116. void motor_mode(uint8_t id, char type)  //电机运行模式 电机canid  模式值1位置模式2速度模式 3 电流模式0运控模式
  117. {
  118.   exid_count(0x12, Master_CAN_ID, CanID);
  119.   canMsg.can_id = ExtId | CAN_EFF_FLAG;  //ExtId  0x12000001
  120.   canMsg.can_dlc = 8;
  121.   data_count_dcs(0x7005, type, 's');
  122.   mcp2515.sendMessage(MCP2515::TXB1, &canMsg);
  123.   delay(4);
  124. }

  125. void motor_speed_value(uint8_t id, float speed_ref) {  //设置速度模式下的参数转速
  126.   exid_count(0x12, Master_CAN_ID, CanID);
  127.   canMsg.can_id = ExtId | CAN_EFF_FLAG;  //ExtId  0x12000001
  128.   canMsg.can_dlc = 8;
  129.   data_count_dcs(0x700A, speed_ref, 'f');
  130.   mcp2515.sendMessage(MCP2515::TXB1, &canMsg);
  131.   delay(4);
  132. }

  133. void motor_pos_zero(uint8_t id = 1)  //位置置0
  134. {
  135.   exid_count(6, Master_CAN_ID, id);
  136.   canMsg.can_id = ExtId | CAN_EFF_FLAG;
  137.   canMsg.can_dlc = 8;
  138.   data_count_zero();
  139.   canMsg.data[0] = 1;
  140.   mcp2515.sendMessage(MCP2515::TXB1, &canMsg);
  141.   delay(4);

  142. }  //位置置0
  143. void motor_pos_value(uint8_t id, float speed_ref) {  //设置位置模式下的位置
  144.   exid_count(0x12, Master_CAN_ID, CanID);
  145.   canMsg.can_id = ExtId | CAN_EFF_FLAG;  //ExtId  0x12000001
  146.   canMsg.can_dlc = 8;
  147.   data_count_dcs(0x7016, speed_ref, 'f');
  148.   mcp2515.sendMessage(MCP2515::TXB1, &canMsg);
  149.   delay(4);
  150. }

  151. void motor_pow_value(uint8_t id, float torque, float limit_cur, float kp = 1, float ki = 0.0158) {  //设置速度 电流限制,kp,kd
  152.   exid_count(0x12, Master_CAN_ID, CanID);
  153.   canMsg.can_id = ExtId | CAN_EFF_FLAG;  //ExtId  0x12000001
  154.   canMsg.can_dlc = 8;
  155.   data_count_dcs(0x7017, torque, 'f');
  156.   mcp2515.sendMessage(MCP2515::TXB1, &canMsg);
  157.   delay(4);
  158.   data_count_dcs(0x7018, limit_cur, 'f');
  159.   mcp2515.sendMessage(MCP2515::TXB1, &canMsg);
  160.   delay(4);
  161.   data_count_dcs(0x7010, kp, 'f');
  162.   mcp2515.sendMessage(MCP2515::TXB1, &canMsg);
  163.   delay(4);
  164.   data_count_dcs(0x7011, ki, 'f');
  165.   mcp2515.sendMessage(MCP2515::TXB1, &canMsg);
  166.   delay(4);
  167. }</font></font></font>
复制代码
xm_motor.h

  1. #include "Arduino.h"
  2. #include <SPI.h>
  3. #include "mcp2515.h"

  4. #define pi 3.14159265359f
  5. #define Communication_Type_MotorEnable 0x03
  6. #define Master_CAN_ID 0x00
  7. #define CanID 0x01
  8. #define P_MIN -12.5f
  9. #define P_MAX 12.5f
  10. #define V_MIN -30.0f
  11. #define V_MAX 30.0f
  12. #define KP_MIN 0.0f
  13. #define KP_MAX 500.0f
  14. #define KD_MIN 0.0f
  15. #define KD_MAX 5.0f
  16. #define T_MIN -12.0f
  17. #define T_MAX 12.0f
  18. #define MAX_P 720
  19. #define MIN_P -720

  20. #define Communication_Type_GetID 0x00           //获取设备的ID和64位MCU唯一标识符
  21. #define Communication_Type_MotionControl 0x01         //用来向主机发送控制指令
  22. #define Communication_Type_MotorRequest 0x02        //用来向主机反馈电机运行状态
  23. #define Communication_Type_MotorEnable 0x03            //电机使能运行
  24. #define Communication_Type_MotorStop 0x04            //电机停止运行
  25. #define Communication_Type_SetPosZero 0x06            //设置电机机械零位
  26. #define Communication_Type_CanID 0x07                //更改当前电机CAN_ID
  27. #define Communication_Type_Control_Mode 0x12
  28. #define Communication_Type_GetSingleParameter 0x11        //读取单个参数
  29. #define Communication_Type_SetSingleParameter 0x12        //设定单个参数
  30. #define Communication_Type_ErrorFeedback 0x15            //故障反馈帧
  31. //参数读取宏定义
  32. #define Run_mode 0x7005        
  33. #define Iq_Ref   0x7006
  34. #define Spd_Ref  0x700A
  35. #define Limit_Torque 0x700B
  36. #define Cur_Kp 0x7010
  37. #define Cur_Ki 0x7011
  38. #define Cur_Filt_Gain 0x7014
  39. #define Loc_Ref 0x7016
  40. #define Limit_Spd 0x7017
  41. #define Limit_Cur 0x7018
  42. #define Gain_Angle 720/32767.0
  43. #define Bias_Angle 0x8000
  44. #define Gain_Speed 30/32767.0
  45. #define Bias_Speed 0x8000
  46. #define Gain_Torque 12/32767.0
  47. #define Bias_Torque 0x8000
  48. #define Temp_Gain   0.1

  49. #define Motor_Error 0x00
  50. #define Motor_OK 0X01
  51. enum CONTROL_MODE   //控制模式定义
  52. {
  53.     Motion_mode = 0,//运控模式  
  54.     Position_mode,  //位置模式
  55.     Speed_mode,     //速度模式  
  56.     Current_mode    //电流模式
  57. };
  58. enum ERROR_TAG      //错误回传对照
  59. {
  60.     OK                 = 0,//无故障
  61.     BAT_LOW_ERR        = 1,//欠压故障
  62.     OVER_CURRENT_ERR   = 2,//过流
  63.     OVER_TEMP_ERR      = 3,//过温
  64.     MAGNETIC_ERR       = 4,//磁编码故障
  65.     HALL_ERR_ERR       = 5,//HALL编码故障
  66.     NO_CALIBRATION_ERR = 6//未标定
  67. };
  68. typedef struct{           //小米电机结构体
  69.         uint8_t CAN_ID;       //CAN ID
  70.     uint8_t MCU_ID;       //MCU唯一标识符【后8位,共64位】
  71.         float Angle;          //回传角度
  72.         float Speed;          //回传速度
  73.         float Torque;         //回传力矩
  74.         float Temp;                          //回传温度
  75.         
  76.         uint16_t set_current;
  77.         uint16_t set_speed;
  78.         uint16_t set_position;
  79.         
  80.         uint8_t error_code;
  81.         
  82.         float Angle_Bias;
  83.         
  84. }MI_Motor;
  85. extern MI_Motor mi_motor;//预先定义1个小米电机

  86. void xm_can_start();
  87. void motor_enable( uint8_t id=1 ) ;
  88. void motor_mode( uint8_t id ,char type );
  89. void motor_speed_value( uint8_t id ,float speed_ref );//-30rad-30rad
  90. void motor_yk( uint8_t id ,float torque, float MechPosition, float speed, float kp, float kd );
  91. void motor_pos_zero( uint8_t id=1 ); //位置置0
  92. void motor_pos_value( uint8_t id ,float speed_ref );
  93. void motor_pow_value( uint8_t id , float torque,float limit_cur ,float  kp,float kd );

  94. int filter(int queue[], char n) ; //数值过滤
  95. int check(byte ao_port, byte n); //获取ad口电压</font></font></font>
复制代码

main.ino


  1. #include "xm_motor.h"
  2. String comdata = "";  //蓝牙字符
  3. void setup() {
  4.   while (!Serial)
  5.     ;
  6.   Serial.begin(115200);

  7.   xm_can_start();                         //初始化can设置
  8.   motor_enable(1);                        //使能id 1电机
  9.   motor_pos_zero(1);                      //位置置0
  10.   motor_mode(1, 1);                       //电机运行模式 电机canid 模式值 1位置模式2速度模式 3 电流模式0运控模式
  11.   motor_pow_value(1, 30, 20, 0.2, 0.13);  //uint8_t id , float torque 位置模式速度限制 ,float limit_cur ,float  kp=0.8,float ki=0.13  id 速度 电流限制,kp,kd  速度 0~30rad/s 6.28rad 等于1圈  电流最大23A
  12.   Serial.println("Example: Write to CAN");
  13. }
  14. void loop() {

  15.   int speed_value = 100;
  16.   int speed_valueb = map(speed_value, 0, 1023, 0, 30);  //前进模拟量
  17.   float bb = (float)speed_value;
  18.   bb = bb / 163.9423;
  19.   if (bb > 6.2) { bb = 6.2; }
  20.   motor_pos_value(1, bb);          //电机位置模式赋值,id,位置角度rad 2派=360度。

  21.   delay(20);
  22. }
复制代码

在main.ino中更改loop里speed_value的值即可更改角度,在0-1023范围内进行更改。

评分

参与人数 1黑币 +50 收起 理由
admin + 50 共享资料的黑币奖励!

查看全部评分

分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享淘帖 顶 踩
回复

使用道具 举报

沙发
ID:444392 发表于 2024-4-26 22:55 | 只看该作者
之前我理解错了,你说的双绞线应该就是使用网线
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表