Control a TinkerKit Braccio arm with an Arduino UNO Q, ROS 2, Edge Impulse, and Gazebo.
ROS 2 host -> tcp_bridge -> UNO Q agent -> servos
This repository contains:
- Arduino firmware using the official arduino-libraries/Braccio library.
- ROS 2 USB serial and remote TCP bridges that convert
sensor_msgs/JointStatecommands into Braccio servo angles. - A ROS 2 teleop node for keyboard-free testing.
- A Gazebo/ros2_control simulation package for developing without hardware.
- Edge Impulse integration scaffolding for gesture or classifier-driven arm poses.
- Edge Impulse CSV data capture for commanded servo motion and robot status.
- USB camera vision with OpenCV color tracking, plus notes for Pixy and ESP32-CAM expansion.
firmware/unoq_braccio_firmware/ USB serial firmware for UNO Q + Braccio
app_lab/braccio_smoke_test/ Arduino App Lab hardware smoke test
app_lab/braccio_remote_agent/ Arduino App Lab network control agent
app_lab/usb_camera_streamer/ UNO Q attached USB camera MJPEG streamer
app_lab/braccio_web_agent/ Combined web dashboard control/camera agent
app_lab/bricks/unoq_braccio_bridge/ Reusable App Lab Braccio bridge brick
web_app/ Browser dashboard for remote arm control
ros2_ws/src/unoq_braccio_bringup/ ROS 2 launch files and runtime config
ros2_ws/src/unoq_braccio_driver/ Serial driver and demo command nodes
ros2_ws/src/unoq_braccio_sim/ URDF, Gazebo world, ros2_control config
edge_impulse/ Classifier mapping examples and notes
scripts/ Setup and helper scripts
docs/ Hardware and workflow documentation
Architecture details: docs/architecture.md.
- Arduino UNO Q
- TinkerKit Braccio robot arm and Braccio shield
- USB serial or Wi-Fi/Ethernet network connection to the ROS 2 host
- 5 V power supply for the Braccio servos
The firmware expects the Arduino Braccio library to be installed through the
Arduino IDE Library Manager or arduino-cli.
For full setup on Windows, macOS, and Linux, see docs/platform-setup.md.
Tested target: ROS 2 Jazzy on Ubuntu 24.04.
cd ros2_ws
rosdep install --from-paths src --ignore-src -r -y
colcon build --symlink-install
source install/setup.bash
cd ..For a first hardware check through Arduino App Lab, use
app_lab/braccio_smoke_test. For direct
arduino-cli flashing:
arduino-cli lib install Braccio
arduino-cli core install arduino:zephyr
arduino-cli board list
arduino-cli compile --fqbn arduino:zephyr:unoq firmware/unoq_braccio_firmware
arduino-cli upload -p /dev/ttyACM0 --fqbn arduino:zephyr:unoq firmware/unoq_braccio_firmwareThe Braccio shield sits on the UNO Q headers. Do not use arduino:avr:uno;
UNO Q builds target the Zephyr-based MCU core with arduino:zephyr:unoq.
Validated Windows network upload path:
arduino-cli board list
arduino-cli upload -p 192.168.1.64 --fqbn arduino:zephyr:unoq .\firmware\unoq_braccio_firmware --upload-field password=arduino123Replace 192.168.1.64 and arduino123 with your UNO Q network address and
upload password. This path avoids the Windows USB ADB device offline issue.
source ros2_ws/install/setup.bash
ros2 launch unoq_braccio_bringup hardware.launch.py serial_port:=/dev/ttyACM0Publish a test pose:
ros2 run unoq_braccio_driver pose_demo --ros-args -p pose:=readyWindows USB debug:
arduino-cli board list
arduino-cli monitor -p COM4 --fqbn arduino:zephyr:unoq --config baudrate=115200Replace COM4 with the port shown by arduino-cli board list. The firmware
uses 115200 baud. Send this line to test the arm command protocol:
M 90 90 90 90 90 25
Expected response:
OK
Install and run the App Lab project in app_lab/braccio_remote_agent on the
UNO Q, then launch the ROS 2 TCP bridge:
source ros2_ws/install/setup.bash
ros2 launch unoq_braccio_bringup remote.launch.py host:=<UNO_Q_IP_ADDRESS> port:=8765Send the same test pose:
ros2 run unoq_braccio_driver pose_demo --ros-args -p pose:=readysource ros2_ws/install/setup.bash
ros2 launch unoq_braccio_bringup sim.launch.pyThen send the same demo poses:
ros2 run unoq_braccio_driver pose_demo --ros-args -p pose:=waveThe Gazebo package models all six Braccio command joints, the gripper-mounted camera body, red/blue/yellow pick blocks, and colored drop zones. See ros2_ws/src/unoq_braccio_sim/README.md.
For a simple target-position test inspired by Braccio pick/drop simulators:
ros2 run unoq_braccio_driver ik_pose_demo --ros-args \
-p x:=0.30 -p y:=0.00 -p z:=0.06 -p gripper:=25The USB serial firmware and remote App Lab agent both use the same command protocol:
M <base> <shoulder> <elbow> <wrist_vertical> <wrist_rotation> <gripper>
Angles are integer degrees. The firmware clamps values to the conservative Braccio operating ranges before moving servos.
- Train an Edge Impulse classifier for gestures, voice intents, camera labels, or sensor states.
- Export the model as a Linux SDK, Python SDK, or Arduino library.
- Use
edge_impulse/label_to_pose.yamlto map classifier labels to named arm poses. - Publish poses to
/braccio/joint_commandassensor_msgs/JointState.
See edge_impulse/README.md for the integration pattern.
Standard Braccio servos do not provide measured feedback such as current, torque, temperature, or actual shaft position. This project captures commanded joint angles, deltas, estimated command rates, labels, and firmware/remote-agent status.
Start a hardware bridge, then start CSV capture:
source ros2_ws/install/setup.bash
ros2 launch unoq_braccio_bringup data_capture.launch.py \
output_file:=edge_impulse/captures/braccio_capture.csv \
label:=readyQuery firmware stats directly over serial:
S
Example response:
STAT uptime_ms=12345 move_count=3 last_move_ms=640 last_command_ms=12000 target=90,90,90,90,90,25
See edge_impulse/data_capture.md.
The easiest sight path is a USB camera on the ROS 2 host:
source ros2_ws/install/setup.bash
ros2 launch unoq_braccio_bringup vision_usb.launch.py camera_index:=0 label:=objectThis publishes camera frames, debug frames, object centroid stats, and simple labels that can feed the Edge Impulse mapper. See docs/vision.md.
If the USB camera is plugged into the UNO Q instead, run
app_lab/usb_camera_streamer on the UNO Q and use:
source ros2_ws/install/setup.bash
ros2 launch unoq_braccio_bringup vision_remote.launch.py \
stream_url:=http://<UNO_Q_IP_ADDRESS>:8080/stream \
label:=objectFor a gripper-mounted camera, watch /braccio/vision_stats and center the
object near x_norm=0 and y_norm=0. A cautious visual alignment helper is
available but disabled by default:
ros2 launch unoq_braccio_bringup vision_assist.launch.py enabled:=falseFor Edge Impulse object detection and item-specific pick/place:
source ros2_ws/install/setup.bash
ros2 launch unoq_braccio_bringup edge_impulse_pick_place.launch.py \
stream_url:=http://192.168.1.64:8080/stream \
runner_command:="python3 edge_impulse/runner_template.py --image {image}" \
workflow_file:=edge_impulse/pick_place_workflows.yamlFor Linux Edge Impulse .eim setup and API-key authentication, see
edge_impulse/linux_setup.md. Keep API keys in
EDGE_IMPULSE_API_KEY, not in committed files.
Public Edge Impulse project:
https://studio.edgeimpulse.com/studio/1029890
Project labels used for pick/place examples: Red Block, Blue Block, and
Yellow Block.
Run the UNO Q remote control and camera App Lab apps, then start the dashboard:
Recommended single App Lab app:
app_lab/braccio_web_agent
This exposes arm control on 8765 and camera streaming on 8080.
This is an App Lab project folder, not a PowerShell command. Open it or copy
its contents in Arduino App Lab and run it on the UNO Q.
The web agent uses direct Servo control for the Braccio shield pin mapping, so
it does not require Braccio.h to be installed in the App Lab environment.
cd web_app
python server.py --host 0.0.0.0 --port 5000 --unoq-host 192.168.1.64Open:
http://localhost:5000
The page shows the camera stream, robot status, preset poses, and manual joint sliders.
Gripper tuning: this build allows the gripper to close up to 110 degrees.
Use the dashboard grip_test preset first, then grip_full only if needed.
The reusable Braccio servo bridge lives in:
app_lab/bricks/unoq_braccio_bridge
Use it when creating new App Lab apps that need direct Braccio shield control
from the UNO Q. The current app_lab/braccio_web_agent app vendors the same
brick source into its sketch/ folder so it can be imported and run as a
normal App Lab app today.



