Position control sets motor error 0x1000 for one direction only

I am running a D6374 motor on a 48v Odrive 3.5 with RazorsEdge firmware. I am using the AMT 102 encoder with 8192 ppr. The motor runs nice in velocity mode in both directions.

In position mode, the motor runs perfect in positive direction. Going from 0 to 10000 degrees is problem free. When I set it to go from 10.000 degrees to 0 degress the motor spins up and all of a sudden it accelerates too much and sets error 0x1000 (ERROR_CURRENT_UNSTABLE) on motor.
There is no problem going from -10000 degrees to 0 so it is not the position that causes this, it is the rotation direction.

I have tried switching two of the phases and flipped the encoder, but with no luck. I have tried two different input modes, INPUT_MODE_TRAP_TRAJ, INPUT_MODE_VEL_RAMP, both with same result.

edit: Does anybody know what’s going on?

Yay! Well, at least that error check works lol.

That’s really strange that it’s only working in one direction. Nobody using RazorsEdge has reported that issue before. Typically this behaviour is caused by noise on the encoder lines or mechanical slippage.

Yay! Yes, something works :smiley:
I don’t think I would say that the error check works. Why would “ERROR_CURRENT_UNSTABLE” be caused by noise on the encoder lines or mechanical slippage?

The encoder is screwd and taped to the motor housing. I also get this error message (both directions) if I change the setpoint to 0counts while the motor is moving quite fast towards 1M counts. It’s like it does not decelerate to zero velocity before it accelerates in negative direction. Sometimes it just gives the error message before even trying…

Do I need a better power supply?

My (temporary) solution to this problem is to run the motor in input mode “INPUT_MODE_PASSTHROUGH”. I am not able to set the acceleration with this mode, but that’s OK for now.

PS: I am very impressed by the performance of the drive/motor! wow, just wow :exploding_head:

Because if that happens, the current controller fails and loses control of the current in the motor, sending it beyond motor.current_limit. When that happens, we shut the ODrive down to prevent damage.

Does this happen even in PASSTHROUGH mode?

Can you graph motor.current_control.Iq_setpoint vs motor.current_control.Iq_measured and try to reproduce the problem please?

There is no problem in passthrough mode.
Running back and forth with insane acceleration works very good.

0 - motor.current_control.Iq_setpoint
1 - motor.current_control.Iq_measured

PASSTHROUGH - reversing direction
Pass-through_Forward_spikes_when_reversing_direction_at_full_speed


I tested TrapTraj mode again, and plotted the current
The spike occurred at approximately full speed.

0 - motor.current_control.Iq_setpoint
1 - motor.current_control.Iq_measured

TRAP_TRAJ - spike at full speed
TrapTraj_spike_at%20full_speed

some more info:
in the last trap_traj test the motor accelerated to the velocity setpoint, and then the current was set too high. This made the motor spin much faster than the trap_traj velocity setpoint. At the end the motor stopped at the correct position setpoint.

The reason that the motor was not disabled, might be that i set the vel_limit_tolerance a bit high (2).

I did some more testing today, and the increased speed happens when the motor start to decelerate. It does not matter how fast it is spinning and it usually just happens when going negative direction. Sometimes it works, but most of the time it doesn’t.

When the motor starts to decelerate, the position setpoint is set directly to the user setpoint without any ramping and the velocity setpoint is set directly to 0. At this point, the motor spins up a bit and ends at the desired position.

0 - Pos Setpoint
1 - Vel Setpoint
2 - Vel Estimate
TrapTraj_PosSP_VelSP_VelEST

I did some testing in the trapTraj.cpp but was not able to fix it with the short amount of time available.
Maybe I will look into it later if time.

It’s slightly more interesting than this.

It seems to happen when the velocity is < 0 AND the motor is decelerating. Acceleration positive, velocity setpoint negative. Curious.

This is a graph of the trapTraj C++ implementation doing two moves chained together (I believe it’s at the first time that acceleration goes positive). Normal moves are similarly boring. I don’t think it’s in the actual trapTraj code, but it could be in the controller code.

What are your trapTraj.config values? Also, if you can give me all the parameters you used when getting that bad behaviour I can try to replicate it here.

Hi,
I finally found some time to get the parameters. Too much to do! :exploding_head:
Below is a copy of the .json file, I am using Axis0. With this setup I am able to replicate the happenings. I will be available for more testing if there is anything you would like me to test or do, things have calmed down a bit now.

{
    "config": {
        "brake_resistance": 2.0,
        "max_regen_current": 0.0,
        "enable_uart": true,
        "enable_i2c_instead_of_can": false,
        "enable_ascii_protocol_on_usb": true,
        "dc_bus_undervoltage_trip_level": 8.0,
        "dc_bus_overvoltage_trip_level": 59.92000198364258,
        "power_supply_wattage": 0.0
    },
    "axis0": {
        "config": {
            "startup_motor_calibration": false,
            "startup_encoder_index_search": true,
            "startup_encoder_offset_calibration": false,
            "startup_closed_loop_control": false,
            "startup_sensorless_control": false,
            "startup_homing": false,
            "enable_step_dir": false,
            "counts_per_step": 2.0,
            "watchdog_timeout": 0.0,
            "step_gpio_pin": 1,
            "dir_gpio_pin": 2,
            "can_node_id": 113,
            "can_heartbeat_rate_ms": 0
        },
        "motor": {
            "config": {
                "pre_calibrated": true,
                "pole_pairs": 7,
                "calibration_current": 10.0,
                "resistance_calib_max_voltage": 2.0,
                "phase_inductance": 2.872193363145925e-05,
                "phase_resistance": 0.04717841371893883,
                "direction": -1,
                "motor_type": 0,
                "current_lim": 60.0,
                "current_lim_tolerance": 1.25,
                "inverter_temp_limit_lower": 100.0,
                "inverter_temp_limit_upper": 120.0,
                "requested_current_range": 60.0,
                "current_control_bandwidth": 1000.0
            }
        },
        "controller": {
            "config": {
                "control_mode": 3,
                "input_mode": 1,
                "pos_gain": 25.0,
                "vel_gain": 0.00019999999494757503,
                "vel_integrator_gain": 0.0024999999441206455,
                "vel_limit": 327680.0,
                "vel_limit_tolerance": 3.0,
                "vel_ramp_rate": 1.0,
                "homing_speed": 8192.0,
                "inertia": 0.0,
                "input_filter_bandwidth": 2.0
            }
        },
        "encoder": {
            "config": {
                "mode": 0,
                "use_index": true,
                "find_idx_on_lockin_only": false,
                "pre_calibrated": true,
                "abs_spi_cs_gpio_pin": 0,
                "zero_count_on_find_idx": true,
                "cpr": 8192,
                "offset": -4827,
                "offset_float": 0.2564374804496765,
                "enable_phase_interpolation": true,
                "bandwidth": 1000.0,
                "calib_range": 0.019999999552965164,
                "calib_scan_distance": 50.26548385620117,
                "calib_scan_omega": 12.566370964050293,
                "idx_search_unidirectional": false,
                "ignore_illegal_hall_state": false
            }
        },
        "sensorless_estimator": {
            "config": {
                "observer_gain": 1000.0,
                "pll_bandwidth": 1000.0,
                "pm_flux_linkage": 0.0015800000401213765
            }
        },
        "trap_traj": {
            "config": {
                "vel_limit": 327680.0,
                "accel_limit": 1137777.75,
                "decel_limit": 1137777.75
            }
        },
        "min_endstop": {
            "config": {
                "gpio_num": 5,
                "enabled": true,
                "offset": -8192,
                "is_active_high": false,
                "debounce_ms": 100.0
            }
        },
        "max_endstop": {
            "config": {
                "gpio_num": 6,
                "enabled": true,
                "offset": 0,
                "is_active_high": false,
                "debounce_ms": 100.0
            }
        }
    },
    "axis1": {
        "config": {
            "startup_motor_calibration": false,
            "startup_encoder_index_search": false,
            "startup_encoder_offset_calibration": false,
            "startup_closed_loop_control": false,
            "startup_sensorless_control": false,
            "startup_homing": false,
            "enable_step_dir": false,
            "counts_per_step": 2.0,
            "watchdog_timeout": 0.0,
            "step_gpio_pin": 7,
            "dir_gpio_pin": 8,
            "can_node_id": 0,
            "can_heartbeat_rate_ms": 0
        },
        "motor": {
            "config": {
                "pre_calibrated": false,
                "pole_pairs": 7,
                "calibration_current": 10.0,
                "resistance_calib_max_voltage": 2.0,
                "phase_inductance": 0.0,
                "phase_resistance": 0.0,
                "direction": 0,
                "motor_type": 0,
                "current_lim": 10.0,
                "current_lim_tolerance": 1.25,
                "inverter_temp_limit_lower": 100.0,
                "inverter_temp_limit_upper": 120.0,
                "requested_current_range": 60.0,
                "current_control_bandwidth": 1000.0
            }
        },
        "controller": {
            "config": {
                "control_mode": 3,
                "input_mode": 1,
                "pos_gain": 20.0,
                "vel_gain": 0.0005000000237487257,
                "vel_integrator_gain": 0.0010000000474974513,
                "vel_limit": 20000.0,
                "vel_limit_tolerance": 1.2000000476837158,
                "vel_ramp_rate": 10000.0,
                "homing_speed": 2000.0,
                "inertia": 0.0,
                "input_filter_bandwidth": 2.0
            }
        },
        "encoder": {
            "config": {
                "mode": 0,
                "use_index": false,
                "find_idx_on_lockin_only": false,
                "pre_calibrated": false,
                "abs_spi_cs_gpio_pin": 0,
                "zero_count_on_find_idx": true,
                "cpr": 8192,
                "offset": 0,
                "offset_float": 0.0,
                "enable_phase_interpolation": true,
                "bandwidth": 1000.0,
                "calib_range": 0.019999999552965164,
                "calib_scan_distance": 50.26548385620117,
                "calib_scan_omega": 12.566370964050293,
                "idx_search_unidirectional": false,
                "ignore_illegal_hall_state": false
            }
        },
        "sensorless_estimator": {
            "config": {
                "observer_gain": 1000.0,
                "pll_bandwidth": 1000.0,
                "pm_flux_linkage": 0.0015800000401213765
            }
        },
        "trap_traj": {
            "config": {
                "vel_limit": 20000.0,
                "accel_limit": 5000.0,
                "decel_limit": 5000.0
            }
        },
        "min_endstop": {
            "config": {
                "gpio_num": 0,
                "enabled": false,
                "offset": 0,
                "is_active_high": false,
                "debounce_ms": 100.0
            }
        },
        "max_endstop": {
            "config": {
                "gpio_num": 0,
                "enabled": false,
                "offset": 0,
                "is_active_high": false,
                "debounce_ms": 100.0
            }
        }
    },
    "can": {
        "config": {
            "baud_rate": 500000
        }
    }
}

Those parameters don’t really match the graph above. In the graph it looks like you go from ~20000 counts to 250000 counts at… about 30000 counts / rev? But The trap_traj configuration is much higher than that.

You are totally right. I don’t know the exact config for the motor used in the test with the graph. The .json file gives the same behaviour, even though the config is a bit different.

Ok, strange things going on here. I have implemented a CAN-communication (based on CAN Open), and when running it on 200Hz, i get the jumps in vel_SP etc (as shown in the graph).

Today i tested the latest version of RazorsEdge without any modification, and it seems to work when i command everything from Odrivetool. I will investigate more next week.

I use the CAN for real time position control of three motors in a XYZ-table. Is it capable of receiving commands at 200Hz in input_mode = 5 (trap_traj)? Or is another mode preferred?

In theory it should work. But if you made your own can protocol instead of using ours, you need to make sure you’re setting the inout_pos_changed flag so that it recalculates

That being said, I’ve heard there may be issues with streaming trajectory points

I did some more tests yesterday and I got it working in Trap_Traj mode. It might need more testing, but it looks very promising. My fix was to only update
axis->controller_.input_pos_updated();
whenever the a new position setpoint was received. So if the CAN PDO contained the same position setpoint as the current setpoint, I skipped updating the trajectory planner. Previously this method was executed for every setpoint received.

I still need to test it with position commands from the real time system, but I am able to run step inputs and also continuous sinusoidal setpoints :partying_face:

Oooh I like that idea. I’m going to steal that.

Speaking of, we’ve wanted to implement CANopen on the drive for a long time. Would you be willing to share your code?

Sure! I can make some documentation for how to use it and send you the code when ready.