ef78f19e72
Plus a little freshen up of the readme's
9.6 KiB
9.6 KiB
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 (0–255) |
/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)