@Athena,
Thread theme: Scanning strategy
Since IR_TRACKER_v1 and OBS_SCAN_v1 share the same servo and the same IR sensor and make incompatible assumptions about sensor values, they should not run at the same time. These are both Robot Skills in my ARC project, so my intended method is:
- Let IR_TRACKER_v1 own the main loop (it will be the active robot skill controlling the IR sensor most of the time).
- Have IR_TRACKER_v1 check for $obsScanRequested == 1.
- Pause the tracker logic when a scan is requested.
- Start OBS_SCAN_v1 with controlCommand("OBS_SCAN_v1", "ScriptStartWait").
- Resume IR_TRACKER_v1 after the scanning script completes.
I think this is a clean design because it provides:
- One robot skill in control of the IR sensor at a time.
- No conflicting servo motion.
- Predictable scan behavior.
- Easier debugging.
For the initial scan before movement, I am having ROVER_CMD_v5 start OBS_SCAN_v1 by inserting it into the startup order along with UART_v7, LEG_v5, and IR_TRACKER_v1. All flags start at 0:
- setVar("$waypointSent", 0);
- setVar("$sendWaypointNow", 0);
- setVar("$obsScanRequested", 0);
- etc.
Startup sequence (ROVER_CMD_v5):
- FIRST START: controlCommand("UART_v7", "ScriptStart"); - UART is running; flags are 0. The UART script loops waiting for flag changes.
- SECOND START: controlCommand("OBS_SCAN_v1", "ScriptStartWait"); - OBS_SCAN_v1 completes the obstacle scan and either sets a global variable $obsDet = 1 or times out and issues a global variable to STOP. UART picks this up and the rover is shut down if needed.
- THIRD START: controlCommand("LEG_v5", "ScriptStartWait"); - LEG_v5 populates all waypoint data global variables and sets "$sendWaypointNow = 1". The UART loop detects this flag and sends WAYPOINT DATA to the Arduino.
- FOURTH START: controlCommand("IR_TRACKER_v1", "ScriptStart"); - from this point forward, the only flag toggled for obstacle scanning will be "$obsScanRequested". It is initially 0 and is set to 1 when the Arduino counts steps to the first scan interval.
ROVER_CMD_v5 (current script):
// ROVER_CMD_v5
controlCommand("UART_v7.1", "ScriptStart");
controlCommand("OBS_SCAN_v1", "ScriptStartWait");
// Load the leg values
controlCommand("LEG_v5", "ScriptStartWait");//scriptstartwait
// At this point LEG_v5 will have queued the waypoint transmission
print("ROVER_CMD_v5: leg loaded and UART running");
controlCommand("IR_TRACKER_v1", "ScriptStart");
What do you think of this strategy for the initial obstacle scan? The remaining obstacle scans will be handled by IR_TRACKER_v1 to avoid conflicts with the IR sensor data.
Note: It appears the Arduino is connected to ARC via a UART/serial robot skill (UART_v7) running on the PC. Also, there is a small naming ambiguity in the scripts: earlier I refer to $obsScanRequested but the UART_v7 handler sets $obstacleScanRequested on receiving an RSP_OBS_SCAN_REQUEST. I’m calling this out so we can ensure the global variable names match across Robot Skills.
If you agree with the strategy above, can you rewrite an updated Arduino_v7 that will work with the latest UART_v7 posted below?
Thank you,
Jack
UART_v7
var UART_INDEX = 0;
var UART_HEADER = 0xA5;
var CMD_STOP_MOTORS = 0x01;
var CMD_ACTUAL_BEARING = 0x03;
var CMD_RIGHT_PIVOT = 0x04;
var CMD_LEFT_PIVOT = 0x05;
var CMD_WAYPOINT_DATA = 0x06;
var RSP_NAV_ACK = 0x10;
var RSP_TOTAL_STEPS = 0x11;
var RSP_DESIRED_BEARING = 0x12;
var RSP_OBS_SCAN_REQUEST = 0x13;
var rxState = 0;
var rxCmd = 0;
var rxLen = 0;
var rxIndex = 0;
var rxPayload = [];
var rxChecksum = 0;
function safeInt(v, def) {
var n = parseInt(v);
if (isNaN(n)) return def;
return n;
}
function clamp(v, min, max) {
if (v < min) return min;
if (v > max) return max;
return v;
}
function u16ToBytes(value) {
value = value & 0xFFFF;
return [value & 0xFF, (value >> 8) & 0xFF];
}
function u32ToBytes(value) {
value = value >>> 0;
return [
value & 0xFF,
(value >> 8) & 0xFF,
(value >> 16) & 0xFF,
(value >> 24) & 0xFF
];
}
function bytesToU16LE(b0, b1) {
return (b0 & 0xFF) | ((b1 & 0xFF) << 8);
}
function bytesToU32LE(b0, b1, b2, b3) {
return ((b0 & 0xFF) |
((b1 & 0xFF) << 8) |
((b2 & 0xFF) << 16) |
((b3 & 0xFF) << 24)) >>> 0;
}
function checksumXor(cmd, len, payload) {
var c = (cmd ^ len) & 0xFF;
for (var i = 0; i < payload.length; i++) {
c ^= (payload[i] & 0xFF);
}
return c & 0xFF;
}
function sendPacket(cmd, payload) {
var checksum = checksumXor(cmd, payload.length, payload);
UART.hardwareUartWrite(UART_INDEX, [UART_HEADER, cmd, payload.length]);
if (payload.length > 0) {
UART.hardwareUartWrite(UART_INDEX, payload);
}
UART.hardwareUartWrite(UART_INDEX, [checksum]);
}
function sendStopMotors() {
sendPacket(CMD_STOP_MOTORS, []);
print("TX: STOP MOTORS");
}
function sendActualBearing() {
var bearing100 = safeInt(getVar("$actualBearing100", 9000), 9000);
bearing100 = clamp(bearing100, 0, 18000);
sendPacket(CMD_ACTUAL_BEARING, u16ToBytes(bearing100));
print("TX: ACTUAL BEARING x100 = " + bearing100);
}
function sendWaypointData() {
var pathTotalDistance = safeInt(getVar("$pathTotalDistance", 0), 0) >>> 0;
var combinedStepsDesired = safeInt(getVar("$combinedStepsDesired", 0), 0) >>> 0;
var oppositeDistance = safeInt(getVar("$oppositeDistance", 0), 0) >>> 0;
var bearingAlignment = safeInt(getVar("$bearingAlignment", 0), 0) & 0xFF;
var trackDir = safeInt(getVar("$trackDir", 0), 0);
trackDir = (trackDir === 0) ? 0 : 1;
var desiredBearing100 = clamp(safeInt(getVar("$desiredBearing100", 9000), 9000), 0, 18000);
var actualBearing100 = clamp(safeInt(getVar("$actualBearing100", 9000), 9000), 0, 18000);
var scanIntervalSteps = safeInt(getVar("$scanIntervalSteps", 3056), 3056) >>> 0;
setVar("$desiredBearing100", desiredBearing100);
setVar("$actualBearing100", actualBearing100);
var payload = [];
payload = payload.concat(u32ToBytes(pathTotalDistance));
payload = payload.concat(u32ToBytes(combinedStepsDesired));
payload = payload.concat(u32ToBytes(oppositeDistance));
payload.push(bearingAlignment);
payload = payload.concat(u16ToBytes(desiredBearing100));
payload = payload.concat(u16ToBytes(actualBearing100));
payload.push(trackDir);
payload = payload.concat(u32ToBytes(scanIntervalSteps));
if (payload.length !== 22) {
print("Waypoint payload length error: " + payload.length);
return;
}
sendPacket(CMD_WAYPOINT_DATA, payload);
print("TX: WAYPOINT DATA");
print(" pathTotalDistance=" + pathTotalDistance);
print(" combinedStepsDesired=" + combinedStepsDesired);
print(" oppositeDistance=" + oppositeDistance);
print(" bearingAlignment=" + bearingAlignment);
print(" desiredBearing100=" + desiredBearing100);
print(" actualBearing100=" + actualBearing100);
print(" trackDir=" + trackDir);
print(" scanIntervalSteps=" + scanIntervalSteps);
}
function sendRightPivot() {
var steps = safeInt(getVar("$pivotSteps", 0), 0) >>> 0;
sendPacket(CMD_RIGHT_PIVOT, u16ToBytes(steps));
print("TX: RIGHT PIVOT steps=" + steps);
}
function sendLeftPivot() {
var steps = safeInt(getVar("$pivotSteps", 0), 0) >>> 0;
sendPacket(CMD_LEFT_PIVOT, u16ToBytes(steps));
print("TX: LEFT PIVOT steps=" + steps);
}
function handleRxPacket(cmd, payload) {
if (cmd == RSP_NAV_ACK) {
print("RX: NAV ACK");
setVar("$navAckReceived", 1);
return;
}
if (cmd == RSP_TOTAL_STEPS) {
if (payload.length == 4) {
var steps = bytesToU32LE(payload[0], payload[1], payload[2], payload[3]);
setVar("$totalStepsTaken", steps);
setVar("$legComplete", 1);
print("RX: TOTAL STEPS = " + steps);
}
return;
}
if (cmd == RSP_DESIRED_BEARING) {
if (payload.length == 2) {
var bearing100 = bytesToU16LE(payload[0], payload[1]);
print("RX: DESIRED BEARING x100 = " + bearing100);
setVar("$desiredBearing100", bearing100);
}
return;
}
if (cmd == RSP_OBS_SCAN_REQUEST) {
print("RX: OBSTACLE SCAN REQUEST");
setVar("$obstacleScanRequested", 1);
return;
}
print("RX: Unknown cmd=" + cmd + " len=" + payload.length);
}
function processIncomingBytes() {
var available = UART.hardwareUartAvailable(UART_INDEX);
while (available > 0) {
var b = UART.hardwareUartRead(UART_INDEX, 1)[0] & 0xFF;
available--;
if (rxState == 0) {
if (b == UART_HEADER) rxState = 1;
continue;
}
if (rxState == 1) {
rxCmd = b;
rxChecksum = rxCmd;
rxState = 2;
continue;
}
if (rxState == 2) {
rxLen = b;
rxChecksum ^= rxLen;
rxPayload = [];
rxIndex = 0;
rxState = (rxLen == 0) ? 4 : 3;
continue;
}
if (rxState == 3) {
rxPayload.push(b);
rxChecksum ^= b;
rxIndex++;
if (rxIndex >= rxLen) rxState = 4;
continue;
}
if (rxState == 4) {
var cs = b & 0xFF;
if (cs == (rxChecksum & 0xFF)) {
handleRxPacket(rxCmd, rxPayload);
} else {
print("RX checksum error. cmd=" + rxCmd + " expected=" + (rxChecksum & 0xFF) + " got=" + cs);
}
rxState = 0;
rxCmd = 0;
rxLen = 0;
rxIndex = 0;
rxPayload = [];
rxChecksum = 0;
continue;
}
}
}
print("UART_v7 started");
while (true) {
if (getVar("$sendWaypointNow", 0) == 1 && getVar("$waypointSent", 0) == 0) {
sendWaypointData();
print("$sendWaypointNow: " + getVar("$sendWaypointNow",0));
setVar("$sendWaypointNow", 0);
setVar("$waypointSent", 1);
}
if (getVar("$sendStopNow", 0) == 1) {
sendStopMotors();
setVar("$sendStopNow", 0);
}
if (getVar("$sendActualBearingNow", 0) == 1) {
sendActualBearing();
setVar("$sendActualBearingNow", 0);
}
if (getVar("$sendRightPivotNow", 0) == 1) {
sendRightPivot();
setVar("$sendRightPivotNow", 0);
}
if (getVar("$sendLeftPivotNow", 0) == 1) {
sendLeftPivot();
setVar("$sendLeftPivotNow", 0);
}
processIncomingBytes();
sleep(20);
}
Related Hardware (view all EZB hardware)
