ODrive Project: AGV

Hi,
I am using Odrive to make a differential drive AGV.
To start with, I have set up one motor and I am trying to run it in position control.

  1. My objective is to run the motor for a particular no. of counts. For example, I want to run the motor for 200 counts in incremental mode. The problem I am facing is that when i use the move_incremental function with 200 as counts value, my shadow_count shows 240 counts increment. Please see below:
    In [105]: odrv0.axis0.encoder.shadow_count
    Out[105]: 1

In [106]: odrv0.axis0.controller.move_incremental(200, 0)

In [107]: odrv0.axis0.encoder.shadow_count
Out[107]: 241

  1. I also want to run the motor in absolute positioning mode. For example, i want to go from 200 to 1000 position. In this case, I would like to know:
    a. How to make my encoder count retentive so that after i reboot the controller, current position of encoder is saved in the controller?
    b. How do I control the positioning parameters like, start speed, stop speed, acceleration time, deceleration time etc?

I have many questions regarding this project. I will be using the same thread so that other users can refer the same.

Thanks for help in advance.

For 2.a. I’m not sure. Have you tried odrv0.save_configuration() after calibration?

For 2.b. make sure all your parameters are set correctly, because it sounds like you have an overshoot issue. Tune them in this order:

  1. Make sure odrv0.axis0.motor.config is properly configured and the motor calibrates succesfully.
  2. Tune your odrv0.axis0.controller.config parameters - https://docs.odriverobotics.com/commands.html#tuning-parameters - use odrv0.axis0.controller.pos_setpoint to test. For large movements, this should produce as violent as possible moves, but still properly controlled movements without overshoot or vibration.
  3. Tune odrv0.axis0.trap_traj.config - use odrv0.axis0.controller.move_to_pos() to test, this should produce nice and smooth moves, use this to limit the violence of large moves.

If you are busy tuning step 2 or 3 and find you need to change something from step 1 (such as current_limit), beware this usually means you need to start tuning all over again.

For 2.a., it depends on the encoder you are using. If you are using an incremental encoder (such as the CUI AMT-102), keep in mind that the motor is free to move while odrive is unpowered, and these moves will not be registered. Thus the stored position will be of little value upon powering up.

This can be remedied to some point using an absolute encoder. I think experimental support for absolute encoders is available. However, even with an absolute encoder you will not be able to detect multiples of full revolutions performed while powered off, which I guess is relevant in an AGV use case.

For 2.b. you should take a look at the trapezoidal trajectory planner documentation (scroll a bit down in the page).

Thank you for your support.
I have overcome the encoder error. Now I am able to control the motor as per the required position. Using shadow count helped in solving my encoder problem.
My only hurdle which i couldn’t solve was 2.a. I am using a hall sensor as of now.
Does Odrive come with an inbuilt memory where I can store previous encoder counts?

My next challenge is:

  1. Use IO pins to give RUN command to the motors in velocity mode.
  2. Interface with Arduino and transfer target position data from Arduino and give start command via communication.

Any suggestions? Your help is highly appreciated.

Thanks.

By design the ODrive does not store the encoder position between reboots because in order to control the motor, the driver must know how the stator coils are positioned relative to permanent magnets in the motor. The ODrive determines this during the calibration sequence. Since the shaft could move while the driver and encoder are powered off, the ODrive must repeat the calibration sequence, or have some means to synchronize a previous calibration to the current motor position (such as an encoder with an index pulse). With an encoder that has an index pulse, the ODrive will be able to re-align the previous calibration by spinning forward until it hits the index pulse which may or may not be acceptable for your application. With an absolute encoder, it would be possible to synchronize a previous calibration immediately on startup without having to move the motor shaft at all, but my understanding is that support for this is in a very early stage.
If you’re absolutely sure that the motor shaft will not move while the ODrive and encoder are powered off, you could add support in the firmware for storing the encoder count in the persistent memory of the ODrive, but if the shaft did happen to move while the ODrive was powered off, the ODrive would have bad problems (motor overheating, vibrating, moving with reduced torque, etc.).

Thanks for your input @robopilot99.

I am currently interfacing Arduino with Odrive using the GitHub repo.

I am getting a weird behavior.

This is what my serial monitor shows:
ODriveArduino
Setting parameters…
Ready!
Send the character ‘0’ or ‘1’ to calibrate respective motor (you must do this before you can command movement)
Send the character ‘s’ to exectue test move
Send the character ‘b’ to read bus voltage
Send the character ‘p’ to read motor positions in a 10s loop

After I press 0 and enter, it shows:
Axis0: Requesting state 4
Axis0: Requesting state 7
Axis0: Requesting state 8

Then I do test move. I have changed the test move code to odrive.setvelocity(0,100.0f). On pressing ‘s’ and enter, sometimes the result is as expected but many times the Current_State on odrive tool shows 1 i.e. Axis_State_Idle.

Hence, My question is why doesnt the Odrive go into Closed Loop Control state even when I am following the calibration sequence?

It seems there is no problem with Arduino Code. It is the Odrive which is not responding properly.

Please suggest what could be the issue.

Case 1: After reboot, I do full scale calibration. There is no error on motor or encoder. The calibration is done by taking two rounds in CW direction and 2 rounds in CCW direction. Then I switch the state to closed loop control. On giving setvelocity command, the motor runs as required. If I do full scale calibration for the second time, the encoder shows an error 0x002. Hence the drive fails to go to closed loop control and hence it doesnt run.

Case 2: After reboo, I do full scale calibration and then try to put drive in closed loop control mode. But this does not happen. On checking errors, there is error in encoder. Error number is 0x0002.

I have repeated the steps multiple times and only 2 cases are observed in random order. What could be the issue.

Please advise. Your replies are much appreciated.

Thanks.

The easiest way to debug this is to connect with the Odrivetool and call dump_errors(odrv0). That should give you more information about the error.

Thanks @Riewert: Always to the rescue. :smiley:

Ok so on checking errors I got this:
In [6]: dump_errors(odrv0)
axis0
axis: Error(s):
ERROR_ENCODER_FAILED
motor: no error
encoder: Error(s):
ERROR_CPR_OUT_OF_RANGE
controller: no error
axis1
axis: Error(s):
ERROR_ENCODER_FAILED
motor: no error
encoder: Error(s):
ERROR_ILLEGAL_HALL_STATE
controller: no error

My Axis 1 is not connected. I am using only Axis 0. As far as my motor is concerned, it uses hall sensor. It has 5 wires: 5v, GND, Hu, Hv, Hw. All are connected perfectly. The CPR defined is 24 as the motor has 4 pole pairs and 8 poles.

Please advise why the error is occurring.

One more observation:
In [7]: odrv0.axis0.encoder
Out[7]:
error = 0x0002 (int)
is_ready = True (bool)
index_found = False (bool)
shadow_count = 51 (int)
count_in_cpr = 3 (int)
interpolation = 0.5 (float)
phase = -0.9645490646362305 (float)
pos_estimate = 51.00566482543945 (float)
pos_cpr = 3.024973154067993 (float)
hall_state = 6 (int)
vel_estimate = 0.0 (float)
calib_scan_response = 47.0 (float)
config:
mode = 1 (int)
use_index = False (bool)
find_idx_on_lockin_only = False (bool)
pre_calibrated = True (bool)
zero_count_on_find_idx = True (bool)
cpr = 24 (int)
offset = 21 (int)
offset_float = 1.4210782051086426 (float)
enable_phase_interpolation = True (bool)
bandwidth = 100.0 (float)
calib_range = 0.019999999552965164 (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)

Even with the error, is_ready = True.
Also, sometimes the in full state calibration sequence, the motor shaft turn 2 round cw and 2 rounds ccw. After that the motor runs perfectly fine.

Either odrv0.axis0.encoder.config.cpr or your number of odrv0.axis0.motor.config.pole_pairs is incorrect.

I’m not sure how to determine the correct settings. What I usually do is increase odrv0.axis0.encoder.config.calib_range incrementally until the error goes away. Then you should be able to guess what the correct CPR value is, and return the calib_range back to it default low value.

@Riewert Vielen Danke.

odrv0.axis0.encoder.config.calib_range - using this solved my problem.

Cheers!

Be sure to set the calib_range back to its default low values, and set the correct CPR and pole pairs. Don’t leave the calib_range at a high value, because it may help you detect mistakes later on.

@Riewert calib_range is set to 0.1 as of now.
The CPR and Pole Pairs is correct which is also verified by manufacturer. Now I am not getting any errors after trying running the motor many times. My finding is that sometimes the full_calibration_sequence works fine with no errors, then I run the motor. Now if I again try to do the full_calibration_sequence, the drive shows encoder error.

Ok so what kind of problems will I face if I keep the value of calib_range 0.1? If I change it back to 0.01 (which was the previous value), I am afraid that the encoder error problem will again occur.

Please advise.

Don’t worry calib_range = 0.1 should be fine.

In my case there is a lot of mechanical resistance, so sometimes during calibration it sticks for a count which can cause this error. This is difficult to prevent from happening. The best thing to do, is once you have succesfully calibrated, you should set odrv0.axis0.motor.config.pre_calibrated = true and odrv0.axis0.encoder.config.pre_calibrated = true then odrv0.save_configuration().

You should now see when you startup that odrv0.axis0.motor.is_calibrated = true and odrv0.axis0.encoder.is_ready = true after reboots. You no longer need to calibrate.

Thanks for the explanation @Riewert.
I owe you a beer. Hopefully in November when I am travelling to Germany. :smiley:

A new challenge.

Now I have connected 2 axis to the Odrive.

Axis 0 works perfectly fine. Axis 1 behavior is weird.

Axis 1: On Odrive reboot, full state calibration works fine. Then I run the axis on velocity mode for 3 seconds at velocity 100. The rotation direction is CW. After the motor stops after 3 seconds, the motor starts to rotate in CCW direction for 1 round and stops. On checking dump_error, there are no errors found.
Then I run the motor continuously in CW direction for 10 seconds and without stopping i give a command to run in CCW direction for 10 seconds. Here the motor stops and shows encoder error 0x010.
This problem is only resolved on reboot of Odrive.

What could be the possible issue?
Please advise.

The overshoot you are seeing is because you haven’t tuned your gains yet. I’m on mobile, but search on Google for tuning gains Odrive. There is a nice section in the documentation explaining how to tune them.

Maybe you previously tuned axis0, then just copy the gains over to axis1.

Btw, I’m Dutch, not German.

@Riewert Sorry. In one of the threads on this forum, I read that you are from germany. Maybe you were just travelling. My bad.

Anyways, I will go through the documentation. As far as Industrial AC Servo motors are concerned, they are tuned by auto-tuning which is similar to full calibration sequence in Odrive. Hence, I assumed the gains to be tuned automatically.

Also, Is the encoder error 0x0010: Encoder Hall state error occurring due to tuning or any other reason? I read a thread which said that it occurs due to noise issue and can be solved by adding a 22nf capacitor.

Yes this is probably noise, especially if the error does not pop up straight away. There are a number of ways to reduce the noise; use shielded wires, avoid having the wires near the motor wires, some extra capacitors definitely helps, shorten the wires if possible, also make sure your connections are clean.

@P_V

Maybe not the answer you’re looking for but still worth mentioning I beleive

I’ve been in your shoes about a year ago (trying to make a differential drive agv with odrive).

It’s funny because in this thread I read an experience rather close to the one I experienced myself.

Just so you know, I really struggled with with hall effect sensors on odrive. Maybe I should have insisted with the forum. Odrive really has strong community support.
However, the design decision to enforce this calibration sequence before accepting to spin made me feel really uncomfortable with an agv application (I didn’t like the idea of the agv moving outside of my control).

In the end I used a pair of vesc boards interfaced over UART and I am super happy with them. My belief is that vesc is more suited for a speed control application than odrive, in particular for e-mobility. But I would love for someone to contradict me here. So please keep us posted.

@Riewert
In general thanks for your support on the forum. I’d love to pay you a beer too! Turns out that I do travel to the Netherlands several times a year for work. I was in Delf a few months ago. I rarely have opportunities to meet fellow odrive active users in person. Could be fun to share odrive stories over a beer.