Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Gears, bug corrections and cleaning #1

Open
wants to merge 39 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
cfe2328
Benjamin
20chupin Jun 16, 2023
f271cb3
class definitions out of the main + mock
20chupin Jun 19, 2023
3fcec43
Adaptation of load to the saving of None
20chupin Jun 19, 2023
92aa694
Test multiprocessing
20chupin Jun 19, 2023
33fe18a
Multiprocessing V1
20chupin Jun 20, 2023
d9644ed
Workinggg
Jun 19, 2023
b389cb6
Clean gui init
20chupin Jun 20, 2023
f9c7189
Saving
20chupin Jun 21, 2023
45e8d85
MotorController
20chupin Jun 21, 2023
704d3a0
Save
Jun 19, 2023
b12874d
Clean and docstring
20chupin Jun 21, 2023
0673864
Torque constant
Jun 20, 2023
14f4833
torque_constant
20chupin Jun 22, 2023
c64e3b1
New torque constant
20chupin Jun 26, 2023
08aa63b
Uncertainty
20chupin Jun 28, 2023
ec1ac2e
Write to json
20chupin Jun 28, 2023
fdc42be
Cleaning
20chupin Jun 28, 2023
37f51a4
QTimer for saving data
20chupin Jul 3, 2023
8110adc
error[0]
20chupin Jul 3, 2023
a52d9b3
Save period
20chupin Jul 3, 2023
12bfa41
MotorController
20chupin Jul 3, 2023
6f0e5e6
Motor time
20chupin Jul 3, 2023
3ef62f5
Comments
20chupin Jul 5, 2023
122f4c6
XP + bug
Jun 24, 2023
839fd1d
Errors + black
20chupin Jul 6, 2023
8e5a633
# noinspection PyTypeChecker and self.axis
20chupin Jul 6, 2023
5cef596
Green line
Jun 29, 2023
2c999c6
Reading w/ Kevin and Amedeo
20chupin Jul 11, 2023
4a95f58
Resisting current gears
20chupin Jul 13, 2023
f5c93a6
Instructions
20chupin Jul 13, 2023
073c64b
GPIO
20chupin Jul 13, 2023
f6cbdb5
Add limits
20chupin Jul 13, 2023
d7ee318
Errors
20chupin Jul 13, 2023
fde8a5e
Last minute changes
Jul 1, 2023
44e9066
Limits
20chupin Jul 14, 2023
8732372
Mistake
20chupin Jul 14, 2023
7dafdca
torque_max
Jul 1, 2023
7c1ed46
Cleaning
20chupin Jul 14, 2023
8e83950
Finale doc
20chupin Jul 14, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
134 changes: 1 addition & 133 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,133 +1 @@
# Ergocycle S2M
I'm not sure `odrive` can be used on the last version of Python, so I have not updated it from ``
## Run with an IDE or from a terminal
If you are not sure what configuration is currently saved in the ODrive it is better to run first the
`initial_calibration.py` file. The pinched noise is totally normal even if it seems unbearable.
If no error has been detected, you are good to go!
Run the `main.py` file. You should see the GUI appear.
If you want a simple understanding of the code, you can read and run the `cadence_control.py` file.
## Package installation
/!\ Once the package is installed the imports will be from the package, not from the files in the repository.
If you are developing and doing a lot of changes, it is better to uninstall the package and run the files from an IDE
or a terminal.
To install the package, write in a terminal:
```commandline
python setup.py install
```
You might need the `sudo` command:
```commandline
sudo python setup.py install
```
Please note that you must have registered your hardware information in the `hardware_and_security.json` file.
If any information needs to be changed, the package must be reinstalled.
Then, you will be able to start the ergocycle with the command :
```commandline
start_ergocycle
```
and to read any saved data with the command:
```commandline
read_ergocycle_file file_path
```
## Hardware
### Relays
The relays are not controlled by the Odrive, they are there to ensure that when the power source that powers the Odrive is sut down, the current cannot go into it.
As soon as the Odrive is powered, the relays are too and the motor is directly controlled by the Odrive.
To work, if the Odrive is connected to the COM pins and the motor to the NO pins,
the COM pins need to be connected to the High pins (so the normally open relay closes when alimented).
### Reduction ratio
There are two gear stages. The first one has 8 teeth at the entry and 36 (motor) at the exit (blue gear).
The second one has supposedly (according to my researches and check on the hardware) 10 teeth at the entry and 91 at the exit.
The reduction ratio is then Ze/Zs = ws/we = 8/36*10/91
## Firmware/Software
2022/02/09:
- Firmware: 0.5.5, *a priori* the last firmware available for the Odrive v3.6 56V (end of life)
- To update the firmware on the Raspberry, use `sudo odrivetool dfu path/to/firmware/file.elf`
- Software: odrive 0.6.3.post0, the latest version at this time
- To update the software on the Raspberry, use `pip install odrive==0.6.3.post0`
## Protocol
### Torque constant and resisting torque
#### Resisting torque
The first step is to determine the resisting torque of the motr. The resisting torque is the minimum torque that the
motor has to apply to make the pedals move.
To do so, you must use the torque control on the system with the neutral load (just the pedals, not any additional
load). The torque profile must be a ramp and the torque at which the motor is moving is the resisting torque. You
should do it several times in order to have a precise value. It is advised to do it for each rotation direction and with
different `torque_ramps`.
At this point, we don't know the torque constant, so we can only have the corresponding current.
The script `find_resisting_torque/resisting/torque.py` allows to compute the current corresponding to the resisting
torque from `.json` files obtained with the `find_resisting_torque/find_resisting_torque.py` on the motor. _A priori_
the motor won't turn if it as less current than this `resisting_current`.
#### Torque constant
## Troubleshooting
### Calibration
Every calibration must be done without mechanical load nor watchdog since the motor has to reboot several times.
#### Complete calibration
The complete calibration must be done without any mechanical load.
### Watchdog
The watchdog timeout can't be as little as the user wants.
For instance, for a feeding time of `0.01`, the watchdog can't be as small as `0.05`.
I've set it to `0.1`.
### Connection to the Odrive
I had trouble connecting the Odrive to the Raspberry Pi. The solution that worked for me was to write in a terminal:
```
echo 'SUBSYSTEM=="usb", ATTR{idVendor}=="1209", ATTR{idProduct}=="0d[0-9][0-9]", MODE="0666"' | sudo tee /etc/udev/rules.d/91-odrive.rules
sudo udevadm control --reload-rules
sudo udevadm trigger
```
To test the connection a simple script is sufficient :
```
import odrive

print("Look for an odrive ...")
odrive.find_any()
print("Found")
```
Or when launching `odrivetool` in a terminal, it should be written :
```
ODrive control utility v0.5.4
Connected to ODrive 206F3694424D as odrv0
```
/!\ If a script using the connection to the Odrive is running, you cannot connect to the Odrive through `odrivetool`
### Motor is not moving at all
The simple test that should work before trying anything else is :
```
odrv0.axis0.requested_state = AXIS_STATE_FULL_CALIBRATION_SEQUENCE
```
I had trouble controlling the motor with the relay. When connecting it directly to the Odrive it worked.
### Errors about the current, the resistance, the inductance
Verify you have :
`resistance_calib_max_voltage > calibration_current * phase_resistance`
`resistance_calib_max_voltage < 0.5 * vbus_voltage`

For me (TSDZ2 connected directly to the Odrive), it worked with the following config :
```
I_bus_hard_max: inf (float)
I_bus_hard_min: -inf (float)
I_leak_max: 0.10000000149011612 (float)
R_wL_FF_enable: False (bool)
acim_autoflux_attack_gain: 10.0 (float)
acim_autoflux_decay_gain: 1.0 (float)
acim_autoflux_enable: False (bool)
acim_autoflux_min_Id: 10.0 (float)
acim_gain_min_flux: 10.0 (float)
bEMF_FF_enable: False (bool)
calibration_current: 30.0 (float)
current_control_bandwidth: 100.0 (float)
current_lim: 35.0 (float)
current_lim_margin: 8.0 (float)
dc_calib_tau: 0.20000000298023224 (float)
inverter_temp_limit_lower: 100.0 (float)
inverter_temp_limit_upper: 120.0 (float)
motor_type: 0 (uint8)
phase_inductance: 2.409255648672115e-05 (float)
phase_resistance: 0.04511798173189163 (float)
pole_pairs: 8 (int32)
pre_calibrated: True (bool)
requested_current_range: 25.0 (float)
resistance_calib_max_voltage: 10.0 (float)
torque_constant: 0.20999999344348907 (float)
torque_lim: inf (float)
```
### Position control and velocity control
Setting the `odrv0.axis0.controller.config.control_mode`, the `odrv0.axis0.controller.config.input_mode` and the
`odrv0.axis0.requested_state` must be done only once and not between each change of position or velocity !
All the information you need is in `documentation/ergocycleS2M.pdf`.
23 changes: 17 additions & 6 deletions cadence_control.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,29 +5,38 @@
import numpy as np
import time

from ergocycleS2M.motor_control.motor import MotorController
from ergocycleS2M.motor_control.motor_controller import MotorController
from ergocycleS2M.data_processing.load import load, compute_data

# Initialisation
# Creation of a motor object that will be used to control the motor
motor = MotorController()

# Data saving
# Variables for data saving
t0 = time.time()
t1 = time.time()
f_sample = 20 # Hz
t_next = 0

# Control
# The instruction is the cadence of the pedals in rpm
instruction = 30
# The ramp rate is the rate at which the cadence will increase or decrease in rpm/s
ramp_rate = 6
# The directions available are "Forward" and "Reverse" see the documentation for more details and see what the
# directions refer to on a photo of the ergocycle
motor.set_direction("Reverse")
# The cadence control method will set the cadence of the pedals to the desired value with the desired ramp rate
motor.cadence_control(instruction, cadence_ramp_rate=ramp_rate)

# Loop to save the data (can be done in a separate thread)
while t1 - t0 < 10:
t1 = time.time()
if t1 - t0 > t_next:
# The difference between `spin_box` and `instruction` is not relevant here. It is when used with a graphic user
# interface, in this case the spin box is the value displayed on the interface and the instruction is the
# torque or the cadence that is sent to the motor, because no matter what the control model specified by the
# user is, the control of the motor can only be done with a torque or a cadence control.
motor.minimal_save_data_to_file(
"cadence_control_example", spin_box=instruction, instruction=instruction, ramp_instruction=ramp_rate
"cadence_control_example", gear=0, spin_box=instruction, instruction=instruction, ramp_instruction=ramp_rate
)
t_next += 1 / f_sample

Expand All @@ -38,8 +47,10 @@
t = np.asarray(data["time"])

# Plot
_, cadence, *_ = compute_data(data["vel_estimate"], data["turns"], data["iq_measured"])
_, cadence, *_ = compute_data(data["vel_estimate"], data["turns"], data["iq_measured"], data["gear"])
plt.plot(t, cadence, label="Pedals cadence")
# The real instruction sent to the motor is not saved in the data, but it can be computed from the ramp_rate and the
# instruction if needed.
plt.plot(t, -np.min([list(ramp_rate * t), data["instruction"]], axis=0), label="Instruction")
plt.title("Ramped cadence control")
plt.xlabel("Time (s)")
Expand Down
16 changes: 0 additions & 16 deletions calib_torque.py

This file was deleted.

Binary file added calibration_files/Calib1.bio
Binary file not shown.
Binary file added calibration_files/Calib2.bio
Binary file not shown.
Binary file added calibration_files/Calib3.bio
Binary file not shown.
Binary file added calibration_files/Calib4.bio
Binary file not shown.
Binary file added calibration_files/Calib5.bio
Binary file not shown.
46 changes: 46 additions & 0 deletions calibration_files/resisting_current_gear_1.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
{
"velocities": [
-3.4120407462220586,
-6.825516923704728,
-10.238383447830685,
-13.64796277595554,
-17.064630128195486,
-20.476294325933317,
-23.88442265500885,
-27.30425639813553,
-30.715214906702542,
-34.1235301245941,
-37.53436877080029,
-40.94614917954288
],
"intensities_std": [
0.20134716162186858,
0.1702650221351979,
0.20586088855711746,
0.26082112876762414,
0.34875508223431334,
0.519981824014429,
0.5900712039616689,
0.9144900400963688,
1.009164603613374,
1.518491570633324,
1.3814229696799087,
1.9581028206099105
],
"velocities_std": [
0.23798876502463326,
0.1133226039405681,
0.10196502625161918,
0.11067510954684111,
0.1207289251362517,
0.13436317626303587,
0.13875809498665231,
0.16727527052278335,
0.13259369373451754,
0.1849650389184444,
0.1643218331395464,
0.40623457670842267
],
"a": 0.03380779858882961,
"b": 0.8872093238163651
}
49 changes: 49 additions & 0 deletions calibration_files/resisting_current_gear_10.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
{
"velocities": [
-3.4131318421539003,
-6.825291518567895,
-10.235453080804414,
-13.647720409250189,
-17.059243895968454,
-20.473035534966563,
-23.885975200013778,
-27.299204483423267,
-30.70959353775562,
-34.12389353978015,
-37.539720093584044,
-40.31614969667087,
-43.05072026543655
],
"intensities_std": [
0.3189242567582075,
0.6439382942516751,
0.6333561570994258,
0.6723625093811607,
0.6534935524053814,
0.7224324734239292,
0.8116124338671254,
1.0532807705888074,
1.1240853340444623,
1.6428424185752302,
1.4422977533628334,
2.2712644428290885,
2.564576979914713
],
"velocities_std": [
0.15890002927971816,
0.2919853668120776,
0.2588566578487991,
0.19979624041730495,
0.1888732226614335,
0.20505021959167202,
0.2419878419254406,
0.2878692375460531,
0.2923843259419282,
0.3190076362138282,
0.3273148411130423,
0.38783138509707044,
0.39911529950743857
],
"a": 0.19483370030561692,
"b": 2.517670315420316
}
46 changes: 46 additions & 0 deletions calibration_files/resisting_current_gear_2.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
{
"velocities": [
-3.4126778307158743,
-6.823725593082668,
-10.23750268395414,
-13.650760427263458,
-17.06491538017027,
-20.477396532146432,
-23.88639135840322,
-27.303663151284002,
-30.707168174368594,
-34.13344826309183,
-37.544105193827626,
-40.942522948940855
],
"intensities_std": [
0.20018342983745457,
0.17955567669673272,
0.2134997427687467,
0.2655619622139137,
0.3476182566914793,
0.5210104978328366,
0.5617469793859872,
0.8717163503416356,
1.036202590148378,
1.560986389624314,
1.3818801642761775,
1.9686330620998214
],
"velocities_std": [
0.22083991379312792,
0.10508787921939915,
0.10989697305512404,
0.11519432771257837,
0.13071221460865895,
0.14486091445928367,
0.13364660430486577,
0.19317633038210588,
0.1534370840317438,
0.17940358090886516,
0.18660607191495165,
0.3513860430631813
],
"a": 0.04115635809031276,
"b": 0.8785364704740983
}
Loading