Simple FOC内置通信接口学习(二):电机命令接口

本文参(zhao)考(chao)至官方文档https://docs.simplefoc.com/docs_chinese/commander_interface

Commander 接口

Commander是一个简单而灵活的监控,配置和控制接口,使用类似G代码的通信协议。由于是基于“ASCII”字符命令ID,从而在任何mcu上解析都简单高效。接收到命令ID后,将调用绑定到此命令的函数,并接收到命令字符后面的剩余字符串。

命令1 命令2
img img

此类似于G代码的接口提供回调来配置和调整:

  • 无刷直流或步进电机
    • PID控制器
    • 低通滤波器
    • 运动控制
    • 监测
    • 约束
    • 启用/禁用
    • 传感器偏移
    • 相电阻
    • ...
  • PID控制器
  • 低通滤波器
  • 浮点变量

此外,利用commander接口可以轻松创建自己的命令,并以可能需要的任何方式扩展此接口。

当用户发送命令时会发生什么?

commander收到字符串MVD0.765时:
它首先检查命令ID,发现是'M',则将剩余字符串发送给电机回调函数处理。电机的回调函数进一步检查命令ID是什么,发现是'V',则将剩余字符串发送到速度PID回调函数。然后速度PID回调函数扫描命令ID并发现它是'D',因此会设置D环数值。

指令 电机回调(cmd idM PID回调(cmd idV
img img img

另一个例子是,如果Commander收到:OE

它找到的第一个ID是'O',比如是motor,则将剩余的字符串发送给此命令的回调函数(本例中为电机的回调函数)。然后,电机的回调函数发现是命令“E”,并获知这个命令是要获取还是设置其所指示的状态(已启用/已禁用)。它检查剩余的字符值是否为空,如果为空则意味着用户发送的是get请求。

指令 电机回调 (cmd id O )
img img

使用Commander接口

命令接口在Commander 类中实现。

// Commander 接口构型
// - serial  - 可选择接收 HardwareSerial 或 Stream 实例
// - eol     - 可选择接收 eol 字符 - 默认另起一行: "\n" 
// - echo    - 可选的 echo 行结束符(命令行反馈)  - 默认 false
Commander commander = Commander(Serial, "\n", false);

行结束符EOL是Commander类的可选输入,表示命令字符的结束。用户可以在此处定义自己的命令结束字符,但默认情况下使用的字符是换行符\n。比如

注意:EOL行结束符

不同的操作系统有不同的默认行结束符。而换行符可能是最常见的字符,linux用户也有回车符'\r'。如果你希望用的是换行符作为命令字符的结束,请确保将它传给Commander类的构造函数中。

echo标志位可用作调试功能,但不建议用于实时电机控制和配置!

下一步是添加commander函数,该函数将读取所绑定的Serial实例到Arduinoloop()

void loop(){
  ...
  commander.run(); // 从 constructor 读取 Serial 实例
}

如果没有将Serial实例传给Commander构造函数,则可以将其绑定给 run() 函数。

void loop(){
  ...
  commander.run(Serial); // 从 run 读取 Serial 实例
}

或者,如果你希望使用不带 Serial 且仅使用字符串变量的commander,则可以向 run()函数提供和 char* 变量:

char* my_string = "user command";
commander.run(my_string); // 读取字符串

串口输出

Commander 类会尝试将输出打印到构造函数中提供的串口实例。如果在构造函数中没串口实例,则会始终在 run() 函数中的串口实例。如果以上都没有,则不会在任何地方输出,但用户仍然能够使用它。

配置

Commander有两个配置参数:

  • verbose-串口输出模式
  • decimal_places-浮点数的小数位数

通过设置参数decimal_places,可以轻松更改浮点数的小数位数:

commander.decimal_places = 4; // 默认为3位小数

通过设置参数verbose,可以轻松更改串口输出模式

// VerboseMode::nothing        - 不显示任何信息 - 适用于与监视器结合使用时
// VerboseMode::on_request     - 仅显示用户请求的信息
// VerboseMode::user_friendly  - 向用户显示文本信息(默认)
commander.verbose = VerboseMode::user_friendly;

有三种类型的输出模式:

  • VerboseMode::nothing-此模式不会向串口终端输出任何内容-例如,当Commandermonitoring 结合使用时,它就有效避免Arduino的串口绘图仪中出现未知值
  • VerboseMode::on_request-此模式仅输出get和set命令的结果,不会输出任何其他不必要的(可读的)文本。
  • VerboseMode::user_friendly-此模式是默认模式,适用于由用户使用串口监视器发送命令的情况。除了所有必要的get和set值外,该模式还将输出额外的文本,以便于用户理解。

添加命令

你可以用add()来添加给定的命令字符的回调函数,该函数接收命令字符、函数指针和命令标签:

// 在 commander 中创建 command A 
// - command id - 字符串
// - 回调   - 函数指针 - 返回 void (char* cmd)
// - 标签      - 命令标签 (可选)
commander.add('A',doSomething,"do something");

对于可以用作回调函数的函数类型,唯一的实际要求是它们需要返回void,并且必须接收char*字符串:

void doSomething(char* cmd){ ... }

使用这个简单的接口,你可以非常简单地创建自己的命令,并使用一行代码将它们订阅到Commander

除了此用于添加通用回调的灵活接口之外,Commander类还为以下对象实现了标准化回调:

  • 无刷直流电动机 (BLDCMotor) - commander.motor(&motor, cmd)
  • 步进电机 (StepperMotor) - commander.motor(&motor, cmd)
  • PID控制器(PIDController) - commander.pid(&pid, cmd)
  • 低通滤波器 (LowPassFilter) - commander.lpf(&lpf, cmd)
  • 任何数值变量(float) - commander.scalar(&variable, cmd)

例如,如果你想完整配置一个motor,你的代码可能如下所示:

BLDCMotor motor = .....
Commander commander = ....

// 定义封装通用回调
void onMotor(char* cmd){commander.motor(&motor, cmd);}

void setup(){
  ...
  commander.add('m',onMotor,"my motor");
  ...
}
void loop(){
  ...
  commander.run();
}

如果希望调整速度PID,更改电机的目标值,同时希望消除由于不需要的其他功能而产生的不必要的内存开销,那么你的代码可能如下所示:

BLDCMotor motor = .....
Commander commander = ....

// 定义封装通用回调
void onPid(char* cmd){commander.pid(&motor.PID_velocity, cmd);}
void onLpf(char* cmd){commander.lpf(&motor.LPF_velocity, cmd);}
void onTarget(char* cmd){commander.scalar(&motor.tagret, cmd);}

void setup(){
  ...
  commander.add('C',onPid,"PID vel");
  commander.add('L',onLpf,"LPF vel");
  commander.add('T',onTarget,"target vel");
  ...
}
void loop(){
  ...
  commander.run();
}

这个接口为用户提供了一种简单的方式,可以同时通信和配置多个电机、PID控制器、低通滤波器、标量变量或者自定义命令。它还能使自定义控制回路的调整更加容易,因为你可以非常轻松地使用pid控制器PIDController关闭回路,只需将其添加到commander即可实时调整。

你可以在库examplesexamples/utils/communication\u test/commander文件夹中找到更多示例。

命令列表

所有内置命令和子命令都在库源文件src/communication/commands.h中定义。如果你希望更改某个命令的字符id,则可以在此进行操作。😄

通常,我们可以将命令分为:

Commander命令

在你的程序中使用 Commander时,用户可以使用三个内置的默认命令:

  • ? - 列出所有可用的命令
  • # - 获取/设置小数点位数
    • 示例:
      • 小数点位数 #
      • 设置小数点精确到后5位: #5
  • @ - 获取/设置Commander的输出模式
    • 示例:
      • 获取当前模式: @
      • 设置user frinedly模式:@3
      • 设置nothing模式:@0
      • 设置on request模式: @1

list命令?会显示所有添加到Commander的命令和他的标签。比如如果我们添加了如下命令:

void setup(){
  ...
  commander.add('M',doSomeMotor,"some motor");
  commander.add('P',doSomePID,"some pid");
  commander.add('R',doSomeOtherMotor,"some other motor");
  ...
}

以下是以 user-friendly模式输出 ? 的示例:

$ ?
M: some motor
P: some pid
R: some other motor

PID命令

当对 PIDController 类:commander.pid(&pid,cmd)使用标准回调函数时,用户拥有一组可用的命令:

  • P: PID控制器P增益
  • I: PID控制器I增益
  • D: PID控制器D增益
  • R: PID控制器输出斜率
  • L:PID控制器输出约束

例如,如果在commander中添加了PID控制器:

PIDController pid = ....
Commander commander = ...

void onPid(char* cmd){ commander.pid(&pid,cmd); }
void setup(){
  ...
  commander.add('C',onPid,"my pid");
  ...
}
void loop(){
  ...
  commander.run();
}

你将能够从串口监视器配置 (set 和 get) 其参数:

$ CP           # 获取 P 增益
P: 1.0
$ CD0.05       # 设置 D 增益
D: 0.05
$ CO           # 未知命令
err
$ CL3.25       # 设置输出限制
limit: 3.25

低通滤波器命令

使用 LowPassFilter 类的标准回调函数时:commander.lpf(&lpf,cmd)用户有一个可用的命令:

  • F: 低通滤波器时间常数

例如,如果在commander中添加了低通滤波器:

LowPassFilter filter = ....
Commander commander = ...

void onLpf(char* cmd){ commander.lpf(&filter,cmd); }
void setup(){
  ...
  commander.add('A',onLpf,"my lpf");
  ...
}
void loop(){
  ...
  commander.run();
}

你将能够从串口监视器配置(set 和 get)其参数:

$ AF           # 获取时间常数
Tf: 1.0
$ AF0.05       # 设置时间常数
Tf: 0.05
$ AW           # 未知命令
err

电机指令

当对BLDCMotorStepperMotor类使用标准回调函数时:commander.motor(&motor,cmd)用户将拥有一组可用的命令:

  • Q - I_Q的PID控制器和低通滤波器(有关命令,请参见pidlpf
  • D - I_DPID控制器和低通滤波器(有关命令,请参见pidlpf
  • V - 速度PID控制器和低通滤波器(有关命令,pidlpf
  • A - 角度PID控制器和低通滤波器-(有关命令,请参见pidlpf
  • L -约束
    • C - 电流
    • U - 电压
    • V - 速度
  • C - 运动控制模式配置
    • D - 运动控制的下采样频率
    • 0 - 力矩
    • 1 - 速度
    • 2 - 角度
    • 3 - 速度开环
    • 4 - 角度开环
  • T - 力矩控制模式
    • 0 - 电压
    • 1 - 直流电流
    • 2 - FOC电流
  • E - 电机状态 (启用/禁用)
    • 0 - 启用
    • 1 - 禁用
  • R - 电机相电阻
  • S - 传感器偏移
    • M - 传感器偏移
    • E - 传感器电气零点
  • W - PWM设置
    • T - pwm 调制类型
    • C - pwm 波形中心布尔
  • M - 监控
    • D - 监测的下采样频率
    • C - 清除监视器
    • S - 设置监控变量
    • G - 获取变量值
  • '' - 目标获取/设置

img

例如,如果在commander中添加了无刷直流电机:

BLDCMotor motor = ....
Commander commander = ...

void onMotor(char* cmd){ commander.motor(&motor,cmd); }
void setup(){
  ...
  commander.add('M',onMotor,"my motor");
  ...
}
void loop(){
  ...
  commander.run();
}

你将能够从串口监视器配置(set and get)其参数:

$ MVP                 # 获取 PID 速度 P 增益
PID vel| P: 0.20
$ MVP1.2              # 设置 PID 速度 P 增益
PID vel| P: 1.20
$ MAI                 # 获取 PID 角度 I 增益
PID angle| I: 0.00 
$ MAF                 # 获取 LPF 角度时间常数
LPF angle| Tf: 0.00
$ MLV50.4             # 设置速度限制
Limits| vel: 50.4
$ MLC                 # 获取电流限制
Limits| curr: 0.5
$ MT                  # 获取力矩控制模式
Torque: volt
$ MT1                 # 设置力矩控制模式
Torque: dc curr
$ MT2                 # 设置力矩控制模式
Torque: foc curr
$ ME                  # 获取电机状态:启用/禁用
Status: 1
$ MSM                 # 获取传感器偏移
Sensor| offset: 0.0
$ MSM1.2              # 设置传感器偏移
Sensor| offset: 1.2
$ MC                  # 获取运动控制模式
Motion: torque
$ MC3                 # 设置运动控制模式
Motion: vel open
$ MC2                 # 设置运动控制模式
Motion: angle
$ MCD100              # 获取运动控制下采样
Motion: downsample: 100
$ MMG0                # 获取变量 target
Monitor | target: 0.0
$ MMG1                # 获取变量 voltage q
Monitor | Vq: 1.4
$ MMG6                # 获取变量 angle(角度)
Monitor | angle: 23.5 
$ MMG6                # 获取变量 angle(角度)
Monitor | angle: 24.6 
$ MMG6                # 获取变量 angle(角度)
Monitor | angle: 25.5 
$ M0                  # 设置 target
Target: 0.0
$ M0.4                # 设置 target
Target: 0.4
$ @1                  # 设置verbose模式: on_request
Verb | on! 
$ MMG6                # 获取变量 angle(角度)
26.5
$ MMG5                # 获取变量 velocity(速度)
2.57
$ #6                  # 设置保留小数点后六位
Decimal: 6
$ MMG6                # 获取变量 angle(角度)
27.732821
$ @0                  # 设置verbose模式: nothing
Verb: off!
$ MMG6                # 获取变量 angle(角度)
$ MMG6                # 获取变量 angle(角度)
$ @2                  # 设置verbose模式: user_friendly
Verb: on!
$ MMG6                # 获取变量 angle(角度)
Monitor | angle: 25.532131 

电机监控命令

Commander接口使用户能够控制 monitoring功能的输出。两者的结合使用户能够自由控制电机配置和调参,以及自由控制所输出的变量。为了使用其功能,用户需要启用对电机的监控,代码如下所示:

BLDCMotor motor = ....
Commander commander = ...

void onMotor(char* cmd){ commander.motor(&motor,cmd); }
void setup(){
  ...
  motor.useMonitoring(Serial);
  commander.add('M',onMotor,"my motor");
  ...
}
void loop(){
  ...
  motor.monitor();
  commander.run();
}

最后,一旦电机添加到commander接口,用户将能够使用以下命令配置监控:

  • M - 监控
    • D - 监测的下采样频率
    • C - 清除监视器
    • S - 设置监控变量

使用这些命令,你可以更改 monitor() 函数的下采样频率(motor.monitor_downsampling),该函数将确定输出采样频率。例如,如果 loop 时间约为1ms,motor.monitor_downsampling为100,则每100ms输出一次电机变量。
如果motor.monitor_downsampling为0, monitor() 函数将被禁用。如果motor.monitor_variables为空(等于0),则同样 monitor() 函数被禁用。因此,命令C有效地执行以下操作:

// 回调 command MC
motor.monitor_variables = 0;

最后,命令MS用于获取/设置 motor.monitor_variables

因此,通信可如下所示:

$ MMD                 # 获取监视器下采样率
Monitor | downsample: 10 
$ MMD1000             # 设置监视器下采样率
Monitor | downsample: 1000 
$ MMS                 # 获取监视器变量
Monitor | 0000000
$ MMS1000001          # 设置监视器变量 (target and angle)
Monitor | 1000001
1.000 0.999
1.000 0.985
1.000 1.064
.....
1.000 1.040
$ MMS0100000          # 设置监视器变量 (voltage q)
Monitor | 0100000
1.234
-0.345
...
0.772
$ MMC                 # 清除监视变量
Monitor | clear
$ MMS                 # 获取监视变量
Monitor | 0000000

📈 Good practice for visualization

如果用monitor来调整运动控制函数或者只是为了对不同变量进行可视化的话,有必要禁用掉commander的输出,这样以来串口监视器中就只有monitor的输出。为此,可以发送命令@0使用模式VerboseMode::nothing。详见 [Commander命令](#commander命令)

使用motor命令的示例代码

这是在代码中使用motor命令进行监控的一个简单示例。有关更多示例,请浏览库示例,尤其是examples/utils/communication_tes/commander文件夹。

#include <SimpleFOC.h>

// 无刷直流电机和驱动器实例
BLDCMotor motor = BLDCMotor(11);
BLDCDriver3PWM driver = BLDCDriver3PWM(9, 5, 6, 8);

// 编码器实例
Encoder encoder = Encoder(2, 3, 500);
// 通道 A 和 B 回调
void doA(){encoder.handleA();}
void doB(){encoder.handleB();}


// commander接口
Commander command = Commander(Serial);
void onMotor(char* cmd){ command.motor(&motor, cmd); }

void setup() {

  // 初始化编码传感器硬件
  encoder.init();
  encoder.enableInterrupts(doA, doB); 
  // 连接电机和传感器
  motor.linkSensor(&encoder);

  // 配置驱动器
  // 电源电压 [V]
  driver.voltage_power_supply = 12;
  driver.init();
  // 连接电机
  motor.linkDriver(&driver);

  // 设置控制环类型
  motor.controller = MotionControlType::torque;

  // 使用串口监视电机初始化
  // 监视端口
  Serial.begin(115200);
  // 如无必要,可注释掉
  motor.useMonitoring(Serial);
  motor.monitor_downsample = 0; // 初始化禁用实时监视

  // 初始化电机
  motor.init();
  // 校准编码器,启动FOC
  motor.initFOC();

  // 设置初始目标值
  motor.target = 2;

  // 定义电机 id
  command.add('A', onMotor, "motor");

  // 运行用户命令配置电机(完整命令列表见docs.simplefoc.com)
  Serial.println(F("Motor commands sketch | Initial motion control > torque/voltage : target 2V."));
  
  _delay(1000);
}


void loop() {
  // 设置FOC相电压迭代
  motor.loopFOC();

  // 设置外部环目标迭代函数
  motor.move();

  // 监视
  motor.monitor();
  // 用户通信
  command.run();
}

SimpleFOCStudio

SimpleFOCStudio是由@JorgeMaker 构建的一个很棒的应用程序我们会尽量在没有库的情况下保持最新。它是一个python应用程序,使用commander接口来调试和配置电机。

有关如何安装和使用此应用程序的更多信息,请访问 docs.

posted @ 2022-02-08 19:00  因为风的缘故~  阅读(880)  评论(0编辑  收藏  举报