bldc/applications/er/ErConfig.qml

756 lines
27 KiB
QML
Executable File

import QtQuick 2.5
import QtQuick.Controls 2.2
import QtQuick.Layouts 1.3
import Vedder.vesc.commands 1.0
import Vedder.vesc.configparams 1.0
import Vedder.vesc.utility 1.0
Item {
id: container
anchors.fill: parent
anchors.margins: 10
property Commands mCommands: VescIf.commands()
property ConfigParams mMcConf: VescIf.mcConfig()
property ConfigParams mAppConf: VescIf.appConfig()
property var canDevs: []
property bool readSettingsDone: false
property var er_msg: []
property var er_set: []
property var er_io: []
Component.onCompleted: {
er_msg.ER_MSG_SET_MODE_PARAMS = 0
er_msg.ER_MSG_GET_MODE_PARAMS = 1
er_msg.ER_MSG_GET_IO = 2
er_msg.ER_MSG_RESTORE_SETTINGS = 3
er_msg.ER_MSG_SET_MOTORS_ENABLED = 4
er_msg.ER_MSG_SET_PEDAL_TEST_MODE = 5
er_set.p_throttle_hyst = 0.04
er_set.p_pedal_current = 20.0
er_set.p_start_gain = 4.0
er_set.p_start_gain_end_speed = 15.0
er_set.p_output_power = 1.0
er_set.p_top_speed_erpm = 2000
er_set.p_brake_current_front = 0.5
er_set.p_brake_current_rear = 0.5
er_set.p_brake_current_both = 1.0
er_io.mode_btn = false
er_io.brake_front = false
er_io.brake_rear = false
er_io.kill_sw = false
er_io.led_eco_on = false
er_io.led_sport_on = false
er_io.led_low_batt_on = false
er_io.led_fault_on = false
er_io.stand = false
}
Timer {
repeat: true
interval: 200
running: true
onTriggered: {
readIo()
}
}
Connections {
target: mCommands
onCustomAppDataReceived: {
var dv = new DataView(data, 0)
var ind = 0
var cmd = dv.getUint8(ind++)
if (cmd == er_msg.ER_MSG_SET_MODE_PARAMS) {
VescIf.emitStatusMessage("ER Params Set OK", true)
} else if (cmd == er_msg.ER_MSG_GET_MODE_PARAMS) {
er_set.p_throttle_hyst = dv.getFloat32(ind); ind += 4
er_set.p_pedal_current = dv.getFloat32(ind); ind += 4
er_set.p_start_gain = dv.getFloat32(ind); ind += 4
er_set.p_start_gain_end_speed = dv.getFloat32(ind); ind += 4
er_set.p_output_power = dv.getFloat32(ind); ind += 4
er_set.p_top_speed_erpm = dv.getFloat32(ind); ind += 4
er_set.p_brake_current_front = dv.getFloat32(ind); ind += 4
er_set.p_brake_current_rear = dv.getFloat32(ind); ind += 4
er_set.p_brake_current_both = dv.getFloat32(ind); ind += 4
readSettingsDone = true
updateSliders()
} else if (cmd == er_msg.ER_MSG_GET_IO) {
er_io.mode_btn = dv.getUint8(ind); ind += 1
er_io.brake_front = dv.getUint8(ind); ind += 1
er_io.brake_rear = dv.getUint8(ind); ind += 1
er_io.kill_sw = dv.getUint8(ind); ind += 1
er_io.led_eco_on = dv.getUint8(ind); ind += 1
er_io.led_sport_on = dv.getUint8(ind); ind += 1
er_io.led_low_batt_on = dv.getUint8(ind); ind += 1
er_io.led_fault_on = dv.getUint8(ind); ind += 1
er_io.stand = dv.getUint8(ind); ind += 1
updateIoStatus()
} else if (cmd == er_msg.ER_MSG_SET_MOTORS_ENABLED) {
if (dv.getUint8(ind++)) {
VescIf.emitStatusMessage("ER Motors Enabled", true)
} else {
VescIf.emitStatusMessage("ER Motors Disabled", true)
}
} else if (cmd == er_msg.ER_MSG_SET_PEDAL_TEST_MODE) {
if (dv.getUint8(ind++)) {
VescIf.emitStatusMessage("Pedal Test Enabled", true)
} else {
VescIf.emitStatusMessage("Pedal Test Disabled", true)
}
}
}
}
function readSettings() {
var buffer = new ArrayBuffer(1)
var dv = new DataView(buffer)
var ind = 0
dv.setUint8(ind++, er_msg.ER_MSG_GET_MODE_PARAMS)
mCommands.sendCustomAppData(buffer)
}
function writeSettings() {
if (!readSettingsDone) {
return
}
var buffer = new ArrayBuffer(37)
var dv = new DataView(buffer)
var ind = 0
dv.setUint8(ind, er_msg.ER_MSG_SET_MODE_PARAMS); ind += 1
dv.setFloat32(ind, er_set.p_throttle_hyst); ind += 4
dv.setFloat32(ind, er_set.p_pedal_current); ind += 4
dv.setFloat32(ind, er_set.p_start_gain); ind += 4
dv.setFloat32(ind, er_set.p_start_gain_end_speed); ind += 4
dv.setFloat32(ind, er_set.p_output_power); ind += 4
dv.setFloat32(ind, er_set.p_top_speed_erpm); ind += 4
dv.setFloat32(ind, er_set.p_brake_current_front); ind += 4
dv.setFloat32(ind, er_set.p_brake_current_rear); ind += 4
dv.setFloat32(ind, er_set.p_brake_current_both); ind += 4
mCommands.sendCustomAppData(buffer)
}
function restoreSettings() {
var buffer = new ArrayBuffer(1)
var dv = new DataView(buffer)
var ind = 0
dv.setUint8(ind++, er_msg.ER_MSG_RESTORE_SETTINGS)
mCommands.sendCustomAppData(buffer)
}
function readIo() {
var buffer = new ArrayBuffer(1)
var dv = new DataView(buffer)
var ind = 0
dv.setUint8(ind++, er_msg.ER_MSG_GET_IO)
mCommands.sendCustomAppData(buffer)
}
function setMotorsEnabled(enabled) {
var buffer = new ArrayBuffer(2)
var dv = new DataView(buffer)
var ind = 0
dv.setUint8(ind++, er_msg.ER_MSG_SET_MOTORS_ENABLED)
dv.setUint8(ind++, enabled)
mCommands.sendCustomAppData(buffer)
}
function setPedalTestMode(enabled) {
var buffer = new ArrayBuffer(2)
var dv = new DataView(buffer)
var ind = 0
dv.setUint8(ind++, er_msg.ER_MSG_SET_PEDAL_TEST_MODE)
dv.setUint8(ind++, enabled)
mCommands.sendCustomAppData(buffer)
}
function updateSliders() {
repSliders.itemAt(0).value = er_set.p_pedal_current
repSliders.itemAt(1).value = er_set.p_start_gain
repSliders.itemAt(2).value = er_set.p_start_gain_end_speed
repSliders.itemAt(3).value = er_set.p_output_power
repSliders.itemAt(4).value = er_set.p_top_speed_erpm
repSliders.itemAt(5).value = er_set.p_brake_current_front
repSliders.itemAt(6).value = er_set.p_brake_current_rear
repSliders.itemAt(7).value = er_set.p_brake_current_both
}
function updateIoStatus() {
ioRepeater.itemAt(0).color = (er_io.mode_btn ? "green": "darkGray")
ioRepeater.itemAt(1).color = (er_io.brake_front ? "green" : "darkGray")
ioRepeater.itemAt(2).color = (er_io.brake_rear ? "green" : "darkGray")
ioRepeater.itemAt(3).color = (er_io.kill_sw ? "green" : "darkGray")
ioRepeater.itemAt(4).color = (er_io.led_eco_on ? "green" : "darkGray")
ioRepeater.itemAt(5).color = (er_io.led_sport_on ? "green" : "darkGray")
ioRepeater.itemAt(6).color = (er_io.led_low_batt_on ? "green" : "darkGray")
ioRepeater.itemAt(7).color = (er_io.led_fault_on ? "green" : "darkGray")
ioRepeater.itemAt(8).color = (er_io.stand ? "green" : "darkGray")
var title_new = ""
if (er_io.led_eco_on) {
title_new = "Eco Settings"
} else if (er_io.led_sport_on) {
title_new = "Sport Settings"
} else {
title_new = "Normal Settings"
}
if (settingsBox.title != title_new) {
readSettings()
}
settingsBox.title = title_new
}
function getCanIds() {
if (canDevs.length == 0) {
disableDialog()
canDevs = Utility.scanCanVescOnly(VescIf)
enableDialog()
}
}
function selectRearMotor() {
var res = true
if (mCommands.getSendCan()) {
VescIf.canTmpOverride(false, 0)
} else {
getCanIds()
if (canDevs.length != 0) {
VescIf.canTmpOverride(true, canDevs[0])
} else {
res = false
}
}
if (!res) {
VescIf.emitMessageDialog("Select Rear Motor",
"Could not select rear motor. Make sure that the CAN-bus is plugged in.",
false, false)
}
return res
}
function unselectRearMotor() {
VescIf.canTmpOverrideEnd()
mCommands.getMcconf()
if (!Utility.waitSignal(mMcConf, "2updated()", 4000)) {
VescIf.emitMessageDialog("Read Configuration",
"Could not read motor configuration.",
false, false)
}
}
ScrollView {
anchors.fill: parent
clip: true
contentWidth: availableWidth
ColumnLayout {
id: mainColumn
anchors.fill: parent
Text {
Layout.fillWidth: true
color: "White"
horizontalAlignment: Text.AlignHCenter
font.pointSize: 20
text: "Erockit Console"
}
GroupBox {
id: wizardBox
title: qsTr("Quick Setup")
Layout.fillWidth: true
GridLayout {
anchors.topMargin: -5
anchors.bottomMargin: -5
anchors.fill: parent
columns: 2
columnSpacing: 5
rowSpacing: 0
ImageButton {
Layout.fillWidth: true
Layout.preferredWidth: 500
Layout.preferredHeight: 80
buttonText: "Setup\nRear\nMotor"
imageSrc: "qrc:/res/icons/motor.png"
onClicked: {
setupRearDialog.open()
}
}
ImageButton {
Layout.fillWidth: true
Layout.preferredWidth: 500
Layout.preferredHeight: 80
buttonText: "Setup\nFront\nMotor"
imageSrc: "qrc:/res/icons/motor.png"
onClicked: {
setupFrontDialog.open()
}
}
ImageButton {
Layout.fillWidth: true
Layout.preferredWidth: 500
Layout.preferredHeight: 80
buttonText: "Restore\nPedal\nSettings"
imageSrc: "qrc:/res/icons/Restart-96.png"
onClicked: {
restoreSettings()
readSettings()
}
}
CheckBox {
Layout.fillWidth: true
Layout.preferredWidth: 500
Layout.preferredHeight: 80
text: "Pedal Test"
onToggled: {
setPedalTestMode(checked)
}
}
}
}
GroupBox {
title: qsTr("IO Status")
Layout.fillWidth: true
GridLayout {
anchors.fill: parent
columns: 2
Repeater {
id: ioRepeater
model: ["mode_btn", "brake_front", "brake_rear", "kill_sw",
"led_eco_on", "led_sport_on", "led_low_batt_on", "led_fault_on",
"stand"]
Rectangle {
Layout.fillWidth: true
radius: 5
height: 30
color: "darkGray"
Text {
anchors.centerIn: parent
text: modelData
color: "white"
}
}
}
}
}
GroupBox {
id: settingsBox
title: qsTr("Settings for selected mode")
Layout.fillWidth: true
GridLayout {
id: sliderGrid
anchors.fill: parent
columns: 3
rows: repSliders.count
flow: GridLayout.TopToBottom
// Name, min, max, val, unitFactor, decimals, unit, propertyName
property var repModel: [
["Pedal\nCurrent", 1.5, 50, 15, 1, 1, " A", "p_pedal_current"],
["Start Gain", 1, 10, 4, 1, 1, "", "p_start_gain"],
["Start Gain\nEnd Speed", 1, 100, 15, 1, 1, "\nkm/h", "p_start_gain_end_speed"],
["Power", 0, 1, 0.8, 100, 0, " %", "p_output_power"],
["Max Power\nERPM", 210, 5000, 2000, 1, 0, "", "p_top_speed_erpm"],
["Front\nBrake", 0, 1, 0.5, 100, 0, " %", "p_brake_current_front"],
["Rear\nBrake", 0, 1, 0.5, 100, 0, " %", "p_brake_current_rear"],
["Both\nBrakes", 0, 1, 1, 100, 0, " %", "p_brake_current_both"],
]
Repeater {
model: sliderGrid.repModel
Text {
color: "white"
text: modelData[0]
}
}
Repeater {
id: repSliders
model: sliderGrid.repModel
Slider {
Layout.fillWidth: true
from: modelData[1]; to: modelData[2]; value: modelData[3]
onValueChanged: {
repUnit.itemAt(index).text = parseFloat(value * modelData[4]).toFixed(modelData[5]) + modelData[6]
}
onPressedChanged: {
if (!pressed) {
er_set[modelData[7]] = value
writeSettings()
}
}
}
}
Repeater {
id: repUnit
model: sliderGrid.repModel
Text {
color: "white"
text: parseFloat(repSliders.itemAt(index).value * modelData[4]).toFixed(modelData[5]) + modelData[6]
}
}
}
}
Item {
Layout.fillHeight: true
}
}
}
function disableDialog() {
commDialog.open()
mainColumn.enabled = false
setMotorsEnabled(false)
}
function enableDialog() {
commDialog.close()
mainColumn.enabled = true
setMotorsEnabled(true)
}
Dialog {
id: commDialog
title: "Processing..."
closePolicy: Popup.NoAutoClose
modal: true
focus: true
width: parent.width - 20
x: 10
y: parent.height / 2 - height / 2
parent: container
ProgressBar {
anchors.fill: parent
indeterminate: visible
}
}
Dialog {
id: setupRearDialog
standardButtons: Dialog.Ok | Dialog.Cancel
modal: true
focus: true
rightMargin: 10
leftMargin: 10
closePolicy: Popup.CloseOnEscape
title: "Setup Rear Motor Parameters"
parent: container
property var canDevs: []
y: parent.y + parent.height / 2 - height / 2
ColumnLayout {
anchors.fill: parent
Text {
Layout.fillWidth: true
color: "#ffffff"
verticalAlignment: Text.AlignVCenter
wrapMode: Text.WordWrap
text: "This is going to spin up the rear motor. Make " +
"sure that nothing is in the way."
}
}
onAccepted: {
if (!selectRearMotor()) {
return
}
disableDialog()
mCommands.getMcconf()
if (!Utility.waitSignal(mMcConf, "2updated()", 4000)) {
VescIf.emitMessageDialog("Read Configuration",
"Could not read motor configuration.",
false, false)
unselectRearMotor()
enableDialog()
return
}
mMcConf.updateParamDouble("si_gear_ratio", 112 / 18, null)
mMcConf.updateParamInt("si_motor_poles", 8, null)
mMcConf.updateParamDouble("si_wheel_diameter", 0.58, null)
mMcConf.updateParamEnum("si_battery_type", 0, null)
mMcConf.updateParamInt("si_battery_cells", 14, null)
mMcConf.updateParamDouble("si_battery_ah", 120, null)
mMcConf.updateParamDouble("l_current_max", 380, null)
mMcConf.updateParamDouble("l_current_min", -120, null)
mMcConf.updateParamDouble("l_in_current_max", 300, null)
mMcConf.updateParamDouble("l_in_current_min", -100, null)
mMcConf.updateParamDouble("l_abs_current_max", 480, null)
mMcConf.updateParamDouble("l_min_vin", 20, null)
mMcConf.updateParamDouble("l_max_vin", 70, null)
mMcConf.updateParamDouble("l_battery_cut_start", 47.6, null)
mMcConf.updateParamDouble("l_battery_cut_end", 42, null)
mMcConf.updateParamDouble("l_temp_motor_start", 125, null)
mMcConf.updateParamDouble("l_temp_motor_end", 135, null)
mMcConf.updateParamDouble("l_temp_accel_dec", 0, null)
mMcConf.updateParamDouble("foc_f_zv", 30000, null)
mMcConf.updateParamEnum("foc_sensor_mode", 2, null)
mMcConf.updateParamDouble("foc_openloop_rpm", 350, null)
mMcConf.updateParamDouble("foc_sl_erpm", 6000, null)
mMcConf.updateParamBool("foc_sample_high_current", 1, null)
mMcConf.updateParamDouble("foc_phase_filter_max_erpm", 1600, null)
// Field Weakening
mMcConf.updateParamDouble("foc_fw_current_max", 40, null)
mMcConf.updateParamEnum("m_motor_temp_sens_type", 4, null)
mMcConf.updateParamInt("m_hall_extra_samples", 4, null)
mCommands.setMcconf(false)
if (!Utility.waitSignal(mCommands, "2ackReceived(QString)", 4000)) {
VescIf.emitMessageDialog("Write Configuration",
"Could not write motor configuration.",
false, false)
unselectRearMotor()
enableDialog()
return
}
// Resistance and inductance
var rl = Utility.measureRLBlocking(VescIf)
if (rl.length == 0 || rl[0] < 1e-10) {
VescIf.emitMessageDialog("Measure RL",
"Could not measure resistance and inductance",
false, false)
unselectRearMotor()
enableDialog()
return
}
rl[1] = rl[1] * 1e-6
mMcConf.updateParamDouble("foc_motor_r", rl[0], null)
mMcConf.updateParamDouble("foc_motor_l", rl[1] * 0.9, null)
// Flux linkage
var linkage = Utility.measureLinkageOpenloopBlocking(VescIf, 100, 2000, 0.3, rl[0], rl[1])
if (linkage <= 1e-10) {
VescIf.emitMessageDialog("Measure Flux Linkage",
"Could not measure flux linkage",
false, false)
unselectRearMotor()
enableDialog()
return
}
Utility.waitMotorStop(VescIf, 50, 6000)
mMcConf.updateParamDouble("foc_motor_flux_linkage", linkage, null)
// Calculate current controller and observer gains
mMcConf.updateParamDouble("foc_observer_gain", (1.0e-3 / (linkage * linkage)) * 1e6, null)
var tc = 200e-6
var bw = 1.0 / tc
mMcConf.updateParamDouble("foc_current_kp", rl[1] * bw, null)
mMcConf.updateParamDouble("foc_current_ki", rl[0] * bw, null)
// Temperature
var t_motor = Utility.getMcValuesBlocking(VescIf).temp_motor
mMcConf.updateParamBool("foc_temp_comp", 1, null)
mMcConf.updateParamDouble("foc_temp_comp_base_temp", t_motor, null)
// Hall table
var hall = Utility.measureHallFocBlocking(VescIf, 150)
if (hall[0] != 0) {
VescIf.emitMessageDialog("Measure Hall Sensors",
"Could not measure hall sensors",
false, false)
unselectRearMotor()
enableDialog()
return
}
mMcConf.updateParamInt("foc_hall_table__0", hall[1], null)
mMcConf.updateParamInt("foc_hall_table__1", hall[2], null)
mMcConf.updateParamInt("foc_hall_table__2", hall[3], null)
mMcConf.updateParamInt("foc_hall_table__3", hall[4], null)
mMcConf.updateParamInt("foc_hall_table__4", hall[5], null)
mMcConf.updateParamInt("foc_hall_table__5", hall[6], null)
mMcConf.updateParamInt("foc_hall_table__6", hall[7], null)
mMcConf.updateParamInt("foc_hall_table__7", hall[8], null)
mCommands.setMcconf(false)
if (!Utility.waitSignal(mCommands, "2ackReceived(QString)", 4000)) {
VescIf.emitMessageDialog("Write Configuration",
"Could not write motor configuration.",
false, false)
unselectRearMotor()
enableDialog()
return
}
unselectRearMotor()
enableDialog()
VescIf.emitMessageDialog("Setup Rear Motor",
"Done!",
true, false)
}
}
Dialog {
id: setupFrontDialog
standardButtons: Dialog.Ok | Dialog.Cancel
modal: true
focus: true
rightMargin: 10
leftMargin: 10
closePolicy: Popup.CloseOnEscape
title: "Setup Front Motor Parameters"
parent: container
property var canDevs: []
y: parent.y + parent.height / 2 - height / 2
ColumnLayout {
anchors.fill: parent
Text {
Layout.fillWidth: true
color: "#ffffff"
verticalAlignment: Text.AlignVCenter
wrapMode: Text.WordWrap
text: "This is going to spin up the front motor. Make " +
"sure that nothing is in the way."
}
}
onAccepted: {
disableDialog()
mCommands.getMcconf()
if (!Utility.waitSignal(mMcConf, "2updated()", 4000)) {
VescIf.emitMessageDialog("Read Configuration",
"Could not read motor configuration.",
false, false)
enableDialog()
return
}
mMcConf.updateParamBool("m_invert_direction", 1, null)
// Resistance and inductance
var rl = Utility.measureRLBlocking(VescIf)
if (rl.length == 0 || rl[0] < 1e-10) {
VescIf.emitMessageDialog("Measure RL",
"Could not measure resistance and inductance",
false, false)
enableDialog()
return
}
rl[1] = rl[1] * 1e-6
mMcConf.updateParamDouble("foc_motor_r", rl[0], null)
mMcConf.updateParamDouble("foc_motor_l", rl[1], null)
// Flux linkage
var linkage = Utility.measureLinkageOpenloopBlocking(VescIf, 20, 2000, 0.2, rl[0], rl[1])
if (linkage <= 1e-10) {
VescIf.emitMessageDialog("Measure Flux Linkage",
"Could not measure flux linkage",
false, false)
enableDialog()
return
}
mMcConf.updateParamDouble("foc_motor_flux_linkage", linkage, null)
Utility.waitMotorStop(VescIf, 50, 5000)
// Calculate current controller and observer gains
mMcConf.updateParamDouble("foc_observer_gain", (1.0e-3 / (linkage * linkage)) * 1e6, null)
var tc = 600e-6
var bw = 1.0 / tc
mMcConf.updateParamDouble("foc_current_kp", rl[1] * bw, null)
mMcConf.updateParamDouble("foc_current_ki", rl[0] * bw, null)
// Temperature
var t_motor = Utility.getMcValuesBlocking(VescIf).temp_motor
mMcConf.updateParamBool("foc_temp_comp", 1, null)
mMcConf.updateParamDouble("foc_temp_comp_base_temp", t_motor, null)
mCommands.setMcconf(false)
if (!Utility.waitSignal(mCommands, "2ackReceived(QString)", 4000)) {
VescIf.emitMessageDialog("Write Configuration",
"Could not write motor configuration.",
false, false)
enableDialog()
return
}
enableDialog()
VescIf.emitMessageDialog("Setup Front Motor",
"Done!",
true, false)
}
}
}