Skip to the content
4 layers35 × 30.4 mmSiplab wristband, the printed circuit board in 3D

Bachelor Thesis Paper: Sparse Pose Estimation data collection, evaluation backend, frontend, as well as all resources for the creation of the Hidden Dataset using commodity sensors.

Size
35 × 30.4 mm
Copper layers
4
Thickness
1.6 mm
Parts
67
Nets
90
Pads
362
Tracks, vias
934, 173
Pours
7
Routed
Yes
KiCad
KiCad 8.0 (file 20240108)
Created
3 Jan 2026
Updated
3 Apr 2026

Overview

Siplab wristband is an open-source printed circuit board by bastocon, from the bastocon/sparse-pose-benchmark repository on GitHub, published under the MIT licence. It is a 4-layer board of 35 × 30.4 mm with 67 parts on 90 nets, routed with 934 tracks and 173 vias.

It is built around the BMD-340-A-R, BMP388 and LIS3MDL, with the LSM6DSOX, NPM1100-QDAB-R7 and CM8V-T1A-32.768KHZ-9PF-20PPM-TA-QC among its other parts. Its parts include 28 resistors, 17 capacitors, 7 connectors, 5 ICs and 4 diodes.

Main components

8

README

README.md

Bachelor Thesis

Benchmarking Deep Learning-Based Pose Estimation from Sparse IMUs

Source description

These are the interesting subdirectories:

/data

Insert needed datasets here. It could be, that this folder needs to be created first.

text
data
├── smplx_models
│   └── smpl
│       └── SMPL_FEMALE.npz
│       └── ...
├── AMASS
│   └── ACCAD
│       └── ...
├── ...

/src/experimental

Contains all evaluated methods, and under /pip also the evaluate.py and preprocess.py used for the benchmark.

/src/platform

Contains the code for the platform, seperated in /frontend and /backend.

The server is located in /src/platform/backend/src/index.ts, and the default submissions directory in /src/platform/backend/submissions.

/src/tracking

Each subdirectory has its own README's.

  • /uwb_wearables/recording/processing. uwb_wearables in general contains all the code for hardware and firmware of the commodity sensors. The recording/processing directory in particular contains the code to process the raw uwb.csv and imu.csv, including the orientation estimator.
  • /processing contains all the post-processing. It also contains info.csv and shape.csv if you need the cut intervals, beta shape parameters or similar.
  • VirtualCaliper is the program used to generate the SMPL shape parameters.

Execution Environments

Docker for backend evaluation

Do one of these things:

  1. Setup or rebuild: docker-compose build

  2. To create a non-persistent session to execute the evaluation: docker-compose run --rm --remove-orphans pip-env

  3. Optional: If you want to be sure, that it builds correctly after changes, do: docker-compose build --no-cache

Docker for Visualization with aitviewer

Same as for the backend, but use vis-env. e.g. docker-compose run --rm --remove-orphans vis-env. Note that the visualizer.py files need to be run with xvfb-run -s "-screen 0 1280x720x24" python path/to/visualizer.py, as the docker does not has access to the display.

Conda/Micromamba

We have the following environments: uip, pip-env, mocap. You can find them under

  • /pip_env.yml. Please refer to /pip_env_README.md and worst case /docker/pip-env/Dockerfile in the comments for an installation guide. The setup for rbdl was not easy. Works with python 3.7.
  • /mocap_env.yml
  • /src/experimental/UIP/environment.yml. As this python version differs, rbdl might has to be re-build. Works with python 3.8.17.

Execution of Benchmark and Methods

Running evaluation of Benchmark

Use pip-env. Be in project root. These scripts automatically evaluate all .pt files named as the datasets that are in a folder. You always have to provide the path of the folder and not the files for this evaluation.

Preparation:

  • Have the predictions ready, and files named uip_db.pt, totalcapture.pt, amass.pt, gip_person1.pt, gip_perspn2.pt, hidden.pt in folder/ - no substructure.
  • Have the files config.json (optional but recommended), submission_info.json (really optional, used for the display of information on the website).

Example config.json:

text
{
    "eval_tran": true
}

Run the evaluation of the benchmark:

bash
python ./src/experimental/pip/evaluate.py eval --path <path_to_folder>

If you also run GIP (as its predicted as two single person), or want to re-evaluate the overall scores, additionally do this to calculate the overall scores.

bash
python ./src/experimental/pip/evaluate.py overall --path <path_to_folder>

The result will be in folder/evaluation.json. The first part shows the results per dataset per sequence, and the last rows ("overall") have the final results. More details can be found in the Additional Information section.

For clearity: An example file structure

text
/.../submissions/1_PIP
    ├── amass.py
    ├── totalcapture.pt
    ├── ...
    ├── config.json
    ├── submission_info.json

Tip: If you only want to test things and run different versions, create an archive folder inside your actual evaluation folder. As the backend only searches for the exact files in the top level, they are not evaluated. E.g. folder/totalcapture.pt will be evauated, but folder/archive/totalcapture.pt will not.

Running PIP method

Uses environment "pip-env".

Go into project root. Preprocess:

bash
python src/experimental/pip/preprocess.py

Evaluating PIP on a method:

bash
python src/experimental/pip/evaluate.py predict --dataset <dataset>

with dataset in [hidden, totalcapture, uip_db, amass, gip_person1, gip_person2]

Get the output in a unified format. It creates an output file in the project root that has to be moved where desired.

bash
python src/experimental/utils/change_pt_file.py correct_pip_output <path-to-results-file.pt>

Running UIP method

Use environment "uip" from src/experimental/UIP/environment.yml file, I used python 3.8.17. Go into /src/experimental/UIP

For preprocessing:

bash
python modules/dataset/preprocess.py

For evaluating UIP on a dataset:

bash
python modules/evaluate/evaluator.py --network UIP\
                    --ckpt_path data/processed/UIP/pretrained.pt\
                    --data_dir data/processed/UIP/ground_truth_datasets/HiddenDataset\
                    --eval_trans\
                    --normalize_uwb\
                    --add_guassian_noise\
                    --model_args_file config/model_args.json\
                    --eval_save_dir data/processed/UIP/evaluation_res/HiddenDataset

To change the UIP output from the per-sequence based file to one file for benchmark evaluation:

bash
python src/experimental/utils/change_pt_file.py correct_uip_output <path/to/uip-output-folder> <path/to/gt-file-for-betas-gender>

# Or specifically (notice that UIP creates folders with spaces, do not forget the "")
python src/experimental/utils/change_pt_file.py correct_uip_output "/local/home/**/data/processed/UIP/result/HiddenDataset/Ultra Inertial Poser" /local/home/**/data/processed/ground_truth_datasets/hidden_test_gt.pt

This produces an output, that has to be renamed and put into one submission folder for evaluation.

Check out the official UIP GitHub README for open questions.

Running PNP method

Use pip-env environment, same as in PIP method.

For a pleasant experience, start in project root. Comment the datasets you want to use in the process.py before execution. Preprocessing

bash
python src/experimental/PNP/process.py

Prediction: Comment in/out the datasets you want to run in test.py before execution.

bash
cd src/experimental/PNP
python test.py

Get the output in a unified format. It creates an output file in the project root that has to be moved where desired.

bash
python src/experimental/utils/change_pt_file.py correct_pnp_output <path-to-results-file.pt>

Utility Functions

Visualize

To visualize a sequence, and/or ground truth to it, use this:

bash
python ./tracking/processing/vis.py --input <gt-file.pt> --output <prediction.pt> --seq 1 --second-input <gip-person2.pt> --origin
  • All args are optional
  • --input expects a ground truth file, specifically a file in which there is pose of shape [N, T, 24, 3].
  • --output expects a prediction file, specifically a file which has keys 0, ..., N-1 and where each i has a pose of shape [T, 24, 3].
  • --seq The sequence ID to show
  • --second-input same as --input, but especially useful for visualization of a second person like in GIP-DB.
  • --origin to reset origin in x and z axis (no height).

Platform

Can be found in /src/platform, seperated into frontend and backend.

To test it:

bash
cd /src/platform/frontend
ng serve
bash
cd /src/platform/backend
npm run dev

Additional information

Evaluation Metrics Schema

This is the template for the evaluation.json file, generated for each method. All of the following values can be null/nan.

{ sequence_metrics: { [dataset: string]: { [sequenceId: number]: { frames: number, tran_win_count: number, angle_mean: number, angle_std: number, sip_mean: number, sip_std: number, joint_pos_mean: number, joint_pos_std: number, jitter_mean: number, jitter_std: number, abs_jitter_mean: number, abs_jitter_std: number, drift_mean: number, drift_std: number, foot_pen_mean: number, foot_pen_std: number, foot_sliding_mean: number, mpjve_mean: number, mpjve_std: number, trans_2m_mean: number, trans_5m_mean: number, }, ... } }, overall: { [dataset: string]: { frames: number, angle: number, sip: number, joint_pos: number, jitter: number, abs_jitter: number, drift: number, foot_pen: number, foot_sliding: number, mpjve: number, trans_2m: number, trans_5m: number, }, ... } }

Submission Info (saved on disk)

This is generaetd for each method from the server and optional, it defines the public information for each submission.

{ shortName: string, version: string, fullName: string, pubTitle: string, pubAuthors: string, pubVenue: string, pubUrl: string, description: string, hardware: string, parameters: string, availability: string, availabilityUrl: string, status: string, contactEmail: string }

License and Acknowledgement

This project is released under the MIT license. Our code is partially based on PIP.

Hidden Dataset

Recorded at SIP Lab, Computer Science Department, ETH Zurich, Switzerland.

General Information

The sequences are numbered AA01, AA02, AA03 where AA is the subject identifier. Not all subjects have all three sequences.

File Structure

Main directory

In the main directory are the test.pt and train.pt, which contain the dataset with the synthetic magnetometer variant. This is the default version of this dataset. pose is stored in axis angle format. Note that tran does not start at origin to ensure compatibility for the camera feed.

test.pt has the following keys:

python
acc, ori, betas, gender, vuwb, offset (= None)

train.pt has the following keys:

python
acc, ori, betas, gender, vuwb, offset (= None), pose, tran

Videos

This directory contains all the videos for each subject. Note that not the subject (TM**) does not have videos, and for some sequences only 3 of 4 cameras worked. The subfolders are front, back, left, right indicating from where you usually see the person from. As most participants looked at the instructor, front is the default viewing direction for most people, the other directions are derived from that. The videos are pre-cut, but you have to do the exact frame-synchronization.

Extras

In the Extras folder, one can find additional versions of the dataset, with more data access for advanced processing. It contains test_no-mag.pt and train_no-mag with the same test and train data and keys, but where the orientation estimation is done without magnetometer readings. This leads to a significantly higher orientation error and more difficulty to estimate pose from.

It also contains the sequences with the following raw data. We recommend checking out the code and how it was generated, before using them. Wo = World frame of OptiTrack, so in which it was recorded. Wx= World frame of Xsens, where the pose originates. vis = Visualization/export frame (Y-up), this is the system in which everything like pose is exported by default if nothing is mentioned otherwise. Check out the paper "Benchmarking Deep Learning-Based Pose Estimation" for a more detailed definition of the coordinate systems etc.

python
"pose": to_numpy(pose_vis),  # (N, 72)
"pose_wo": to_numpy(pose_wo),  # (N, 72) in wo frame
"pose_unoptimized_vis": to_numpy(archive_pose_vis),  # (N, 72) in vis frame, but no optimization"
"pose_raw_xsensframe": to_numpy(data1["poses"][begin_idx:end_idx+1]),  # (N, 72) in Xsens frame

"tran": to_numpy(tran_vis),  # (N, 3), Optitrack Translation in vis frame
"tran_wo": to_numpy(tran_wo),  # (N, 3), Optitrack Translation in wo frame
"tran_unoptimized_vis": to_numpy(archive_tran_vis),  # (N, 3) OptiTrack translation in vis frame, but no optimization
"tran_raw_xsensframe": to_numpy(data1["trans"][begin_idx:end_idx+1]),  # (N, 3) in Xsens frame

# sensor readings
"rot":  to_numpy(rot_vis),            # (N, S, 3, 3) rotmat
"rot_wo":  to_numpy(rot_wo),            # (N, S, 3, 3) rotmat in wo frame
"acc":  to_numpy(acc_vis),            # (N, S, 3)
"acc_wo":  to_numpy(acc_wo),            # (N, S, 3) in wo frame
"acc_sensor":  to_numpy(acc_sensor),            # (N, S, 3) sensor frame acc
"acc_gtrotated": to_numpy(acc_vis_opt),  # (N, S, 3) # acc rotated with optitrack rot to remove sensor drift
"uwb":  to_numpy(uwb),               # (N, S, S), sensor UWB, frame independant

# OptiTrack ground truth
'opt_rot': to_numpy(opt_rot_vis),     # (N, S, 3, 3) rotmat
'opt_rot_wo': to_numpy(opt_rot_wo),     # (N, S, 3, 3) rotmat in wo frame
'opt_rot_wo_raw': to_numpy(archive_opt_rot_wo),     # (N, S, 3, 3) rotmat in wo frame, but no filtering
'opt_acc': to_numpy(opt_acc),        # (N, S, 3)

Parts

67
QtyValueFootprintReferences
150RResistor_SMD:R_0402_1005MetricR3, R4, R5, R6, R7, R9, R10, R15, R16, R17, R18, R19, R20, R22, R23
9100nFCapacitor_SMD:C_0402_1005MetricC6, C7, C8, C9, C10, C11, C12, C16, C17
310uFCapacitor_SMD:C_0603_1608MetricC3, C4, C5
34.7kResistor_SMD:R_0402_1005MetricR8, R13, R14
34.7uFCapacitor_SMD:C_0603_1608MetricC13, C14, C15
2100kResistor_SMD:R_0402_1005MetricR11, R12
210kResistor_SMD:R_0402_1005MetricR2, R21
23kResistor_SMD:R_0402_1005MetricR27, R28
25.1kResistor_SMD:R_0402_1005MetricR34, R35
2538850208siplab_wristband:Molex_SlimStack_53885-0208_2x10_P0.50mm_VerticalJ5, J6
2541020204siplab:Molex_SlimStack_54102-0204_2x10_P0.50mm_VerticalJ4, J7
11.5kResistor_SMD:R_0402_1005MetricR1
11kResistor_SMD:R_0402_1005MetricR26
11uFCapacitor_SMD:C_0402_1005MetricC2
12.2uFCapacitor_SMD:C_0603_1608MetricC1
12.2uHInductor_SMD:L_0805_2012MetricL1
132.768kHzCrystal:Crystal_SMD_2012-2Pin_2.0x1.2mmY1
1BATConnector_PinSocket_2.00mm:PinSocket_1x02_P2.00mm_VerticalJ1
1BMD-340-A-Rsiplab_wristband:RIGADO_BMD-340-A-RU5
1BMP388siplab_wristband:PQFN50P200X200X80-10NU4
1BTNButton_Switch_SMD:SW_SPST_EVQP7ASW1
1GREENLED_SMD:LED_0603_1608MetricD3
1LEDLED_SMD:LED_ASMB-KTF0-0A306D4
1LIS3MDLPackage_LGA:LGA-12_2x2mm_P0.5mmU3
1LOGOsiplab_wristband:logo_largeG***
1LSM6DSOXPackage_LGA:LGA-14_3x2.5mm_P0.5mm_LayoutBorder3x4yU2
1NPM1100-QDAB-R7siplab_wristband:QFN50P400X400X90-25NU1
1PROGConnector_JST:JST_GH_SM06B-GHS-TB_1x06-1MP_P1.25mm_HorizontalJ3
1PWRButton_Switch_SMD:SW_SPDT_PCM12SW3
1REDLED_SMD:LED_0402_1005MetricD1
1RSTButton_Switch_SMD:SW_SPST_EVQP7ASW2
1USB_C_Receptacle_USB2.0Connector_USB:USB_C_Receptacle_GCT_USB4105-xx-A_16P_TopMnt_HorizontalJ2
1YELLOWLED_SMD:LED_0402_1005MetricD2

More from bastocon/sparse-pose-benchmark

1
  • 2 layers35 × 15.9 mmUWB adaptor, the printed circuit board in 3D

    UWB adaptor

    bastocon/sparse-pose-benchmark

    Bachelor Thesis Paper: Sparse Pose Estimation data collection, evaluation backend, frontend, as well as all resources for the creation of the Hidden Dataset using commodity sensors.

    14 partsMIT

Similar boards

  • 2 layersNpm1100 evaluation pcb, the printed circuit board in 3D

    Npm1100 evaluation pcb

    Bharath-Kumar001/embedded_assignment

    This repository contains the files and documentation of embedded assignment pcb design and implementing rtos.

    12 parts
  • 2 layers20.5 × 28.7 mmConfig3, the printed circuit board in 3D

    Config3

    Arjun-Anubis/AGV_embedded

    A submission for the AGV selection round tasks as can by found here https://drive.google.com/drive/folders/1egEmc1-yBnJ_g8xXa28QhqrrsOWX-JJO by Arjun Ghosh

    12 parts
  • 2 layers33.9 × 34.3 mm2nPM1100, the printed circuit board in 3D

    2nPM1100

    Sinis16/AtlasCircuits

    25 parts
  • 4 layers20 × 20 mmCansat sensor board, the printed circuit board in 3D

    Cansat sensor board

    AnmolS-EE/TMU-CanSat-Electrical-Onboarding-2026

    17 partsMIT
  • 2 layers25.4 × 17.8 mmAdafruit LIS3MDL + LSM6DSOX, the printed circuit board in 3D

    Adafruit LIS3MDL + LSM6DSOX

    SamRBetts/Project-Crumple-Zone

    Welcome to all version-controlled CAD for the ME capstone CanSat project!

    28 parts
  • 6 layers55 × 35 mmPilot, the printed circuit board in 3D

    Pilot

    Stab-Rabbit-coding/Serenity-UAV

    A functional, security conscious, Unmanned Aerial Vehicle based on the Firefly Class spacecraft Serenity from the 2002 show..

    136 parts1CERN-OHL-W-2.0
  • 4 layers100 × 180 mmX65-sbc revB1, the printed circuit board in 3D

    X65-sbc revB1

    jsyk/x65

    The 65c02 and 65c816s-based modern retro computer

    344 parts59MIT

Questions

How big is the Siplab wristband PCB?

It measures 35 × 30.4 mm and has 4 copper layers, 1.6 mm thick.

What parts does the Siplab wristband use?

67 parts, built around the BMD-340-A-R, BMP388, LIS3MDL and LSM6DSOX. The full list of its parts is on this page, with links to each part's symbol and footprint where PCBKit's library has them.

Where can I download the Siplab wristband KiCad files?

From the bastocon/sparse-pose-benchmark repository on GitHub, at the commit this page was made from: the board is src/tracking/uwb_wearables/hardware/siplab_wristband/siplab_wristband.kicad_pcb and its schematic src/tracking/uwb_wearables/hardware/UWB_adaptor/UWB_adaptor.kicad_sch. PCBKit opens the board in the browser, with nothing to install.

Can I use the Siplab wristband design in my own project?

Its repository publishes it under the MIT licence: you may reuse it on that licence's terms (keep the author's notice, and for a copyleft licence share your changes alike).