Geared motors with Hall and encoders not spinning in encoder mode

Hello,

We got this motors https://www.aliexpress.com/item/4000763502158.html?spm=a2g0s.9042311.0.0.535f4c4dzYR1aj

They have both Hall and encoders.

I have tried to configure them in encoder mode first, but without success.

According to description ppr is 1024 as well as shadow count
CPR should be 4096 ( ppr * 4 )
Pole pairs is 10 ( we opened one of spare motors and counted poles - 20 in total ).

With this values it’s not able to calibrate complaining about CPR_OUT_OF_RANGE, if i relax calib_range to 1 - it’s fine, but in closed loop mode it’s not spinning at all.
According to plots current is being increased till reaching the limit, but still motors is not spinning.
There is no errors.
It’s able to spin in sensorless mode - absolutely fine.
What else should i try to tweak in order to make it spin?

Also almost same issue in hall mode - it works from scratch with configs that is provided in https://docs.odriverobotics.com/hoverboard, even though pole pairs and cpr is different, it’s not spinning when i set correct pole pairs and cpr ( in hall it’s 256 ).

Also tried with firmware 0.5.1, but it didn’t change anything.

ODrive is 3.6 56V.

Did you count stator poles or magnet poles? pole_pairs refers to magnet poles IIRC.

It’s a shame it doesn’t have an index pulse on that encoder :frowning:

We counted magnet poles.
It’s able to calibrate with use_index without error and pre_calibrated applies without problem.

Eh, from the aliexpress page ‘Wire Definition’ it looks as if it doesn’t have an index pulse, only A and B outputs from the encoder.
But it’s working now? :slight_smile:

Nah, all described above was with use_index = True, so from the beginning of this topic it have been set…

Er yeah, but use_index=True is meaningless (and wrong) if you have an AB encoder with no index pulse!?
It might let you get into closed_loop when it shouldn’t, for example… If the disconnected index signal were activated by some noise, then the ODrive would think it has a valid commutation reference when it doesn’t, so you could get all sorts of weird behaviour.

1 Like

@towen is right. You should set use_index = False or you will likely run into problems.

@towen, @Wetmelon, Sorry for the late response, was busy with other stuff…
use_index = False doesn’t change anything.
Here’s my params

odrv0.axis0.encoder
Out[3]:
error = 0x0000 (int)
is_ready = True (bool)
index_found = False (bool)
shadow_count = 34 (int)
count_in_cpr = 34 (int)
interpolation = 0.5 (float)
phase = 1.1254816055297852 (float)
pos_estimate = 34.234375 (float)
pos_cpr = 34.234375 (float)
hall_state = 5 (int)
vel_estimate = 0.0 (float)
calib_scan_response = 750.0 (float)
config:
mode = 0 (int)
use_index = False (bool)
find_idx_on_lockin_only = False (bool)
pre_calibrated = False (bool)
zero_count_on_find_idx = True (bool)
cpr = 4096 (int)
offset = 370 (int)
offset_float = 0.7300000190734863 (float)
enable_phase_interpolation = True (bool)
bandwidth = 1000.0 (float)
calib_range = 1.0 (float)
calib_scan_distance = 50.26548385620117 (float)
calib_scan_omega = 12.566370964050293 (float)
idx_search_unidirectional = False (bool)
ignore_illegal_hall_state = False (bool)
set_linear_count(count: int)

odrv0.axis0.motor
Out[4]:
error = 0x0000 (int)
armed_state = 3 (int)
is_calibrated = True (bool)
current_meas_phB = -0.21392440795898438 (float)
current_meas_phC = 0.1053016185760498 (float)
DC_calib_phB = -1.5582488775253296 (float)
DC_calib_phC = -1.2733618021011353 (float)
phase_current_rev_gain = 0.02500000037252903 (float)
thermal_current_lim = 23.819385528564453 (float)
get_inverter_temp()
current_control:
p_gain = 0.11520425975322723 (float)
i_gain = 118.32422637939453 (float)
v_current_control_integral_d = -0.0003348196332808584 (float)
v_current_control_integral_q = -0.009427683427929878 (float)
Ibus = -0.00022683361021336168 (float)
final_v_alpha = -0.002130721462890506 (float)
final_v_beta = -0.0009554330608807504 (float)
Iq_setpoint = 0.0015156264416873455 (float)
Iq_measured = -0.08455902338027954 (float)
Id_measured = 0.028356090188026428 (float)
I_measured_report_filter_k = 1.0 (float)
max_allowed_current = 60.75 (float)
overcurrent_trip_level = 67.5 (float)
gate_driver:
drv_fault = 0 (int)
timing_log:
TIMING_LOG_GENERAL = 0 (int)
TIMING_LOG_ADC_CB_I = 2614 (int)
TIMING_LOG_ADC_CB_DC = 12862 (int)
TIMING_LOG_MEAS_R = 6678 (int)
TIMING_LOG_MEAS_L = 6686 (int)
TIMING_LOG_ENC_CALIB = 6998 (int)
TIMING_LOG_IDX_SEARCH = 0 (int)
TIMING_LOG_FOC_VOLTAGE = 6946 (int)
TIMING_LOG_FOC_CURRENT = 7370 (int)
config:
pre_calibrated = False (bool)
pole_pairs = 10 (int)
calibration_current = 10.0 (float)
resistance_calib_max_voltage = 2.0 (float)
phase_inductance = 0.00011520426050992683 (float)
phase_resistance = 0.11832422018051147 (float)
direction = 1 (int)
motor_type = 0 (int)
current_lim = 10.0 (float)
current_lim_tolerance = 1.25 (float)
inverter_temp_limit_lower = 100.0 (float)
inverter_temp_limit_upper = 120.0 (float)
requested_current_range = 60.0 (float)
current_control_bandwidth = 1000.0 (float)

odrv0.axis0.controller
Out[7]:
error = 0x0000 (int)
pos_setpoint = -3.7656259536743164 (float)
vel_setpoint = -0.0 (float)
vel_integrator_current = 0.0015156264416873455 (float)
current_setpoint = 0.0 (float)
vel_ramp_target = 0.0 (float)
vel_ramp_enable = False (bool)
config:
control_mode = 2 (int)
pos_gain = 20.0 (float)
vel_gain = 0.0005000000237487257 (float)
vel_integrator_gain = 0.0010000000474974513 (float)
vel_limit = 20000.0 (float)
vel_limit_tolerance = 1.2000000476837158 (float)
vel_ramp_rate = 10000.0 (float)
setpoints_in_cpr = False (bool)
set_pos_setpoint(pos_setpoint: float, vel_feed_forward: float, current_feed_forward: float)
set_vel_setpoint(vel_setpoint: float, current_feed_forward: float)
set_current_setpoint(current_setpoint: float)
move_to_pos(pos_setpoint: float)
move_incremental(displacement: float, from_goal_point: bool)
start_anticogging_calibration()

configs were erased and then i set
cpr = 4096
pole_pairs = 10
calib_range = 1 ( otherwise it spins in 1 direction and then gives error cpr out of range, same as when use index = True )

it calibrated without errors
but when i try to spin it in closed loop, it makes small move and then only current is growing, but wheel is not spinning at all

So once again, you have fudged something without understanding what it is you are doing, to make it calibrate when it actually can’t.
calib_range=1 sets the calibration tolerance to 100% i.e. simply disables the CPR_OUT_OF_RANGE error. It is set by default to 2% for a very good reason - the drive will not be able to commutate unless it knows where the magnets are in relation to the coils.
This is a sanity check to make sure that PP electrical cycles == one mechanical revolution, to some reasonable tolerance (2%). It checks that your pole pairs and encoder counts are consistent.
Setting this value to 1 means “I don’t care that the encoder information is totally wrong, go anyway” - and you get the expected result.

Are you quite sure that you have a 4096ppr encoder?
Can you rotate the shaft manually by exactly one turn and see what encoder.pos_estimate says?
(easiest if you call encoder.set_linear_count(0) first.)

Here’s Video

I also have performed another test taking into account what you told about calib_range - decreased the cpr = 1024 and it was able to calibrate with calib_range = 0.09, but still it didn’t change anything in its behavior ( p.s. firstly i tried to calibrate with cpr 4096 and calib_range 0.5, but it failed to calibrate )

odrv0.axis0.motor
Out[7]:
error = 0x0000 (int)
armed_state = 0 (int)
is_calibrated = True (bool)
current_meas_phB = -0.030586719512939453 (float)
current_meas_phC = -0.025983095169067383 (float)
DC_calib_phB = -1.5405229330062866 (float)
DC_calib_phC = -1.5853506326675415 (float)
phase_current_rev_gain = 0.02500000037252903 (float)
thermal_current_lim = 36.71196746826172 (float)
get_inverter_temp()
current_control:
p_gain = 0.10986275225877762 (float)
i_gain = 121.54440307617188 (float)
v_current_control_integral_d = 0.0 (float)
v_current_control_integral_q = 0.0 (float)
Ibus = 0.0 (float)
final_v_alpha = 0.0 (float)
final_v_beta = 0.0 (float)
Iq_setpoint = 0.0 (float)
Iq_measured = 0.0 (float)
Id_measured = 0.0 (float)
I_measured_report_filter_k = 1.0 (float)
max_allowed_current = 60.75 (float)
overcurrent_trip_level = 67.5 (float)
gate_driver:
drv_fault = 0 (int)
timing_log:
TIMING_LOG_GENERAL = 0 (int)
TIMING_LOG_ADC_CB_I = 2586 (int)
TIMING_LOG_ADC_CB_DC = 12846 (int)
TIMING_LOG_MEAS_R = 6602 (int)
TIMING_LOG_MEAS_L = 6606 (int)
TIMING_LOG_ENC_CALIB = 6922 (int)
TIMING_LOG_IDX_SEARCH = 0 (int)
TIMING_LOG_FOC_VOLTAGE = 6870 (int)
TIMING_LOG_FOC_CURRENT = 0 (int)
config:
pre_calibrated = False (bool)
pole_pairs = 10 (int)
calibration_current = 10.0 (float)
resistance_calib_max_voltage = 2.0 (float)
phase_inductance = 0.00010986275447066873 (float)
phase_resistance = 0.12154440581798553 (float)
direction = 1 (int)
motor_type = 0 (int)
current_lim = 10.0 (float)
current_lim_tolerance = 1.25 (float)
inverter_temp_limit_lower = 100.0 (float)
inverter_temp_limit_upper = 120.0 (float)
requested_current_range = 60.0 (float)
current_control_bandwidth = 1000.0 (float)

odrv0.axis0.encoder
Out[8]:
error = 0x0000 (int)
is_ready = True (bool)
index_found = False (bool)
shadow_count = 44 (int)
interpolation = 0.5 (float)
phase = 2.316248893737793 (float)
pos_estimate = 44.234375 (float)
pos_cpr = 44.234375 (float)
hall_state = 6 (int)
vel_estimate = 0.0 (float)
calib_scan_response = 746.0 (float)
config:
mode = 0 (int)
use_index = False (bool)
find_idx_on_lockin_only = False (bool)
pre_calibrated = False (bool)
zero_count_on_find_idx = True (bool)
cpr = 1024 (int)
offset = 415 (int)
offset_float = 1.3510000705718994 (float)
enable_phase_interpolation = True (bool)
bandwidth = 1000.0 (float)
calib_range = 0.09000000357627869 (float)
calib_scan_distance = 50.26548385620117 (float)
calib_scan_omega = 12.566370964050293 (float)
ignore_illegal_hall_state = False (bool)
set_linear_count(count: int)

odrv0.axis0.controller
Out[9]:
error = 0x0000 (int)
pos_setpoint = 0.0 (float)
vel_setpoint = 0.0 (float)
vel_integrator_current = 0.0 (float)
current_setpoint = 0.0 (float)
vel_ramp_target = 0.0 (float)
vel_ramp_enable = False (bool)
config:
control_mode = 3 (int)
pos_gain = 20.0 (float)
vel_gain = 0.0005000000237487257 (float)
vel_integrator_gain = 0.0010000000474974513 (float)
vel_limit = 20000.0 (float)
vel_limit_tolerance = 1.2000000476837158 (float)
vel_ramp_rate = 10000.0 (float)
setpoints_in_cpr = False (bool)
set_pos_setpoint(pos_setpoint: float, vel_feed_forward: float, current_feed_forward: float)
set_vel_setpoint(vel_setpoint: float, current_feed_forward: float)
set_current_setpoint(current_setpoint: float)
move_to_pos(pos_setpoint: float)
move_incremental(displacement: float, from_goal_point: bool)
start_anticogging_calibration()

Well that’s interesting… :face_with_raised_eyebrow: I don’t understand how it could move the motor by 10 electrical cycles but the encoder moves only 1/4 rev. Can you see it skipping any poles or does it move smoothly? How far does it turn on calibration? Can you post a video of that?
From your video, I agree it looks like a 4096ppr encoder. Although it’s hard to be certain it’s not 4000?

It’s possible that you have some electrical noise coupling onto your encoder wires, which is causing it to give inconsistent readings when the motor is active (but it’s OK when the motor is off). Although I would have expected this to cause additional counts, not fewer counts.
You could try lowering calibration_current, and/or placing 22nF capacitors to GND on each of your A and B inputs.

No, it’s definitely 4096, as per the spec ppr is 1024. Plus manual movement also shows this, it’s very sensitive, but i see when the mark didn’t reached it’s origin on which it was 0.
Calibration looks smooth, here Calibration video
And the output of plots


Everything is same as if it was cpr 4096 and calib_range 1
Yeah, we thought about give a try to the capacitors, but didn’t checked it for some reason. ( Before with other motors it wasn’t improvement, but opposite - the encoders couldn’t calibrate at all always giving different errors )
I’ll check calibration_current and maybe capacitors later.

In that calibration video, it is moving by 1/4 rev… Exactly what I would expect if the pole count was set too low.
Stupid suggestion (another fudge) but have you tried setting pole_pairs to 40?

It could be one of those unusual kinds of motor geometry that multiplies the effective pole count (although it doesn’t look that unusual!)

Yeah, i tried in the past and it was able to calibrate with calib_range 0.1, but behavior didn’t change at all. Double checked right now, same - it moves to pose 800 and back to 0 upon calibration and not spinning in closed loop.

In that case, I’m stumped. Sorry. :frowning:
You’ll have to wait for one of the devs to comment.

Looking at encoder.cpp (Encoder::run_offset_calibration), it looks like my understanding of the calibration movement was wrong. It moves by a fixed amount, not dependent on configured pole pairs. But forcing it to do the calibration more slowly or over a longer distance might yield more accurate results - especially if your motor is extremely coggy.
Have you tried increasing encoder.config.calib_scan_distance or reducing calib_scan_omega ?

odrv0.erase_configuration()
odrv0.reboot()

odrv0.axis0.motor.config.pole_pairs = 10
odrv0.axis0.encoder.config.cpr = 4096

odrv0.save_configuration()
odrv0.axis0.requested_state = AXIS_STATE_FULL_CALIBRATION

Can you run this and video it?

If it doesn’t work, try increasing your lock-in current: odrv0.axis0.general_lockin.current

It’s unable to calibrate like this complaining about CPR ( described in previous comments and topic description )
I’m not using firmware 0.5.1, so i have tried changing this odrv0.axis1.config.general_lockin.current ( used axis1 as its easier to film it ).
I changed value from 10 to 15 and 20, then tried also increasing from 10 by 2 - 12, 14…20.
Without any changes.
I’m also unable to add link to the video, because website counts it like a spam for some reason. (As well as all my previous posts :frowning: )

Hmm… I made a change to your user profile, you might be able to submit links now.

So how far does the motor turn when doing the calibration? On v0.5.1 it should go 1 full turn and then back the other way to 0

Video
And if i put calib_range to ~0.9 it spins as previously 1/4 and back.