Homing on a CDPR


Our adventure on mastering a cable-driven 3D-printer is continuing. This time we are figuring out the endstop setup for the cartesian Z-axis, which is called D-axis in the kinematics.

The D-axis consists of three motors pulling on three steel cables, which terminate on the end effector. We planned to use worm gearboxes on this axis in order to have a blocked system in switched off modus. But due to pressing schedule we had to go with gear reduction by a belt drive. Now, since a belt drive won’t block itself in contrast to the worm gear, we have to tighten the cables on every startup even before we are able to start a homing sequence.

Ideally, this would happen automagically. So, the question is if it’s possible to drive the motors first in opposite direction for turns X before starting the homing sequence, that drive the end effector against the endstops and then travels back to a home position.

This would allow us to have an equal amount of cable wound out at the home position and the end effector would be level.

Another question: If I have setup Odrive to go into the closed loop state after startup and I add also homing at startup, will it enter closed loop after homing?

Here’s an image of the setup for better understanding.

1 Like

I will reply to myself.

Got it so far working. The D-axis is now doing the homing sequence after calibration. I managed to drive the motor in opposite direction with a negative homing speed of -0.25, but after reaching the offset position the axis goes into AXIS_STATE_IDLE and the end effector falls down on the print bed.

I can confirm, that the endstops work and that the axis is correctly homed, but I would need it to hold the position and go into AXIS_STATE_CLOSED_LOOP_CONTROL.

@Wetmelon Is there a way to achieve this?

Have you tried setting axis.config.startup_closed_loop_control as well as startup_homing?
I think this should enter closed loop just after the homing state

Yes, I’m using both settings at startup.

One thing I also noticed is, that the D-axis cables need to have some slack on startup, or the calibration sequence stops when the end effector lifts off its stand changing the endstop state.

You’re trying to coordinate 3 axes’ homing procedure with a system that only handles 1 axis. It might sorta work but it’ll always be jank. Use your motion controller to do this instead.

This is a workaround until we get Duet talking over CAN (instead of CAN-FD), but before that happens, we will have to live with this.

The workaround will be good enough for now if we can get the motors to hold position after homing. Is there something to pay attention to if you want to achieve closed loop state after homing at startup?

After several days of testing with different mechanical endstop setups and different config setup, I’ve come to the conclusion that there must be something wicked in the code.

This is our config:

odrv0.config.enable_uart = False

odrv0.axis0.config.turns_per_step = 0.001953125
odrv0.axis0.config.step_gpio_pin = 1
odrv0.axis0.config.dir_gpio_pin = 2

odrv0.axis1.config.turns_per_step = 0.001953125
odrv0.axis1.config.step_gpio_pin = 7
odrv0.axis1.config.dir_gpio_pin = 8

odrv0.axis0.config.enable_step_dir = True
odrv0.axis1.config.enable_step_dir = True

odrv0.config.brake_resistance = 2

odrv0.axis0.config.startup_motor_calibration = True
odrv0.axis0.config.startup_encoder_offset_calibration = True
odrv0.axis0.config.startup_closed_loop_control = True
odrv0.axis0.config.startup_homing = False

odrv0.axis0.motor.config.current_lim = 50
odrv0.axis0.motor.config.pole_pairs = 7
odrv0.axis0.motor.config.torque_constant = 0.03445833
odrv0.axis0.motor.config.motor_type = MOTOR_TYPE_HIGH_CURRENT
odrv0.axis0.motor.config.calibration_current = 40

odrv0.axis0.encoder.config.cpr = 8192
odrv0.axis0.encoder.config.mode = ENCODER_MODE_INCREMENTAL
odrv0.axis0.encoder.config.calib_range = 0.05
odrv0.axis0.encoder.config.bandwidth = 1000

odrv0.axis0.controller.config.vel_limit = 90
odrv0.axis0.controller.config.pos_gain = 35
odrv0.axis0.controller.config.vel_integrator_gain = 1.25
odrv0.axis0.controller.config.vel_gain = 0.20

odrv0.axis0.min_endstop.config.enabled = False

odrv0.axis1.config.startup_motor_calibration = True
odrv0.axis1.config.startup_encoder_offset_calibration = True
odrv0.axis1.config.startup_closed_loop_control = True
odrv0.axis1.config.startup_homing = True

odrv0.axis1.motor.config.current_lim = 50
odrv0.axis1.motor.config.pole_pairs = 7
odrv0.axis1.motor.config.torque_constant = 0.03445833
odrv0.axis1.motor.config.motor_type = MOTOR_TYPE_HIGH_CURRENT
odrv0.axis1.motor.config.calibration_current = 40

odrv0.axis1.encoder.config.cpr = 8192
odrv0.axis1.encoder.config.mode = ENCODER_MODE_INCREMENTAL
odrv0.axis1.encoder.config.calib_range = 0.05
odrv0.axis1.encoder.config.bandwidth = 1000

odrv0.axis1.controller.config.vel_limit = 90
odrv0.axis1.controller.config.pos_gain = 35
odrv0.axis1.controller.config.vel_integrator_gain = 1.25
odrv0.axis1.controller.config.vel_gain = 0.20
odrv0.axis1.controller.config.homing_speed = -0.25
odrv0.axis1.controller.config.vel_ramp_rate = 0.05

odrv0.axis1.trap_traj.config.vel_limit = 0.3
odrv0.axis1.trap_traj.config.accel_limit = 0.01
odrv0.axis1.trap_traj.config.decel_limit = 0.01

odrv0.axis1.min_endstop.config.gpio_num = 3
odrv0.axis1.min_endstop.config.pullup = True
odrv0.axis1.min_endstop.config.is_active_high = False
odrv0.axis1.min_endstop.config.offset = 0.25
odrv0.axis1.min_endstop.config.debounce_ms = 200
odrv0.axis1.min_endstop.config.enabled = True

odrv0.axis1.max_endstop.config.gpio_num = 6
odrv0.axis1.max_endstop.config.pullup = True
odrv0.axis1.max_endstop.config.is_active_high = True
odrv0.axis1.max_endstop.config.offset = -0.1
odrv0.axis1.max_endstop.config.debounce_ms = 200
odrv0.axis1.max_endstop.config.enabled = False

As you can see, we are driving the axis with negative speed is opposite direction to lift the end effector from the carrier first. We tried first having the endstops integrated in the carrier, so the end effector lays on the endstops. They triggered once the end effector didn’t touch the endstops anymore. This setup worked partly. The axis got homed, but the axis state didn’t go to closed-loop like expected, but into idle state causing the end effector to fall down.

I read somewhere, that if the endstop is pressed after triggering the system will go into idle state, as security measure I assume. This was the case with the first setup, as the endstop stayed high during the homing sequence.

Now, we tried it the other way around, and put the endstop above the end effector causing it to trigger only when high enough to touch it and then driving the axis down to the offset position. The endstop will stay low after trigger, so this shouldn’t cause issues anymore.

But, the issue with the axis state going into idling remains.
There are no errors on axis1 (don’t pay attention to axis0, that’s another issue that has nothing to do with this one)
The axis1 is homed, but it is in idle state.

I have my suspicion that driving the axis with negative speed is messing something in the code. @Wetmelon what’s your opinion?

Edit: We just tested the homing sequence by putting the axis manually in closed-loop and by lifting the end effector up. Then we drove the axis with positive speed (as it is meant to be driven) and triggered the endstop by hand. Again homing works, offset position is reached and then the axis goes into idling.

Second edit: :thinking: and suddenly homing works!? A combination of soft reboot, hard reboot, I don’t know… very mysterious. But anyway, it’s now finally working :partying_face:

1 Like

How are you starting the homing sequence? If you’re using axis0.requested_state = AXIS_STATE_HOMING, it will go idle after. You can simply command CLOSED_LOOP after HOMING, or use the startup sequence features

We are using the startup sequence feature. After rebooting the Odrives via console command and power-cycle the homing sequence suddenly started working, so all good now. Power-cycling might have done the trick 🤷