Add container that can be run on a remote machine to move the robot

This commit is contained in:
2026-05-28 22:21:44 +00:00
parent c6d02bbe43
commit 949c763968
7 changed files with 248 additions and 1 deletions
+31
View File
@@ -88,6 +88,37 @@ services:
- POLL_INTERVAL=${POLL_INTERVAL:-15} - POLL_INTERVAL=${POLL_INTERVAL:-15}
restart: unless-stopped restart: unless-stopped
teleop:
build:
context: teleop
dockerfile: Dockerfile
platforms:
- linux/amd64
- linux/arm64
image: raspbot_v2_teleop:latest
network_mode: host
ipc: host
stdin_open: true
tty: true
devices:
- ${JOYSTICK_DEV:-/dev/input/js0}:/dev/input/js0
- ${JOYSTICK2_DEV:-/dev/input/js1}:/dev/input/js1
- ${JOYSTICK_EVENT_DEV:-/dev/input/event0}:/dev/input/event0
environment:
- ROS_DOMAIN_ID=${ROS_DOMAIN_ID:-0}
# Joystick config name — must match a file in teleop_twist_joy/config/ (without .config.yaml).
# e.g. ps5, ps4, xbox, atk3. Leave unset to use teleop_twist_joy defaults.
- JOY_CONFIG=${JOY_CONFIG:-}
# FastDDS profile restricts DDS traffic to a single interface.
# Override FASTDDS_INTERFACE in .env or on the command line (e.g. eth0, wlan1).
- FASTDDS_DEFAULT_PROFILES_FILE=/fastdds_wifi.xml
- FASTDDS_INTERFACE=${FASTDDS_INTERFACE:-wlan0}
# Not started by default — run explicitly:
# keyboard: docker compose run --rm teleop teleop-keyboard
# joystick: docker compose run --rm teleop teleop-joystick
profiles:
- teleop
webui: webui:
build: build:
context: webui context: webui
+21
View File
@@ -0,0 +1,21 @@
ARG ROS_DISTRO=kilted
FROM ros:${ROS_DISTRO}-ros-base
ARG ROS_DISTRO
RUN apt-get update && apt-get install -y --no-install-recommends \
ros-${ROS_DISTRO}-teleop-twist-keyboard \
ros-${ROS_DISTRO}-teleop-twist-joy \
&& rm -rf /var/lib/apt/lists/*
COPY src/fastdds_wifi.xml /fastdds_wifi.xml
COPY src/teleop-keyboard /usr/local/bin/teleop-keyboard
COPY src/teleop-joystick /usr/local/bin/teleop-joystick
RUN chmod +x /usr/local/bin/teleop-keyboard /usr/local/bin/teleop-joystick
RUN echo '#!/bin/bash' > /entrypoint.sh \
&& echo 'set -e' >> /entrypoint.sh \
&& echo "source /opt/ros/${ROS_DISTRO}/setup.bash" >> /entrypoint.sh \
&& echo 'exec "$@"' >> /entrypoint.sh \
&& chmod +x /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]
+158
View File
@@ -0,0 +1,158 @@
# Teleop
Container for manually driving the robot via keyboard or joystick.
Both modes publish `TwistStamped` on `/cmd_vel` as required by the mecanum drive controller.
The container is not started by the default `docker compose up` — it must be launched explicitly with `docker compose run`.
## Prerequisites
Build the image once before first use:
```bash
docker compose build teleop
```
## Keyboard
```bash
docker compose run --rm teleop teleop-keyboard
```
Use the standard `teleop_twist_keyboard` bindings (`i`, `,`, `j`, `l`, etc.) to drive.
Press `k` to stop.
## Joystick
```bash
docker compose run --rm teleop teleop-joystick
```
The script starts `joy_node` and `teleop_twist_joy_node` together; both are stopped
when you press Ctrl+C.
The controller layout defaults to `ps5`. Override `JOY_CONFIG` with any config name
from the `teleop_twist_joy` package (e.g. `ps4`, `xbox`, `atk3`):
```bash
JOY_CONFIG=xbox docker compose run --rm teleop teleop-joystick
```
To list all available configs:
```bash
docker compose run --rm teleop bash -c \
"ls \$(ros2 pkg prefix teleop_twist_joy)/share/teleop_twist_joy/config/"
```
The container expects the joystick at `/dev/input/js0`.
If your device is at a different path, set `JOYSTICK_DEV` before running:
```bash
JOYSTICK_DEV=/dev/input/js1 docker compose run --rm teleop teleop-joystick
```
## Running from a remote machine
The container includes a FastDDS profile that restricts DDS discovery and traffic
to a single network interface, preventing traffic leaking onto unintended interfaces
(e.g. a wired link alongside the robot's Wi-Fi).
The interface defaults to `wlan0`. Override it with the `FASTDDS_INTERFACE` variable:
```bash
FASTDDS_INTERFACE=wlan1 docker compose run --rm teleop teleop-keyboard
```
Or set it permanently in your `.env` file:
```
FASTDDS_INTERFACE=wlan0
```
The robot containers do not currently apply this FastDDS profile, so both ends must
be reachable on the configured interface for DDS discovery to succeed. Ensure the
`ROS_DOMAIN_ID` matches on all machines.
## Useful commands
### Working out the network interface to bind to
TODO: show how to find the correct network interface to bind FastDDS to...
TODO: Create a script to do this?
Look at the output of `ip addr`, and see which interface is on the sane network as the robot.
For example, this output
```bash
___snip___
3: wlp195s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether 84:9e:56:9c:a1:b5 brd ff:ff:ff:ff:ff:ff
inet 192.168.1.207/24 brd 192.168.1.255 scope global dynamic noprefixroute wlp195s0
valid_lft 79183sec preferred_lft 79183sec
inet6 fe80::32f4:b953:b7ca:e415/64 scope link noprefixroute
valid_lft forever preferred_lft forever
___snip___
```
shows that interface wlp195s0 is on the same network as my robot at ip address 192.168.1.166/24.
You should therefore set `FASTDDS_INTERFACE=wlp195s0`.
### Working out the correct devices to pass through
I am assuming you have a PS DualSense controller, because this is what I used to test.
```bash
cat /proc/bus/input/devices | grep -A 8 "DualSense"
N: Name="Sony Interactive Entertainment DualSense Wireless Controller"
P: Phys=
S: Sysfs=/devices/pci0000:00/0000:00:08.3/0000:c7:00.0/usb3/3-1/3-1:1.3/0003:054C:0CE6.0001/input/input3
U: Uniq=a0:ab:51:b5:7e:fb
H: Handlers=event3 js0
B: PROP=0
B: EV=20000b
B: KEY=7fdb000000000000 0 0 0 0
B: ABS=3003f
--
N: Name="Sony Interactive Entertainment DualSense Wireless Controller Motion Sensors"
P: Phys=
S: Sysfs=/devices/pci0000:00/0000:00:08.3/0000:c7:00.0/usb3/3-1/3-1:1.3/0003:054C:0CE6.0001/input/input5
U: Uniq=a0:ab:51:b5:7e:fb
H: Handlers=event5 js1
B: PROP=40
B: EV=19
B: ABS=3f
B: MSC=20
--
N: Name="Sony Interactive Entertainment DualSense Wireless Controller Touchpad"
P: Phys=
S: Sysfs=/devices/pci0000:00/0000:00:08.3/0000:c7:00.0/usb3/3-1/3-1:1.3/0003:054C:0CE6.0001/input/input6
U: Uniq=a0:ab:51:b5:7e:fb
H: Handlers=mouse0 event6
B: PROP=5
B: EV=b
B: KEY=2420 10000 0 0 0 0
B: ABS=260800000000003
```
This shows me that the primary control input is on `js0` and `event3`. So I would need to launch the container like this:
```bash
# Note that /dev/input/js0 is already mapped by default
JOYSTICK_EVENT_DEV=/dev/input/event3 docker compose run --rm teleop teleop-joystick
```
### Test the joystick is bound in the container
Install the jstest package
```bash
# In the container
apt update
apt-get install joystick
# Inside the container, the joystick is always mapped to js0
jstest /dev/input/js0
```
+20
View File
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<profiles xmlns="http://www.eprosima.com/XMLSchemas/fastRTPS_Profiles">
<transport_descriptors>
<transport_descriptor>
<transport_id>udp_wifi</transport_id>
<type>UDPv4</type>
<interfaceWhiteList>
<address>${FASTDDS_INTERFACE}</address>
</interfaceWhiteList>
</transport_descriptor>
</transport_descriptors>
<participant profile_name="default_profile" is_default_profile="true">
<rtps>
<userTransports>
<transport_id>udp_wifi</transport_id>
</userTransports>
<useBuiltinTransports>false</useBuiltinTransports>
</rtps>
</participant>
</profiles>
+13
View File
@@ -0,0 +1,13 @@
#!/bin/bash
ros2 run joy joy_node &
JOY_PID=$!
trap "kill $JOY_PID 2>/dev/null" EXIT
PARAMS=(-p publish_stamped_twist:=true)
if [[ -n "${JOY_CONFIG}" ]]; then
CONFIG_PATH="$(ros2 pkg prefix teleop_twist_joy)/share/teleop_twist_joy/config/${JOY_CONFIG}.config.yaml"
PARAMS=(--params-file "$CONFIG_PATH" "${PARAMS[@]}")
fi
exec ros2 run teleop_twist_joy teleop_node \
--ros-args "${PARAMS[@]}" "$@"
+3
View File
@@ -0,0 +1,3 @@
#!/bin/bash
exec ros2 run teleop_twist_keyboard teleop_twist_keyboard \
--ros-args -p stamped:=true "$@"
+2 -1
View File
@@ -20,4 +20,5 @@ If you are getting a message like:
This could be because there is no `/tf` topic available. This topic is used to describe the relationship between coordinate systems (robot position, lidar position, camera position etc). This could be because there is no `/tf` topic available. This topic is used to describe the relationship between coordinate systems (robot position, lidar position, camera position etc).
If this is the case, you can changet the fixed frame reference to the lidar itself. Change the `Global Options -> Fixed Frame` option from `map` to `laser` to tell the system to use the laser as the base of all transforms. If this is the case, you can changet the fixed frame reference to the lidar itself. Change the `Global Options -> Fixed Frame` option from `map` to `laser` to tell the system to use the laser as the base of all transforms.