Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
34 changes: 34 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
name: CI

on:
push: {}
pull_request: {}
workflow_dispatch:

jobs:
lint-and-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: prefix-dev/setup-pixi@v0.9.4
with:
cache: ${{ !env.ACT }}

- name: Install system deps for AHControl (Rust)
run: sudo apt-get update && sudo apt-get install -y libudev-dev pkg-config

- name: Run pre-commit
run: pixi run pre-commit run --all-files

- name: PythonExample unit tests
run: pixi run test-python-example

- name: Demo unit tests
run: pixi run test-demo

- name: Demo AHSimulation (mj_mink) unit tests
run: pixi run test-demo-mj-mink

- name: AHControl (Rust) unit tests
run: pixi run test-ahcontrol
13 changes: 13 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
*.pyc
push.json
.pytest_cache/

# Python package metadata (pip install -e)
*.egg-info/

# Rust build output
target/

# Dora / local output and logs
Demo/out/
out/
33 changes: 33 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# See https://pre-commit.com for usage. All hooks limited to PythonExample/.
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0
hooks:
- id: trailing-whitespace
files: ^PythonExample/
- id: end-of-file-fixer
files: ^PythonExample/
exclude: \.pytest_cache
- id: check-yaml
files: ^PythonExample/
- id: check-added-large-files
args: ["--maxkb=1000"]
files: ^PythonExample/
- id: check-merge-conflict
files: ^PythonExample/

- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.8.4
hooks:
- id: ruff
args: [--fix]
files: ^PythonExample/

- repo: local
hooks:
- id: pytest
name: pytest
entry: bash -c 'cd PythonExample && PYTHONPATH= pixi run python -m pytest tests/ -v'
language: system
pass_filenames: false
files: ^PythonExample/
2 changes: 0 additions & 2 deletions ArduinoExample/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,5 +35,3 @@ Rx arduino pin connected to Tx pin on TTLinker / Tx arduino pin connected to Rx
![Arduino_Mega_PortCom](../assets/Arduino_Mega.jpg)

Rx and Tx pin used on arduino Mega are RX1 & TX1 (19 & 18)


33 changes: 31 additions & 2 deletions Demo/AHControl/README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,39 @@
# Motor control node

The motor configuration is set in a TOML file (cf. [r_hand.toml](config/r_hand.toml)).
In this file you can set the motors ID, and angle offsets for each finger.
Use canonical calibration from the repo `config/calibration/` (recommended): e.g. `--config ../../config/calibration/r_hand_team_julia.toml` when running from Demo/AHControl. See [canonical_hand_config_design.md](../../docs/canonical_hand_config_design.md). Finger order is read from `config/hand_geometry.toml` when using a calibration file under `config/calibration/`. Alternatively, a legacy TOML under AHControl `config/` (e.g. [r_hand.toml](config/r_hand.toml)) is still supported for motor IDs and angle offsets.

# Tools
- *change_id*: to help you change the id of a motor. `cargo run --bin=change_id -- -h` for a list of parameters
- *goto*: to move a single motor to a given position. `cargo run --bin=goto -- -h` for a list of parameters
- *get_zeros*: to help you set the motor zeros, it sets the motors in the compliant mode and write the TOML file to the console. `cargo run --bin=get_zeros -- -h` for a list of parameters
- *set_zeros*: to move the hand in the "zero" position according to the config file. `cargo run --bin=set_zeros -- -h` for a list of parameters

# Calibration

Full procedure (verify IDs, create calibration file, get_zeros, set_zeros, run with profile) is in [docs/canonical_hand_config_design.md](../../docs/canonical_hand_config_design.md) under "Calibration procedures".

# Commands

Run from the project root. If you use pixi for Rust, run `pixi shell` first. Set `PORT` to your serial device (e.g. `/dev/ttyACM0` on Linux, `COM3` on Windows). For canonical calibration use a file under `config/calibration/` (e.g. `config/calibration/r_hand_team_julia.toml`).
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you using Rust vs. just python? or is this for future development?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is a refactoring, I wanted to make sure all demos (rust + python) still run. We can decide whether we want to use Rust for development. Rust is also new to me.


Linux:

```bash
export PORT=/dev/ttyACM0
export CFG="config/calibration/r_hand_team_julia.toml"
cargo run --manifest-path Demo/Cargo.toml --bin=set_zeros -- --serialport $PORT --config $CFG
cargo run --manifest-path Demo/Cargo.toml --bin=goto -- --serialport $PORT --id 1 --pos 0.0
cargo run --manifest-path Demo/Cargo.toml --bin=change_id -- --serialport $PORT --old-id 1 --new-id 2
cargo run --manifest-path Demo/Cargo.toml --bin=get_zeros -- --serialport $PORT --config $CFG
```

Windows (PowerShell):

```powershell
$PORT = "COM3"
$CFG = "config/calibration/r_hand_team_krishan.toml"
cargo run --manifest-path Demo/Cargo.toml --bin=set_zeros -- --serialport $PORT --config $CFG
cargo run --manifest-path Demo/Cargo.toml --bin=goto -- --serialport $PORT --id 1 --pos 0.0
cargo run --manifest-path Demo/Cargo.toml --bin=change_id -- --serialport $PORT --old-id 1 --new-id 2
cargo run --manifest-path Demo/Cargo.toml --bin=get_zeros -- --serialport $PORT --config $CFG
```
43 changes: 5 additions & 38 deletions Demo/AHControl/src/bin/get_zeros.rs
Original file line number Diff line number Diff line change
@@ -1,42 +1,10 @@
use clap::Parser;

use eyre::{eyre, Result};
use facet::Facet;
use facet_pretty::FacetPretty;
use rustypot::servo;
use std::io;
use std::{error::Error, time::Duration};

use std::{fs, thread};

// use std::io::Read;
#[derive(Debug, Facet)]
struct Fingers {
#[allow(dead_code)] // Disable dead code warning for the entire struct
motors: Vec<Motors>,
}

#[derive(Debug, Facet)]
struct Motors {
#[allow(dead_code)] // Disable dead code warning for the entire struct
finger_name: String,
#[allow(dead_code)] // Disable dead code warning for the entire struct
motor1: Motor,
#[allow(dead_code)] // Disable dead code warning for the entire struct
motor2: Motor,
}

#[derive(Debug, Facet)]
struct Motor {
#[allow(dead_code)]
id: u8,
#[allow(dead_code)]
offset: f64,
#[allow(dead_code)]
invert: bool,
#[allow(dead_code)]
model: String,
}
use std::path::Path;
use std::{error::Error, time::Duration, thread};

#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
Expand All @@ -58,10 +26,9 @@ fn main() -> Result<(), Box<dyn Error>> {
let baudrate: u32 = args.baudrate;
let configfile: String = args.config;
println!("Opening {:?}", configfile);
let toml_str = fs::read_to_string(configfile).expect("Failed to read config file");

let mut motors_conf: Fingers =
facet_toml::from_str(&toml_str).expect("Failed to deserialize config file");
let mut motors_conf: AHControl::Fingers =
AHControl::load_fingers_from_path(Path::new(&configfile))
.map_err(|e| eyre!("config load failed: {}", e))?;

// println!("{}", motors_conf.pretty());
let serial_port = serialport::new(serialport, baudrate)
Expand Down
46 changes: 6 additions & 40 deletions Demo/AHControl/src/bin/set_zeros.rs
Original file line number Diff line number Diff line change
@@ -1,42 +1,9 @@
use clap::Parser;

use eyre::{eyre, Result};
use rustypot::servo;
use std::{error::Error, time::Duration};

use facet::Facet;
use facet_pretty::FacetPretty;

use std::{fs, thread};

// use std::io::Read;
#[derive(Debug, Facet)]
struct Fingers {
#[allow(dead_code)] // Disable dead code warning for the entire struct
motors: Vec<Motors>,
}

#[derive(Debug, Facet)]
struct Motors {
#[allow(dead_code)] // Disable dead code warning for the entire struct
finger_name: String,
#[allow(dead_code)] // Disable dead code warning for the entire struct
motor1: Motor,
#[allow(dead_code)] // Disable dead code warning for the entire struct
motor2: Motor,
}

#[derive(Debug, Facet)]
struct Motor {
#[allow(dead_code)]
id: u8,
#[allow(dead_code)]
offset: f64,
#[allow(dead_code)]
invert: bool,
#[allow(dead_code)]
model: String,
}
use rustypot::servo;
use std::path::Path;
use std::{error::Error, time::Duration, thread};

#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
Expand All @@ -58,10 +25,9 @@ fn main() -> Result<(), Box<dyn Error>> {
let baudrate: u32 = args.baudrate;
let configfile: String = args.config;
println!("Opening {:?}", configfile);
let toml_str = fs::read_to_string(configfile).expect("Failed to read config file");

let motors_conf: Fingers =
facet_toml::from_str(&toml_str).expect("Failed to deserialize config file");
let motors_conf: AHControl::Fingers =
AHControl::load_fingers_from_path(Path::new(&configfile))
.map_err(|e| eyre!("config load failed: {}", e))?;

println!("{}", motors_conf.pretty());
let serial_port = serialport::new(serialport, baudrate)
Expand Down
Loading