| name | scout-robot |
| description | Use when controlling, connecting to, or programming a Moorebot Scout robot - covers ROS topics, SSH access, message types, and common pitfalls like md5 mismatches on roller_eye messages |
Moorebot Scout Robot Control
Overview
Moorebot Scout is an ARM64 Linux robot (Debian 9 stretch, kernel 4.4) running ROS 1. Control it by publishing/subscribing to ROS topics via the master (XMLRPC on port 11311). No rosbridge needed.
Configuration: ~/.scout/config.json
Store connection details in ~/.scout/config.json so they persist across sessions. Read this file first before connecting. If it doesn't exist, discover the robot and create it.
{
"local": {
"scout_ip": "<LAN_IP>",
"ssh_user": "root",
"ssh_password": "<PASSWORD>",
"ros_port": 11311
},
"tailscale": {
"scout_ip": "<TAILSCALE_IP>",
"scout_hostname": "<TAILSCALE_HOSTNAME>",
"my_ip": "<YOUR_TAILSCALE_IP>",
"ros_port": 11311
}
}
Why two sections: Local LAN connection auto-detects the host IP for goroslib. Tailscale requires explicitly passing your own Tailscale IP (my_ip) via -local so the robot can reach your ROS subscriber.
Workflow:
- Read
~/.scout/config.json
- Try
local.scout_ip first (faster, lower latency)
- If local unreachable, fall back to
tailscale.scout_ip with tailscale.my_ip as -local
- If config doesn't exist, scan/ask for IP, then write the file
Connection commands:
./scout-ctrl -host <local.scout_ip>:<ros_port> -cmd battery
./scout-ctrl -host <tailscale.scout_ip>:<ros_port> -local <tailscale.my_ip> -cmd battery
ssh <ssh_user>@<scout_ip>
Local Connection
The Scout connects to your LAN via WiFi. Find its IP from your router or the Moorebot app.
Verify connectivity:
ping <SCOUT_IP>
nc -z -w 3 <SCOUT_IP> 11311 && echo "ROS master: OK"
nc -z -w 3 <SCOUT_IP> 22 && echo "SSH: OK"
Probe ROS master (no dependencies needed):
import xmlrpc.client
proxy = xmlrpc.client.ServerProxy('http://<SCOUT_IP>:11311/')
code, msg, state = proxy.getSystemState('/probe')
publishers, subscribers, services = state
SSH Access
Enable ROOT: In Moorebot app, tap firmware version 5 times, ROOT option appears, enter 123456. The default password plt from go-scout docs may not work on newer firmware.
Tailscale (Remote Access)
Tailscale is installed on the Scout for access outside the LAN. Kernel 4.4 has no TUN module, so it runs with --tun=userspace-networking. Init script at /etc/init.d/tailscaled. When using goroslib over Tailscale, pass -local <tailscale.my_ip> so the robot can reach your ROS subscriber.
Movement Control
Publish geometry_msgs/Twist to /cmd_vel:
| Twist Field | Effect | Range |
|---|
linear.y | Forward (+) / Backward (-) | 0.05 - 1.0 |
linear.x | Strafe right (+) / left (-) | ~0.2 |
angular.z | Rotate left (+) / right (-) | ~1.5 - 3.0 |
Always send a zero Twist to stop after movement.
Key ROS Topics
| Topic | Type | Direction | Purpose |
|---|
/cmd_vel | geometry_msgs/Twist | Publish | Movement (see above) |
/CoreNode/jpg | roller_eye/frame | Subscribe | Camera JPEG frames |
/CoreNode/h264 | roller_eye/frame | Subscribe | H264 video stream |
/CoreNode/aac | roller_eye/frame | Subscribe | Audio stream |
/SensorNode/simple_battery_status | roller_eye/status | Subscribe | status[0] = charging, [1] = percent |
/SensorNode/imu | sensor_msgs/Imu | Subscribe | IMU orientation/acceleration |
/SensorNode/tof | sensor_msgs/Range | Subscribe | Time-of-flight distance (meters) |
/SensorNode/light | sensor_msgs/Illuminance | Subscribe | Light sensor |
/CoreNode/obj | roller_eye/detect | Subscribe | Object detection results |
Battery status[0] values: BATTERY_CHARGING=0, BATTERY_UNCHARGE=1, BATTERY_FULL=2, BATTERY_UNKNOWN=3
Key ROS Services
| Service | Purpose |
|---|
/nav_low_bat | Send robot to charging station (empty request/response) |
/CoreNode/adjust_light | Adjust light (int32 cmd: 0=decrease, 1=increase) |
/NavPathNode/nav_patrol | Start patrol |
/NavPathNode/nav_patrol_stop | Stop patrol |
/NavPathNode/nav_cancel | Cancel navigation |
/NavPathNode/nav_list_path | List saved paths |
Message Type Gotcha: roller_eye md5
ROS computes message md5 including constants. If using goroslib, you MUST include all constants in msg.Definitions or the subscriber will be rejected.
roller_eye/status (md5: 6919a5b3f28e39f677c7c81d4e5a2ef4)
Field: int32[] status. Has 29 int8 constants that must be declared:
type Status struct {
msg.Package `ros:"roller_eye"`
msg.Definitions `ros:"int8 PROCESS_OK=0,int8 PROCESS_ERROR=-1,int8 OBJ_DETECT_CHARGE=1,int8 RECORD_START=1,int8 RECORD_STOP=2,int8 RECORD_ERROR=3,int8 P2P_AV_PLAYING=1,int8 P2P_AV_STOP=2,int8 P2P_AV_ERROR=3,int8 WIFI_MODE_AP=0,int8 WIFI_MODE_STA=1,int8 WIFI_STATUS_DISCONNECT=0,int8 WIFI_STATUS_CONNECTED=1,int8 WIFI_STATUS_CONNECTING=2,int8 WIFI_STATUS_WRONG_KEY=3,int8 WIFI_STATUS_CONN_FAIL=4,int8 WIFI_STATUS_STOP=5,int8 BACK_UP_DETECT=1,int8 BACK_UP_ALIGN=2,int8 BACK_UP_BACK=3,int8 BACK_UP_SUCCESS=4,int8 BACK_UP_FAIL=5,int8 BACK_UP_INACTIVE=6,int8 BACK_UP_CANCEL=7,int8 BACK_UP_REDETECT=8,int8 BATTERY_CHARGING=0,int8 BATTERY_UNCHARGE=1,int8 BATTERY_FULL=2,int8 BATTERY_UNKOWN=3"`
Status []int32
}
roller_eye/frame
type Frame struct {
msg.Package `ros:"roller_eye"`
msg.Definitions `ros:"int8 VIDEO_STREAM_H264=0,int8 VIDEO_STREAM_JPG=1,int8 AUDIO_STREAM_AAC=2"`
Seq uint32
Stamp uint64
Session uint32
Type int8
Oseq uint32
Par1 int32
Par2 int32
Par3 int32
Par4 int32
Data []uint8
}
To find other message definitions: check ~/workspace/Scout-open-source/roller_eye/msg/*.msg and roller_eye/srv/*.srv.
scout-ctrl CLI
Download pre-built binaries from GitHub Releases (linux/amd64, linux/arm64, linux/armv7, darwin/amd64, darwin/arm64, windows/amd64). Or build from source: cd go-ctrl && go build -o scout-ctrl .
./scout-ctrl -host <SCOUT_IP>:11311 -cmd status
./scout-ctrl -host <SCOUT_IP>:11311 -cmd battery
./scout-ctrl -host <SCOUT_IP>:11311 -cmd camera
./scout-ctrl -host <SCOUT_IP>:11311 -cmd forward -speed 0.2 -time 1
./scout-ctrl -host <SCOUT_IP>:11311 -cmd backward -speed 0.2 -time 1
./scout-ctrl -host <SCOUT_IP>:11311 -cmd left -time 0.5
./scout-ctrl -host <SCOUT_IP>:11311 -cmd right -time 0.5
./scout-ctrl -host <SCOUT_IP>:11311 -cmd spin
./scout-ctrl -host <SCOUT_IP>:11311 -cmd stop
./scout-ctrl -host <SCOUT_IP>:11311 -cmd home
If ~/.scout/config.json exists, the default host is read from local.scout_ip. Otherwise pass -host explicitly.
Common Mistakes
| Mistake | Fix |
|---|
roller_eye/status md5 mismatch | Include ALL int8 constants in msg.Definitions |
| SSH password rejected | Enable ROOT via app first (tap firmware 5x, enter 123456) |
apt-get 404 errors on Scout | Debian stretch is EOL; download static binaries instead |
| Robot doesn't move | Forward/back is linear.y, not linear.x |
goroslib unable to set Host remotely | Pass -local <YOUR_IP> (required for Tailscale, not for LAN) |
tailscaled: no such device | Use --tun=userspace-networking (kernel has no TUN) |
Related Projects
- Scout-open-source โ Moorebot official (C++/ROS, msg defs in
roller_eye/msg/)
- go-scout โ Third-party Go controller with gamepad + video display