-
Notifications
You must be signed in to change notification settings - Fork 12
/
Copy pathros_diffdrive_robot.ino
224 lines (183 loc) · 6.11 KB
/
ros_diffdrive_robot.ino
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
/*
Arduino code to control a differential drive robot via ROS Twist messages using a NodeMcu or ESP8266
If you have questions or improvements email me at [email protected]
Launch a ros serial server to connect to:
roslaunch rosserial_server socket.launch
Launch a teleop gamepad node:
roslaunch teleop_twist_joy teleop.launch joy_config:="insert gamepad type"
MIT License
Copyright (c) 2018 Reinhard Sprung
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <math.h>
#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
#include <ros.h>
#include <geometry_msgs/Twist.h>
#define LED_BUILTIN 2 // Remapping the built-in LED since the NodeMcu apparently uses a different one.
#define LED_BUILTIN_RED 16 // If using a NodeMcu v1, then there's another red onboard led.
// The min amount of PWM the motors need to move. Depends on the battery, motors and controller.
#define PWM_MIN 300
// Due to an update of the Arduino environment, PWMRANGE is no longer defined in Arduino.h, so we have to define it ourselves. Make sure the value corresponds to your microcontroller. Arduino default is 255, while NodeMCU uses 1023 (at the time of creating this project).
#ifndef PWMRANGE
#define PWMRANGE 1023
#endif
// Declare functions
void setupPins();
void setupSerial();
void setupWiFi();
bool rosConnected();
void onTwist(const geometry_msgs::Twist &msg);
float mapPwm(float x, float out_min, float out_max);
// Pins
const uint8_t R_PWM = D1;
const uint8_t R_BACK = D2;
const uint8_t R_FORW = D3;
const uint8_t L_BACK = D5;
const uint8_t L_FORW = D6;
const uint8_t L_PWM = D7;
// Wifi
// If access point is defined, a Wifi network with this name will be created.
// Remove if you want to connect to an existing network.
#define ACCESS_POINT_SSID "SMARTCAR"
#ifndef ACCESS_POINT_SSID
ESP8266WiFiMulti wifi;
#endif
// ROS serial server
IPAddress server(192, 168, 4, 2);
ros::NodeHandle node;
ros::Subscriber<geometry_msgs::Twist> sub("/cmd_vel", &onTwist);
bool _connected = false;
void setup()
{
setupPins();
setupSerial();
setupWiFi();
// Connect to rosserial socket server and init node. (Using default port of 11411)
Serial.printf("Connecting to ROS serial server at %s\n", server.toString().c_str());
node.getHardware()->setConnection(server);
node.initNode();
node.subscribe(sub);
}
void setupPins()
{
// Status LED
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, HIGH);
pinMode(L_PWM, OUTPUT);
pinMode(L_FORW, OUTPUT);
pinMode(L_BACK, OUTPUT);
pinMode(R_PWM, OUTPUT);
pinMode(R_FORW, OUTPUT);
pinMode(R_BACK, OUTPUT);
stop();
}
void setupSerial()
{
Serial.begin(115200);
Serial.println();
}
void setupWiFi()
{
#ifdef ACCESS_POINT_SSID
WiFi.disconnect();
Serial.println("Creating Wifi network");
if (WiFi.softAP(ACCESS_POINT_SSID))
{
Serial.println("Wifi network created");
Serial.print("SSID: ");
Serial.println(WiFi.softAPSSID());
Serial.print("IP: ");
Serial.println(WiFi.softAPIP());
}
#else
WiFi.softAPdisconnect();
WiFi.disconnect();
WiFi.mode(WIFI_STA);
wifi.addAP("--wifi_1--", "--password_1--");
wifi.addAP("--wifi_2--", "--password_2--");
wifi.addAP("--wifi_3--", "--password_3--");
Serial.println("Connecting to Wifi");
while (wifi.run() != WL_CONNECTED)
{
delay(500);
Serial.print(".");
}
Serial.println("\nWiFi connected");
Serial.print("SSID: ");
Serial.println(WiFi.SSID());
Serial.print("IP: ");
Serial.println(WiFi.localIP());
#endif
}
void stop()
{
digitalWrite(L_FORW, 0);
digitalWrite(L_BACK, 0);
digitalWrite(R_FORW, 0);
digitalWrite(R_BACK, 0);
analogWrite(L_PWM, 0);
analogWrite(R_PWM, 0);
}
void onTwist(const geometry_msgs::Twist &msg)
{
if (!_connected)
{
stop();
return;
}
// Cap values at [-1 .. 1]
float x = max(min(msg.linear.x, 1.0f), -1.0f);
float z = max(min(msg.angular.z, 1.0f), -1.0f);
// Calculate the intensity of left and right wheels. Simple version.
// Taken from https://hackernoon.com/unicycle-to-differential-drive-courseras-control-of-mobile-robots-with-ros-and-rosbots-part-2-6d27d15f2010#1e59
float l = (x - z) / 2;
float r = (x + z) / 2;
// Then map those values to PWM intensities. PWMRANGE = full speed, while PWM_MIN = the minimal amount of power at which the motors begin moving.
uint16_t lPwm = mapPwm(fabs(l), PWM_MIN, PWMRANGE);
uint16_t rPwm = mapPwm(fabs(r), PWM_MIN, PWMRANGE);
// Set direction pins and PWM
digitalWrite(L_FORW, l > 0);
digitalWrite(L_BACK, l < 0);
digitalWrite(R_FORW, r > 0);
digitalWrite(R_BACK, r < 0);
analogWrite(L_PWM, lPwm);
analogWrite(R_PWM, rPwm);
}
void loop()
{
if (!rosConnected())
stop();
node.spinOnce();
}
bool rosConnected()
{
// If value changes, notify via LED and console.
bool connected = node.connected();
if (_connected != connected)
{
_connected = connected;
digitalWrite(LED_BUILTIN, !connected); // false -> on, true -> off
Serial.println(connected ? "ROS connected" : "ROS disconnected");
}
return connected;
}
// Map x value from [0 .. 1] to [out_min .. out_max]
float mapPwm(float x, float out_min, float out_max)
{
return x * (out_max - out_min) + out_min;
}