c-programming-workbook-for-robotic-development-a-step-by-step-guide-to-building-and-programming-your-robot-with-opencv-and-ros
c-programming-workbook-for-robotic-development-a-step-by-step-guide-to-building-and-programming-your-robot-with-opencv-and-ros
Development
A Step-by-Step Guide to Building and Programming your Robot with
OpenCV and ROS
Alex J. Collins
All rights reserved
Reservation of rights. Except for brief quotations used in critical reviews and
certain other noncommercial uses allowed by copyright law, no part of this
publication may be duplicated, distributed, or transmitted in any way without the
publisher's prior written consent. This prohibition includes photocopying,
recording, and other electronic or mechanical methods.
Types of Robots:
Applications of Robotics:
Robotics has a wide range of applications across various industries and fields.
Some of the most common include:
History of robotics
The concept of creating artificial beings capable of performing tasks has
fascinated humans for centuries. While the term "robot" wasn't coined until the
20th century, the history of robotics can be traced back to ancient civilizations.
Ancient Civilizations
Industrial Revolution
● Textile machinery: The Industrial Revolution saw the development
of automated machines, such as Jacquard looms, to perform
repetitive tasks in textile manufacturing. These machines, while not
specifically designed to resemble human beings, marked a significant
step towards the automation of industrial processes.
● Early industrial robots: In the early 20th century, the first industrial
robots were developed, such as the Unimate, which was used to
handle hot metal parts in automobile factories. These robots, while
relatively simple compared to modern machines, represented a
crucial breakthrough in the application of robotics to industrial tasks.
The 20th Century and Beyond
● The term "robot": The term "robot" was popularized by Czech
playwright Karel Čapek in his 1920 play "R.U.R." This play
introduced the concept of artificial beings that could perform human
labor and sparked widespread discussion about the potential benefits
and risks of robotics.
● Advancements in electronics and computing: The development of
transistors, integrated circuits, and computers in the mid-20th century
led to significant advancements in robotics. These technological
breakthroughs enabled robots to become more intelligent, versatile,
and capable of performing a wider range of tasks.
● Artificial intelligence: The emergence of artificial intelligence (AI)
in the 1950s and 1960s provided robots with the ability to learn,
reason, and problem-solve. AI has played a crucial role in enabling
robots to adapt to new situations, interact with their environments,
and make decisions autonomously.
● Modern robotics: Today, robots are used in a wide range of
applications, from manufacturing to healthcare to space exploration.
Advancements in robotics continue to push the boundaries of what is
possible, with the development of more sophisticated and capable
machines. For example, robots are now being used to perform
complex surgical procedures, explore distant planets, and even
provide companionship to humans. As technology continues to
evolve, it is likely that robots will play an even more significant role
in our lives in the years to come.
Applications of robotics
Robotics has a wide range of applications across various industries and fields.
Some of the most common include:
Manufacturing
Transportation
● Autonomous vehicles: Robots are being developed to drive cars,
trucks, and other vehicles without human intervention. Autonomous
vehicles have the potential to improve safety, reduce traffic
congestion, and reduce emissions.
● Delivery services: Robots can deliver packages and groceries to
customers, reducing the need for human drivers. For example, robots
can be used to deliver packages to homes and businesses in urban
areas, reducing traffic congestion and emissions.
Entertainment
● Toys and games: Robots can provide entertainment for children and
adults. For example, robotic toys can interact with children,
providing educational and engaging experiences.
● Theme parks: Robots can be used to create interactive and
immersive experiences for visitors. For example, robotic characters
can interact with guests, providing entertainment and education.
● Virtual reality: Robots can be used to create realistic virtual
environments for users to explore. For example, robotic arms can be
used to create haptic feedback, allowing users to feel and interact
with virtual objects.
Other Applications
● Military: Robots can be used for reconnaissance, bomb disposal, and
combat. For example, robots can be used to explore dangerous areas,
disarm bombs, and provide support to troops on the battlefield.
● Law enforcement: Robots can be used for surveillance, crowd
control, and hazardous materials handling. For example, robots can
be used to monitor crowds, detect dangerous materials, and assist in
rescue operations.
● Customer service: Robots can provide customer service through
chatbots and virtual assistants. For example, chatbots can be used to
answer customer questions and provide support.
Declaring Variables:
To declare a variable, you specify its type followed by its name. For example:
C++
int age; // Declares an integer variable named age
float price; // Declares a floating-point variable named price
char grade; // Declares a character variable named grade
Assigning Values:
You can assign values to variables using the assignment operator (=). For
example:
C++
age = 25;
price = 9.99;
grade = 'A';
C++ supports various data types to represent different kinds of data. Some
common data types include:
● Integer types:
○ int: Represents whole numbers (e.g., 10, -5, 0)
○ short: Represents smaller integers (e.g., -32768 to 32767)
○ long: Represents larger integers (e.g., -2,147,483,648 to
2,147,483,647)
○ unsigned int: Represents non-negative whole numbers (e.g., 0 to
4,294,967,295)
○ unsigned short: Represents smaller non-negative whole numbers
(e.g., 0 to 65,535)
○ unsigned long: Represents larger non-negative whole numbers
(e.g., 0 to 18,446,744,073,709,551,615)
● Floating-point types:
○ float: Represents real numbers with a decimal point (e.g., 3.14,
-0.5)
○ double: Represents larger real numbers with more precision
(e.g., 123456789.123456)
○ long double: Represents even larger real numbers with even
more precision (e.g., 1.7976931348623157e+308)
● Character type:
○ char: Represents a single character (e.g., 'A', 'b', ' ')
● Boolean type:
○ bool: Represents true or false values (e.g., true, false)
Operators
Operators are symbols used to perform operations on variables and values. C++
supports various operators, including:
● Arithmetic operators:
○ +: Addition
○ -: Subtraction
○ *: Multiplication
○ /: Division
○ %: Modulus (remainder after division)
○ ++: Increment (add 1)
○ --: Decrement (subtract 1)
● Comparison operators:
○ ==: Equal to
○ !=: Not equal to
○ <: Less than
○ >: Greater than
○ <=: Less than or equal to
○ >=: Greater than or equal to
●
● Logical operators:
○ &&: Logical AND
○ ||: Logical OR
○ !: Logical NOT
●
● Assignment operators:
○ =: Assignment
○ +=: Add and assign
○ -=: Subtract and assign
○ *=: Multiply and assign
○ /=: Divide and assign
○ %=: Modulus and assign
○ <<=: Left shift and assign
○ >>=: Right shift and assign
○ &=: Bitwise AND and assign
○ |=: Bitwise OR and assign
○ ^=: Bitwise XOR and assign
Example:
C++
int x = 10;
int y = 5;
x++; // Increment x by 1
y--; // Decrement y by 1
if (x > y) {
// x is greater than y
} else if (x == y) {
// x is equal to y
} else {
// x is less than y
}
Additional notes:
If-Else Statements
Basic structure:
C++
if (condition) {
// Code to execute if the condition is true
} else {
// Code to execute if the condition is false
}
Example:
C++
int age = 18;
Loops
For loops:
For loops are used when you know in advance how many times you want to
repeat a block of code.
Basic structure:
C++
for (initialization; condition; update) {
// Code to be executed
}
Example:
C++
for (int i = 0; i < 5; i++) {
std::cout << "Iteration " << i << std::endl;
}
While loops:
While loops are used when you want to repeat a block of code until a certain
condition becomes false.
Basic structure:
C++
while (condition) {
// Code to be executed
}
Example:
C++
int count = 0;
Do-while loops:
Do-while loops are similar to while loops, but they guarantee that the code block
will be executed at least once.
Basic structure:
C++
do {
// Code to be executed
} while (condition);
Example:
C++
int count = 0;
do {
std::cout << "Count: " << count << std::endl;
count++;
} while (count < 5);
Nested loops:
You can nest loops within other loops to create more complex control flow
structures.
Example:
C++
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 2; j++) {
std::cout << "i = " << i << ", j = " << j << std::endl;
}
}
The break statement can be used to exit a loop prematurely. The continue statement
can be used to skip the current iteration of a loop and proceed to the next
iteration.
Defining Functions
To define a function in C++, you specify its return type, name, and parameters
(if any).
Basic structure:
C++
return_type function_name(parameter_type parameter1, parameter_type parameter2, ...) {
// Function body
return value;
}
Example:
C++
int add(int a, int b) {
int sum = a + b;
return sum;
}
Calling Functions
To call a function, you provide the function name and any necessary arguments.
Example:
C++
int result = add(5, 3);
std::cout << "The sum is: " << result << std::endl;
Function overloading allows you to define multiple functions with the same
name but different parameters. The compiler determines which function to call
based on the arguments provided.
Example:
C++
int add(int a, int b) {
return a + b;
}
Modular Programming
Classes
A class is a blueprint for creating objects. It defines the properties and methods
that objects of that class will have.
Basic structure:
C++
class ClassName {
public:
// Public methods
private:
// Private attributes
};
Objects
An object is an instance of a class. It has its own values for the properties
defined by the class.
Creating objects:
C++
ClassName objectName;
Encapsulation
Benefits of encapsulation:
Inheritance
Inheritance is the ability of one class (derived class) to inherit properties and
methods from another class (base class). This promotes code reuse and creates
hierarchical relationships between classes.
Example:
C++
class Animal {
public:
void makeSound() {
std::cout << "Generic animal sound\n";
}
};
Example:
C++
void makeAnimalSound(Animal& animal) {
animal.makeSound();
}
int main() {
Dog dog;
Cat cat;
Types of polymorphism:
Access Modifiers
Access modifiers control the visibility of class members.
Example:
C++
class MyClass {
public:
MyClass() {
std::cout << "Constructor called\n";
}
~MyClass() {
std::cout << "Destructor called\n";
}
};
To capture images from a camera using OpenCV, you typically follow these
steps:
Example:
C++
#include <opencv2/opencv.hpp>
int main() {
VideoCapture cap(0); // Open the default camera
if (!cap.isOpened()) {
std::cerr << "Error opening video capture" << std::endl;
return -1;
}
// Set camera resolution to 640x480
cap.set(CAP_PROP_FRAME_WIDTH, 640);
cap.set(CAP_PROP_FRAME_HEIGHT, 480);
while (true) {
Mat frame;
cap >> frame; // Capture a frame from the camera
if (frame.empty()) {
break;
}
imshow("Frame", frame);
cap.release();
destroyAllWindows();
return 0;
}
Displaying Images
● You can adjust the camera resolution and frame rate using the set()
method of the VideoCapture class. For example, to set the resolution to
640x480, you can use cap.set(CAP_PROP_FRAME_WIDTH, 640); and
cap.set(CAP_PROP_FRAME_HEIGHT, 480);.
● You can save captured images to disk using the imwrite() function. For
example, imwrite("captured_image.jpg", frame); will save the current frame to
a JPEG file named "captured_image.jpg".
● OpenCV supports various image formats, including JPEG, PNG,
BMP, and TIFF. You can specify the desired image format when
saving the image.
● You can also display multiple images simultaneously by creating
multiple windows and showing each image in its respective window.
By understanding these concepts and following the provided code examples, you
can effectively capture and display images using OpenCV and build more
advanced computer vision applications.
Filtering
● Averaging filter: Replaces each pixel with the average value of its
neighbors. This can be used to reduce noise and smooth the image.
● Gaussian filter: Applies a Gaussian kernel to the image, which gives
more weight to pixels closer to the center of the kernel. This can be
used to reduce noise and blur the image.
● Median filter: Replaces each pixel with the median value of its
neighbors. This can be used to remove noise while preserving edges.
● Bilateral filter: Combines spatial filtering and range filtering to
preserve edges while reducing noise.
● Canny edge detector: Detects edges in an image using a multi-stage
algorithm.
Example:
C++
Mat blurred;
GaussianBlur(frame, blurred, Size(5, 5), 0); // Apply Gaussian blur
Mat edges;
Canny(blurred, edges, 100, 300); // Detect edges
Segmentation
Example:
C++
Mat gray;
cvtColor(frame, gray, COLOR_BGR2GRAY); // Convert to grayscale
Mat thresh;
threshold(gray, thresh, 127, 255, THRESH_BINARY); // Threshold the image
Feature Detection
Example:
C++
vector<KeyPoint> keypoints;
Ptr<FeatureDetector> detector = ORB::create();
detector->detect(gray, keypoints);
C++
CascadeClassifier face_cascade;
face_cascade.load("haarcascade_frontalface_default.xml");
vector<Rect> faces;
face_cascade.detectMultiScale(gray, faces, 1.3, 5);
Object Tracking
while (true) {
// ...
Rect result;
bool tracked = tracker->update(frame, result);
// ...
}
Object detection and tracking can be combined to track objects over time. First,
you can use object detection to initialize the tracker with the initial bounding box
of the object. Then, you can use the tracker to update the object's position in
subsequent frames.
Example:
C++
// Detect objects
vector<Rect> faces;
face_cascade.detectMultiScale(gray, faces, 1.3, 5);
// Track objects
for (size_t i = 0; i < trackers.size(); i++) {
Rect result;
bool tracked = trackers[i]->update(frame, result);
// ...
}
To get started with ROS, you will need to install it on your computer. You can
download the ROS installation instructions from the official ROS
website: https://www.ros.org/
Installation Steps:
To create a workspace:
Bash
cd ~/my_workspace
rosdep init
rosdep update
3. Source the setup script: Source the setup script for your ROS
distribution to add the workspace to your environment. For example:
Bash
source ~/my_workspace/devel/setup.bash
Now you are ready to create ROS packages and start developing robot
applications.
Bash
2. Add your code and resources: Place your C++ source code, Python
scripts, and other files within the package directory.
3. Build the package: Use the catkin_make command to build the
package.
Running ROS Nodes
A ROS node is an executable process that runs within the ROS system. Nodes
can communicate with each other by publishing and subscribing to topics, or by
calling services.
Bash
Nodes
Nodes are the basic building blocks of a ROS system. They are executable
processes that can publish and subscribe to topics, call and provide services, and
interact with other nodes. Nodes can be written in various programming
languages, such as C++, Python, and Java.
Topics
Messages
Messages are structured data types that are used to communicate between nodes.
They can contain a variety of information, such as sensor data, control
commands, and status updates. ROS provides a standard set of message types,
but you can also define your own custom message types.
Services
Services are remote procedure calls that can be used to request and respond to
services. Services are defined by a request and a response message. Nodes can
provide services by implementing the service definition, and other nodes can call
services by sending a request message.
Parameter Server
The parameter server is a centralized database that stores key-value pairs. These
parameters can be used to configure nodes and share data between different
components of a robot system.
Master
The master is a central server that maintains a registry of all nodes and topics in
the system. It is responsible for coordinating communication between nodes and
resolving topic names.
TF (Transform Server)
Rviz
Rviz is a 3D visualization tool that can be used to display sensor data, robot
models, and other information. It is a powerful tool for debugging and
understanding the behavior of your robot.
Gmapping
MoveIt
MoveIt is a motion planning framework that can be used to plan and execute
robot motions. It provides tools for collision checking, path planning, and
trajectory generation.
ROS provides a variety of tools and libraries that can be used to develop robot
applications. These include:
● roscore: The core process of ROS that runs the master and other
essential components.
● roslaunch: A tool for launching multiple nodes at once.
● rosbag: A tool for recording and playing back ROS data.
● rviz: A 3D visualization tool.
● rqt: A GUI-based tool for visualizing and debugging ROS data.
● roscpp: The C++ client library for ROS.
● rospy: The Python client library for ROS.
By understanding these components and tools, you can effectively develop and
deploy robot applications using ROS.
C++
#include <ros/ros.h>
#include <std_msgs/String.h>
ros::spin();
return 0;
}
Python
import rospy
from std_msgs.msg import String
def talker():
pub = rospy.Publisher('my_topic', String, queue_size=10)
rospy.init_node('my_node', anonymous=True)
rate = rospy.Rate(10) # 10hz
while not rospy.is_shutdown():
hello_str = "hello world %s" % rospy.get_time()
rospy.loginfo(hello_str)
pub.publish(hello_str)
rate.sleep()
if __name__ == '__main__':
try:
talker()
except rospy.ROSInterruptException:
pass
Example:
C++
#include <ros/ros.h>
#include <std_msgs/String.h>
ros::spin();
return 0;
}
Additional Considerations
● Node Names: Nodes should have unique names to avoid conflicts.
You can use the ros::this_node::getName() function to get the name of the
current node.
● Topic Names: Topics should also have unique names. Use
hierarchical names to organize your topics.
● Message Types: Choose the appropriate message type for the data
you want to publish or subscribe to. ROS provides a variety of
standard message types, but you can also create custom message
types.
● Queue Size: When creating a publisher or subscriber, you can specify
the queue size. This determines how many messages can be buffered
before they are discarded.
● Node Handles: A node handle is a handle to the ROS system. You
can use it to create publishers, subscribers, and services.
● ROS Spin: The ros::spin() function is used to keep the node running
and processing messages.
● ROS Logging: ROS provides a logging system that can be used to
print messages to the console. The ROS_INFO macro can be used to
print informational messages.
Example:
C++
#include <ros/ros.h>
#include <std_msgs/String.h>
ros::Rate loop_rate(10); // 10 Hz
int count = 0;
while (ros::ok()) {
std_msgs::String msg;
std::stringstream ss;
ss << "hello world " << count;
msg.data = ss.str();
ROS_INFO("%s", msg.data.c_str());
pub.publish(msg);
ros::spinOnce();
loop_rate.sleep();
++count;
}
return 0;
}
Subscribing to Messages
To subscribe to a topic, you use the subscribe() method of the NodeHandle class. This
method returns a Subscriber object that can be used to receive messages.
Example:
C++
#include <ros/ros.h>
#include <std_msgs/String.h>
ros::spin();
return 0;
}
Additional Considerations
● Message Types: You can define custom message types using the
message_generator tool or by manually editing a .msg file. For example, to
create a message type for a robot's pose, you could define a .msg file
like this:
Code snippet
geometry_msgs/Pose pose
Advanced Topics
● Asynchronous Publishing: You can use the asyncSpin() function to
spin the node asynchronously, allowing you to perform other tasks
while waiting for messages.
● Message Filtering: You can use filters to select only the messages
you are interested in. For example, you can filter messages based on
their timestamp or content.
● Topic Remapping: You can remap topics using the rosrun command or
the rosparam command. This can be useful for testing or debugging
your nodes.
● ROS Topics and Services: ROS also provides a service system that
can be used to request and respond to services. Services are like
functions that can be called by other nodes.
● ROS Parameters: ROS parameters are key-value pairs that can be
used to configure nodes and share data between different components
of a robot system. You can use the rosparam command to set and get
parameters.
Wheeled Robots
Wheeled robots are one of the most common types of robots. They are typically
used for tasks that require mobility, such as navigation, exploration, and
transportation.
● Advantages:
○ Simple and efficient design
○ Easy to control
○ Can navigate on flat surfaces
○ Cost-effective
○ Low maintenance
● Disadvantages:
○ Limited terrain capabilities
○ May be unstable on uneven surfaces
○ Not suitable for rough terrain or stairs
Legged Robots
Legged robots are designed to mimic the movement of animals. They are
typically used for tasks that require agility and adaptability to different terrains.
● Advantages:
○ Can navigate on rough terrain
○ Highly maneuverable
○ Can climb stairs and obstacles
○ Can access areas that are inaccessible to wheeled robots
● Disadvantages:
○ Complex design
○ Challenging to control
○ Higher energy consumption
○ More expensive than wheeled robots
Flying Robots
Flying robots, also known as drones or unmanned aerial vehicles (UAVs), are
used for aerial tasks such as surveillance, inspection, and delivery.
● Advantages:
○ Can access areas that are difficult or impossible for other
robots to reach
○ Can cover large areas quickly
○ Can be used for aerial photography and videography
○ Can be used for inspection and monitoring of infrastructure
● Disadvantages:
○ Require careful control to avoid collisions
○ May be subject to weather conditions
○ Can be noisy and intrusive
○ May have limited payload capacity
Types of Actuators:
Sensors are devices that measure physical quantities such as light, sound,
temperature, and motion. They are used to provide feedback to the robot's
control system.
Types of Sensors:
Additional Considerations
● Safety: Ensure that your robot is safe to operate and that it does not
pose a risk to people or property.
● Maintenance: Develop a maintenance schedule for your robot to
keep it in good working condition.
● Regulations: Be aware of any local regulations or standards that
apply to your robot.
● Cost: Keep track of the cost of components and assembly to ensure
that you stay within budget.
By following these steps and taking your time, you can successfully assemble
your robot and prepare it for testing and operation.
Chapter 6: ROS Integration
Setting up a ROS workspace
A ROS workspace is a directory that contains your ROS packages and other
project files. It provides a structured environment for organizing and managing
your ROS projects.
Creating a Workspace
Example:
Bash
mkdir ~/my_workspace
cd ~/my_workspace
catkin_init_workspace
Once you have created your workspace, you need to build it to make your
packages available to ROS.
Bash
cd ~/my_workspace
catkin_make
To make your workspace available to ROS, you need to source the setup script.
This script adds the workspace to your environment variables.
1. Source the setup script: Use the source command to source the setup
script. The location of the setup script will depend on your ROS
distribution and workspace configuration. Typically, it is located in
the devel subdirectory of your workspace.
Example:
Bash
source ~/my_workspace/devel/setup.bash
Example:
Bash
catkin_create_pkg my_package std_msgs rospy roscpp geometry_msgs
This will create a new package named my_package with dependencies on the
std_msgs, rospy, roscpp, and geometry_msgs packages.
To add an existing ROS package to your workspace, you can copy the package
directory into the src subdirectory of your workspace.
Example:
Bash
cp -r ~/other_workspace/my_other_package ~/my_workspace/src
Managing Dependencies
Example:
Bash
rosdep install --from-paths src --ignore-src --rosdistro <your_ros_distro>
Additional Tips
Using rosdep
Bash
rosdep install --from-paths src --ignore-src --rosdistro <your_ros_distro>
Replace <your_ros_distro> with the name of your ROS distribution (e.g., noetic,
melodic).
If you prefer to install packages manually, you can clone them from their
repositories. Each ROS package has its own repository on GitHub.
Example:
To install the image_transport package, you can use the following command:
Bash
cd ~/my_workspace/src
git clone https://github.com/ros-perception/image_transport.git
Once you have cloned the package, you need to build it using catkin_make.
The package.xml file in your package directory specifies the dependencies for your
package. You can list the dependencies in the <depend> tag.
Example:
XML
<package>
<name>my_package</name>
<version>0.0.0</version>
<description>Brief description of your package</description>
<author>Your Name</author>
<license>BSD</license>
<url type="repository">https://github.com/your_organization/my_package</url>
<depend>std_msgs</depend>
<depend>rospy</depend>
<depend>sensor_msgs</depend>
<depend>geometry_msgs</depend>
</package>
If you are using Ubuntu or Debian, you can also use the apt package manager to
install ROS packages. If you are using Fedora or CentOS, you can use the yum
package manager.
Example (Ubuntu):
Bash
sudo apt install ros-<your_ros_distro>-<package_name>
Example (Fedora/CentOS):
Bash
sudo yum install ros-<your_ros_distro>-<package_name>
By following these steps, you can install the required packages for your ROS
project and ensure that your workspace is set up correctly.
ROS Drivers
ROS drivers are specific packages that provide interfaces for various hardware
devices. These drivers often include functions for initializing the device, reading
data from the device, and controlling the device.
Example:
To interface with a Kinect depth camera, you can use the kinect2_bridge package.
This package provides a bridge between the Kinect driver and ROS, allowing
you to access depth and RGB data from the camera.
Using kinect2_bridge:
Bash
sudo apt install ros-<your_ros_distro>-kinect2_bridge
Bash
roslaunch kinect2_bridge kinect2_bridge.launch
If a suitable driver is not available for your hardware device, you may need to
create a custom driver. This involves writing code to communicate with the
device and publish or subscribe to ROS messages.
Example:
1. Write a C++ node: This node will handle communication with the
sensor and publish sensor data to a ROS topic.
2. Use the ros::NodeHandle class: This class provides methods for creating
publishers, subscribers, and services.
3. Use the appropriate ROS message types: For example, to publish
sensor data, you might use the sensor_msgs/Image message type.
4. Handle errors and exceptions: Your driver should be able to handle
errors and exceptions that may occur during communication with the
sensor.
ROS provides a variety of tools and libraries that can be used to interface with
hardware devices. These include:
In addition to these tools and libraries, ROS also provides a number of helpful
resources for interfacing with hardware devices. These resources include
tutorials, documentation, and community forums.
Additional Considerations
Joystick Teleoperation
C++
#include <ros/ros.h>
#include <geometry_msgs/Twist.h>
while (ros::ok()) {
// Get joystick input
int x_axis = get_joystick_axis(0); // Replace with your joystick library
int y_axis = get_joystick_axis(1);
cmd_vel_pub.publish(msg);
ros::spinOnce();
ros::Rate(10).sleep();
}
return 0;
}
Keyboard Teleoperation
A keyboard can also be used to teleoperate robots. You can define key mappings
to control the robot's movement and other functions.
Example:
C++
#include <ros/ros.h>
#include <geometry_msgs/Twist.h>
while (ros::ok()) {
char key = getchar();
geometry_msgs::Twist msg;
switch (key) {
case 'w':
msg.linear.x = 0.5;
break;
case 's':
msg.linear.x = -0.5;
break;
case 'a':
msg.angular.z = 0.5;
break;
case 'd':
msg.angular.z = -0.5;
break;
default:
break;
}
cmd_vel_pub.publish(msg);
}
return 0;
}
Additional Considerations
Line Following
Line following is a common task for robots. It involves using sensors to detect a
line and controlling the robot's movement to stay on the line.
Example:
C++
#include <ros/ros.h>
#include <sensor_msgs/Image.h>
#include <geometry_msgs/Twist.h>
// Calculate the error between the desired line position and the actual line position
// Publish a velocity command to steer the robot towards the line
}
return 0;
}
Obstacle Avoidance
Obstacle avoidance is another common task for robots. It involves using sensors
to detect obstacles and controlling the robot's movement to avoid them.
Example:
C++
#include <ros/ros.h>
#include <sensor_msgs/LaserScan.h>
#include <geometry_msgs/Twist.h>
ros::spin();
return 0;
}
Additional Tips
Object Detection
Haar Cascades:
Example:
C++
CascadeClassifier face_cascade;
face_cascade.load("haarcascade_frontalface_default.xml");
vector<Rect> faces;
face_cascade.detectMultiScale(gray_image, faces, 1.3, 5);
Example:
C++
HOGDescriptor hog;
hog.compute(gray_image, descriptors);
svm.predict(descriptors, labels);
Deep Learning:
C++
dnn::Net net = dnn::readNetFromCaffe("deploy.prototxt", "model.caffemodel");
Mat blob = dnn::blobFromImage(image, 1.0, Size(300, 300), Scalar(104, 117, 123), false);
net.setInput(blob);
vector<Mat> detections = net.forward();
// Process detections
Object Tracking
Optical Flow:
Example:
C++
Ptr<OpticalFlowTracker> tracker = OpticalFlowTracker::create();
tracker->init(prev_frame, rect);
Kalman Filter:
Example:
C++
KalmanFilter kf(4, 4, 0);
// Initialize Kalman filter parameters
Mean Shift:
Example:
C++
TermCriteria termcrit(CV_TERMCRIT_EPS | CV_TERMCRIT_ITER, 10, 1);
Mat roi;
meanshift(frame, roi, termcrit);
Additional Considerations
● Choosing the right method: The best method for object detection
and tracking depends on the specific application, object
characteristics, and available resources.
● Combining methods: You can combine different methods to improve
performance and robustness.
● Handling occlusions: Object tracking can be challenging when
objects are occluded or disappear from view.
● Real-time performance: For real-time applications, it is important to
optimize the algorithms and hardware to achieve sufficient frame
rates.
● Evaluation: Evaluate the performance of your object detection and
tracking system using appropriate metrics, such as accuracy,
precision, recall, and frame rate.
Marker-Based AR
Example:
int main() {
cv::Mat frame = cv::imread("image_with_marker.jpg");
// Create dictionary
cv::Ptr<cv::aruco::Dictionary> dictionary =
cv::aruco::getPredefinedDictionary(cv::aruco::DICT_6X6_250);
// Detect markers
std::vector<int> ids;
std::vector<std::vector<cv::Point2f>> corners;
cv::aruco::detectMarkers(frame, dictionary, corners, ids);
// Draw markers
cv::aruco::drawDetectedMarkers(frame, corners, ids);
cv::imshow("frame", frame);
cv::waitKey(0);
return 0;
}
Markerless AR
Markerless AR does not require the use of markers. Instead, it relies on features
in the environment to track the camera's pose.
Example:
Additional Considerations
● Performance: AR applications can be computationally intensive,
especially for real-time applications. Optimization techniques can be
used to improve performance.
● Hardware requirements: AR applications often require powerful
hardware, such as a GPU, to achieve real-time performance.
● User experience: The user experience is crucial for AR applications.
Consider factors such as naturalness, intuitiveness, and engagement.
● Applications: AR has a wide range of applications, including
gaming, education, healthcare, and industrial training.
Basic Concepts
A* Algorithm
The A* algorithm is a popular graph search algorithm that is often used for
pathfinding. It combines the heuristic search of the best-first search algorithm
with the complete search of Dijkstra's algorithm.
Pseudocode:
function A* (start, goal)
open_set := {start}
closed_set := {}
g_score[start] := 0
f_score[start] := g_score[start] + h(start)
while open_set is not empty
current := node in open_set with lowest f_score
if current == goal
return reconstruct_path(came_from, current)
open_set.remove(current)
closed_set.add(current)
for each neighbor in neighbors(current)
if neighbor in closed_set
continue
tentative_g_score := g_score[current] + dist(current, neighbor)
if tentative_g_score < g_score[neighbor] or neighbor not in open_set
came_from[neighbor] := current
g_score[neighbor] := tentative_g_score
f_score[neighbor] := g_score[neighbor] + h(neighbor)
if neighbor not in open_set
open_set.add(neighbor)
return failure
C++ implementation:
C++
#include <queue>
#include <vector>
#include <unordered_map>
struct Node {
int x, y;
int g_score, f_score;
};
struct CompareNode {
bool operator()(const Node& a, const Node& b) {
return a.f_score > b.f_score;
}
};
open_set.push(start);
g_score[start.x * grid[0].size() + start.y] = 0;
f_score[start.x * grid[0].size() + start.y] = g_score[start.x * grid[0].size() + start.y] + heuristic(start,
goal);
while (!open_set.empty()) {
Node current = open_set.top();
open_set.pop();
if (current == goal) {
return reconstruct_path(came_from, current);
}
if (new_x >= 0 && new_x < grid.size() && new_y >= 0 && new_y < grid[0].size() &&
grid[new_x][new_y] == 0) {
Node neighbor = {new_x, new_y};
int tentative_g_score = g_score[current.x * grid[0].size() + current.y] + 1; // Assuming each
step has a cost of 1
return {};
}
Dijkstra's Algorithm
Dijkstra's algorithm is another popular graph search algorithm that is often used
for pathfinding. It finds the shortest path between a starting node and all other
nodes in the graph.
Pseudocode:
function Dijkstra(graph, source)
dist[source] := 0
for all nodes v in graph
if v ≠ source
dist[v] := ∞
prev[v] := null
Q := {all nodes in graph}
while Q is not empty
u := node in Q with smallest dist[u]
remove u from Q
for each neighbor v of u
alt := dist[u] + length(u, v)
if alt < dist[v]
dist[v] := alt
prev[v] := u
return dist, prev
C++ implementation:
C++
// ... (similar to A* implementation, but without the heuristic function)
Additional Considerations
Common Approaches
Example:
C++
// Calculate repulsive forces from obstacles
double repulsive_force_x = 0.0;
double repulsive_force_y = 0.0;
for (int i = 0; i < obstacles.size(); i++) {
double dx = obstacles[i].x - robot_x;
double dy = obstacles[i].y - robot_y;
double distance = sqrt(dx * dx + dy * dy);
if (distance < obstacle_radius) {
repulsive_force_x += k_repulsive * dx / distance;
repulsive_force_y += k_repulsive * dy / distance;
}
}
Example:
C++
// Calculate velocity obstacles for each obstacle
vector<VelocityObstacle> velocity_obstacles;
for (int i = 0; i < obstacles.size(); i++) {
velocity_obstacles.push_back(calculate_velocity_obstacle(robot_position, obstacles[i], robot_velocity,
obstacle_velocity));
}
// Find the intersection of the velocity obstacles
VelocityObstacle intersection_velocity_obstacle = find_intersection(velocity_obstacles);
Sampling-Based Methods
Example (RRT):
C++
// Generate a random sample
Node sample = generate_random_sample();
Motion Control
Feedback Control
Feedback control involves using sensors to measure the actual output of a system
and adjusting the control signal to achieve the desired output. This helps to
compensate for disturbances and uncertainties in the system.
Example:
C++
// PID control for a robot's joint
double error = desired_angle - current_angle;
double integral_error += error * dt;
double derivative_error = (error - previous_error) / dt;
Motion control and feedback control can be combined to achieve precise and
robust control of a robot. The feedback control system can be used to measure
the actual output of the robot and adjust the control signal accordingly, while the
motion control system can be used to generate the desired output.
Example:
C++
// PID control for a robot's joint
double error = desired_angle - current_angle;
double integral_error += error * dt;
double derivative_error = (error - previous_error) / dt;
Additional Considerations
Key Concepts
Applications in Robotics
● Perception: Machine learning can be used to analyze sensor data,
such as images and lidar scans, to perceive the environment.
● Decision Making: Machine learning can be used to make decisions,
such as planning paths or selecting actions.
● Adaptation: Machine learning can enable robots to adapt to new
situations and environments.
Challenges in Robotics
● Large Datasets: Robotics often requires large datasets to train
machine learning models.
● Noise and Uncertainty: Sensor data can be noisy and uncertain,
making it challenging to train accurate models.
● Real-time Performance: Machine learning models must be able to
run in real-time to be useful in robotics applications.
● Generalization: Machine learning models must be able to generalize
to new situations and environments.
Despite these challenges, machine learning is a powerful tool that can enable
robots to perform complex tasks and adapt to new environments. By
understanding the fundamentals of machine learning, you can leverage this
technology to develop advanced robotics applications.
Key Concepts
● Agent: The entity that learns and makes decisions.
● Environment: The world the agent interacts with.
● State: The current situation of the agent.
● Action: The choices the agent can make.
● Reward: A numerical value that indicates the outcome of an action.
● Policy: A strategy that maps states to actions.
● Value Function: A function that estimates the expected future reward
from a given state.
Applications in Robotics
● Robot Manipulation: Reinforcement learning can be used to train
robots to perform complex manipulation tasks, such as grasping
objects or assembling components.
● Autonomous Navigation: Reinforcement learning can be used to
teach robots to navigate in complex environments, avoiding obstacles
and reaching their goals.
● Human-Robot Interaction: Reinforcement learning can be used to
train robots to interact with humans in a natural and intuitive way.
Challenges and Considerations
● Exploration vs. Exploitation: The agent must balance exploration
(trying new actions) and exploitation (choosing actions that have
been previously rewarded).
● Sparse Rewards: In many robotics tasks, rewards may be sparse,
making it difficult for the agent to learn.
● Sample Efficiency: Reinforcement learning can be computationally
expensive, requiring a large number of interactions with the
environment.
● Safety: Reinforcement learning agents must be trained to avoid
dangerous or harmful actions.
Example: Training a Robot to Grasp an Object
1. Define the state space: The state of the robot could be represented by
the position and orientation of its arm, as well as the position and
orientation of the object.
2. Define the action space: The actions could be the joint velocities of
the robot's arm.
3. Define the reward function: The reward could be positive for
successful grasps and negative for failed attempts or collisions.
4. Train the agent: Use a reinforcement learning algorithm, such as
DQN, to train the agent to grasp the object.
5. Evaluate the agent: Test the agent's performance on a validation
dataset.
Image Classification
Example:
Example:
Image Segmentation
Example:
Pose Estimation
Example:
A robot could use pose estimation to determine the position and orientation of a
target object.
Other Applications
By leveraging deep learning techniques, robots can perform complex tasks with
high accuracy and adaptability. As deep learning continues to advance, we can
expect to see even more innovative applications in robotics and other fields.
Part IV: Projects
Chapter 11: Line-Following Robot
Designing and building a line-following robot
Design Considerations
C++
#include <Arduino.h>
void setup() {
pinMode(leftSensorPin, INPUT);
pinMode(rightSensorPin, INPUT);
pinMode(leftMotorPin, OUTPUT);
pinMode(rightMotorPin, OUTPUT);
}
void loop() {
int leftSensorValue = analogRead(leftSensorPin);
int rightSensorValue = analogRead(rightSensorPin);
Additional Considerations
● Line Color: The line color should be chosen carefully to ensure that
the sensors can detect it accurately.
● Line Width: The line width should be appropriate for the robot's size
and sensor sensitivity.
● Lighting Conditions: The lighting conditions can affect the sensor
readings. Consider using artificial lighting if necessary.
● Obstacle Avoidance: Implement obstacle avoidance features to
prevent the robot from colliding with objects.
● Tuning: Tune the control parameters to optimize the robot's
performance.
By following these steps and considering the factors mentioned above, you can
successfully design and build a line-following robot.
Example Code:
C++
#include <opencv2/opencv.hpp>
int main() {
VideoCapture cap(0); // Open the camera
while (true) {
Mat frame;
cap >> frame; // Capture a frame
// Convert to grayscale
cvtColor(frame, frame, COLOR_BGR2GRAY);
// Find lines
vector<Vec4i> lines;
HoughLines(edges, lines, 1, CV_PI/180, 100);
// Draw lines
for (size_t i = 0; i < lines.size(); i++) {
Point p1(lines[i][0], lines[i][1]);
Point p2(lines[i][2], lines[i][3]);
line(frame, p1, p2, Scalar(0, 0, 255), 3);
}
imshow("frame", frame);
if (waitKey(1) == 27) {
break;
}
}
return 0;
}
Additional Considerations:
Steps:
Example:
C++
// Assuming the robot has two DC motors
void control_motors(double left_speed, double right_speed) {
// Set motor speeds
}
// ... (code for detecting the line)
// Control motors
control_motors(left_speed, right_speed);
Additional Considerations:
Ultrasonic Sensors
Ultrasonic sensors emit high-frequency sound waves and measure the time it
takes for the waves to return after bouncing off an object. This allows the sensor
to determine the distance to the object.
Advantages:
Disadvantages:
Infrared Sensors
Advantages:
Disadvantages:
● May be affected by environmental factors, such as sunlight or heat
sources.
● May not be able to detect objects that do not emit or reflect infrared
radiation.
LiDAR sensors use laser light to measure distance to objects. They can create a
3D map of the environment, which is useful for navigation and obstacle
avoidance.
Advantages:
Disadvantages:
● Expensive.
● May be affected by environmental factors, such as fog or dust.
Radar
Radar sensors use radio waves to detect objects. They are often used in long-
range applications, such as autonomous vehicles.
Advantages:
Disadvantages:
● Expensive.
● May be affected by interference from other radio sources.
Example:
Potential field methods create a potential field around obstacles and attract the
robot towards the goal while repelling it away from obstacles.
Example:
C++
// Calculate repulsive forces from obstacles
double repulsive_force_x = 0.0;
double repulsive_force_y = 0.0;
for (int i = 0; i < obstacles.size(); i++) {
double dx = obstacles[i].x - robot_x;
double dy = obstacles[i].y - robot_y;
double distance = sqrt(dx * dx + dy * dy);
if (distance < obstacle_radius) {
repulsive_force_x += k_repulsive * dx / distance;
repulsive_force_y += k_repulsive * dy / distance;
}
}
Sampling-Based Methods
Example (RRT):
C++
// Generate a random sample
Node sample = generate_random_sample();
// Find the nearest node in the tree
Node nearest_node = find_nearest_node(tree, sample);
Grid-Based Methods
Grid-based methods represent the environment as a grid of cells and search for a
path through the grid. This can be useful for environments with known obstacles.
Example (A algorithm):*
C++
// ... (A* algorithm implementation)
Additional Considerations
Obstacle Detection
To avoid obstacles, you need to use sensors to detect objects in the robot's path.
Common sensors for obstacle detection include:
Once you have detected obstacles, you need to adjust the robot's path to avoid
them. This can be done using a variety of techniques, such as:
C++
// Calculate repulsive forces from obstacles
double repulsive_force_x = 0.0;
double repulsive_force_y = 0.0;
for (int i = 0; i < obstacles.size(); i++) {
double dx = obstacles[i].x - robot_x;
double dy = obstacles[i].y - robot_y;
double distance = sqrt(dx * dx + dy * dy);
if (distance < obstacle_radius) {
repulsive_force_x += k_repulsive * dx / distance;
repulsive_force_y += k_repulsive * dy / distance;
}
}
// Calculate attractive force towards goal
double attractive_force_x = k_attractive * (goal_x - robot_x);
double attractive_force_y = k_attractive * (goal_y - robot_y);
Additional Considerations
SLAM Algorithms
There are various SLAM algorithms, each with its own strengths and
weaknesses. Some popular ones include:
Bash
roslaunch gmapping slam_gmapping.launch
This will launch the gmapping node, which will create a map of the environment
and publish it on the map topic.
Mission Planning
● Define the goal: Determine the desired destination or task for the
robot.
● Plan the path: Use a path planning algorithm (e.g., A*, Dijkstra's) to
find a feasible path to the goal.
● Consider obstacles and constraints: The path planning algorithm
should take into account obstacles and other constraints in the
environment.
Mission Execution
Example:
Python
import rospy
from nav_msgs.msg import Odometry
from geometry_msgs.msg import Twist
from tf.transformations import euler_from_quaternion
def odom_callback(odom_msg):
global x, y, theta
x = odom_msg.pose.pose.position.x
y = odom_msg.pose.pose.position.y
_, _, theta = euler_from_quaternion([odom_msg.pose.pose.orientation.x,
odom_msg.pose.pose.orientation.y,
odom_msg.pose.pose.orientation.z,
odom_msg.pose.pose.orientation.w])
def main():
rospy.init_node('my_robot')
odom_sub = rospy.Subscriber('/odom', Odometry, odom_callback)
cmd_vel_pub = rospy.Publisher('/cmd_vel', Twist, queue_size=10)
rospy.spin()
if __name__ == '__main__':
main()
Additional Considerations
Drone Delivery
Drone delivery is a rapidly growing field that involves using drones to transport
goods to customers. This technology has the potential to revolutionize the
logistics industry by providing faster and more efficient delivery services.
Humanoid Robots
Humanoid robots are designed to resemble humans and can perform tasks that
require human-like capabilities. These robots have a wide range of potential
applications, including healthcare, manufacturing, and entertainment.
It's been quite a journey, hasn't it? We've seen how robots can be used for
everything from exploring Mars to helping us in our daily lives. But with this
incredible potential comes great responsibility. It's important to consider the
ethical implications and societal impacts of robotics.
As we look ahead, it's clear that robotics will continue to play a vital role in our
lives. From healthcare to transportation, manufacturing to entertainment, robots
are already making a significant impact. But as technology advances, it's crucial
that we develop responsible and ethical guidelines for the development and use
of robots.