mirror of
https://github.com/odriverobotics/ODrive.git
synced 2026-09-23 00:59:54 +08:00
Merge branch 'devel' into devel_fast
This commit is contained in:
+4
-2
@@ -5,14 +5,16 @@ Please add a note of your changes below this heading if you make a Pull Request.
|
||||
* Added periodic sending of encoder position on CAN
|
||||
|
||||
### Changed
|
||||
|
||||
* Modified encoder offset calibration to work correctly when calib_scan_distance is not a multiple of 4pi
|
||||
* Moved thermistors from being a top level object to belonging to Motor objects. Also changed errors: thermistor errors rolled into motor errors
|
||||
* Use DMA for DRV8301 setup
|
||||
* Make NVM configuration code more dynamic so that the layout doesn't have to be known at compile time.
|
||||
* GPIO initialization logic was changed. GPIOs now need to be explicitly set to the mode corresponding to the feature that they are used by. See `<odrv>.config.gpioX_mode`.
|
||||
* Previously, if two components used the same interrupt pin (e.g. step input for axis0 and axis1) then the one that was configured later would override the other one. Now this is no longer the case (the old component remains the owner of the pin).
|
||||
|
||||
### API Miration Notes
|
||||
### API Migration Notes
|
||||
|
||||
* `odrive.axis.fet_thermistor`, `odrive.axis.motor_thermistor` moved to `odrive.axis.motor` object
|
||||
* `enable_uart` and `uart_baudrate` were renamed to `enable_uart0` and `uart0_baudrate`.
|
||||
* `enable_i2c_instead_of_can` was replaced by the separate settings `enable_i2c0` and `enable_can0`.
|
||||
* `<axis>.motor.gate_driver` was moved to `<axis>.gate_driver`.
|
||||
|
||||
@@ -58,20 +58,26 @@ OnboardThermistorCurrentLimiter fet_thermistors[AXIS_COUNT] = {
|
||||
}
|
||||
};
|
||||
|
||||
OffboardThermistorCurrentLimiter motor_thermistors[AXIS_COUNT];
|
||||
|
||||
Motor motors[AXIS_COUNT] = {
|
||||
{
|
||||
&htim1, // timer
|
||||
TIM_1_8_PERIOD_CLOCKS, // control_deadline
|
||||
1.0f / SHUNT_RESISTANCE, // shunt_conductance [S]
|
||||
m0_gate_driver, // gate_driver
|
||||
m0_gate_driver // opamp
|
||||
m0_gate_driver, // opamp
|
||||
fet_thermistors[0],
|
||||
motor_thermistors[0]
|
||||
},
|
||||
{
|
||||
&htim8, // timer
|
||||
(3 * TIM_1_8_PERIOD_CLOCKS) / 2, // control_deadline
|
||||
1.0f / SHUNT_RESISTANCE, // shunt_conductance [S]
|
||||
m1_gate_driver, // gate_driver
|
||||
m1_gate_driver // opamp
|
||||
m1_gate_driver, // opamp
|
||||
fet_thermistors[1],
|
||||
motor_thermistors[1]
|
||||
}
|
||||
};
|
||||
|
||||
@@ -101,7 +107,6 @@ MechanicalBrake mechanical_brakes[AXIS_COUNT];
|
||||
SensorlessEstimator sensorless_estimators[AXIS_COUNT];
|
||||
Controller controllers[AXIS_COUNT];
|
||||
TrapezoidalTrajectory trap[AXIS_COUNT];
|
||||
OffboardThermistorCurrentLimiter motor_thermistors[AXIS_COUNT];
|
||||
|
||||
std::array<Axis, AXIS_COUNT> axes{{
|
||||
{
|
||||
@@ -112,8 +117,6 @@ std::array<Axis, AXIS_COUNT> axes{{
|
||||
encoders[0], // encoder
|
||||
sensorless_estimators[0], // sensorless_estimator
|
||||
controllers[0], // controller
|
||||
fet_thermistors[0], // fet_thermistor
|
||||
motor_thermistors[0], // motor_thermistor
|
||||
motors[0], // motor
|
||||
trap[0], // trap
|
||||
endstops[0], endstops[1], // min_endstop, max_endstop
|
||||
@@ -132,8 +135,6 @@ std::array<Axis, AXIS_COUNT> axes{{
|
||||
encoders[1], // encoder
|
||||
sensorless_estimators[1], // sensorless_estimator
|
||||
controllers[1], // controller
|
||||
fet_thermistors[1], // fet_thermistor
|
||||
motor_thermistors[1], // motor_thermistor
|
||||
motors[1], // motor
|
||||
trap[1], // trap
|
||||
endstops[2], endstops[3], // min_endstop, max_endstop
|
||||
|
||||
@@ -14,8 +14,6 @@ Axis::Axis(int axis_num,
|
||||
Encoder& encoder,
|
||||
SensorlessEstimator& sensorless_estimator,
|
||||
Controller& controller,
|
||||
OnboardThermistorCurrentLimiter& fet_thermistor,
|
||||
OffboardThermistorCurrentLimiter& motor_thermistor,
|
||||
Motor& motor,
|
||||
TrapezoidalTrajectory& trap,
|
||||
Endstop& min_endstop,
|
||||
@@ -28,25 +26,15 @@ Axis::Axis(int axis_num,
|
||||
encoder_(encoder),
|
||||
sensorless_estimator_(sensorless_estimator),
|
||||
controller_(controller),
|
||||
fet_thermistor_(fet_thermistor),
|
||||
motor_thermistor_(motor_thermistor),
|
||||
motor_(motor),
|
||||
trap_traj_(trap),
|
||||
min_endstop_(min_endstop),
|
||||
max_endstop_(max_endstop),
|
||||
mechanical_brake_(mechanical_brake),
|
||||
current_limiters_(make_array(
|
||||
static_cast<CurrentLimiter*>(&fet_thermistor),
|
||||
static_cast<CurrentLimiter*>(&motor_thermistor))),
|
||||
thermistors_(make_array(
|
||||
static_cast<ThermistorCurrentLimiter*>(&fet_thermistor),
|
||||
static_cast<ThermistorCurrentLimiter*>(&motor_thermistor)))
|
||||
mechanical_brake_(mechanical_brake)
|
||||
{
|
||||
encoder_.axis_ = this;
|
||||
sensorless_estimator_.axis_ = this;
|
||||
controller_.axis_ = this;
|
||||
fet_thermistor_.axis_ = this;
|
||||
motor_thermistor.axis_ = this;
|
||||
motor_.axis_ = this;
|
||||
trap_traj_.axis_ = this;
|
||||
min_endstop_.axis_ = this;
|
||||
@@ -180,9 +168,6 @@ bool Axis::do_checks() {
|
||||
|
||||
// Sub-components should use set_error which will propegate to this error_
|
||||
motor_.effective_current_lim();
|
||||
for (ThermistorCurrentLimiter* thermistor : thermistors_) {
|
||||
thermistor->do_checks();
|
||||
}
|
||||
motor_.do_checks();
|
||||
// encoder_.do_checks();
|
||||
// sensorless_estimator_.do_checks();
|
||||
@@ -201,11 +186,10 @@ bool Axis::do_checks() {
|
||||
// @brief Update all esitmators
|
||||
bool Axis::do_updates() {
|
||||
// Sub-components should use set_error which will propegate to this error_
|
||||
for (ThermistorCurrentLimiter* thermistor : thermistors_) {
|
||||
thermistor->update();
|
||||
}
|
||||
encoder_.update();
|
||||
sensorless_estimator_.update();
|
||||
motor_.fet_thermistor_.update();
|
||||
motor_.motor_thermistor_.update();
|
||||
min_endstop_.update();
|
||||
max_endstop_.update();
|
||||
bool ret = check_for_errors();
|
||||
|
||||
@@ -97,8 +97,6 @@ public:
|
||||
Encoder& encoder,
|
||||
SensorlessEstimator& sensorless_estimator,
|
||||
Controller& controller,
|
||||
OnboardThermistorCurrentLimiter& fet_thermistor,
|
||||
OffboardThermistorCurrentLimiter& motor_thermistor,
|
||||
Motor& motor,
|
||||
TrapezoidalTrajectory& trap,
|
||||
Endstop& min_endstop,
|
||||
@@ -227,19 +225,12 @@ public:
|
||||
Encoder& encoder_;
|
||||
SensorlessEstimator& sensorless_estimator_;
|
||||
Controller& controller_;
|
||||
OnboardThermistorCurrentLimiter& fet_thermistor_;
|
||||
OffboardThermistorCurrentLimiter& motor_thermistor_;
|
||||
Motor& motor_;
|
||||
TrapezoidalTrajectory& trap_traj_;
|
||||
Endstop& min_endstop_;
|
||||
Endstop& max_endstop_;
|
||||
MechanicalBrake& mechanical_brake_;
|
||||
|
||||
// List of current_limiters and thermistors to
|
||||
// provide easy iteration.
|
||||
std::array<CurrentLimiter*, 2> current_limiters_;
|
||||
std::array<ThermistorCurrentLimiter*, 2> thermistors_;
|
||||
|
||||
osThreadId thread_id_;
|
||||
const uint32_t stack_size_ = 2048; // Bytes
|
||||
volatile bool thread_id_valid_ = false;
|
||||
|
||||
@@ -228,10 +228,13 @@ bool Encoder::run_offset_calibration() {
|
||||
else
|
||||
return false;
|
||||
|
||||
// go to motor zero phase for start_lock_duration to get ready to scan
|
||||
// go to start position of forward scan for start_lock_duration to get ready to scan
|
||||
int i = 0;
|
||||
axis_->run_control_loop([&](){
|
||||
if (!axis_->motor_.enqueue_voltage_timings(voltage_magnitude, 0.0f))
|
||||
float phase = wrap_pm_pi(0 - config_.calib_scan_distance / 2.0f);
|
||||
float v_alpha = voltage_magnitude * our_arm_cos_f32(phase);
|
||||
float v_beta = voltage_magnitude * our_arm_sin_f32(phase);
|
||||
if (!axis_->motor_.enqueue_voltage_timings(v_alpha, v_beta))
|
||||
return false; // error set inside enqueue_voltage_timings
|
||||
axis_->motor_.log_timing(TIMING_LOG_ENC_CALIB);
|
||||
return ++i < start_lock_duration * current_meas_hz;
|
||||
|
||||
@@ -50,8 +50,8 @@ static bool config_read_all() {
|
||||
config_manager.read(&axes[i].max_endstop_.config_) &&
|
||||
config_manager.read(&axes[i].mechanical_brake_.config_) &&
|
||||
config_manager.read(&motors[i].config_) &&
|
||||
config_manager.read(&fet_thermistors[i].config_) &&
|
||||
config_manager.read(&axes[i].motor_thermistor_.config_) &&
|
||||
config_manager.read(&motors[i].fet_thermistor_.config_) &&
|
||||
config_manager.read(&motors[i].motor_thermistor_.config_) &&
|
||||
config_manager.read(&axes[i].config_);
|
||||
}
|
||||
return success;
|
||||
@@ -70,8 +70,8 @@ static bool config_write_all() {
|
||||
config_manager.write(&axes[i].max_endstop_.config_) &&
|
||||
config_manager.write(&axes[i].mechanical_brake_.config_) &&
|
||||
config_manager.write(&motors[i].config_) &&
|
||||
config_manager.write(&fet_thermistors[i].config_) &&
|
||||
config_manager.write(&axes[i].motor_thermistor_.config_) &&
|
||||
config_manager.write(&motors[i].fet_thermistor_.config_) &&
|
||||
config_manager.write(&motors[i].motor_thermistor_.config_) &&
|
||||
config_manager.write(&axes[i].config_);
|
||||
}
|
||||
return success;
|
||||
@@ -90,8 +90,8 @@ static void config_clear_all() {
|
||||
axes[i].max_endstop_.config_ = {};
|
||||
axes[i].mechanical_brake_.config_ = {};
|
||||
motors[i].config_ = {};
|
||||
fet_thermistors[i].config_ = {};
|
||||
axes[i].motor_thermistor_.config_ = {};
|
||||
motors[i].fet_thermistor_.config_ = {};
|
||||
motors[i].motor_thermistor_.config_ = {};
|
||||
axes[i].clear_config();
|
||||
}
|
||||
}
|
||||
@@ -104,6 +104,7 @@ static bool config_apply_all() {
|
||||
&& axes[i].min_endstop_.apply_config()
|
||||
&& axes[i].max_endstop_.apply_config()
|
||||
&& motors[i].apply_config()
|
||||
&& motors[i].motor_thermistor_.apply_config()
|
||||
&& axes[i].apply_config();
|
||||
}
|
||||
return success;
|
||||
|
||||
@@ -10,13 +10,19 @@ Motor::Motor(TIM_HandleTypeDef* timer,
|
||||
uint16_t control_deadline,
|
||||
float shunt_conductance,
|
||||
TGateDriver& gate_driver,
|
||||
TOpAmp& opamp) :
|
||||
TOpAmp& opamp,
|
||||
OnboardThermistorCurrentLimiter& fet_thermistor,
|
||||
OffboardThermistorCurrentLimiter& motor_thermistor) :
|
||||
timer_(timer),
|
||||
control_deadline_(control_deadline),
|
||||
shunt_conductance_(shunt_conductance),
|
||||
gate_driver_(gate_driver),
|
||||
opamp_(opamp) {
|
||||
opamp_(opamp),
|
||||
fet_thermistor_(fet_thermistor),
|
||||
motor_thermistor_(motor_thermistor) {
|
||||
apply_config();
|
||||
fet_thermistor_.motor_ = this;
|
||||
motor_thermistor_.motor_ = this;
|
||||
}
|
||||
|
||||
// @brief Arms the PWM outputs that belong to this motor.
|
||||
@@ -110,7 +116,16 @@ bool Motor::do_checks() {
|
||||
set_error(ERROR_DRV_FAULT);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!motor_thermistor_.do_checks()) {
|
||||
axis_->error_ |= Axis::ERROR_MOTOR_FAILED;
|
||||
set_error(ERROR_MOTOR_THERMISTOR_OVER_TEMP);
|
||||
return false;
|
||||
}
|
||||
if (!fet_thermistor_.do_checks()) {
|
||||
axis_->error_ |= Axis::ERROR_MOTOR_FAILED;
|
||||
set_error(ERROR_FET_THERMISTOR_OVER_TEMP);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -124,11 +139,9 @@ float Motor::effective_current_lim() {
|
||||
current_lim = std::min(current_lim, axis_->motor_.current_control_.max_allowed_current);
|
||||
}
|
||||
|
||||
// Apply axis current limiters
|
||||
for (const CurrentLimiter* const limiter : axis_->current_limiters_) {
|
||||
current_lim = std::min(current_lim, limiter->get_current_limit(config_.current_lim));
|
||||
}
|
||||
|
||||
// Apply thermistor current limiters
|
||||
current_lim = std::min(current_lim, motor_thermistor_.get_current_limit(config_.current_lim));
|
||||
current_lim = std::min(current_lim, fet_thermistor_.get_current_limit(config_.current_lim));
|
||||
effective_current_lim_ = current_lim;
|
||||
|
||||
return effective_current_lim_;
|
||||
|
||||
@@ -99,7 +99,9 @@ public:
|
||||
uint16_t control_deadline,
|
||||
float shunt_conductance,
|
||||
TGateDriver& gate_driver,
|
||||
TOpAmp& opamp);
|
||||
TOpAmp& opamp,
|
||||
OnboardThermistorCurrentLimiter& fet_thermistor,
|
||||
OffboardThermistorCurrentLimiter& motor_thermistor);
|
||||
|
||||
bool arm();
|
||||
void disarm();
|
||||
@@ -130,6 +132,8 @@ public:
|
||||
const float shunt_conductance_;
|
||||
TGateDriver& gate_driver_;
|
||||
TOpAmp& opamp_;
|
||||
OnboardThermistorCurrentLimiter& fet_thermistor_;
|
||||
OffboardThermistorCurrentLimiter& motor_thermistor_;
|
||||
|
||||
Config_t config_;
|
||||
Axis* axis_ = nullptr; // set by Axis constructor
|
||||
|
||||
@@ -14,8 +14,7 @@ ThermistorCurrentLimiter::ThermistorCurrentLimiter(uint16_t adc_channel,
|
||||
temperature_(NAN),
|
||||
temp_limit_lower_(temp_limit_lower),
|
||||
temp_limit_upper_(temp_limit_upper),
|
||||
enabled_(enabled),
|
||||
error_(ERROR_NONE)
|
||||
enabled_(enabled)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -26,8 +25,6 @@ void ThermistorCurrentLimiter::update() {
|
||||
|
||||
bool ThermistorCurrentLimiter::do_checks() {
|
||||
if (enabled_ && temperature_ >= temp_limit_upper_ + 5) {
|
||||
error_ = ERROR_OVER_TEMP;
|
||||
axis_->error_ |= Axis::ERROR_OVER_TEMP;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -69,6 +66,12 @@ OffboardThermistorCurrentLimiter::OffboardThermistorCurrentLimiter() :
|
||||
decode_pin();
|
||||
}
|
||||
|
||||
bool OffboardThermistorCurrentLimiter::apply_config() {
|
||||
config_.parent = this;
|
||||
decode_pin();
|
||||
return true;
|
||||
}
|
||||
|
||||
void OffboardThermistorCurrentLimiter::decode_pin() {
|
||||
adc_channel_ = channel_from_gpio(get_gpio(config_.gpio_pin));
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#ifndef __THERMISTOR_HPP
|
||||
#define __THERMISTOR_HPP
|
||||
|
||||
class Axis; // declared in axis.hpp
|
||||
class Motor; // declared in motor.hpp
|
||||
|
||||
#include "current_limiter.hpp"
|
||||
#include <autogen/interfaces.hpp>
|
||||
@@ -28,8 +28,7 @@ public:
|
||||
const float& temp_limit_lower_;
|
||||
const float& temp_limit_upper_;
|
||||
const bool& enabled_;
|
||||
Error error_;
|
||||
Axis* axis_ = nullptr; // set by Axis constructor
|
||||
Motor* motor_ = nullptr; // set by Motor::apply_config()
|
||||
};
|
||||
|
||||
class OnboardThermistorCurrentLimiter : public ThermistorCurrentLimiter, public ODriveIntf::OnboardThermistorCurrentLimiterIntf {
|
||||
@@ -68,6 +67,8 @@ public:
|
||||
|
||||
Config_t config_;
|
||||
|
||||
bool apply_config();
|
||||
|
||||
private:
|
||||
void decode_pin();
|
||||
};
|
||||
|
||||
@@ -109,10 +109,13 @@ def find_all(path, serial_number,
|
||||
# Save JSON to cache
|
||||
if not cache_path is None:
|
||||
logger.debug("Creating new JSON cache file {}".format(cache_path))
|
||||
os.makedirs(cache_dir, exist_ok=True)
|
||||
with open(cache_path, 'w+') as json_cache:
|
||||
json_cache.write(json_string)
|
||||
logger.debug("Saved JSON to cache file {}".format(cache_path))
|
||||
try:
|
||||
os.makedirs(cache_dir, exist_ok=True)
|
||||
with open(cache_path, 'w+') as json_cache:
|
||||
json_cache.write(json_string)
|
||||
logger.debug("Saved JSON to cache file {}".format(cache_path))
|
||||
except Exception as ex:
|
||||
logger.warn("Failed to cache JSON: {}".format(ex))
|
||||
|
||||
channel._interface_definition_crc = json_crc16
|
||||
|
||||
|
||||
@@ -355,7 +355,8 @@ interfaces:
|
||||
bit: 17
|
||||
doc: the min endstop was not enabled during homing
|
||||
OverTemp:
|
||||
doc: Check `fet_thermistor.error` and `motor_thermistor.error` for more information.
|
||||
# unused
|
||||
doc: Check `motor.error` for more details.
|
||||
step_dir_active: readonly bool
|
||||
current_state: readonly AxisState
|
||||
requested_state: AxisState
|
||||
@@ -440,8 +441,6 @@ interfaces:
|
||||
# status_reg_2: readonly uint32
|
||||
# ctrl_reg_1: readonly uint32
|
||||
# ctrl_reg_2: readonly uint32
|
||||
fet_thermistor: OnboardThermistorCurrentLimiter
|
||||
motor_thermistor: OffboardThermistorCurrentLimiter
|
||||
motor: Motor
|
||||
controller: Controller
|
||||
encoder: Encoder
|
||||
@@ -495,7 +494,6 @@ interfaces:
|
||||
ODrive.OnboardThermistorCurrentLimiter:
|
||||
c_is_class: True
|
||||
attributes:
|
||||
error: ThermistorCurrentLimiter.Error
|
||||
temperature: readonly float32
|
||||
config:
|
||||
c_is_class: False
|
||||
@@ -511,7 +509,6 @@ interfaces:
|
||||
ODrive.OffboardThermistorCurrentLimiter:
|
||||
c_is_class: True
|
||||
attributes:
|
||||
error: ThermistorCurrentLimiter.Error
|
||||
temperature: readonly float32
|
||||
config:
|
||||
c_is_class: False
|
||||
@@ -609,6 +606,8 @@ interfaces:
|
||||
DcBusOverRegenCurrent: {doc: too much current pushed into the power supply}
|
||||
DcBusOverCurrent: {doc: too much current pulled out of the power supply}
|
||||
ModulationIsNan:
|
||||
MotorThermistorOverTemp: {doc: The motor thermistor measured a temperature above motor.motor_thermistor.config.temp_limit_upper}
|
||||
FetThermistorOverTemp: {doc: The inverter thermistor measured a temperature above motor.fet_thermistor.config.temp_limit_upper}
|
||||
armed_state:
|
||||
typeargs: {fibre.Property.mode: readonly}
|
||||
values:
|
||||
@@ -623,6 +622,8 @@ interfaces:
|
||||
DC_calib_phC: {type: float32, c_name: DC_calib_.phC}
|
||||
phase_current_rev_gain: float32
|
||||
effective_current_lim: readonly float32
|
||||
fet_thermistor: OnboardThermistorCurrentLimiter
|
||||
motor_thermistor: OffboardThermistorCurrentLimiter
|
||||
current_control:
|
||||
c_is_class: False
|
||||
attributes:
|
||||
@@ -1024,12 +1025,6 @@ valuetypes:
|
||||
doc:
|
||||
Endstops must be enabled to use this feature.
|
||||
|
||||
ODrive.ThermistorCurrentLimiter.Error:
|
||||
nullflag: None
|
||||
flags:
|
||||
OverTemp:
|
||||
doc: The thermistor temperature upper limit was exceeded.
|
||||
|
||||
ODrive.Encoder.Mode:
|
||||
values:
|
||||
Incremental:
|
||||
|
||||
+24
-11
@@ -58,8 +58,12 @@ def discovered_device(device):
|
||||
while globals()['inUse']:
|
||||
time.sleep(0.1)
|
||||
globals()['odrives'][odrive_name] = device
|
||||
globals()['odrives_status'][odrive_name] = True
|
||||
print("Found " + str(serial_number))
|
||||
print("odrive list: " + str([key for key in globals()['odrives'].keys()]))
|
||||
# tell GUI the status of known ODrives (previously connected and then disconnected ODrives will be "False")
|
||||
socketio.emit('odrives-status', json.dumps(globals()['odrives_status']))
|
||||
# triggers a getODrives socketio message
|
||||
socketio.emit('odrive-found')
|
||||
|
||||
def start_discovery():
|
||||
@@ -68,9 +72,12 @@ def start_discovery():
|
||||
shutdown = fibre.Event()
|
||||
fibre.find_all("usb", None, discovered_device, shutdown, shutdown, log)
|
||||
|
||||
def handle_disconnect():
|
||||
def handle_disconnect(odrive_name):
|
||||
print("lost odrive")
|
||||
#socketio.emit('odrive-disconnected')
|
||||
globals()['odrives_status'][odrive_name] = False
|
||||
# emit the whole list of odrive statuses
|
||||
# in the GUI, mark and use status as ODrive state.
|
||||
socketio.emit('odrives-status', json.dumps(globals()['odrives_status']))
|
||||
|
||||
@socketio.on('findODrives')
|
||||
def getODrives(message):
|
||||
@@ -115,8 +122,9 @@ def get_odrives(data):
|
||||
odriveDict = {}
|
||||
#for (index, odrv) in enumerate(globals()['odrives']):
|
||||
# odriveDict["odrive" + str(index)] = dictFromRO(odrv)
|
||||
for key in globals()['odrives'].keys():
|
||||
odriveDict[key] = dictFromRO(globals()['odrives'][key])
|
||||
for key in globals()['odrives_status'].keys():
|
||||
if globals()['odrives_status'][key] == True:
|
||||
odriveDict[key] = dictFromRO(globals()['odrives'][key])
|
||||
globals()['inUse'] = False
|
||||
emit('odrives', json.dumps(odriveDict))
|
||||
|
||||
@@ -126,10 +134,11 @@ def get_property(message):
|
||||
# will be {"path": "odriveX.axisY.blah.blah"}
|
||||
while globals()['inUse']:
|
||||
time.sleep(0.1)
|
||||
globals()['inUse'] = True
|
||||
val = getVal(globals()['odrives'], message["path"].split('.'))
|
||||
globals()['inUse'] = False
|
||||
emit('ODriveProperty', json.dumps({"path": message["path"], "val": val}))
|
||||
if globals()['odrives_status'][message["path"].split('.')[0]]:
|
||||
globals()['inUse'] = True
|
||||
val = getVal(globals()['odrives'], message["path"].split('.'))
|
||||
globals()['inUse'] = False
|
||||
emit('ODriveProperty', json.dumps({"path": message["path"], "val": val}))
|
||||
|
||||
@socketio.on('setProperty')
|
||||
def set_property(message):
|
||||
@@ -194,7 +203,7 @@ def postVal(odrives, keyList, value, argType):
|
||||
else:
|
||||
pass # dont support that type yet
|
||||
except fibre.protocol.ChannelBrokenException:
|
||||
handle_disconnect()
|
||||
handle_disconnect(odrv)
|
||||
except:
|
||||
print("exception in postVal")
|
||||
|
||||
@@ -210,7 +219,7 @@ def getVal(odrives, keyList):
|
||||
else:
|
||||
return RO.get_value()
|
||||
except fibre.protocol.ChannelBrokenException:
|
||||
handle_disconnect()
|
||||
handle_disconnect(odrv)
|
||||
except:
|
||||
print("exception in getVal")
|
||||
return 0
|
||||
@@ -235,7 +244,7 @@ def callFunc(odrives, keyList):
|
||||
if isinstance(RO, fibre.remote_object.RemoteFunction):
|
||||
RO.__call__()
|
||||
except fibre.protocol.ChannelBrokenException:
|
||||
handle_disconnect()
|
||||
handle_disconnect(odrv)
|
||||
except:
|
||||
print("fcn call failed")
|
||||
|
||||
@@ -252,7 +261,11 @@ if __name__ == "__main__":
|
||||
import odrive.utils # for dump_errors()
|
||||
import fibre
|
||||
|
||||
# global for holding references to all connected odrives
|
||||
globals()['odrives'] = {}
|
||||
# global dict {'odriveX': True/False} where True/False reflects status of connection
|
||||
# on handle_disconnect, set it to False. On connection, set it to True
|
||||
globals()['odrives_status'] = {}
|
||||
globals()['discovered_devices'] = []
|
||||
# spinlock
|
||||
globals()['inUse'] = False
|
||||
|
||||
@@ -56,9 +56,6 @@
|
||||
:axis="axis.name"
|
||||
:odrives="odrives"
|
||||
></Axis>
|
||||
<!--<div class="odrive-status">
|
||||
ODrive:{{ODriveConnected}}
|
||||
</div>-->
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -129,22 +126,6 @@ export default {
|
||||
currentDash: function () {
|
||||
return this.$store.state.currentDash;
|
||||
},
|
||||
ODriveConnected: function () {
|
||||
// if server and odrive disconnected, disconnected
|
||||
// if server connected and odrive disco, connecting
|
||||
// if server and odrive connected, connected
|
||||
let ret;
|
||||
if (this.$store.state.serverConnected && this.$store.state.ODriveConnected) {
|
||||
ret = "connected";
|
||||
}
|
||||
else if (this.$store.state.serverConnected && !this.$store.state.ODriveConnected) {
|
||||
ret = "connecting...";
|
||||
}
|
||||
else {
|
||||
ret = "disconnected";
|
||||
}
|
||||
return ret;
|
||||
},
|
||||
samplingText: function () {
|
||||
let ret;
|
||||
if (this.$store.state.sampling) {
|
||||
|
||||
@@ -29,9 +29,6 @@
|
||||
"AXIS_STATE_ENCODER_DIR_FIND" : 10,
|
||||
"AXIS_STATE_HOMING" : 11,
|
||||
|
||||
"THERMISTOR_CURRENT_LIMITER_ERROR_NONE" : 0,
|
||||
"THERMISTOR_CURRENT_LIMITER_ERROR_OVER_TEMP" : 1,
|
||||
|
||||
"ENCODER_MODE_INCREMENTAL" : 0,
|
||||
"ENCODER_MODE_HALL" : 1,
|
||||
"ENCODER_MODE_SINCOS" : 2,
|
||||
@@ -102,6 +99,8 @@
|
||||
"MOTOR_ERROR_DC_BUS_OVER_REGEN_CURRENT" : 16384,
|
||||
"MOTOR_ERROR_DC_BUS_OVER_CURRENT" : 32768,
|
||||
"MOTOR_ERROR_MODULATION_IS_NAN" : 65536,
|
||||
"MOTOR_ERROR_MOTOR_THERMISTOR_OVER_TEMP" : 131072,
|
||||
"MOTOR_ERROR_FET_THERMISTOR_OVER_TEMP" : 262144,
|
||||
|
||||
"ARMED_STATE_DISARMED" : 0,
|
||||
"ARMED_STATE_WAITING_FOR_TIMINGS" : 1,
|
||||
|
||||
@@ -17,7 +17,8 @@
|
||||
"config": {
|
||||
"mode": null,
|
||||
"use_index": null,
|
||||
"cpr": null
|
||||
"cpr": null,
|
||||
"calib_scan_distance": null
|
||||
}
|
||||
},
|
||||
"controller": {
|
||||
@@ -51,7 +52,8 @@
|
||||
"config": {
|
||||
"mode": null,
|
||||
"use_index": null,
|
||||
"cpr": null
|
||||
"cpr": null,
|
||||
"calib_scan_distance": null
|
||||
}
|
||||
},
|
||||
"controller": {
|
||||
|
||||
+38
-17
@@ -23,25 +23,42 @@ protocol.registerSchemesAsPrivileged([
|
||||
|
||||
// function to get determine correct command for python
|
||||
function getPyCmd() {
|
||||
let spawnRet = spawnSync('python',['-V']);
|
||||
let vString;
|
||||
if (spawnRet.stdout.toString().length > 1){
|
||||
vString = spawnRet.stdout.toString();
|
||||
// call both 'python' and 'python3' to figure out the correct command
|
||||
let spawnRet = spawnSync('python', ['-V']);
|
||||
let success = spawnRet.status != null;
|
||||
let outputString;
|
||||
let cmd = '';
|
||||
if (success) {
|
||||
if (spawnRet.stdout.toString().length > 1) {
|
||||
outputString = spawnRet.stdout.toString();
|
||||
}
|
||||
else {
|
||||
outputString = spawnRet.stderr.toString();
|
||||
}
|
||||
if (outputString.includes("Python 3")) {
|
||||
cmd = 'python';
|
||||
}
|
||||
}
|
||||
else {
|
||||
vString = spawnRet.stderr.toString();
|
||||
}
|
||||
|
||||
if (vString.split(' ')[1].split('.')[0] == '2') {
|
||||
return 'python3';
|
||||
}
|
||||
else {
|
||||
return 'python';
|
||||
if (cmd == '') {
|
||||
spawnRet = spawnSync('python3', ['-V']);
|
||||
success = spawnRet.status != null;
|
||||
if (success) {
|
||||
if (spawnRet.stdout.toString().length > 1) {
|
||||
outputString = spawnRet.stdout.toString();
|
||||
}
|
||||
else {
|
||||
outputString = spawnRet.stderr.toString();
|
||||
}
|
||||
if (outputString.includes("Python 3")) {
|
||||
cmd = 'python3';
|
||||
}
|
||||
}
|
||||
}
|
||||
return cmd;
|
||||
}
|
||||
|
||||
function createWindow() {
|
||||
|
||||
|
||||
// Create the browser window.
|
||||
win = new BrowserWindow({
|
||||
width: 800,
|
||||
@@ -125,15 +142,19 @@ app.on('ready', async () => {
|
||||
// launch python server on event from renderer process (gui) and pipe stdout/stderr to it
|
||||
ipcMain.on('start-server', () => {
|
||||
server = spawn(getPyCmd(), effectiveCommand);
|
||||
server.stdout.on('data',function(data) {
|
||||
console.log(data.toString('utf8'));
|
||||
server.stdout.on('data', function (data) {
|
||||
try {
|
||||
console.log(data.toString('utf8'));
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
try {
|
||||
win.webContents.send('server-stdout', String(data.toString('utf8')));
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
});
|
||||
server.stderr.on('data',function(data) {
|
||||
server.stderr.on('data', function (data) {
|
||||
console.log(data.toString('utf8'));
|
||||
try {
|
||||
win.webContents.send('server-stderr', String(data.toString('utf8')));
|
||||
|
||||
+27
-25
@@ -2,7 +2,7 @@
|
||||
<div
|
||||
class="axis"
|
||||
@click.self="showError = !showError;"
|
||||
:class="{ noError: !error, error: error}"
|
||||
:class="{inactive: !connected, noError: !error, error: error}"
|
||||
>
|
||||
{{ axis }}
|
||||
<div v-show="showError" class="error-popup card" @click.self="showError = !showError">
|
||||
@@ -66,6 +66,9 @@ const motorErrors = {
|
||||
0x00002000: "MOTOR_ERROR_BRAKE_DUTY_CYCLE_NAN",
|
||||
0x00004000: "MOTOR_ERROR_DC_BUS_OVER_REGEN_CURRENT",
|
||||
0x00008000: "MOTOR_ERROR_DC_BUS_OVER_CURRENT",
|
||||
0x00010000: "MOTOR_ERROR_MODULATION_IS_NAN",
|
||||
0x00020000: "MOTOR_ERROR_MOTOR_THERMISTOR_OVER_TEMP",
|
||||
0x00040000: "MOTOR_ERROR_FET_THERMISTOR_OVER_TEMP",
|
||||
};
|
||||
|
||||
let encoderErrors = {
|
||||
@@ -107,6 +110,9 @@ export default {
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
connected() {
|
||||
return this.$store.state.ODrivesConnected[this.axis.split('.')[0]];
|
||||
},
|
||||
axisErrorMsg() {
|
||||
let retMsg = "none";
|
||||
let errCode = this.axisErr;
|
||||
@@ -125,10 +131,7 @@ export default {
|
||||
errs.push(axisErrors[errKey]);
|
||||
}
|
||||
}
|
||||
retMsg = "";
|
||||
for (const err of errs) {
|
||||
retMsg = retMsg + " " + err;
|
||||
}
|
||||
retMsg = errs.join(', ');
|
||||
}
|
||||
|
||||
return retMsg;
|
||||
@@ -150,10 +153,7 @@ export default {
|
||||
errs.push(motorErrors[errKey]);
|
||||
}
|
||||
}
|
||||
retMsg = "";
|
||||
for (const err of errs) {
|
||||
retMsg = retMsg + " " + err;
|
||||
}
|
||||
retMsg = errs.join(', ');
|
||||
}
|
||||
|
||||
return retMsg;
|
||||
@@ -175,10 +175,7 @@ export default {
|
||||
errs.push(encoderErrors[errKey]);
|
||||
}
|
||||
}
|
||||
retMsg = "";
|
||||
for (const err of errs) {
|
||||
retMsg = retMsg + " " + err;
|
||||
}
|
||||
retMsg = errs.join(', ');
|
||||
}
|
||||
|
||||
return retMsg;
|
||||
@@ -195,10 +192,7 @@ export default {
|
||||
errs.push(controllerErrors[errKey]);
|
||||
}
|
||||
}
|
||||
retMsg = "";
|
||||
for (const err of errs) {
|
||||
retMsg = retMsg + " " + err;
|
||||
}
|
||||
retMsg = errs.join(', ');
|
||||
}
|
||||
|
||||
return retMsg;
|
||||
@@ -238,14 +232,18 @@ export default {
|
||||
created() {
|
||||
// set up timeout loop for grabbing axis error values
|
||||
let update = () => {
|
||||
fetchParam(this.axis + ".error");
|
||||
fetchParam(this.axis + '.motor.error');
|
||||
fetchParam(this.axis + '.controller.error');
|
||||
fetchParam(this.axis + '.encoder.error');
|
||||
this.axisErr = getVal(this.axis + '.error');
|
||||
this.motorErr = getVal(this.axis + '.motor.error');
|
||||
this.controllerErr = getVal(this.axis + '.controller.error');
|
||||
this.encoderErr = getVal(this.axis + '.encoder.error');
|
||||
// Do we have an active connection to the ODrive that contains this axis?
|
||||
if (this.$store.state.ODrivesConnected[this.axis.split('.')[0]]) {
|
||||
fetchParam(this.axis + ".error");
|
||||
fetchParam(this.axis + '.motor.error');
|
||||
fetchParam(this.axis + '.controller.error');
|
||||
fetchParam(this.axis + '.encoder.error');
|
||||
this.axisErr = getVal(this.axis + '.error');
|
||||
this.motorErr = getVal(this.axis + '.motor.error');
|
||||
this.controllerErr = getVal(this.axis + '.controller.error');
|
||||
this.encoderErr = getVal(this.axis + '.encoder.error');
|
||||
}
|
||||
// ODrive not connected
|
||||
setTimeout(update, 1000);
|
||||
}
|
||||
update();
|
||||
@@ -279,4 +277,8 @@ export default {
|
||||
color: black;
|
||||
margin-left: 0px;
|
||||
}
|
||||
|
||||
.inactive {
|
||||
color: grey;
|
||||
}
|
||||
</style>
|
||||
@@ -40,7 +40,7 @@ export default {
|
||||
methods: {
|
||||
newVal: function (e) {
|
||||
let val = parseMath(e.target.value);
|
||||
if (val != false) {
|
||||
if (val !== false) {
|
||||
this.value = val;
|
||||
console.log("input = " + e.target.value + ", val = " + this.value);
|
||||
this.$store.commit("setActionVal", {dashID: this.dashID, actionID: this.id, val: this.value});
|
||||
|
||||
@@ -47,8 +47,9 @@ export default {
|
||||
putVal: function (e) {
|
||||
let keys = this.path.split('.');
|
||||
keys.shift();
|
||||
console.log("input recieved: " + e.target.value);
|
||||
let val = parseMath(e.target.value);
|
||||
if (val != false) {
|
||||
if (val !== false) {
|
||||
putVal(keys.join('.'), val);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -37,7 +37,7 @@ export default {
|
||||
setBR(e) {
|
||||
console.log("from setBR " + e.target.value);
|
||||
let val = parseMath(e.target.value);
|
||||
if (val != false) {
|
||||
if (val !== false) {
|
||||
this.brake_resistance = val;
|
||||
let configStub = undefined;
|
||||
configStub = {
|
||||
|
||||
@@ -26,7 +26,7 @@ export default {
|
||||
methods: {
|
||||
setCPR(e) {
|
||||
let val = parseMath(e.target.value);
|
||||
if (val != false) {
|
||||
if (val !== false) {
|
||||
this.cpr = val;
|
||||
let configStub = undefined;
|
||||
if (this.data.axis == "axis0") {
|
||||
|
||||
@@ -27,7 +27,7 @@ export default {
|
||||
methods: {
|
||||
setCPR(e) {
|
||||
let val = parseMath(e.target.value);
|
||||
if (val != false) {
|
||||
if (val !== false) {
|
||||
this.cpr = val;
|
||||
let configStub = undefined;
|
||||
if (this.data.axis == "axis0") {
|
||||
|
||||
@@ -100,7 +100,7 @@ export default {
|
||||
},
|
||||
setVelocityLimit(e) {
|
||||
let val = parseMath(e.target.value);
|
||||
if (val != false) {
|
||||
if (val !== false) {
|
||||
this.vel_limit = parseFloat(e.target.value);
|
||||
this.vel_set = true;
|
||||
this.sendConfig();
|
||||
@@ -108,7 +108,7 @@ export default {
|
||||
},
|
||||
setCurrentLimit(e) {
|
||||
let val = parseMath(e.target.value);
|
||||
if (val != false) {
|
||||
if (val !== false) {
|
||||
this.current_lim = parseFloat(e.target.value);
|
||||
this.current_set = true;
|
||||
this.sendConfig();
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import odriveEnums from "../../../assets/odriveEnums.json";
|
||||
import { putVal } from "../../../lib/odrive_utils.js"
|
||||
//import odriveEnums from "../../../assets/odriveEnums.json";
|
||||
//import { putVal } from "../../../lib/odrive_utils.js"
|
||||
|
||||
export default {
|
||||
name: "wizardMotorCal",
|
||||
@@ -21,9 +21,6 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
calibrate() {
|
||||
// ask ODrive to measure resistance and inductance
|
||||
let path = "odrive0." + this.data.axis + ".requested_state";
|
||||
putVal(path, odriveEnums.AXIS_STATE_MOTOR_CALIBRATION);
|
||||
this.$emit('page-comp-event', {data: "motor calibration", axis: this.data.axis});
|
||||
},
|
||||
},
|
||||
|
||||
+129
-8
@@ -1,16 +1,24 @@
|
||||
import store from "../store.js";
|
||||
import * as socketio from "../comms/socketio.js";
|
||||
import {wait, waitFor} from "./utils.js";
|
||||
import odriveEnums from "../assets/odriveEnums.json"
|
||||
|
||||
// helper functions and utilities for getting ODrive values
|
||||
|
||||
// given a path like "odrive0.axis0.config.blah", return the value
|
||||
export function getParam(path) {
|
||||
let keys = path.split('.');
|
||||
let odriveObj = store.state.odrives;
|
||||
for (const key of keys) {
|
||||
odriveObj = odriveObj[key];
|
||||
if (store.state.ODrivesConnected[keys[0]]) {
|
||||
let odriveObj = store.state.odrives;
|
||||
for (const key of keys) {
|
||||
odriveObj = odriveObj[key];
|
||||
}
|
||||
return odriveObj;
|
||||
}
|
||||
else {
|
||||
console.log("getParam for " + path + " is for disconnected ODrive");
|
||||
return undefined;
|
||||
}
|
||||
return odriveObj;
|
||||
}
|
||||
|
||||
// wrapper for val field
|
||||
@@ -51,10 +59,15 @@ export function parseMath(inString) {
|
||||
|
||||
export function putVal(path, value) {
|
||||
console.log("path: " + path + ", val: " + value + ", type: " + typeof value);
|
||||
socketio.sendEvent({
|
||||
type: "setProperty",
|
||||
data: {path: path, val: value, type: typeof value}
|
||||
})
|
||||
if (store.state.ODrivesConnected[path.split('.')[0]]) {
|
||||
socketio.sendEvent({
|
||||
type: "setProperty",
|
||||
data: {path: path, val: value, type: typeof value}
|
||||
});
|
||||
}
|
||||
else {
|
||||
console.log("requesting " + path + " from disconnected odrive")
|
||||
}
|
||||
}
|
||||
|
||||
// path is path to function, args is list of parameters
|
||||
@@ -81,6 +94,114 @@ export function getUnit(odrive, path) {
|
||||
return unit;
|
||||
}
|
||||
|
||||
export function clearErrors(odrive, axis) {
|
||||
// odrive is odrive path like 'odrive0'
|
||||
// axis is 'axis0', etc
|
||||
let paths = [];
|
||||
[".error", ".motor.error", ".encoder.error", ".controller.error"].forEach((path) => {
|
||||
paths.push(odrive + axis + path);
|
||||
});
|
||||
paths.forEach((path) => {
|
||||
putVal(path, 0);
|
||||
})
|
||||
}
|
||||
|
||||
export async function motorCalibration(odrive, axis) {
|
||||
// set up our continuous fetch
|
||||
let state;
|
||||
let motorError;
|
||||
|
||||
let updateVals = () => {
|
||||
fetchParam(odrive + axis + ".current_state");
|
||||
fetchParam(odrive + axis + ".motor.config.phase_resistance");
|
||||
fetchParam(odrive + axis + ".motor.config.phase_inductance");
|
||||
fetchParam(odrive + axis + ".motor.is_calibrated");
|
||||
fetchParam(odrive + axis + ".motor.error")
|
||||
|
||||
state = getVal(odrive + axis + ".current_state");
|
||||
motorError = getVal(odrive + axis + ".motor.error");
|
||||
if (run) {
|
||||
setTimeout(updateVals, 100);
|
||||
}
|
||||
}
|
||||
|
||||
// set up our state watch function
|
||||
let end = () => {
|
||||
// return {done: boolean, data <whatever>}
|
||||
if (state != odriveEnums.AXIS_STATE_MOTOR_CALIBRATION) {
|
||||
return {done: true, data: motorError}
|
||||
}
|
||||
else {
|
||||
return {done: false}
|
||||
}
|
||||
}
|
||||
|
||||
// start getting live updates
|
||||
let run = true;
|
||||
updateVals();
|
||||
|
||||
// send motor calibration command to correct odrive and axis
|
||||
putVal(odrive + axis + ".requested_state", odriveEnums.AXIS_STATE_MOTOR_CALIBRATION);
|
||||
|
||||
// give it some time to start
|
||||
await wait(500);
|
||||
|
||||
// only two things can happen now, either we successfully calibrate or we error out for some reason
|
||||
const result = await waitFor(end);
|
||||
|
||||
// stop our parameter updates
|
||||
run = false;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export async function encoderCalibration(odrive,axis) {
|
||||
// set up our continuous fetch
|
||||
let state;
|
||||
let encoderError;
|
||||
|
||||
let updateVals = () => {
|
||||
fetchParam(odrive + axis + ".current_state");
|
||||
fetchParam(odrive + axis + ".encoder.is_ready");
|
||||
fetchParam(odrive + axis + ".encoder.error")
|
||||
|
||||
state = getVal(odrive + axis + ".current_state");
|
||||
encoderError = getVal(odrive + axis + ".encoder.error");
|
||||
if (run) {
|
||||
setTimeout(updateVals, 100);
|
||||
}
|
||||
}
|
||||
|
||||
// set up our state watch function
|
||||
let end = () => {
|
||||
// return {done: boolean, data <whatever>}
|
||||
if (state != odriveEnums.AXIS_STATE_ENCODER_OFFSET_CALIBRATION) {
|
||||
return {done: true, data: encoderError}
|
||||
}
|
||||
else {
|
||||
return {done: false}
|
||||
}
|
||||
}
|
||||
|
||||
// start getting live updates
|
||||
let run = true;
|
||||
updateVals();
|
||||
|
||||
// start encoder offset cal
|
||||
putVal(odrive + axis + ".requested_state", odriveEnums.AXIS_STATE_ENCODER_OFFSET_CALIBRATION);
|
||||
|
||||
// give it some time to start
|
||||
await wait(500);
|
||||
|
||||
// only two things can happen now, either we successfully calibrate or we error out for some reason
|
||||
const result = await waitFor(end);
|
||||
|
||||
// stop our parameter updates
|
||||
run = false;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// standins for wizard and gui unit displays
|
||||
// TODO - convert between odrive api units and user-defined units like degrees and rpm
|
||||
export let odriveUnits = {
|
||||
|
||||
@@ -30,4 +30,29 @@ export let deleteBy = (obj, test) => {
|
||||
delete obj[key];
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// sets up a timeout to wait for fun() to evaluate to true;
|
||||
// fun must return {done: boolean, data: <whatever>}
|
||||
export let waitFor = (fun) => {
|
||||
return new Promise(resolve => {
|
||||
let check = () => {
|
||||
let res = fun();
|
||||
if (res.done) {
|
||||
resolve(res.data);
|
||||
}
|
||||
else {
|
||||
setTimeout(check,100);
|
||||
}
|
||||
}
|
||||
check();
|
||||
});
|
||||
}
|
||||
|
||||
// utility function to allow time for things to happen
|
||||
// async wait
|
||||
export let wait = (time) => {
|
||||
return new Promise(resolve => {
|
||||
setTimeout(() => resolve(), time);
|
||||
});
|
||||
}
|
||||
+22
-13
@@ -21,7 +21,7 @@ export default new Vuex.Store({
|
||||
axes: Array,
|
||||
odriveServerAddress: String,
|
||||
serverConnected: Boolean,
|
||||
ODriveConnected: false,
|
||||
ODrivesConnected: Object,
|
||||
serverOutput: [],
|
||||
dashboards: [
|
||||
{
|
||||
@@ -95,6 +95,13 @@ export default new Vuex.Store({
|
||||
state.odriveConfigs['writeAble'] = payload.writeAble;
|
||||
state.odriveConfigs['writeAbleNumeric'] = payload.writeAbleNumeric;
|
||||
},
|
||||
setODrivesStatus(state, obj) {
|
||||
// obj is {"odriveX": true/false}
|
||||
for (const odrive of Object.keys(obj)){
|
||||
state.ODrivesConnected[odrive] = obj[odrive];
|
||||
console.log(state.ODrivesConnected);
|
||||
}
|
||||
},
|
||||
setAxes(state, axes) {
|
||||
state.axes = axes;
|
||||
},
|
||||
@@ -163,9 +170,6 @@ export default new Vuex.Store({
|
||||
setServerStatus(state, val) {
|
||||
state.serverConnected = val;
|
||||
},
|
||||
setODriveConnected(state, val) {
|
||||
state.ODriveConnected = val;
|
||||
},
|
||||
removeCtrlFromDash(state, obj) {
|
||||
// obj is {dash: dashID, path: control path}
|
||||
for (const dash of state.dashboards) {
|
||||
@@ -325,7 +329,6 @@ export default new Vuex.Store({
|
||||
type: "odrive-found",
|
||||
callback: () => {
|
||||
console.log("odrive-found recieved from server");
|
||||
context.commit("setODriveConnected", true);
|
||||
context.dispatch("getOdrives");
|
||||
}
|
||||
})
|
||||
@@ -370,15 +373,21 @@ export default new Vuex.Store({
|
||||
});
|
||||
socketio.addEventListener({
|
||||
type: "odrive-disconnected",
|
||||
callback: () => {
|
||||
console.log("odrive disconnected");
|
||||
context.commit("setODriveConnected", false);
|
||||
console.log("restarting server...");
|
||||
window.ipcRenderer.send('kill-server');
|
||||
window.ipcRenderer.send('start-server');
|
||||
context.dispatch('setServerAddress', context.state.odriveServerAddress);
|
||||
callback: (odrive_name) => {
|
||||
console.log(odrive_name + " disconnected");
|
||||
//console.log("restarting server...");
|
||||
//window.ipcRenderer.send('kill-server');
|
||||
//window.ipcRenderer.send('start-server');
|
||||
//context.dispatch('setServerAddress', context.state.odriveServerAddress);
|
||||
}
|
||||
})
|
||||
});
|
||||
socketio.addEventListener({
|
||||
type: "odrives-status",
|
||||
callback: (odrives_status) => {
|
||||
console.log("From odrives-status msg " + odrives_status);
|
||||
context.commit('setODrivesStatus', JSON.parse(odrives_status));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
})
|
||||
+88
-154
@@ -55,7 +55,15 @@ import configTemplate from "../assets/wizard/configTemplate.json";
|
||||
import wizardPage from "../components/wizard/wizardPage.vue";
|
||||
import odriveEnums from "../assets/odriveEnums.json";
|
||||
import { pages } from "../assets/wizard/wizard.js";
|
||||
import { getVal, putVal, fetchParam } from "../lib/odrive_utils.js";
|
||||
import {
|
||||
getVal,
|
||||
putVal,
|
||||
fetchParam,
|
||||
clearErrors,
|
||||
motorCalibration,
|
||||
encoderCalibration
|
||||
} from "../lib/odrive_utils.js";
|
||||
import {wait} from "../lib/utils.js"
|
||||
|
||||
export default {
|
||||
name: "Wizard",
|
||||
@@ -70,6 +78,7 @@ export default {
|
||||
choiceMade: false,
|
||||
calibrating: false, // for indicating that calibration is in progress
|
||||
calStatus: undefined, // for indicating the status of a calibration attempt
|
||||
odrive: "odrive0.",
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@@ -80,69 +89,31 @@ export default {
|
||||
methods: {
|
||||
// certain actions, like starting motor or encoder calibration,
|
||||
// require special handling. This function is used for those events
|
||||
pageEventHandler(e) {
|
||||
async pageEventHandler(e) {
|
||||
this.choiceMade = false;
|
||||
let clear = () => {
|
||||
let paths = [
|
||||
"odrive0." + e.axis + ".error",
|
||||
"odrive0." + e.axis + ".motor.error",
|
||||
"odrive0." + e.axis + ".encoder.error",
|
||||
"odrive0." + e.axis + ".controller.error",
|
||||
];
|
||||
for (const path of paths) {
|
||||
putVal(path, 0);
|
||||
}
|
||||
console.log("clearing error for " + e.axis);
|
||||
};
|
||||
if (e.data == "motor calibration") {
|
||||
// set a timeout to grab axis resistance and inductance values
|
||||
fetchParam("odrive0." + e.axis + ".error");
|
||||
let apply = () => {
|
||||
if (getVal("odrive0." + e.axis + ".error") == 0) {
|
||||
let configStub = undefined;
|
||||
let inductance = getVal(
|
||||
"odrive0." + e.axis + ".motor.config.phase_inductance"
|
||||
);
|
||||
let resistance = getVal(
|
||||
"odrive0." + e.axis + ".motor.config.phase_resistance"
|
||||
);
|
||||
if (e.axis == "axis0") {
|
||||
configStub = {
|
||||
axis0: {
|
||||
motor: {
|
||||
config: {
|
||||
phase_resistance: resistance,
|
||||
phase_inductance: inductance,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
} else if (e.axis == "axis1") {
|
||||
configStub = {
|
||||
axis1: {
|
||||
motor: {
|
||||
config: {
|
||||
phase_resistance: resistance,
|
||||
phase_inductance: inductance,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
let configStub = {};
|
||||
configStub[e.axis] = {
|
||||
motor: {
|
||||
config: {
|
||||
phase_resistance: getVal(this.odrive + e.axis + ".motor.config.phase_resistance"),
|
||||
phase_inductance: getVal(this.odrive + e.axis + ".motor.config.phase_inductance"),
|
||||
}
|
||||
}
|
||||
this.choiceHandler({
|
||||
choice: "Motor Calibration",
|
||||
configStub: configStub,
|
||||
hooks: [],
|
||||
});
|
||||
this.choiceMade = true;
|
||||
}
|
||||
this.choiceHandler({
|
||||
choice: "Motor Calibration",
|
||||
configStub: configStub,
|
||||
hooks: [],
|
||||
});
|
||||
this.choiceMade = true;
|
||||
};
|
||||
let updateCalInfo = () => {
|
||||
// parse error code, update calibration information
|
||||
let motorError = getVal("odrive0." + e.axis + ".motor.error");
|
||||
let motorError = getVal(this.odrive + e.axis + ".motor.error");
|
||||
if (
|
||||
motorError ==
|
||||
odriveEnums.MOTOR_ERROR_PHASE_RESISTANCE_OUT_OF_RANGE ||
|
||||
motorError == odriveEnums.MOTOR_ERROR_PHASE_RESISTANCE_OUT_OF_RANGE ||
|
||||
motorError == odriveEnums.MOTOR_ERROR_PHASE_INDUCTANCE_OUT_OF_RANGE
|
||||
) {
|
||||
//we got the expected calibration error
|
||||
@@ -150,104 +121,66 @@ export default {
|
||||
this.calStatus = false;
|
||||
}
|
||||
};
|
||||
this.wait = function () {
|
||||
fetchParam("odrive0." + e.axis + ".current_state");
|
||||
fetchParam("odrive0." + e.axis + ".motor.config.phase_resistance");
|
||||
fetchParam("odrive0." + e.axis + ".motor.config.phase_inductance");
|
||||
fetchParam("odrive0." + e.axis + ".motor.is_calibrated");
|
||||
console.log();
|
||||
if (
|
||||
getVal("odrive0." + e.axis + ".current_state") ==
|
||||
odriveEnums.AXIS_STATE_MOTOR_CALIBRATION
|
||||
) {
|
||||
// still calibrating
|
||||
console.log("waiting for motor cal to finish");
|
||||
setTimeout(() => this.wait(), 100);
|
||||
this.calibrating = true;
|
||||
} else if (getVal("odrive0." + e.axis + ".error") != 0) {
|
||||
console.log("motor cal error");
|
||||
this.calibrating = false;
|
||||
this.choiceMade = false;
|
||||
// clear errors, display info.
|
||||
updateCalInfo();
|
||||
clear();
|
||||
} else if (
|
||||
getVal("odrive0." + e.axis + ".motor.is_calibrated") == true &&
|
||||
getVal("odrive0." + e.axis + ".motor.config.phase_resistance") !=
|
||||
0 &&
|
||||
getVal("odrive0." + e.axis + ".motor.config.phase_inductance") != 0
|
||||
) {
|
||||
// calibration is over
|
||||
apply();
|
||||
this.calibrating = false;
|
||||
this.calStatus = true;
|
||||
} else {
|
||||
setTimeout(() => this.wait(), 100);
|
||||
console.log("waiting for motor cal to finish");
|
||||
}
|
||||
};
|
||||
fetchParam("odrive0." + e.axis + ".current_state");
|
||||
fetchParam("odrive0." + e.axis + ".motor.config.phase_resistance");
|
||||
fetchParam("odrive0." + e.axis + ".motor.config.phase_inductance");
|
||||
fetchParam("odrive0." + e.axis + ".motor.is_calibrated");
|
||||
// wait for at least a second for comms to update state of ODrive
|
||||
setTimeout(() => this.wait(), 1000);
|
||||
|
||||
// result is motor.error
|
||||
this.calibrating = true;
|
||||
let result = await motorCalibration(this.odrive, e.axis);
|
||||
this.calibrating = false;
|
||||
fetchParam(this.odrive + e.axis + '.motor.is_calibrated');
|
||||
// no error, we're good!
|
||||
if (result == 0) {
|
||||
wait(250);
|
||||
apply();
|
||||
this.calStatus = true;
|
||||
} else {
|
||||
console.log("motor cal error" + result.data);
|
||||
this.calibrating = false;
|
||||
this.choiceMade = false;
|
||||
// clear errors, display info.
|
||||
updateCalInfo();
|
||||
clearErrors(this.odrive, e.axis);
|
||||
}
|
||||
} else if (e.data == "encoder calibration") {
|
||||
// get old CPR
|
||||
// apply CPR from this.wizardConfig
|
||||
// start calibration
|
||||
// wait for cal to finish
|
||||
// set odrive cpr back to oldVal
|
||||
let oldCPR = getVal("odrive0." + e.axis + ".encoder.config.cpr");
|
||||
let oldCPR = getVal(this.odrive + e.axis + ".encoder.config.cpr");
|
||||
console.log("oldCPR = " + oldCPR);
|
||||
console.log("axis = " + e.axis);
|
||||
|
||||
// set up the calib_scan_distance to a value that will work
|
||||
let pp = this.wizardConfig[e.axis].motor.config.pole_pairs;
|
||||
|
||||
// smallest multiple of 4pi that is bigger than pole_pairs * 2pi
|
||||
let scan_distance = pp % 0 ? pp * 2 * Math.PI : (pp + 1) * 2 * Math.PI;
|
||||
putVal(this.odrive + e.axis + ".encoder.config.calib_scan_distance", scan_distance);
|
||||
putVal(
|
||||
"odrive0." + e.axis + ".encoder.config.cpr",
|
||||
this.odrive + e.axis + ".encoder.config.cpr",
|
||||
this.wizardConfig[e.axis].encoder.config.cpr
|
||||
);
|
||||
putVal(
|
||||
"odrive0." + e.axis + ".requested_state",
|
||||
odriveEnums.AXIS_STATE_ENCODER_OFFSET_CALIBRATION
|
||||
);
|
||||
|
||||
this.wait = function () {
|
||||
fetchParam("odrive0." + e.axis + ".current_state");
|
||||
fetchParam("odrive0." + e.axis + ".encoder.error");
|
||||
fetchParam("odrive0." + e.axis + ".encoder.is_ready");
|
||||
if (
|
||||
getVal("odrive0." + e.axis + ".current_state") ==
|
||||
odriveEnums.AXIS_STATE_ENCODER_OFFSET_CALIBRATION
|
||||
) {
|
||||
console.log("waiting for encoder cal to finish...");
|
||||
setTimeout(() => this.wait(), 100);
|
||||
this.calibrating = true;
|
||||
} else if (getVal("odrive0." + e.axis + ".encoder.error") != 0) {
|
||||
putVal("odrive0." + e.axis + ".encoder.config.cpr", oldCPR);
|
||||
console.log("applying old CPR, error detected");
|
||||
clear();
|
||||
this.calibrating = false;
|
||||
this.calStatus = false;
|
||||
} else if (
|
||||
getVal("odrive0." + e.axis + ".encoder.is_ready") == true
|
||||
) {
|
||||
putVal("odrive0." + e.axis + ".encoder.config.cpr", oldCPR);
|
||||
console.log("applying old CPR");
|
||||
this.choiceMade = true;
|
||||
this.currentStep.choiceMade = true;
|
||||
this.calibrating = false;
|
||||
this.calStatus = true;
|
||||
} else {
|
||||
console.log("waiting for encoder cal to finish...");
|
||||
setTimeout(() => this.wait(), 100);
|
||||
}
|
||||
};
|
||||
fetchParam("odrive0." + e.axis + ".current_state");
|
||||
fetchParam("odrive0." + e.axis + ".encoder.error");
|
||||
fetchParam("odrive0." + e.axis + ".encoder.is_ready");
|
||||
setTimeout(() => this.wait(), 1000);
|
||||
await wait(250);
|
||||
|
||||
this.calibrating = true;
|
||||
let result = await encoderCalibration(this.odrive, e.axis);
|
||||
this.calibrating = false;
|
||||
putVal(this.odrive + e.axis + ".encoder.config.cpr", oldCPR);
|
||||
fetchParam(this.odrive + e.axis + '.encoder.is_ready');
|
||||
if (result == 0){
|
||||
// no encoder error, we're good
|
||||
this.choiceMade = true;
|
||||
this.currentStep.choiceMade = true;
|
||||
this.calStatus = true;
|
||||
}
|
||||
else {
|
||||
clearErrors(this.odrive, e.axis);
|
||||
this.calStatus = false;
|
||||
}
|
||||
}
|
||||
},
|
||||
choiceHandler(e) {
|
||||
async choiceHandler(e) {
|
||||
// apply static configStub
|
||||
this.updateConfig(this.wizardConfig, e.configStub);
|
||||
|
||||
@@ -259,7 +192,6 @@ export default {
|
||||
// ugly, but a special case.
|
||||
// for motors, wait for calibration to finish before giving the green light unless motor.is_calibrated == true
|
||||
// for encoders, wait for calibration to finish unless encoder.is_ready == true
|
||||
this.choiceMade = true;
|
||||
if (
|
||||
this.currentStep == pages.Motor_0 ||
|
||||
this.currentStep == pages.Motor_1
|
||||
@@ -267,20 +199,33 @@ export default {
|
||||
let axis;
|
||||
if (this.currentStep == pages.Motor_0) axis = "axis0";
|
||||
if (this.currentStep == pages.Motor_1) axis = "axis1";
|
||||
if (getVal("odrive0." + axis + ".motor.is_calibrated") == false) {
|
||||
fetchParam(this.odrive + axis + ".motor.is_calibrated");
|
||||
await wait(100);
|
||||
if (getVal(this.odrive + axis + ".motor.is_calibrated") == false) {
|
||||
this.choiceMade = false;
|
||||
}
|
||||
else {
|
||||
this.choiceMade = true;
|
||||
}
|
||||
}
|
||||
if (
|
||||
else if (
|
||||
this.currentStep == pages.Encoder_0 ||
|
||||
this.currentStep == pages.Encoder_1
|
||||
) {
|
||||
let axis;
|
||||
if (this.currentStep == pages.Encoder_0) axis = "axis0";
|
||||
if (this.currentStep == pages.Encoder_1) axis = "axis1";
|
||||
if (getVal("odrive0." + axis + ".encoder.is_ready") == false) {
|
||||
fetchParam(this.odrive + axis + ".encoder.is_ready");
|
||||
await wait(100);
|
||||
if (getVal(this.odrive + axis + ".encoder.is_ready") == false) {
|
||||
this.choiceMade = false;
|
||||
}
|
||||
else {
|
||||
this.choiceMade = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.choiceMade = true;
|
||||
}
|
||||
this.currentStep.choiceMade = this.choiceMade;
|
||||
console.log(JSON.parse(JSON.stringify(this.wizardConfig)));
|
||||
@@ -335,17 +280,6 @@ export default {
|
||||
this.choiceMade = false;
|
||||
},
|
||||
},
|
||||
created() {
|
||||
// when wizard is active, we want to poll for certain values
|
||||
let update = () => {
|
||||
fetchParam("odrive0.axis0.motor.is_calibrated");
|
||||
fetchParam("odrive0.axis1.motor.is_calibrated");
|
||||
fetchParam("odrive0.axis0.encoder.is_ready");
|
||||
fetchParam("odrive0.axis1.encoder.is_ready");
|
||||
setTimeout(() => update(), 1000);
|
||||
};
|
||||
update();
|
||||
},
|
||||
beforeDestroy() {
|
||||
for (const page of Object.keys(pages)) {
|
||||
pages[page].choiceMade = false;
|
||||
|
||||
@@ -69,7 +69,7 @@ Connect the encoder(s) to J4. The A,B phases are required, and the Z (index puls
|
||||
Always think safety before powering up the ODrive if motors are attached. Consider what might happen if the motor spins as soon as power is applied.
|
||||
</div>
|
||||
* Unlike some devices, the ODrive does not recieve power over the USB port so the 24/56 volt power input is required even just to communicate with it using USB. It is ok to power up the ODrive before or after connecting the USB cable.
|
||||
* To power up the ODrive, connect the power source to the DC terminals. Make sure to pay attention to the polarity. A small spark is normal. This is caused by the capacitors charging up.
|
||||
* To power up the ODrive, connect the power source to the DC terminals. Make sure to pay attention to the polarity. Try to connect the power source first and then turn it on to avoid inrush current. If this can't be avoided then a small spark is normal. This is caused by the capacitors charging up.
|
||||
|
||||
## Downloading and Installing Tools
|
||||
Most instructions in this guide refer to a utility called `odrivetool`, so you should install that first.
|
||||
|
||||
+3
-3
@@ -4,11 +4,11 @@
|
||||
Thermistors are elements that change their resistance based on the temperature. They can be used to electrically measure temperature. The ODrive itself has thermistors on board near the FETs to ensure that they don't burn themselves out. In addition to this it's possible to connect your own thermistor to measure the temperature of the connected motors. There are two types of thermistors, Negative Temperature Coefficient (NTC) and Positive Temperature Coefficient (PTC). This indicates whether the resistance goes up or down when the temperature goes up or down. The ODrive only supports the NTC type thermistor.
|
||||
|
||||
## FET thermistor
|
||||
The temperature of the onboard FET thermistors can be read out by using the `odrivetool` under `<axis>.fet_thermistor.temp`. The odrive will automatically start current limiting the motor when the `<axis>.fet_thermistor.config.temp_limit_lower` threshold is exceeded and once `<axis>.fet_thermistor.config.temp_limit_upper` is exceeded the ODrive will stop controlling the motor and set an error. The lower and upper threshold can be changed, but this is not recommended.
|
||||
The temperature of the onboard FET thermistors can be read out by using the `odrivetool` under `<axis>.motor.fet_thermistor.temperature`. The odrive will automatically start current limiting the motor when the `<axis>.motor.fet_thermistor.config.temp_limit_lower` threshold is exceeded and once `<axis>.motor.fet_thermistor.config.temp_limit_upper` is exceeded the ODrive will stop controlling the motor and set an error. The lower and upper threshold can be changed, but this is not recommended.
|
||||
|
||||
## Connecting motor thermistors
|
||||
|
||||
To use your own thermistors with the ODrive a few things have to be clarified first. The use of your own thermistor requires one analog input pin. Under `<axis>.motor_thermistor.config` the configuration of your own thermistor is available with the following fields:
|
||||
To use your own thermistors with the ODrive a few things have to be clarified first. The use of your own thermistor requires one analog input pin. Under `<axis>.motor.motor_thermistor.config` the configuration of your own thermistor is available with the following fields:
|
||||
|
||||
* `gpio_pin`: The GPIO input in used for this thermistor.
|
||||
* `poly_coefficient_0` to `poly_coefficient_3`: Coefficient that needs to be set for your specific setup more on that in [Thermistor coefficients](#thermistor-coefficients).
|
||||
@@ -25,7 +25,7 @@ The way this works is that the thermistor is connected in series with a known re
|
||||
To use a thermistor with the ODrive a voltage divider circuit has to be made that uses `VCCA` as the power source with `GNDA` as the ground. The voltage divider output can be connected to a GPIO pin that supports analog input.
|
||||
|
||||
## Thermistor coefficients
|
||||
Every thermistor and voltage divider circuit is different and thus it's necessary to let the ODrive know how to relate a voltage it measures at the GPIO pin to a temperature. The `poly_coefficient_0` to `poly_coefficient_3` under `<axis>.motor_thermistor.config` are used for this. The `odrivetool` has a convenience function `set_motor_thermistor_coeffs(axis, Rload, R_25, Beta, Tmin, Tmax)` which can be used to calculate and set these coefficients.
|
||||
Every thermistor and voltage divider circuit is different and thus it's necessary to let the ODrive know how to relate a voltage it measures at the GPIO pin to a temperature. The `poly_coefficient_0` to `poly_coefficient_3` under `<axis>.motor.motor_thermistor.config` are used for this. The `odrivetool` has a convenience function `set_motor_thermistor_coeffs(axis, Rload, R_25, Beta, Tmin, Tmax)` which can be used to calculate and set these coefficients.
|
||||
|
||||
* `axis`: Which axis do set the motor thermistor coefficients for (`odrv0.axis0` or `odrv0.axis1`).
|
||||
* `Rload`: The Ohm value of the resistor used in the voltage divider circuit.
|
||||
|
||||
@@ -2,6 +2,7 @@ import usb.util
|
||||
import time
|
||||
import fractions
|
||||
import array
|
||||
import time
|
||||
from odrive.dfuse.DfuState import DfuState
|
||||
|
||||
DFU_REQUEST_SEND = 0x21
|
||||
@@ -64,7 +65,20 @@ class DfuDevice:
|
||||
self.control_msg(DFU_REQUEST_SEND, DFU_CLRSTATUS, 0, None)
|
||||
|
||||
def get_state(self):
|
||||
return self.control_msg(DFU_REQUEST_RECEIVE, DFU_GETSTATE, 0, 1)[0]
|
||||
msg = self.control_msg(DFU_REQUEST_RECEIVE, DFU_GETSTATE, 0, 1)
|
||||
|
||||
# Second chance after giving the device some time to breathe.
|
||||
if len(msg) == 0:
|
||||
time.sleep(0.5)
|
||||
msg = self.control_msg(DFU_REQUEST_RECEIVE, DFU_GETSTATE, 0, 1)
|
||||
|
||||
if len(msg) == 0:
|
||||
raise Exception("Could not get device state. Firmware upgrade will abort. "
|
||||
"Please try again. If odrivetool can't find the device "
|
||||
"anymore after this, follow the instructions in "
|
||||
"https://docs.odriverobotics.com/odrivetool#device-firmware-update "
|
||||
"(\"How to force DFU mode\").")
|
||||
return msg[0]
|
||||
|
||||
def abort(self):
|
||||
self.control_msg(DFU_REQUEST_RECEIVE, DFU_ABORT, 0, 0)
|
||||
|
||||
@@ -37,10 +37,6 @@ AXIS_STATE_LOCKIN_SPIN = 9
|
||||
AXIS_STATE_ENCODER_DIR_FIND = 10
|
||||
AXIS_STATE_HOMING = 11
|
||||
|
||||
# ODrive.ThermistorCurrentLimiter.Error
|
||||
THERMISTOR_CURRENT_LIMITER_ERROR_NONE = 0x00000000
|
||||
THERMISTOR_CURRENT_LIMITER_ERROR_OVER_TEMP = 0x00000001
|
||||
|
||||
# ODrive.Encoder.Mode
|
||||
ENCODER_MODE_INCREMENTAL = 0
|
||||
ENCODER_MODE_HALL = 1
|
||||
@@ -119,6 +115,8 @@ MOTOR_ERROR_BRAKE_DUTY_CYCLE_NAN = 0x00002000
|
||||
MOTOR_ERROR_DC_BUS_OVER_REGEN_CURRENT = 0x00004000
|
||||
MOTOR_ERROR_DC_BUS_OVER_CURRENT = 0x00008000
|
||||
MOTOR_ERROR_MODULATION_IS_NAN = 0x00010000
|
||||
MOTOR_ERROR_MOTOR_THERMISTOR_OVER_TEMP = 0x00020000
|
||||
MOTOR_ERROR_FET_THERMISTOR_OVER_TEMP = 0x00040000
|
||||
|
||||
# ODrive.Motor.ArmedState
|
||||
ARMED_STATE_DISARMED = 0
|
||||
|
||||
@@ -64,10 +64,10 @@ class OperationAbortedException(Exception):
|
||||
|
||||
def set_motor_thermistor_coeffs(axis, Rload, R_25, Beta, Tmin, TMax):
|
||||
coeffs = calculate_thermistor_coeffs(3, Rload, R_25, Beta, Tmin, TMax)
|
||||
axis.motor_thermistor.config.poly_coefficient_0 = float(coeffs[3])
|
||||
axis.motor_thermistor.config.poly_coefficient_1 = float(coeffs[2])
|
||||
axis.motor_thermistor.config.poly_coefficient_2 = float(coeffs[1])
|
||||
axis.motor_thermistor.config.poly_coefficient_3 = float(coeffs[0])
|
||||
axis.motor.motor_thermistor.config.poly_coefficient_0 = float(coeffs[3])
|
||||
axis.motor.motor_thermistor.config.poly_coefficient_1 = float(coeffs[2])
|
||||
axis.motor.motor_thermistor.config.poly_coefficient_2 = float(coeffs[1])
|
||||
axis.motor.motor_thermistor.config.poly_coefficient_3 = float(coeffs[0])
|
||||
|
||||
def dump_errors(odrv, clear=False):
|
||||
axes = [(name, axis) for name, axis in odrv._remote_attributes.items() if 'axis' in name]
|
||||
@@ -80,8 +80,6 @@ def dump_errors(odrv, clear=False):
|
||||
module_decode_map = [
|
||||
(name, odrv, {k: v for k, v in odrive.enums.__dict__ .items() if k.startswith("AXIS_ERROR_")}),
|
||||
('motor', axis, {k: v for k, v in odrive.enums.__dict__ .items() if k.startswith("MOTOR_ERROR_")}),
|
||||
('fet_thermistor', axis, {k: v for k, v in odrive.enums.__dict__ .items() if k.startswith("THERMISTOR_CURRENT_LIMITER_ERROR")}),
|
||||
('motor_thermistor', axis, {k: v for k, v in odrive.enums.__dict__ .items() if k.startswith("THERMISTOR_CURRENT_LIMITER_ERROR")}),
|
||||
('encoder', axis, {k: v for k, v in odrive.enums.__dict__ .items() if k.startswith("ENCODER_ERROR_")}),
|
||||
('controller', axis, {k: v for k, v in odrive.enums.__dict__ .items() if k.startswith("CONTROLLER_ERROR_")}),
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user