Files
ros-raspbot-v2/robot
m5p3nc3r 54ed5c6a82 Refactor the URDF into multiple parts.
Makes it easier to work on an individual part in isolation.
Also change the vscode devconsole visualiser to one that works with includes
2026-05-07 20:41:18 +00:00
..
2026-04-28 14:42:08 +00:00

Robot Controller

ROS 2 nodes for the Yahboom Raspbot V2 — differential-drive motor control, pan/tilt camera orientation, and ultrasonic range sensing.

All three nodes share the same I²C bus. The Linux kernel serialises individual transactions, so they run as separate processes without additional locking.


Motor controller

                      ┌───────────────────────────────────┐
                      │      MotorControllerNode          │
                      │                                   │
 /cmd_vel  ──────────>│  Twist → differential kinematics  │
 (geometry_msgs/Twist)│  left  = linear  (angular × wb/2)│
                      │  right = linear + (angular × wb/2)│
                      │                                   │
 /wheel_speeds ──────>│  Direct per-wheel override        │
 (Float32MultiArray   │  [FL, FR, RL, RR]                 │
  4 × float32)        │                                   │
                      │         ▼                         │
                      │   raspbot_v2_interface            │
                      │   I²C bus 1, addr 0x2B            │
                      │         ▼                         │
                      │   /dev/i2c-1 ─────────> Motors    │
                      │                                   │
 /current_wheel_speeds│<─ telemetry @ 10 Hz               │
 (Float32MultiArray)  │   [FL, FR, RL, RR]                │
                      └───────────────────────────────────┘

Topics

Topic Direction Type Description
/cmd_vel Subscribed geometry_msgs/Twist Velocity command — linear.x (m/s) and angular.z (rad/s)
/wheel_speeds Subscribed std_msgs/Float32MultiArray Direct per-wheel speed override [FL, FR, RL, RR] in library units (0255)
/current_wheel_speeds Published std_msgs/Float32MultiArray Current wheel speeds read from hardware, published at 10 Hz

Parameters

Parameter Default Description
wheel_base 0.3 Distance between left and right wheels in metres
max_speed 1.0 Maximum motor speed in library units

Sending velocity commands

# Drive forward at 0.2 m/s
ros2 topic pub --once /cmd_vel geometry_msgs/msg/Twist \
  "{linear: {x: 0.02}, angular: {z: 0.0}}"

# Turn on the spot
ros2 topic pub --once /cmd_vel geometry_msgs/msg/Twist \
  "{linear: {x: 0.0}, angular: {z: 0.5}}"

# Stop
ros2 topic pub --once /cmd_vel geometry_msgs/msg/Twist \
  "{linear: {x: 0.0}, angular: {z: 0.0}}"

Camera orientation controller

                         ┌──────────────────────────────────────┐
                         │      CameraOrientationNode           │
                         │                                      │
 /joint_command  ────────>│  JointState (names: pan, tilt)      │
 (sensor_msgs/           │  position in radians                 │
  JointState)            │                                      │
                         │  pan  → servo 1  (0°–180°)          │
                         │  tilt → servo 2  (0°–110°)          │
                         │                                      │
                         │         ▼                            │
                         │   raspbot_v2_interface               │
                         │   I²C bus 1, addr 0x2B               │
                         │         ▼                            │
                         │   /dev/i2c-1 ──────> Pan/tilt servos │
                         │                                      │
 /joint_states   <────────│  current angles @ 10 Hz             │
 (sensor_msgs/           │  position in radians                 │
  JointState)            │                                      │
                         └──────────────────────────────────────┘

Topics

Topic Direction Type Description
/joint_command Subscribed sensor_msgs/JointState Commanded pan/tilt angles. Joint names "pan" and "tilt", positions in radians. Unknown joint names are ignored.
/joint_states Published sensor_msgs/JointState Current angles reflected from the last command, published at 10 Hz

Parameters

Parameter Default Description
pan_servo_id 1 Raspbot servo channel for pan
tilt_servo_id 2 Raspbot servo channel for tilt
pan_min_deg 0.0 Pan lower limit (degrees)
pan_max_deg 180.0 Pan upper limit (degrees)
tilt_min_deg 0.0 Tilt lower limit (degrees)
tilt_max_deg 110.0 Tilt upper limit (degrees) — hardware cap
pan_center_deg 90.0 Startup and shutdown park position for pan
tilt_center_deg 60.0 Startup and shutdown park position for tilt
state_rate_hz 10.0 ~/joint_states publish rate

Hardware interface

The node drives the pan and tilt servos over I²C bus 1 (device address 0x2B). The same /dev/i2c-1 device used by the motor controller is sufficient — no additional device node is required.

Commanding the camera

Pan to centre (90°) and tilt to 30°:

ros2 topic pub --once /joint_command sensor_msgs/msg/JointState \
  "{name: ['pan', 'tilt'], position: [1.5708, 0.5236]}"

A single axis can be commanded by omitting the other joint name:

# Pan only
ros2 topic pub --once /joint_command sensor_msgs/msg/JointState \
  "{name: ['pan'], position: [0.0]}"

Ultrasonic range sensor

                        ┌──────────────────────────────────────┐
                        │          UltrasonicNode              │
                        │                                      │
                        │  Sensor off when no subscribers      │
                        │  Sensor on  when subscribers > 0     │
                        │  1 s warm-up after power-on          │
                        │                                      │
                        │         ▼                            │
                        │   raspbot_v2_interface               │
                        │   I²C bus 1, addr 0x2B               │
                        │         ▼                            │
                        │   /dev/i2c-1 ──────> HC-SR04 sensor  │
                        │                                      │
 /ultrasonic/range <────│  Range @ configurable rate           │
 (sensor_msgs/Range)    │  radiation_type = ULTRASOUND         │
                        │  range in metres (REP-117)           │
                        └──────────────────────────────────────┘

Topics

Topic Direction Type Description
/ultrasonic/range Published sensor_msgs/Range Distance in metres. +inf when beyond max range, -inf when closer than min range (REP-117). Only published while subscribers are connected.

Parameters

Parameter Default Description
publish_rate_hz 10.0 Sensor poll and publish rate
frame_id 'ultrasonic' header.frame_id on published messages
min_range_m 0.02 Minimum valid range in metres
max_range_m 4.0 Maximum valid range in metres
field_of_view 0.2618 Sensor cone width in radians (~15°)
warmup_s 1.0 Seconds to wait after powering the sensor on before publishing

Verifying range readings

ros2 topic echo /ultrasonic/range

The sensor activates automatically when a subscriber connects and deactivates when it disconnects.


Launch arguments

Launch arguments can be appended when running the container manually:

docker run --rm \
  --network=host \
  --device /dev/i2c-1 \
  --env ROS_DOMAIN_ID=0 \
  raspbot_v2:latest \
  ros2 launch raspbot_v2 robot.launch.py \
    wheel_base:=0.25 max_speed:=0.8 tilt_center_deg:=45.0
Argument Default Description
wheel_base 0.3 Distance between left and right wheels (m)
max_speed 1.0 Maximum motor speed in library units
pan_center_deg 90.0 Pan angle at startup and shutdown (degrees)
tilt_center_deg 60.0 Tilt angle at startup and shutdown (degrees)
ultrasonic_rate_hz 10.0 Ultrasonic sensor publish rate (Hz)

Project layout

robot/
├── Dockerfile                    # Two-stage build: colcon compile → clean runtime image
├── src/
│   └── raspbot_v2/
│       ├── package.xml
│       ├── setup.py
│       ├── launch/
│       │   └── robot.launch.py   # Starts all three nodes together
│       └── raspbot_v2/
│           ├── motor_controller_node.py
│           ├── camera_orientation_node.py
│           └── ultrasonic_node.py
└── raspbot_v2_interface/         # Vendored Yahboom hardware library
    └── Raspbot_Lib/
        └── Raspbot_Lib.py        # I²C driver (smbus, bus 1, addr 0x2B)