Together with my teammates, I built an autonomous beer pong system using a Kinova Gen3 Lite arm, a 2F gripper, and an Intel RealSense camera. The robot picks up a ping-pong ball from a fixed location, aligns itself to a red cup on a distant table using vision, and executes a fast throwing motion planned in ROS 2 and MoveIt 2.
At a high level, KinoVaPong performs a full beer-pong throw autonomously. Starting from a home pose, the robot picks a ball from a designated pickup spot, lifts to a safe height, rotates toward the detected cup, and then swings its arm to launch the ball toward a fixed cup on a separate table.
I implemented the main ROS 2 control node,
BeerPongThrow, which orchestrates this sequence and calls
MoveIt 2 for motion planning on the Kinova Gen3 Lite. A separate
vision node, RedCupAnglePublisher, computes the yaw angle of
the red cup from the RealSense camera and passes it along so the base
joint can be aligned before the throw.
• Kinova Gen3 Lite 6-DOF arm
• Kinova 2F gripper for grasping the ball
• Intel RealSense RGB-D camera monitoring both tables
• Fixed tables with clearly marked pickup and cup positions
• ROS 2 nodes BeerPongThrow and
RedCupAnglePublisher
• MoveIt 2 motion planning with Kinova
• Planning scene with an attachable “ball” collision object
• Multi-threaded ROS 2 executor for concurrent sensing and control
• Pick-lift-throw sequence in throw_ball_forward()
• Angle-based release monitor on joint_3 at 100 Hz
• Release signal when the joint crosses a target angle window
• Logging and simple error handling for camera and collision calls
camera_start_angle_deg.
BeerPongThrow goes to a home pose, performs a pre-grasp,
grasps the ball, and lifts to a safe height.
joint_1) is set to the camera-derived
yaw; joint_3 is moved to a large backswing and then
swung forward while a timer monitors for the release angle.
The core throwing routine lives in throw_ball_forward().
After the ball is attached as a collision object, the arm transitions
into a swing: joint_3 is driven from roughly −90° (backswing)
to +90° (follow-through) while other joints maintain a configuration
that points the gripper toward the cup.
Instead of stopping at a specific pose, the release uses a 100 Hz
timer that continuously reads joint_3. When the joint
enters a small window around a chosen release angle (for example −45°),
a RELEASE signal is published so the ball is let go in the
middle of a smooth, high-speed swing.
The node runs under a multi-threaded ROS 2 executor, and the swing itself is launched in a separate thread. This lets joint state updates, camera callbacks, and timers run without blocking the main throw sequence. Early versions of the project struggled with inconsistent release timing until we reorganized callbacks and timers to avoid blocking behavior.
In future iterations, I’d like to add a more detailed ballistics model and predict the landing point before executing, as well as automatically optimizing release angles and joint speed profiles for arbitrary cup positions.
To evaluate KinoVaPong, we ran repeated trials with the cup at a fixed distance and systematically adjusted backswing, swing range, and release angle. The RealSense-derived yaw estimate allowed the arm to stay aligned with the cup across trials, so most of the tuning effort focused on vertical angle and joint velocity.
| Experiment | Cup Distance | Shots | Hit Rate |
|---|---|---|---|
| Measurements | 35cm | 1 & 6 | 100% |
The table is a placeholder so the page doesn’t get cluttered. We can update it with the actual trial counts and hit rates from our final experiments.