GM6020的转动控制

转动GM6020(具体方式)

提示:多种方式
慢慢写,现就一种方式。


提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

提示:这里可以添加本文要记录的大概内容:

例如:随着人工智能的不断发展,机器学习这门技术也越来越重要,很多人都开启了学习机器学习,本文就介绍了机器学习的基础内容。


提示:以下是本篇文章正文内容,下面案例可供参考

一、第一种转动方式

这只是运用库直接转动的方法,具体的还是需要自己写

1.首先正常配置
2.开启CAN1(引脚选择注意官方是PD0,PD1)
3.参数配置,因为GM6020电机比特率为1000000,配到这个应该是9 3 1或者9 2 2都可以
4.加上包含文件就可以,如下
(1)bsp_can.c

/****************************************************************************
 *  Copyright (C) 2018 RoboMaster.
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program. If not, see <http://www.gnu.org/licenses/>.
 ***************************************************************************/

#include "bsp_can.h"


moto_info_t motor_info[MOTOR_MAX_NUM];
uint16_t can_cnt;
uint8_t             rx_data[8];
CAN_RxHeaderTypeDef rx_header;

/**
  * @brief  init can filter, start can, enable can rx interrupt
  * @param  hcan pointer to a CAN_HandleTypeDef structure that contains
  *         the configuration information for the specified CAN.
  * @retval None
  */
void can_user_init(CAN_HandleTypeDef* hcan )
{
  CAN_FilterTypeDef  can_filter;

  can_filter.FilterBank = 0;                       // filter 0
  can_filter.FilterMode =  CAN_FILTERMODE_IDMASK;  // mask mode
  can_filter.FilterScale = CAN_FILTERSCALE_32BIT;
  can_filter.FilterIdHigh = 0;
  can_filter.FilterIdLow  = 0;
  can_filter.FilterMaskIdHigh = 0;
  can_filter.FilterMaskIdLow  = 0;                // set mask 0 to receive all can id
  can_filter.FilterFIFOAssignment = CAN_RX_FIFO0; // assign to fifo0
  can_filter.FilterActivation = ENABLE;           // enable can filter
  can_filter.SlaveStartFilterBank  = 14;          // only meaningful in dual can mode

  HAL_CAN_ConfigFilter(hcan, &can_filter);        // init can filter
  HAL_CAN_Start(&hcan1);                          // start can1
  HAL_CAN_ActivateNotification(&hcan1, CAN_IT_RX_FIFO0_MSG_PENDING); // enable can1 rx interrupt
}

/**
  * @brief  can rx callback, get motor feedback info
  * @param  hcan pointer to a CAN_HandleTypeDef structure that contains
  *         the configuration information for the specified CAN.
  * @retval None
  */
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)
{
//  CAN_RxHeaderTypeDef rx_header;
//  uint8_t             rx_data[8];
  if(hcan->Instance == CAN1)
  {
    HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &rx_header, rx_data); //receive can data
  }
  if ((rx_header.StdId >= FEEDBACK_ID_BASE)
   && (rx_header.StdId <  FEEDBACK_ID_BASE + MOTOR_MAX_NUM))                  // judge the can id
  {
    can_cnt ++;
    uint8_t index = rx_header.StdId - FEEDBACK_ID_BASE;                  // get motor index by can_id
    motor_info[index].rotor_angle    = ((rx_data[0] << 8) | rx_data[1]);
    motor_info[index].rotor_speed    = ((rx_data[2] << 8) | rx_data[3]);
    motor_info[index].torque_current = ((rx_data[4] << 8) | rx_data[5]);
    motor_info[index].temp           =   rx_data[6];
  }
  if (can_cnt == 500)
  {
    can_cnt = 0;
//    LED_GREEN_TOGGLE(); // green led blink indicate can comunication successful
  }
}

/**
  * @brief  send motor control message through can bus
  * @param  id_range to select can control id 0x1ff or 0x2ff
  * @param  motor voltage 1,2,3,4 or 5,6,7
  * @retval None
  */
void set_motor_voltage(uint8_t id_range, int16_t v1, int16_t v2, int16_t v3, int16_t v4)
{
  CAN_TxHeaderTypeDef tx_header;
  uint8_t             tx_data[8];

  tx_header.StdId = (id_range == 0)?(0x1ff):(0x2ff);
  tx_header.IDE   = CAN_ID_STD;
  tx_header.RTR   = CAN_RTR_DATA;
  tx_header.DLC   = 8;

  tx_data[0] = (v1>>8)&0xff;
  tx_data[1] =    (v1)&0xff;
  tx_data[2] = (v2>>8)&0xff;
  tx_data[3] =    (v2)&0xff;
  tx_data[4] = (v3>>8)&0xff;
  tx_data[5] =    (v3)&0xff;
  tx_data[6] = (v4>>8)&0xff;
  tx_data[7] =    (v4)&0xff;
  HAL_CAN_AddTxMessage(&hcan1, &tx_header, tx_data,(uint32_t*)CAN_TX_MAILBOX0);
}

(2)bsp_can.h

/****************************************************************************
 *  Copyright (C) 2018 RoboMaster.
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program. If not, see <http://www.gnu.org/licenses/>.
 ***************************************************************************/

#ifndef __BSP_CAN
#define __BSP_CAN

#include "can.h"

#define FEEDBACK_ID_BASE      0x205
#define CAN_CONTROL_ID_BASE   0x1ff
#define CAN_CONTROL_ID_EXTEND 0x2ff
#define MOTOR_MAX_NUM         7

typedef struct
{
    uint16_t can_id;
    int16_t  set_voltage;
    uint16_t rotor_angle;
    int16_t  rotor_speed;
    int16_t  torque_current;
    uint8_t  temp;
}moto_info_t;

void can_user_init(CAN_HandleTypeDef* hcan);
void set_motor_voltage(uint8_t id_range, int16_t v1, int16_t v2, int16_t v3, int16_t v4);
#endif


(3)main.c
在main.c中加入以下即可转动
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


总结

提示:这里对文章进行总结:

例如:以上就是今天要讲的内容,本文仅仅简单介绍了pandas的使用,而pandas提供了大量能使我们快速便捷地处理数据的函数和方法。

二、第二种方式驱动GM6020(开环)

1.在一个新建文件中 bsp_can.c 进行编写代码操作


#include "bsp_can.h"

#include "can.h"
#include "gpio.h"
#include "main.h"


void can_Init(CAN_HandleTypeDef*hcan)
{
	//配置can的过滤器
		  CAN_FilterTypeDef  can_filter;
		  can_filter.FilterBank = 0;                       // filter 0
		  can_filter.FilterMode =  CAN_FILTERMODE_IDMASK;  // mask mode
		  can_filter.FilterScale = CAN_FILTERSCALE_32BIT;
		  can_filter.FilterIdHigh = 0;
		  can_filter.FilterIdLow  = 0;
		  can_filter.FilterMaskIdHigh = 0;
		  can_filter.FilterMaskIdLow  = 0;                // set mask 0 to receive all can id
		  can_filter.FilterFIFOAssignment = CAN_RX_FIFO0; // assign to fifo0
		  can_filter.FilterActivation = ENABLE;           // enable can filter
		  can_filter.SlaveStartFilterBank  = 14;          // only meaningful in dual can mode

		  HAL_CAN_ConfigFilter(hcan, &can_filter);        // init can filter


	HAL_CAN_Start(&hcan1);//开启can
	HAL_CAN_ActivateNotification(&hcan1,CAN_IT_RX_FIFO0_MSG_PENDING);//开启在fifo0中的接收中断


}

void boxSend(uint16_t I1,uint16_t I2,uint16_t I3,uint16_t I4)//电压控制1,范围在-25000到25000
{
	uint8_t Data[8]={0};
	CAN_TxHeaderTypeDef txheader;//创建发送报文结构体
	txheader.DLC=8;
	txheader.IDE=CAN_ID_STD;
	txheader.RTR=CAN_RTR_DATA;
//	txheader.StdId=(ID==0)?(0x1ff):(0x2ff);//判断ID是哪个
	txheader.StdId=0x1ff;
	Data[0]=(uint8_t)(I1>>8);
	Data[1]=(uint8_t)I1;

	Data[2]=(uint8_t)(I2>>8);
	Data[3]=(uint8_t)I2;

	Data[4]=(uint8_t)(I3>>8);
	Data[5]=(uint8_t)I3;

	Data[6]=(uint8_t)(I4>>8);
	Data[7]=(uint8_t)I4;

	HAL_CAN_AddTxMessage(&hcan1,&txheader,Data,(uint32_t*)CAN_TX_MAILBOX0);
}


void motorset(Motor_t*Receive,uint8_t Data[])
{
	Receive->angle=(Data[0]<<8)|Data[1];//转子机械角度
	Receive->speeed=(Data[2]<<8)|Data[3];//转速
	Receive->torque=(Data[4]<<8)|Data[5];//输出转矩
	Receive->temp=Data[6];//温度
}

Motor_t GM6020[4];//控制四个电机

//void boxReceive(void)
//{
//	uint8_t ReceiveData[8]={0};
//	CAN_RxHeaderTypeDef Rxheader;//创建接收报文结构体
//	HAL_CAN_GetRxMessage(&hcan1,CAN_RX_FIFO0,&Rxheader,ReceiveData);//将信息接收到fifo0邮箱
//	switch(Rxheader.StdId)//判断ID
//	{
//	case 0x201:
//	{
//		motorset(&GM6020[0],ReceiveData);//处理接收到的数据,拿出来
//		break;
//	}
//
//	}
//
//}


//在fifo0的中断回调里面完成数据接收

void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef*hcan)
{
	if(hcan->Instance==CAN1)
	{
	    uint8_t ReceiveData[8]={0};
		CAN_RxHeaderTypeDef Rxheader;//创建接收报文结构体
		HAL_CAN_GetRxMessage(&hcan1,CAN_RX_FIFO0,&Rxheader,ReceiveData);//将信息接收到fifo0邮箱
		switch(Rxheader.StdId)//判断ID
		{
		case 0x205:
		{
			motorset(&GM6020[0],ReceiveData);//处理接收到的数据,拿出来
			break;
		}

		}
	}
}






/*
 * bsp_can.c
 *
 *  Created on: Jun 3, 2024
 *      Author: yu
 */

bsp_can.h





//*注意:编写回调函数不要写错了
2.main.c里面加入 
 can_Init(&hcan1);
 boxSend(1000,0,0,0);
	  HAL_Delay(20);
	  电机就可以转动了

# 三、闭环驱动GM6020
main.c的编写
```c
#include "main.h"
#include "can.h"
#include "gpio.h"
//
#include "bsp_can.h"
#include "pid.h"


#include "math.h"//加一个数学包
//加头文件


/* USER CODE BEGIN PV */
extern Motor_t;

int32_t a;//角度,0-8191
int32_t Current_position=0;//当前位置
int32_t current_angle;//当前角度
int32_t Last_angle=0;//上一次角度
double pi=3.14159265358979323846;
double speed;
int32_t new_err;//经过零点以后的值
int32_t err;//记录误差值

/* USER CODE END PV */
//声明变量

  /* USER CODE BEGIN 2 */
  can_Init(&hcan1);
  HAL_CAN_Start(&hcan1);

  Pid_Init(&GM6020_speed_pid[0],5,0,0,10000,0,3000,3000,0);//进行外环的位置初始化
  Pid_Init(&GM6020_speed_pid[1],5,0,0,10000,0,3000,3000,0);//进行位置内环速度的初始化

  /* USER CODE END 2 */
//进行初始化及开启can配置

 while (1)
  {

//	  PID_Calc(&GM6020_speed_pid[0],GM6020[0].speeed,100);


//	  boxSend(GM6020_speed_pid[0].Output,0,0,0);
//	  boxSend(12000,0,0,0);

	  current_angle=GM6020[0].angle;//得到现在的角度
	  err=current_angle-Last_angle;//角度差值
	  Null_Point(&err);//进行一个零点判断
	  Current_position+=err;//得到当前位置

	  PID_Calc(&GM6020_speed_pid[0],Current_position,8191*5);//位置内环计算得到速度

	  PID_Calc(&GM6020_speed_pid[1],GM6020[0].speeed,GM6020_speed_pid[0].Output);//用外环计算得到位置
	  boxSend(GM6020_speed_pid[1].Output,0,0,0);//报文发送过去
	  Last_angle=current_angle;//得到上一次角度
	  HAL_Delay(2);
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}
//写运行代码,获得当前位置

(2)pid.c

/*
 * pid.c
 *
 *  Created on: Jun 16, 2024
 *      Author: yu
 */


#include "pid.h"
#include "bsp_can.h"

PidTypedef GM6020_speed_pid[4]={0};

void Pid_Init(PidTypedef *Pid ,float kp,float ki,float kd,float max_out,float dead_band,float integral,float max_input,uint8_t mode)
{
	Pid->Kp=kp;
	Pid->Ki=ki;
	Pid->Kd=kd;
	Pid->Input=0;
	Pid->Output=0;
	Pid->Integral=integral;//积分
	Pid->Dead_Band=dead_band;//死驱
	Pid->Max_out=max_out;//最大输出
	Pid->Max_input=max_input;//最大输入
	Pid->Integral=0;
	Pid->E[0]=0;
	Pid->E[1]=0;
	Pid->E[2]=0;
	Pid->D_last=0;
}

float func_Limit(float value,float max,float min)
{
	if(value>max)
	{
		return value=max;
	}
	else if(value<min)
	{
		return value=min;
	}
	else
		return value;
}

float func_abs(float value)
{
	if(value>=0)
		return value;
	else
		return -value;
}


void PID_Calc(PidTypedef *Pid,float return_value,float set_value)//速度环
{
	float p=0,i=0,d=0;
	    // 保存前一次和前两次误差
	    Pid->E[0] = Pid->E[1];
	    Pid->E[1] = Pid->E[2];

	Pid->E[2]=func_Limit(set_value-return_value, Pid->Max_input,-(Pid->Max_input));//当前的误差
	if(Pid->Mode==0)//位置环
	{
		if(func_abs(Pid->E[2])<=Pid->Integral)//如果当前误差绝对值小于积分
		{
			Pid->Integral=Pid->Integral+((Pid->Ki)*(Pid->E[2])+(Pid->Ki)*(Pid->E[1]))/2;//暂时不知道它有何用处
		}
		else
		{
			Pid->Integral=Pid->Integral;
		}
		Pid->Integral=func_Limit(Pid->Integral,Pid->Integral_Max,(-Pid->Integral_Max));



		p=Pid->Kp*(Pid->E[2]);
		i=Pid->Integral;
		d=(Pid->Kd)*(Pid->E[2]-Pid->E[1]);
		Pid->D_last=d;//不知道有什么用处
		Pid->Output=(int16_t)func_Limit(p+i+d,Pid->Max_out,-(Pid->Max_out));
//		Pid->Output=p+i+d;

	}
	else if(Pid->Mode==1)//速度环
	{
		if(func_abs(Pid->E[2])>=Pid->Dead_Band)//当偏差值大于等于这个死区,进入这个if
		{
			p=(Pid->Kp)*(Pid->E[2]-Pid->E[1]);
			i=(Pid->Ki)*(Pid->E[2]);
			d=(Pid->Kd)*(Pid->E[2]-2*(Pid->E[1])+Pid->E[0]);//注意没写*也会出错
			Pid->Output=p+i+d;
			Pid->Output=func_Limit(Pid->Output,Pid->Max_out,-(Pid->Max_out));
		}
	}
	else
		Pid->Output=0;

}

int Position_Moto;
int Moto;



void PID_Calc_Position(PidTypedef *Pid,float return_value,float set_value)
{
		float p1=0,i1=0,d1=0;
		    // 保存前一次和前两次误差
		    Pid->E[0] = Pid->E[1];
		    Pid->E[1] = Pid->E[2];

		Pid->E[2]=func_Limit(set_value, Pid->Max_input,-(Pid->Max_input))-return_value;//当前的误差
		if(Pid->Mode==0)//位置环
		{
			if(func_abs(Pid->E[2])<=Pid->Integral)//如果当前误差绝对值小于积分
			{
				Pid->Integral=Pid->Integral+((Pid->Ki)*(Pid->E[2])+(Pid->Ki)*(Pid->E[1]))/2;//暂时不知道它有何用处
			}
			else
			{
				Pid->Integral=Pid->Integral;
			}
			Pid->Integral=func_Limit(Pid->Integral,Pid->Integral_Max,(-Pid->Integral_Max));



			p1=Pid->Kp*(Pid->E[2]);
			i1=Pid->Integral;
			d1=(Pid->Kd)*(Pid->E[2]-Pid->E[1]);
			Pid->D_last=d1;//不知道有什么用处
			Pid->Output=p1+i1+d1;

		}
}


void Null_Point(int32_t *D_value)
{
	if (*D_value>4096)	*D_value=*D_value-8192;
	if (*D_value<-4096)	*D_value=*D_value+8192;

}

(3)pid.h

/*
 * pid.h
 *
 *  Created on: Jun 16, 2024
 *      Author: yu
 */

#ifndef INC_PID_H_
#define INC_PID_H_


#include "bsp_can.h"
#include "main.h"

typedef struct
{
	float Kp;
	float Ki;
	float Kd;

	float Integral;//积分
	float Input;//输入
	int16_t Output;//输出
	float Integral_Max;//积分上限
	float Dead_Band;//死区
	float Max_out;//最大输出
	float Max_input;//最大输入
	float E[3];//误差值
	float D_last;
	uint8_t Mode;//0为位置式 1为增量式

}PidTypedef;//PidTypedef要定义在头文件中,且源文件要包含头文件

extern PidTypedef GM6020_speed_pid[4];
void PID_Calc(PidTypedef *Pid,float return_value,float set_value);
void Pid_Init(PidTypedef *Pid ,float kp,float ki,float kd,float max_out,float dead_band,float integral,float max_input,uint8_t mode);
void PID_Cascade(PidTypedef *Pid,float return_value,float set_value);
void PID_Calc_Position(PidTypedef *Pid,float return_value,float set_value);//外环位置

void Null_Point(int32_t *D_value);
#endif /* INC_	PID_H_ */

代码编程过程中出现的错误以及需要改进的
1.我的电机进行转动,是需要正反转的,所以我需要使用正负号,而不是uint
然后我使用的can通信协议传输数据是八位八位传的,在计算过程中使用float是没有太大关系的,但是当我得到变量OUTPUT以后,我这个output是需要传输到can总线上的,所以它的数据类型我需要给它int16_t,这样才能保证数据是正常存储与发送传输的
2.我在进行PID计算时,我的误差是要进行一个限幅的,而我的限幅是应该对误差进行的限幅,然后还需要考虑符号优先级问题
3.在计算我的当前位置的时候出现了一个问题,就是我的零点判断问题,以半个周期为判断点,超过半个周期则减去T,超过负半个周期则加一个T
4.注意在编写代码时,头文件要写这个地方

/* USER CODE BEGIN Includes */
#include "bsp_can.h"
#include "pid.h"
/* USER CODE END Includes */

否则重新构建则头文件会被消除掉

关于 GM6020 的 PID 配置或规格,在 IT 上下文中通常涉及硬件设备的初始化、通信协议以及控制算法的应用。然而,当前提供的引用内容并未直接提及 GM6020 或其 PID 控制器的具体配置方法[^1]。 PID 控制器是一种常见的反馈控制系统,广泛应用于工业自动化领域。对于 GM6020 设备而言,如果它支持 PID 调节功能,则可能需要通过以下方式进行配置: ### 1. 硬件接口与驱动程序 GM6020 可能依赖特定的操作系统驱动来实现与主机之间的数据交换。例如,使用串口(UART)、USB 或网络接口进行通信。在这种情况下,需确认设备手册中的具体连接方式及其波特率设置等参数[^2]。 ```bash # 假设 GM6020 使用的是 USB 接口,可以尝试加载对应的 Linux 内核模块 sudo modprobe usbserial vendor=0xXXXX product=0xYYYY dmesg | grep ttyUSB # 查看分配给设备的端口号 ``` ### 2. 参数调整工具 部分厂商会提供专用软件用于简化 PID 参数调节过程。这些应用程序允许用户图形化地输入比例(P)、积分(I) 和微分(D)系数,并实时观察效果变化曲线图。如果没有官方客户端可用的话,则可通过命令行发送 AT 指令完成相同目的[^3]。 ```python import serial ser = serial.Serial('/dev/ttyUSB0', baudrate=9600, timeout=1) def set_pid(p_value, i_value, d_value): ser.write(f'SET_PID {p_value} {i_value} {d_value}\r\n'.encode()) set_pid(1.2, 0.5, 0.8) response = ser.readline().decode() print(response.strip()) ser.close() ``` ### 3. 自动调优算法 现代控制器往往内置自动校准机制以减少人为干预需求。该类特性基于 Ziegler-Nichols 法则或其他先进理论模型计算初始推荐值集合供后续优化参考之用[^4]。 尽管上述讨论围绕通用概念展开论述,但由于缺乏针对目标型号的确切描述资料,建议查阅制造商发布的正式文档获取权威指导信息。 相关问题
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值