找回密碼
 立即注冊

QQ登錄

只需一步,快速開始

搜索
查看: 2474|回復(fù): 3
收起左側(cè)

Arduino控制小米微電機(jī)(基于MCP2515)運(yùn)動

[復(fù)制鏈接]
ID:1115042 發(fā)表于 2024-4-1 20:04 | 顯示全部樓層 |閱讀模式
一、前言
最近接觸到一個(gè)項(xiàng)目需要用到小米電機(jī),本來是想用STM32進(jìn)行控制,但是必須得使用Arduino進(jìn)行開發(fā),于是嘗試了一下。小米電機(jī)是基于CAN通訊的,但恰好Arduino不帶CAN通訊協(xié)議,于是難度上了一個(gè)檔次。在查閱了相關(guān)資料后,偶然間看到B站某位大佬的視頻,得到了啟發(fā),在此感謝這位大佬,并成功實(shí)現(xiàn)了目標(biāo)功能,鏈接如下:www.bilibili.com/video/BV1P64y1p7hx/

二、準(zhǔn)備
硬件:
1、MCP2515 SPI2CAN轉(zhuǎn)換器 *1
2、小米微電機(jī) *1
3、電機(jī)轉(zhuǎn)接頭 *1
4、Arduino Nano *1
5、轉(zhuǎn)接線 *若干
6、usb to can轉(zhuǎn)換頭

知識儲備:
1、掌握Arduino開發(fā),能讀懂程序
2、了解CAN協(xié)議
3、了解小米微電機(jī)相關(guān)參數(shù),電機(jī)運(yùn)轉(zhuǎn)方式

三、接線及庫的準(zhǔn)備
參考一篇文章的CAN協(xié)議進(jìn)行接線,鏈接如下:gitcodecom/autowp/arduino-mcp2515/overview?utm_source=csdn_github_accelerator&isLogin=1

7801ea72e0294b8284e1c1a4dd129188.png
3437acd1eb7340fb9f2a59827287334e.png
注意,小米電機(jī)供電為24V,電流在額定電流左右即可。具體相關(guān)參數(shù)在小米電機(jī)手冊中查看。CAN通訊線最好雙絞。

并下載相關(guān)庫函數(shù),導(dǎo)入我們的library文件夾中
CAN庫需要我們另外下載,下載渠道很多,這里貼一個(gè)下載鏈接:githubcom/sandeepmistry/arduino-CAN

四、電機(jī)ID
使用usb2can轉(zhuǎn)換頭連接電腦,并進(jìn)行如下設(shè)置:
c18745a9aa0d4466b46e4fa2bcb5c7f6.png

cd6c219b881c4c2bacc2baa9d7732e07.png

五、運(yùn)行代碼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擴(kuò)展id
  6. uint8_t rx_data[8];         //接收數(shù)據(jù)
  7. uint32_t Motor_Can_ID;      //接收數(shù)據(jù)電機(jī)ID
  8. static uint8_t byte_ls[4];  //轉(zhuǎn)換臨時(shí)數(shù)據(jù)
  9. uint8_t tx_data[8];         //can寫入的數(shù)據(jù)

  10. int filter(int queue[], char n)  //數(shù)值過濾
  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. }  //數(shù)值過濾
  26. int check(byte ao_port, byte n)  //采樣
  27. {

  28.   int check_date[n];  //定義采樣數(shù)組
  29.   for (byte i = 0; i < n; ++i) {
  30.     check_date[i] = analogRead(ao_port);  //獲得指定傳感器數(shù)據(jù)
  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分解成四個(gè)byte數(shù)據(jù)
  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位數(shù)據(jù)變成浮點(diǎn)數(shù) 用在接受數(shù)據(jù)的處理上
  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)  //把浮點(diǎn)數(shù)轉(zhuǎn)換成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)  //計(jì)算擴(kuò)展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. }  //計(jì)算擴(kuò)展ExtId,Communication_Type通信類型,msid主canid,

  75. void data_count_dcs(uint16_t Index, float Value, char Value_type) {
  76.   //計(jì)算can在 單參數(shù)寫入,通信類型 12下發(fā)送的8位數(shù)據(jù),Index 是命令類型0: 運(yùn)控模式1: 位置模式2: 速度模式3: 電流模式Value是0 值,Value_type是數(shù)據(jù)類型,浮點(diǎn)數(shù)用f非浮點(diǎn)用s
  77.   //速度數(shù)值要 注明浮點(diǎn)數(shù) 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. }  //計(jì)算can在 單參數(shù)寫入,通信類型 12下發(fā)送的8位數(shù)據(jù),Index 是命令類型Value是值,Value_type是數(shù)據(jù)類型,浮點(diǎn)數(shù)用f非浮點(diǎn)用s
  96. void data_count_zero()  //can數(shù)據(jù)置零
  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數(shù)據(jù)置零

  107. void motor_enable(uint8_t id = 1)  //電機(jī)使能 電機(jī)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. }  //電機(jī)使能 電機(jī)canid
  116. void motor_mode(uint8_t id, char type)  //電機(jī)運(yùn)行模式 電機(jī)canid  模式值1位置模式2速度模式 3 電流模式0運(yùn)控模式
  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) {  //設(shè)置速度模式下的參數(shù)轉(zhuǎn)速
  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) {  //設(shè)置位置模式下的位置
  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) {  //設(shè)置速度 電流限制,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>
復(fù)制代碼
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           //獲取設(shè)備的ID和64位MCU唯一標(biāo)識符
  21. #define Communication_Type_MotionControl 0x01         //用來向主機(jī)發(fā)送控制指令
  22. #define Communication_Type_MotorRequest 0x02        //用來向主機(jī)反饋電機(jī)運(yùn)行狀態(tài)
  23. #define Communication_Type_MotorEnable 0x03            //電機(jī)使能運(yùn)行
  24. #define Communication_Type_MotorStop 0x04            //電機(jī)停止運(yùn)行
  25. #define Communication_Type_SetPosZero 0x06            //設(shè)置電機(jī)機(jī)械零位
  26. #define Communication_Type_CanID 0x07                //更改當(dāng)前電機(jī)CAN_ID
  27. #define Communication_Type_Control_Mode 0x12
  28. #define Communication_Type_GetSingleParameter 0x11        //讀取單個(gè)參數(shù)
  29. #define Communication_Type_SetSingleParameter 0x12        //設(shè)定單個(gè)參數(shù)
  30. #define Communication_Type_ErrorFeedback 0x15            //故障反饋幀
  31. //參數(shù)讀取宏定義
  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,//運(yùn)控模式  
  54.     Position_mode,  //位置模式
  55.     Speed_mode,     //速度模式  
  56.     Current_mode    //電流模式
  57. };
  58. enum ERROR_TAG      //錯(cuò)誤回傳對照
  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//未標(biāo)定
  67. };
  68. typedef struct{           //小米電機(jī)結(jié)構(gòu)體
  69.         uint8_t CAN_ID;       //CAN ID
  70.     uint8_t MCU_ID;       //MCU唯一標(biāo)識符【后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;//預(yù)先定義1個(gè)小米電機(jī)

  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) ; //數(shù)值過濾
  95. int check(byte ao_port, byte n); //獲取ad口電壓</font></font></font>
復(fù)制代碼

main.ino


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

  7.   xm_can_start();                         //初始化can設(shè)置
  8.   motor_enable(1);                        //使能id 1電機(jī)
  9.   motor_pos_zero(1);                      //位置置0
  10.   motor_mode(1, 1);                       //電機(jī)運(yùn)行模式 電機(jī)canid 模式值 1位置模式2速度模式 3 電流模式0運(yùn)控模式
  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);  //前進(jìn)模擬量
  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);          //電機(jī)位置模式賦值,id,位置角度rad 2派=360度。

  21.   delay(20);
  22. }
復(fù)制代碼

在main.ino中更改loop里speed_value的值即可更改角度,在0-1023范圍內(nèi)進(jìn)行更改。

評分

參與人數(shù) 1黑幣 +50 收起 理由
admin + 50 共享資料的黑幣獎(jiǎng)勵(lì)!

查看全部評分

回復(fù)

使用道具 舉報(bào)

ID:444392 發(fā)表于 2024-4-26 22:55 | 顯示全部樓層
之前我理解錯(cuò)了,你說的雙絞線應(yīng)該就是使用網(wǎng)線
回復(fù)

使用道具 舉報(bào)

ID:1119621 發(fā)表于 2024-5-7 15:32 | 顯示全部樓層
CAN Config Soft V2.3.9可以發(fā)一下嗎?
回復(fù)

使用道具 舉報(bào)

ID:380280 發(fā)表于 2024-10-2 23:51 | 顯示全部樓層
#include "mcp2515.h"
回復(fù)

使用道具 舉報(bào)

您需要登錄后才可以回帖 登錄 | 立即注冊

本版積分規(guī)則

手機(jī)版|小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術(shù)交流QQ群281945664

Powered by 單片機(jī)教程網(wǎng)

快速回復(fù) 返回頂部 返回列表