跳到主要内容
版本:最新版本

混合模式

MIT模式工作说明:

下图是执行器MIT模式的控制框图:

图1 MIT模式控制框图

MIT模式是一种由麻省理工大学开发的用于精确扭矩控制的控制方法,可以实现电流、速度、位置的混合控制,计算公式如下

cref=Kp(pdesθm)+Kd(vdesdθm)+cffc_{ref} = Kp \ast (p_{des} - \theta_m) + Kd \ast (v_{des} - d\theta_m) + c_{ff}

在上式中,速度环和位置环的输出值与前馈电流cffc_{ff}相加得到参考电流crefc_{ref},其中:

pdesp_{des}电机端的期望位置,单位为count;

θm\theta_m电机端的当前位置,单位为count; vdesv_{des}电机端的期望速度,单位为rpm;

θm\theta_m电机端的当前速度,单位为rpm;

在这个模式下共有5个控制参数,需要分为两条CAN帧发送,一条帧为电机的电流,速度,和减速机输出端的位置信息(可在电机参数设置中修改为电机端的位置),另一条为Kp和Kd,报文内容请见CAN2.0B 通信协议。合理的设置kp、kd值和电流值可以有效地控制执行器旋转的刚度、阻尼和力矩。

注意,此处报文发送的位置值默认是减速机输出端的位置,但是驱动板芯片在计算的时候使用的是电机端的位置值

cffc_{ff}的值通常是抵消执行器摩擦需要的电流和机器人动力学计算得到的电流值之和

例子:

  1. 当kp和kd值都为0时,旋转刚度、阻尼都为0,此时相当于电流模式,可以直接控制相电流:
  1. 当kp值为0,kd值不为0时,刚度为0,阻尼非0,输入电流(前馈力矩补偿)与速度量可以控制执行器的旋转速度:
  1. 当kp和kd的值都不为0时有多种情况,这里介绍其中两种:

(1) 当速度量为0时,输入电流(前馈力矩补偿)、位置量可以实现定点控制,如图:

(2)当期望位置pdesp_{des}是随时间变化的连续可导函数时,vdesv_{des}pdesp_{des}的导数,可以实现位置和速度跟踪,实现按照期望速度旋转期望角度的功能。下图是目标轨迹与实际轨迹的测试曲线图:

当kp不为0,kd等于0时,执行器的阻尼为0,此时执行器将会以目标位置为中心发生震荡,不建议这样使用

SDK:

python:

from motor_control import *

# port_name: Windows: "COM*" Linux: "/dev/ttyUSB*"
# id: Executor ID
# Baudrate: The baud rate can only be 921600
port_name = "COM6"
id = [0x00]
Baudrate = 921600
motor = MotorControl(port_name, id, Baudrate)

# Feedback_cycle1(ms): Feedback period of the first status packet,
# Including voltage, pwm, current, speed
# Feedback_cycle2(ms):Feedback period of the second status packet,
# Including the position of the motor and the position of the actuator
# Feedback_cycle3(ms):Feedback period of the third status packet,
# Including MOS tube, motor temperature, chip temperature and warning information,
# error information, operation mode
# cur: current(mA)
Feedback_cycle1 = 10
Feedback_cycle2 = 10
Feedback_cycle3 = 10

# MIT mode(Refer to the tutorial on how to use MIT mode)
cur = [1000]
vel = [0]
pos = [0]
kp = [0]
kd = [0]

reset(motor)
time.sleep(0.6)
mode_selection(motor, MIT_MODE, Feedback_cycle1, Feedback_cycle2, Feedback_cycle3)
time.sleep(1)

# current_pid_set(motor, {0.006}, {0.001})

# control current
write_mit(motor, cur, {0}, {0}, kp, kd)
time.sleep(2)
current = get_v_p_c_v(motor)[0][-2]
write_mit(motor, {0}, {0}, {0}, kp, kd)
time.sleep(2)

# control velocity
kp = [0]
kd = [13.1]
write_mit(motor, {500}, {600}, {0}, kp, kd)
time.sleep(2)
velocity = get_v_p_c_v(motor)[0][-1]
write_mit(motor, {0}, {0}, {0}, kp, kd)
time.sleep(2)

position = get_pos(motor)[0][-1]

# control position
kp = [0.09]
kd = [13.1]
write_mit(motor, {310}, {0}, {position + 1000}, kp, kd)
time.sleep(2)
position = get_pos(motor)[0][-1]

d = 0.002
cycle = 20
num = 1
i = 0
while 1:
t = i * d
ang = pi * math.sin(2 * pi / cycle * t)
pos = ang / pi / 2 * 65536 + position

vel = pi * math.cos(2 * pi / cycle * t)
motor_speed = vel * 60 / 2 / pi * 101

cur = 0
if vel > 0: cur = 500
elif vel < 0: cur = -500

write_mit(motor, {cur}, {motor_speed}, {pos}, kp, kd)
i = i + 1
time.sleep(d)
if t >= cycle * num:
break

c++:

#include <motor_control.h>
#include <multi_motor_control.h>
#include <iostream>
#include <thread>
#include <chrono>

#ifdef _WIN32
#include <windows.h>
#include <mmsystem.h>
#pragma comment(lib, "winmm.lib")
#endif

#define delay(s) std::this_thread::sleep_for(std::chrono::milliseconds((int32_t)(s*1000)))

int main()
{
#ifdef _WIN32
timeBeginPeriod(1);
#endif

#define status_packet1 0xb1
#define status_packet2 0xb2
#define status_packet3 0xb3

// port_name: Windows: "COM*" Linux: "/dev/ttyUSB*"
// id: Executor ID
// Baudrate: The baud rate can only be 921600
string port_name = "COM6";
uint32_t Baudrate = 921600;
uint8_t id = 0x00;
MotorControl motor(port_name, Baudrate, id);

// Feedback_cycle1(ms): Feedback period of the first status packet,
// Including voltage, pwm, current, speed
// Feedback_cycle2(ms):Feedback period of the second status packet,
// Including the position of the motor and the position of the actuator
// Feedback_cycle3(ms):Feedback period of the third status packet,
// Including MOS tube, motor, chip temperature and warning information,
// error information, current operation mode
// cur: current(mA)
uint8_t Feedback_cycle1 = 10;
uint8_t Feedback_cycle2 = 10;
uint8_t Feedback_cycle3 = 10;

// MIT_MODE(Refer to the tutorial on "how to use MIT mode")
int16_t cur = 1000;
int16_t vel = 0;
int32_t pos = 0;
float kp = 0;
float kd = 0;
motor.reset();
motor.mode_selection(Mode::MIT_MODE, Feedback_cycle1, Feedback_cycle2, Feedback_cycle3);

// motor.write_current_pid(0.006, 0.001); // Params: KP, KI

// control current
motor.write_mit(cur, 0, 0, kp, kd);

cout << endl << "MIT mode" << endl;
printf("target_current: %d\n", cur);
delay(2);
MotorStatus status = motor.get_motor_status({status_packet1,status_packet2,status_packet3});
printf("current: %d\n", status.current_value);

motor.write_mit(0, 0, 0, kp, kd);
delay(2);

// control velocity
kp = 0;
kd = 13.1;
motor.write_mit(500, 1000, 0, kp, kd);
printf("target_velocity: %d\n", 1000);
delay(2);
status = motor.get_motor_status({status_packet1,status_packet2,status_packet3});
printf("velocity: %d\n", status.velocity_value);
motor.write_mit(0, 0, 0, kp, kd);
delay(2);

// control position
kp = 0.09;
kd = 13.1;
status = motor.get_motor_status({ status_packet1,status_packet2,status_packet3 });
motor.write_mit(300, 0, status.actuator_position_value + 1000, kp, kd);
printf("target_position: %d\n", status.actuator_position_value + 1000);
delay(5);
status = motor.get_motor_status({status_packet1,status_packet2,status_packet3});
printf("position: %d\n", status.actuator_position_value);

// ---------Running track points---------
d = 0.002; // unit time
cycle = 10; // Track cycle
num = 1; // Execution times

printf("control Trajectory\r\n");
while (1)
{
static size_t i = 0;

double t = i * d;

// Guidable position
double ang = pi * sinf(2 * pi / cycle * t);
int pos = ang / pi / 2 * 65536 + status.actuator_position_value;

// Derivative of position
double vel = pi * cosf(2 * pi / cycle * t);
int motor_speed = vel * 60 / 2 / pi * 101;

// Compensated current
double cur = 0;
if (vel > 0) cur = 500;
else if (vel < 0) cur = -500;

// Controlling position and velocity
motor.write_mit(cur, motor_speed, pos, 0.09, 13.1);

i++;
delay(d);

if (t >= cycle * num)
{
break;
}
}

#ifdef _WIN32
timeEndPeriod(1);
#endif
}