Skip to content

Commit fac135f

Browse files
authored
Merge pull request #363 from neurobionics/357-torque-trajectory-tutorial-script-needs-associated-docs
added associated docs for the basic torque_trajectory tutorial Fixes #357
2 parents 93ed1ed + 6c8f0a7 commit fac135f

File tree

3 files changed

+213
-16
lines changed

3 files changed

+213
-16
lines changed
+159
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
# Torque Trajectory Control Tutorial
2+
3+
This `opensourceleg.control` module provides functiionality for doing torque control. This tutorial demonstrates how to control the Open Source Leg (OSL) using torque trajectories for both the knee and ankle joints.
4+
## Warnings:
5+
6+
1. This example is not meant to be used as a walking controller. The goal of this example is to provide a reference for how a torque trajectory can be loaded and commanded.
7+
2. While runnig this script make sure to have load on the actuators.
8+
3. Please be cautious while changing mass parameters.
9+
10+
## Overview
11+
12+
The script implements a torque control system that:
13+
14+
1. Loads pre-defined torque trajectories for knee and ankle joints
15+
2. Applies these trajectories in a cyclic manner
16+
3. Logs the performance data
17+
4. Generates visualization plots
18+
19+
## Prerequisites
20+
21+
- OpenSourceLeg hardware setup
22+
- Python environment with required dependencies:
23+
24+
- numpy
25+
- matplotlib
26+
27+
- Access to torque trajectory files:
28+
29+
- `ankle.pkl`
30+
- `knee.pkl`
31+
32+
## Command Line Arguments
33+
34+
The script accepts the following command line arguments:
35+
36+
- `--mass`: User mass in kg (default: 1.0)
37+
- `--stride-time`: Stride time in seconds (default: 1.0)
38+
- `--frequency`: Control loop frequency in Hz (default: 200.0)
39+
40+
## Key Parameters
41+
42+
- `USER_MASS`: The mass of the user in kg (configurable via command line)
43+
- `STRIDE_TIME`: The stride time in seconds (configurable via command line)
44+
- `FREQUENCY`: The control loop frequency in Hz (configurable via command line)
45+
- `TRAJECTORY_LEN`: Fixed at 150 points. This is the length of the torque trajectories in the ankle.pkl and knee.pkl files.
46+
- `GEAR_RATIO`: Set to 9 * (83/18)
47+
48+
## Hardware Configuration
49+
50+
The script configures two DephyActuators and two AS5048B encoders:
51+
52+
1. **Actuators**:
53+
54+
- Knee actuator (port=`/dev/ttyACM0`)
55+
- Ankle actuator (port=`/dev/ttyACM1`)
56+
57+
Both configured with:
58+
59+
- Specified gear ratio
60+
- User-defined frequency
61+
- Dephy logging disabled
62+
63+
2. **Encoders**:
64+
65+
- Knee joint encoder (bus=1, A1=True, A2=False)
66+
- Ankle joint encoder (bus=1, A1=False, A2=True)
67+
68+
## Functions
69+
70+
### Get Torque
71+
72+
```python
73+
--8<-- "tutorials/control/torque_trajectory/torque_trajectory.py:22:42"
74+
```
75+
76+
Calculates the torque setpoint for a given time point:
77+
78+
- `t`: Current time in seconds
79+
- `data`: List containing torque trajectory data points
80+
- `user_mass`: Mass of the user in kg
81+
- `stride_time`: Stride time in seconds
82+
- `trajectory_len`: Length of the trajectory
83+
84+
Returns torque setpoint scaled by user mass
85+
86+
### Plot Data
87+
88+
```python
89+
--8<-- "tutorials/control/torque_trajectory/torque_trajectory.py:45:88"
90+
```
91+
92+
Generates three plots:
93+
94+
1. Ankle torque (setpoint vs. actual)
95+
2. Knee torque (setpoint vs. actual)
96+
3. Joint positions (knee and ankle in degrees)
97+
98+
Saves the plot as `plot.png`
99+
100+
## Operation Flow
101+
102+
1. **Initialization**:
103+
104+
- Parses command line arguments
105+
- Sets up logging with specified frequency
106+
- Configures actuators and sensors
107+
- Loads trajectory data from pickle files
108+
109+
2. **Control Sequence**:
110+
111+
- Homes the OSL
112+
- Sets control mode to CURRENT for both actuators
113+
- Sets current gains
114+
- Waits for user input
115+
- Executes torque trajectory
116+
- Logs performance data
117+
118+
3. **Visualization**:
119+
120+
- Generates plots after completion
121+
- Saves plots to "plot.png"
122+
123+
## Usage
124+
125+
1. Ensure trajectory files (`ankle.pkl` and `knee.pkl`) are in the working directory.
126+
We have provided a sample trajectory file in the `tutorials/control/torque_trajectory/` directory for both the ankle and knee joints.
127+
These trajectories are output of an high-level controller (not provided here) for level walking.
128+
The torque trajectories present the joint torque setpoints for one gait cycle, and the units are in Nm/kg.
129+
130+
2. Run the script with optional arguments:
131+
```bash
132+
python torque_trajectory.py --mass 10 --stride-time 1 --frequency 200
133+
```
134+
3. Press Enter when prompted to start the walking trajectory
135+
4. The system will execute the trajectory and generate plots upon completion
136+
137+
## Safety Notes
138+
139+
- This example is not meant to be used as a walking controller
140+
- Please be cautious while changing mass parameters
141+
- Ensure all connections are secure before operation
142+
- Keep emergency stop accessible during operation
143+
144+
## Output
145+
146+
The script generates:
147+
148+
1. Real-time logs in the `./logs` directory with filename "torque_trajectory"
149+
2. A plot file (`plot.png`) showing:
150+
151+
- Torque trajectories for both joints
152+
- Actual vs. commanded torques
153+
- Joint position data in degrees
154+
155+
## Full Script for this tutorial
156+
```python
157+
--8<-- "tutorials/control/torque_trajectory/torque_trajectory.py:1:212"
158+
```
159+
If you have any questions or need further assistance, please post on the [Open Source Leg community forum](https://opensourceleg.org/community).

mkdocs.yml

+1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ nav:
2626
- Control:
2727
- Compiled Controller: tutorials/control/compiled_controller.md
2828
- State Machine: tutorials/control/state_machine.md
29+
- Torque Trajectory: tutorials/control/torque_trajectory.md
2930
- Logging:
3031
- Getting Started: tutorials/logging/getting_started.md
3132
- Configuring Logger: tutorials/logging/configuring_logger.md

tutorials/control/torque_trajectory/torque_trajectory.py

+53-16
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,58 @@
11
"""
2-
This example is not meant to be used as a walking controller but
3-
just to provide a reference for how a torque trajectory can be loaded and commanded.
2+
This example demonstrates how torque trajectories can be loaded and commanded on the Open Source Leg (OSL).
3+
This is not a walking controller.
4+
5+
Authors: Senthur Raj Ayyappan <[email protected]>, Varun Satyadev Shetty <[email protected]>
6+
Neurobionics Lab
7+
Robotics Department
8+
University of Michigan
9+
March 25th, 2025
410
"""
511

12+
import argparse
613
import pickle
714

815
import numpy as np
916

1017
from opensourceleg.actuators import CONTROL_MODES
1118
from opensourceleg.actuators.dephy import DephyActuator
12-
from opensourceleg.logging import LOGGER, Logger
19+
from opensourceleg.logging import Logger
1320
from opensourceleg.robots.osl import OpenSourceLeg
1421
from opensourceleg.sensors.encoder import AS5048B
1522
from opensourceleg.utilities import SoftRealtimeLoop
1623

1724
ANKLE_TRAJECTORY_PATH = "./ankle.pkl"
1825
KNEE_TRAJECTORY_PATH = "./knee.pkl"
1926

20-
TRAJECTORY_LEN = 150
2127

22-
# DO NOT EXCEED 20 KG
23-
USER_MASS = 0 # kg
24-
GEAR_RATIO = 9 * (83 / 18)
28+
def get_torque(t: float, data: list, user_mass: float, stride_time: float, trajectory_len: int) -> int:
29+
"""Calculate the torque setpoint for a given time point in the trajectory.
2530
26-
FREQUENCY = 200
27-
STRIDE_TIME = 1 # sec
31+
Args:
32+
t (float): Current time in seconds
33+
data (list): List containing the torque trajectory data points
34+
user_mass (float): The mass of the user in kg
35+
stride_time (float): The stride time in seconds
36+
trajectory_len (int): The length of the trajectory
2837
38+
Returns:
39+
int: Torque setpoint scaled by user mass
2940
30-
def get_torque(t: float, data: list) -> int:
31-
walking_time = t % STRIDE_TIME
32-
index = int(walking_time * TRAJECTORY_LEN)
33-
return USER_MASS * data[index]
41+
The function:
42+
1. Calculates walking_time by taking modulo of current time with stride_time
43+
2. Converts walking_time to trajectory index based on trajectory_len
44+
3. Returns torque value at that index scaled by user_mass
45+
"""
46+
walking_time = t % stride_time
47+
index = int(walking_time * trajectory_len)
48+
return user_mass * data[index]
3449

3550

3651
def plot_data(plotting_data: list) -> None:
3752
try:
3853
import matplotlib.pyplot as plt
3954
except ImportError:
40-
LOGGER.warning("matplotlib is not installed")
55+
print("matplotlib is not installed, skipping plot")
4156
return
4257

4358
# plot the data using matplotlib
@@ -80,6 +95,28 @@ def plot_data(plotting_data: list) -> None:
8095

8196

8297
if __name__ == "__main__":
98+
# Set up argument parser
99+
parser = argparse.ArgumentParser(description="Torque trajectory control for Open Source Leg")
100+
parser.add_argument("--mass", type=float, default=1.0, help="User mass in kg (default: 1.0)", required=False)
101+
parser.add_argument(
102+
"--stride-time", type=float, default=1, help="Stride time in seconds (default: 1)", required=False
103+
)
104+
parser.add_argument(
105+
"--frequency", type=float, default=200.0, help="Control loop frequency in Hz (default: 200.0)", required=False
106+
)
107+
108+
# Parse arguments
109+
args = parser.parse_args()
110+
111+
# Set global parameters from arguments
112+
USER_MASS = args.mass
113+
STRIDE_TIME = args.stride_time
114+
FREQUENCY = args.frequency
115+
116+
# Fixed parameters
117+
TRAJECTORY_LEN = 150
118+
GEAR_RATIO = 9 * (83 / 18)
119+
83120
torque_logger = Logger(log_path="./logs", file_name="torque_trajectory")
84121
clock = SoftRealtimeLoop(dt=1 / FREQUENCY)
85122

@@ -157,8 +194,8 @@ def plot_data(plotting_data: list) -> None:
157194
for t in clock:
158195
osl.update()
159196

160-
ankle_torque_sp = get_torque(t, ankle_data)
161-
knee_torque_sp = get_torque(t, knee_data)
197+
ankle_torque_sp = get_torque(t, ankle_data, USER_MASS, STRIDE_TIME, TRAJECTORY_LEN)
198+
knee_torque_sp = get_torque(t, knee_data, USER_MASS, STRIDE_TIME, TRAJECTORY_LEN)
162199

163200
osl.ankle.set_output_torque(ankle_torque_sp)
164201
osl.knee.set_output_torque(knee_torque_sp)

0 commit comments

Comments
 (0)