Motor torque during calibration not high enough?

Hello all!

I have been busy last weeks creating a mechanism with lots of gears, levers, cam-followers etc… Almost everything has been 3d printed and the idea is to actuate the mechanism with just one motor, which is installed on the side. I am using the following:

  • Odrive v3.6
  • Arduino UNO
  • Mean Well LRS 350 48 Power Supply 350 W
  • Dual Shaft Motor - D5065 270kv
  • CUIAMT102 encoder

FYI, I am a total beginner when it comes to programming Arduino and in general electronics. I am more a mechanical guy…

I would like to just try and let the motor rotate a couple of times. Everything seems well connected and working. However during calibration the motor does only rotate a little bit and then reverses. Seems like there isn’t enough torque.

here is also a little video: https://youtu.be/trgmmyzzNlI

I am trying the following modified example code (I changed a couple of things and included the calibration_current to try and enhance the torque during calibration):

#include <SoftwareSerial.h>
#include <ODriveArduino.h>

// Printing with stream operator
template<class T> inline Print& operator <<(Print &obj,     T arg) { obj.print(arg);    return obj; }
template<>        inline Print& operator <<(Print &obj, float arg) { obj.print(arg, 4); return obj; }

// Serial to the ODrive
SoftwareSerial odrive_serial(8, 9); //RX (ODrive TX), TX (ODrive RX)
// Note: you must also connect GND on ODrive to GND on Arduino!

// ODrive object
ODriveArduino odrive(odrive_serial);

void setup() {
  // ODrive uses 115200 baud
  odrive_serial.begin(115200);

  // Serial to PC
  Serial.begin(115200);
  while (!Serial) ; // wait for Arduino Serial Monitor to open

  Serial.println("ODriveArduino");
  Serial.println("Setting parameters...");

  // Setting motor parameters
  odrive_serial << "w axis0.controller.config.vel_limit " << 22000.0f << '\n';
  odrive_serial << "w axis0.controller.config.current_lim " << 30.0f << '\n';
  odrive_serial << "w axis0.controller.config.calibration_current " << 30.0f << '\n'; //to increase torque during calibration?

  Serial.println("Ready!");
  Serial.println("Send the character '0' to calibrate the motor (you must do this before you can command movement)");
  Serial.println("Send the character 'r' to rotate motor");
  Serial.println("Send the character 'b' to read bus voltage");
  Serial.println("Send the character 'p' to read motor positions in a 10s loop");
}

void loop() {

  if (Serial.available()) {
    char c = Serial.read();

    // Run calibration sequence
    if (c == '0' || c == '1') {
      int motornum = c-'0';
      int requested_state;

      requested_state = ODriveArduino::AXIS_STATE_MOTOR_CALIBRATION;
      Serial << "Axis" << c << ": Requesting state " << requested_state << '\n';
      odrive.run_state(motornum, requested_state, true);

      requested_state = ODriveArduino::AXIS_STATE_ENCODER_OFFSET_CALIBRATION;
      Serial << "Axis" << c << ": Requesting state " << requested_state << '\n';
      odrive.run_state(motornum, requested_state, true);

      requested_state = ODriveArduino::AXIS_STATE_CLOSED_LOOP_CONTROL;
      Serial << "Axis" << c << ": Requesting state " << requested_state << '\n';
      odrive.run_state(motornum, requested_state, false); // don't wait
    }

    // velocity test move
    if (c == 'r') {
      Serial.println("rotate motor");
      odrive.SetVelocity(0, 10000, 30);
    }

    // stop rotating
    if (c == 'q') {
      Serial.println("Stopping motor");
      odrive.SetVelocity(0, 0, 10);
    }

    // Read bus voltage
    if (c == 'b') {
      odrive_serial << "r vbus_voltage\n";
      Serial << "Vbus voltage: " << odrive.readFloat() << '\n';
    }

    // print motor positions in a 10s loop
    if (c == 'p') {
      static const unsigned long duration = 10000;
      unsigned long start = millis();
      while(millis() - start < duration) {
        for (int motor = 0; motor < 2; ++motor) {
          odrive_serial << "r axis" << motor << ".encoder.pos_estimate\n";
          Serial << odrive.readFloat() << '\t';
        }
        Serial << '\n';
      }
    }
  }
}

FYI I don’t know exactly which torque is required at the moment, but I can operate the mechanism by hand and in my opinion the torque does not seem too high, I think the motor should be able to do this…

I anyone has any tips or help on how to let me rotate the motor with a higher torque? Or for me to do /try anything else?

thank you!

Sven

That’s exactly what it’s supposed to do. :+1:
Probably, your calibration has succeeded. If you can turn it by hand then 30A is more than enough for calibration.
Probably, it’s set to torque mode (because I think that’s the default, and you haven’t specified velocity mode) so it’s sitting on a zero torque setpoint.
You need to set it to velocity control mode, and you should wait for it to enter closed loop before sending the first setpoint.

So change that last false to true, and somewhere before it, write ODriveArduino::CONTROL_MODE_VELOCITY_CONTROL to axis.config.control_mode

thanks for your help,

I don’t think it succeeds with calibration. The motor no longer reacts to anything after what you can see in the YouTube video.

When the motor is not connected to the mechanism, I can control it after calibration. If calibration would have succeeded I would expect at least a little nudge when I set a speed… But it doesn’t move at all…

I tried implementing your code as:

 // Setting motor parameters
  odrive_serial << "w axis0.controller.config.vel_limit " << 22000.0f << '\n';
  odrive_serial << "w axis0.controller.config.current_lim " << 30.0f << '\n';
  odrive_serial << "w axis0.controller.config.calibration_current " << 40.0f << '\n'; //to increase torque during calibration?
  odrive_serial << "w axis0.controller.config.control_mode = CONTROL_MODE_VELOCITY_CONTROL" << '\n';

and I changed the false to true, but so far no success…

Can you read the values from axis.error, axis.motor.error, axis.encoder.error and axis.controller.error ? (ODriveArduino really needs a dump_errors function!)
also I was hoping that ODriveArduino had the enum value for CONTROL_MODE_VELOCITY_CONTROL but it doesn’t.
You should set control_mode to 2, which is the value of CONTROL_MODE_VELOCITY_CONTROL.

If it works when it’s decoupled, then I agree, your code is probably fine.
Even a small amount of torque from the mechanism can easily screw up the calibration, because the motor is in ‘open loop’. That means there’s no encoder information, and the drive has to assume that the motor is perfectly aligned to the magnetic field angle.

Is it possible to decouple the motor from the mechanism during calibration? If not, then you might need an absolute encoder. Or, you could set it up to use the index pulse, (although in my experience that can be tricky to get working.)

To do that, you need to set encoder.config.use_index=True and encoder.config.pre_calibrated=True, then calibrate with the motor detached from the mechanism. Then save the config, and go to the index search state on startup.
Hopefully, the index search will find the same position every time. If it doesn’t, then you have noise causing spurious pulses on the index signal.

Hello

So I tried calibrating again, with the same result as before. I added these lines to my code to print out the motor errors:

// print motor errors
    if (c == 'e') {
          odrive_serial << "r axis0.error\n";
          Serial << odrive.readFloat() << '\n';
          odrive_serial << "r axis0.motor.error\n";
          Serial << odrive.readFloat() << '\n';
          odrive_serial << "r axis0.encoder.error\n";
          Serial << odrive.readFloat() << '\n';
          odrive_serial << "r axis0.controller.error\n";
          Serial << odrive.readFloat() << '\n';

and I also changed the line to put it into velocity mode:

odrive_serial << "w axis0.controller.config.control_mode = 2" << '\n';

The result before doing the calibration is 0,0,0,0, and after the calibration is:

257.0000
0.0000
2.0000
0.0000

Hope this helps you?
I can dismount the motor and do the calibration before and then mount it, however this was not my original intention… If there are no other options then I will buy an absolute encoder… Any suggestion which one?

You can turn up the lockin current, but generally speaking, it’s better to calibrate the motor without any load. With an incremental encoder, you just leave the ODrive running all the time and then if you need to reboot you have to disconnect the output shaft and then calibrate then reconnect it.

Hmmm, I understand the problem but having to disconnect the output shaft everytime the odrive is disconnected from power is very impractical for me…

Also, I would assume that in more than 50% of use cases, the motor is connected to something mechanical? So some mechanical resistance during calibration would seem normal to me? I cant imagine of all those people also need to disconnect the motor shaft to allow for calibration…

I will try and find a method to allow my motor to stay mounted at all times (maybe with the absolute encoder like towen suggested?)… If this is not possible I would honestly be a little bit disappointed in the odrive system since I spent a descent amount of money on it…

Most modern robotics systems in industry use absolute encoders for this very reason. The only ones using incremental encoders alone were old systems using brushed DC motors, and even most of those I worked on still used absolute encoders (resolvers) in addition to analogue speed “encoders” (DC tachos).
Incremental encoders are sometimes combined with Hall sensors (a kind of extremely low-resolution absolute encoder), or else the index pulse of an incremental encoder can be used to initialise its position to within one turn. Although if you rely on the index pulse, you still need to be able to move the machine by some means (e.g. by hand, or with reduced motor torque) before the full torque of the motor becomes available.

ODrive supports various absolute encoders from AMS, CUI and maybe some others. The most popular is the inexpensive AS5047p, however I should note that myself and others have had some issues with these chips recently. :frowning:
I am as yet unable to prove if the problem is with AMS or ODrive.

For your application, the index pulse should be a viable option, because the mechanism doesn’t require a lot of torque. Clearly an open-loop move can move it, just not precisely enough for encoder calibration. The index search only requires motion, it doesn’t care about precision.

1 Like

Every industrial motor that i know of uses an absolute encoder of some sort, or they use a backup battery for multi-turn incremental encoders to ensure the positioning is not lost when the machine is off. The motors are calibrated, then they’re attached to the machine, then the machine is calibrated, and then ideally the machine stays powered on for the next decade.

1 Like

Ok good news! I got it to calibrate while connected to the mechanism!

turned out my code was wrong, I wrote:

odrive_serial << "w axis0.controller.config.calibration_current " << 30.0f << ‘\n’;
but this is wrong and should be:
odrive_serial << "w axis0.motor.config.calibration_current " << 30.0f << ‘\n’;

so calibration is working, no errors after it finishes rotating… But now I would like to let the motor continuously rotate until I tell it to stop.

So I placed the following in my code:

odrive_serial << "w axis0.controller.config.control_mode = 2" << '\n'; //velocity mode

// (some code in between here)

    // velocity test move
    if (c == 'r') {
      Serial.println("rotate motor");
      odrive.SetVelocity(0, 1000, 50); //max 50 Amps
      //odrive.SetPosition(0, 200000);
    }

however it doesn’t work and I get the following errors:

axis: ERROR_CONTROLLER_FAILED 0x00000200
motor: ERROR_CONTROL_DEADLINE_MISSED — 0x00000010
encoder: 0
controller: ERROR_OVERSPEED — 0x00000001

Acc to the documentation I should try and increase the motor speed limit config.vel_limit. However in my code this is set to 22000.0f, which is allready high? However it is confusing since the documentation page says the parameter vel_limit is in rev/s but I would think it is in counts/s?
(https://docs.odriverobotics.com/api/odrive.controller.config#vel_limit)

In my code I try to set it to 1000 counts/s. my encoder has 8192counts so this is a slow speed… So why is this triggering the velocity limit error?

Any help is appreciated!

odrive.SetVelocity(0, 1000, 50); //max 50 Amps

Lol. That’s not what that last value says. You should set it to 0. Use motor.config.current_lim = 50 to set a 50A maximum.

Firmware version 0.5.1 changed everything from counts to revs. Check which version you have with odrv0.fw_version_minor and odrv0.fw_version_revision

Ok thank you for clarifying… I was using the info I found here to write that code:

Are you still sure about the counts to revs? I did try and rotate the motor yesterday (not on the mechanism) using odrive.SetVelocity(0, 8192, 20); and it seemed to rotate once /sec so that does seem to indicate the SetVelocity function using counts… (I am using Arduino so maybe there is a difference there?). I will leave out the last argument.

edit: I think I am on firmware 4.11

On 4.11 it will be in counts. 20000 counts / sec with an 8192 is fairly low, that’s a little over 2 revolutions / second. You’ll probably want to increase that, or increase the velocity limit error margin.