Hello all, this might be a bit of a weird application for Odrive so allow me to explain.
I am making a machine that is meant to throw a juggling object called a diabolo. The machine is basically a 3/4 circular ramp that pushes the diabolo around until it exits out the other end. Here’s a cross-section of the launcher, the green thing is the diabolo and the blue is the arm which pushes the diabolo around the ramp, mounted to the face of my motor. In the picture it is in its “armed position”, then I command it to move around (clockwise in the pic) and once it completes 3/4 of a revolution the ramp ends and diabolo should continue with the tangential velocity it had at the point of leaving the ramp.
The motor I am using is an Eaglepower 8318 100 kV connected to a 6S Lipo. I got this motor due to the high torque it can produce. I did some napkin maths and the max torque in a vacuum with no friction should be below 2Nm, well below what the motor is capable of especially since I can push it well above its 30A continuous rating since I am only doing so for about 110ms. I am setting the Odrive to work in trajectory control mode, I am setting the parameters so that it should reach the target angular velocity approximately after 1/2 a revolution, leaving the remaining 1/4 revolution for it to “coast” (though realistically due to the added load this is really a buffer so the odrive can catch up compared to the target position). I command it to make 3 or 4 revolutions in total, so it has plenty of time to reach its target velocity (even if the load has already left the launcher).
Here’s my conundrum, I just can’t seem to get the gains right to save my life. I understand that this sort of intermittent torque can be very confusing for the odrive and unsurprisingly it’s causing some erratic behaviour. Below are the results from the liveplotter, Blue is target velocity, orange is measured velocity, green is target current and red is measued current. As you can see, the first launch is usually the best. The current is almost zero while I ask the motor to hold position without any load. Then it increases slightly as I apply the load (the diabolo, around 250g). Then I command the launcher to move and the current spikes, the measured velocity follows the target velocity nicely until the object is launched, then there is some expected overshoot as the load is released and the odrive catches up to correct the torque.
What I don’t understand is why the launches are getting progressively worse. As you can see, at all points the current increases, both when it is just holding still and when it is launching the object, this also translates to worse launches. After the third launch you can see how erratic the current/velocity is and most concerningly after the launch it is using 20A to make the motor just hold position with no load attached!
I followed the gains tuning guide on the odrive documentation, so far I have found the best gains to be:
odrv0.axis0.controller.config.pos_gain = 30
odrv0.axis0.controller.config.vel_gain = 0.6
odrv0.axis0.controller.config.vel_integrator_gain = 0
I would like to note that at the beginning the integrator gain was some finite value but I tuned it to zero because as I understand the velocity error was accumuilating over successive launches which caused some weird rotation (even without any load attached), setting the integrator gain to 0 helped attenuate this.
I originally has the position gain set much higher (around 60) and the velocity gain set much lower (around 0.2). This gave a nice rigid “springy” feeling when idle, but it made for progressively less consistent launches. By making the position gain lower and velocity gain higher (up to the current values) it made the launcher launch much more consistently (consistent exit velocity is the aim of the game after all) and it still gave that springy feeling when idling so the arm didn’t sag while the diabolo is placed on it. This seemed to work flawlessly for a while, but I have since modified the arm profile a little bit so that it pushes the diabolo against the ramp at a slight angle (5 degrees) which increases friction a little bit but which would hopefuly make the launches more consistent. While testing this new arm with these old parameters it suddenly went back to quickly causing erratic behaviour even when idling.
I know I can probably go back to the old arm which worked well for about 30-40 launches, but my plan one day is to use this outside the house not connected to my PC, so I really cannot afford this error popping up eventually and causing potential overheating of the motor. I would appreciate any help with regards to how I should tune the gains because at this point I am at a loss and I do not want to cause any problems.
As a final note, I will add that no matter what parameters I choose the first launch always seems the most powerful, uses the least current and seems to follow the trap trajectory the most closely. I guess I could just reboot the machine after every launch, but eventually I would like to make a version which launches multiple diabolos in quick succession, and so understanding how to mitigate this weird behaviour after multiple launches is more important to me now rather than a bodge job. Lastly, just to get ahead before this gets suggested, here is my full config:
odrv0.config.brake_resistance = 2.0
odrv0.config.dc_bus_undervoltage_trip_level = 20
odrv0.config.dc_bus_overvoltage_trip_level = 26
odrv0.config.dc_max_positive_current = 30.0
odrv0.config.dc_max_negative_current = -1.0
odrv0.config.max_regen_current = 0
odrv0.save_configuration()
odrv0.axis0.motor.config.pole_pairs = 20
odrv0.axis0.motor.config.calibration_current = 10
odrv0.axis0.motor.config.resistance_calib_max_voltage = 2
odrv0.axis0.motor.config.motor_type = MOTOR_TYPE_HIGH_CURRENT
odrv0.axis0.motor.config.current_lim = 50
odrv0.axis0.motor.config.current_lim_margin = 15
odrv0.axis0.motor.config.requested_current_range = 70
odrv0.axis0.motor.config.torque_constant = 8.27/100
odrv0.save_configuration()
odrv0.axis0.encoder.config.abs_spi_cs_gpio_pin = 6
odrv0.axis0.encoder.config.mode = ENCODER_MODE_SPI_ABS_AMS
odrv0.axis0.encoder.config.cpr = 16384
odrv0.axis0.encoder.config.bandwidth = 3000
odrv0.axis0.encoder.config.calib_range = 10
odrv0.axis0.config.calibration_lockin.current = 10
odrv0.axis0.config.calibration_lockin.ramp_time = 0.4
odrv0.axis0.config.calibration_lockin.ramp_distance = 3.1415927410125732
odrv0.axis0.config.calibration_lockin.accel = 20
odrv0.axis0.config.calibration_lockin.vel = 20
odrv0.save_configuration()
odrv0.axis0.controller.config.control_mode = CONTROL_MODE_POSITION_CONTROL
odrv0.axis0.controller.config.vel_limit = 30
odrv0.axis0.controller.config.pos_gain = 30
odrv0.axis0.controller.config.vel_gain = 0.6
odrv0.axis0.controller.config.vel_integrator_gain = 0
odrv0.axis0.controller.config.input_mode = INPUT_MODE_TRAP_TRAJ
odrv0.axis0.trap_traj.config.vel_limit = 7
odrv0.axis0.trap_traj.config.accel_limit = 100
odrv0.axis0.trap_traj.config.decel_limit = 150
odrv0.save_configuration()
odrv0.reboot()
odrv0.axis0.requested_state = AXIS_STATE_MOTOR_CALIBRATION
odrv0.axis0.motor.config.pre_calibrated = True
odrv0.axis0.requested_state = AXIS_STATE_ENCODER_OFFSET_CALIBRATION
odrv0.axis0.config.startup_encoder_offset_calibration = True
odrv0.axis0.requested_state = AXIS_STATE_CLOSED_LOOP_CONTROL
odrv0.axis0.config.startup_closed_loop_control = True
odrv0.save_configuration()
odrv0.reboot() ```
Many thanks in advance

