Skip to content

Homing OSL Joints

This tutorial demonstrates how to home the knee and ankle joints on the Open Source Leg (OSL) using the opensourceleg Python library. You’ll learn how to initialize actuators and encoders, configure the robot platform, and use the built-in home() method to calibrate the joints by driving them into physical stops and applying offsets.

Overview

This example shows how to:

  • Initialize DephyActuator instances for the knee and ankle joints
  • Configure AS5048B magnetic absolute encoders via I2C
  • Instantiate the OpenSourceLeg platform with the defined components
  • Use the home() method to detect joint limits and assign calibrated offsets
  • Log joint and encoder positions to the terminal in real-time

Hardware Setup

  • Actuators: Two Dephy Actuators are connected to /dev/ttyACM0 and /dev/ttyACM1. These control the knee and ankle respectively.
  • Encoders: Two AS5048B encoders are connected to I2C bus on the host controller.

For detailed setup instructions, refer to:

Software Setup

Ensure the opensourceleg Python package is installed. Before running the full integration script, verify that each actuator and encoder is working individually. This helps isolate configuration issues.

Code Structure

The tutorial script is structured into seven key sections:

1. Configuration

from opensourceleg.actuators.base import CONTROL_MODES
from opensourceleg.actuators.dephy import DephyActuator
from opensourceleg.logging.logger import Logger
from opensourceleg.robots.osl import OpenSourceLeg
from opensourceleg.sensors.encoder import AS5048B
from opensourceleg.utilities import SoftRealtimeLoop

TIME_TO_STEP = 1.0
FREQUENCY = 200
DT = 1 / FREQUENCY
GEAR_RATIO = 9 * (83 / 18)

Key parameters:

  • FREQUENCY: Control loop rate (200 Hz)
  • DT: Timestep per loop cycle, defined as 1 / FREQUENCY
  • GEAR_RATIO: Compound gear ratio applied to each joint (≈41.5:1)

2. Real-time loop and Logger

    homing_logger = Logger(
        log_path="./logs",
        file_name="homing_joint",
    )

    clock = SoftRealtimeLoop(dt=DT)

This section:

  • Initializes a Logger instance to write homing data to the ./logs directory
  • Starts a SoftRealtimeLoop for accurate timing

3. Actuator Initialization

    actuators = {
        "knee": DephyActuator(
            tag="knee",
            port="/dev/ttyACM0",
            gear_ratio=GEAR_RATIO,
            frequency=FREQUENCY,
            dephy_log=False,
        ),
        "ankle": DephyActuator(
            tag="ankle",
            port="/dev/ttyACM1",
            gear_ratio=GEAR_RATIO,
            frequency=FREQUENCY,
            dephy_log=False,

This section:

  • Instantiates two DephyActuator objects for the knee and ankle
  • Assigns ports, frequency, gear ratio, and disables internal logging

4. Sensor Initialization

    sensors = {
        "joint_encoder_knee": AS5048B(
            tag="joint_encoder_knee",
            bus=1,
            A1_adr_pin=True,
            A2_adr_pin=False,
            zero_position=0,
            enable_diagnostics=False,
        ),
        "joint_encoder_ankle": AS5048B(
            tag="joint_encoder_ankle",
            bus=1,
            A1_adr_pin=False,
            A2_adr_pin=True,
            zero_position=0,
            enable_diagnostics=False,

This section:

  • Instantiates two AS5048B encoders using the I2C interface
  • Each encoder is tagged and assigned its A1/A2 pin configuration
  • Zero position is temporarily set to 0

5. Initialize OSL Platform

    osl = OpenSourceLeg(
        tag="osl",
        actuators=actuators,
        sensors=sensors,

This section:

  • Creates an instance of OpenSourceLeg and passes in the actuator and sensor dictionaries

6. Run Homing Routine

        osl.home(
            homing_voltage=2000,
            homing_frequency=FREQUENCY,
            homing_direction={"knee": -1, "ankle": -1},
            output_position_offset={"knee": 0.0, "ankle": np.deg2rad(30.0)},
            current_threshold=5000,
            velocity_threshold=0.001,

This section:

  • Calls osl.home() with user-defined voltages, direction, and thresholds
  • Each actuator rotates in a specified direction until one of the following is true:
  • Velocity falls below 0.001 rad/s
  • Current exceeds 5000 mA
  • Once stopped, the motor is zeroed based on its position, and a calibrated offset is added (30° for ankle, 0° for knee)

7. Reset Torque and Start Logging

        osl.knee.set_control_mode(CONTROL_MODES.CURRENT)
        osl.knee.set_current_gains()

        osl.ankle.set_control_mode(CONTROL_MODES.CURRENT)
        osl.ankle.set_current_gains()

        osl.knee.set_output_torque(0)
        osl.ankle.set_output_torque(0)

        # homing_logger.set_stream_terminator("\r")
        for t in clock:
            osl.update()

            homing_logger.info(

This section:

  • Switches both actuators to CURRENT control mode
  • Sets joint torques to 0 so they can be moved freely
  • Logs the following information continuously:
  • Elapsed time
  • Output positions of the knee and ankle (in degrees)
  • Raw encoder readings of the knee and ankle (in degrees)

Running the Script

  1. Navigate to the tutorial directory:

    cd tutorials/robots/osl
    
  2. Run the script:

    python homing_joints.py
    
  3. Expected behavior:

    • Both motors rotate slowly in a negative direction
    • Movement stops once physical limits are detected (via current or velocity thresholds)
    • Homing offsets are applied to calibrate the zero position
    • Control mode switches to current control, torque is zeroed
    • Joint and encoder positions print continuously in the terminal

Note: You can use Ctrl+C to terminate the loop at any time.